Asterisk - The Open Source Telephony Project  18.5.0
say.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <[email protected]>
7  * George Konstantoulakis <[email protected]>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*! \file
21  *
22  * \brief Say numbers and dates (maybe words one day too)
23  *
24  * \author Mark Spencer <[email protected]>
25  *
26  * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <[email protected]>
27  *
28  * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <[email protected]>,
29  * Next Generation Networks (NGN).
30  * \note 2007-03-20 : Support for Thai added by Dome C. <[email protected]>,
31  * IP Crossing Co., Ltd.
32  * \note 2021-07-26 : Refactoring to separate string buildup and playback
33  * by Naveen Albert <[email protected]>
34  */
35 
36 /*** MODULEINFO
37  <support_level>core</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 #include <netinet/in.h>
43 #include <time.h>
44 #include <ctype.h>
45 #include <math.h>
46 
47 #ifdef SOLARIS
48 #include <iso/limits_iso.h>
49 #endif
50 
51 #include "asterisk/file.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/say.h"
54 #include "asterisk/lock.h"
55 #include "asterisk/localtime.h"
56 #include "asterisk/utils.h"
57 #include "asterisk/app.h"
58 #include "asterisk/test.h"
59 
60 /* Forward declaration */
61 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
62 
63 struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {
64  const char *fn;
65  char fnbuf[10], asciibuf[20] = "letters/ascii";
66  char ltr;
67  int num = 0;
68  int res = 0;
69  int upper = 0;
70  int lower = 0;
71 
72  struct ast_str *filenames = ast_str_create(20);
73  ast_str_reset(filenames);
74 
75  while (str[num] && !res) {
76  fn = NULL;
77  switch (str[num]) {
78  case ('*'):
79  fn = "digits/star";
80  break;
81  case ('#'):
82  fn = "digits/pound";
83  break;
84  case ('!'):
85  fn = "letters/exclaimation-point";
86  break;
87  case ('@'):
88  fn = "letters/at";
89  break;
90  case ('$'):
91  fn = "letters/dollar";
92  break;
93  case ('-'):
94  fn = "letters/dash";
95  break;
96  case ('.'):
97  fn = "letters/dot";
98  break;
99  case ('='):
100  fn = "letters/equals";
101  break;
102  case ('+'):
103  fn = "letters/plus";
104  break;
105  case ('/'):
106  fn = "letters/slash";
107  break;
108  case (' '):
109  fn = "letters/space";
110  break;
111  case ('0'):
112  case ('1'):
113  case ('2'):
114  case ('3'):
115  case ('4'):
116  case ('5'):
117  case ('6'):
118  case ('7'):
119  case ('8'):
120  case ('9'):
121  strcpy(fnbuf, "digits/X");
122  fnbuf[7] = str[num];
123  fn = fnbuf;
124  break;
125  default:
126  ltr = str[num];
127  if ('A' <= ltr && ltr <= 'Z') {
128  ltr += 'a' - 'A'; /* file names are all lower-case */
129  switch (sensitivity) {
130  case AST_SAY_CASE_UPPER:
131  case AST_SAY_CASE_ALL:
132  upper = !upper;
133  case AST_SAY_CASE_LOWER:
134  case AST_SAY_CASE_NONE:
135  break;
136  }
137  } else if ('a' <= ltr && ltr <= 'z') {
138  switch (sensitivity) {
139  case AST_SAY_CASE_LOWER:
140  case AST_SAY_CASE_ALL:
141  lower = !lower;
142  case AST_SAY_CASE_UPPER:
143  case AST_SAY_CASE_NONE:
144  break;
145  }
146  }
147 
148  if (upper) {
149  strcpy(fnbuf, "uppercase");
150  } else if (lower) {
151  strcpy(fnbuf, "lowercase");
152  } else {
153  strcpy(fnbuf, "letters/X");
154  fnbuf[8] = ltr;
155  }
156  fn = fnbuf;
157  }
158  if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
159  (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
160  ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
161  }
162  if (upper || lower) {
163  continue;
164  }
165  num++;
166  }
167 
168  return filenames;
169 }
170 
171 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)
172 {
173  const char *fn;
174  int res = 0;
175 
176  struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);
177  char *files = ast_str_buffer(filenames);
178 
179  while ((fn = strsep(&files, "&"))) {
180  res = ast_streamfile(chan, fn, lang);
181  if (!res) {
182  if ((audiofd > -1) && (ctrlfd > -1))
183  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
184  else
185  res = ast_waitstream(chan, ints);
186  }
187  ast_stopstream(chan);
188  }
189 
190  ast_free(filenames);
191 
192  return res;
193 }
194 
195 struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)
196 {
197  const char *fn;
198  char fnbuf[256];
199  char ltr;
200  int num = 0;
201 
202  struct ast_str *filenames = ast_str_create(20);
203  ast_str_reset(filenames);
204 
205  while (str[num]) {
206  fn = NULL;
207  switch (str[num]) {
208  case ('*'):
209  fn = "digits/star";
210  break;
211  case ('#'):
212  fn = "digits/pound";
213  break;
214  case ('!'):
215  fn = "letters/exclaimation-point";
216  break;
217  case ('@'):
218  fn = "letters/at";
219  break;
220  case ('$'):
221  fn = "letters/dollar";
222  break;
223  case ('-'):
224  fn = "letters/dash";
225  break;
226  case ('.'):
227  fn = "letters/dot";
228  break;
229  case ('='):
230  fn = "letters/equals";
231  break;
232  case ('+'):
233  fn = "letters/plus";
234  break;
235  case ('/'):
236  fn = "letters/slash";
237  break;
238  case (' '):
239  fn = "letters/space";
240  break;
241  case ('0'):
242  case ('1'):
243  case ('2'):
244  case ('3'):
245  case ('4'):
246  case ('5'):
247  case ('6'):
248  case ('7'):
249  case ('8'):
250  strcpy(fnbuf, "digits/X");
251  fnbuf[7] = str[num];
252  fn = fnbuf;
253  break;
254  default: /* '9' falls here... */
255  ltr = str[num];
256  if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
257  strcpy(fnbuf, "phonetic/X_p");
258  fnbuf[9] = ltr;
259  fn = fnbuf;
260  }
261  if (fn && ast_fileexists(fn, NULL, lang) > 0) {
262  ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
263  }
264  num++;
265  }
266 
267  return filenames;
268 }
269 
270 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
271 {
272  const char *fn;
273  int res = 0;
274 
275  struct ast_str *filenames = ast_get_phonetic_str(str, lang);
276  char *files = ast_str_buffer(filenames);
277 
278  while ((fn = strsep(&files, "&"))) {
279  res = ast_streamfile(chan, fn, lang);
280  if (!res) {
281  if ((audiofd > -1) && (ctrlfd > -1))
282  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
283  else
284  res = ast_waitstream(chan, ints);
285  }
286  ast_stopstream(chan);
287  }
288 
289  ast_free(filenames);
290 
291  return res;
292 }
293 
294 struct ast_str* ast_get_digit_str(const char *str, const char *lang)
295 {
296  const char *fn;
297  char fnbuf[256];
298  int num = 0;
299 
300  struct ast_str *filenames = ast_str_create(20);
301  ast_str_reset(filenames);
302 
303  while (str[num]) {
304  fn = NULL;
305  switch (str[num]) {
306  case ('*'):
307  fn = "digits/star";
308  break;
309  case ('#'):
310  fn = "digits/pound";
311  break;
312  case ('-'):
313  fn = "digits/minus";
314  break;
315  case '0':
316  case '1':
317  case '2':
318  case '3':
319  case '4':
320  case '5':
321  case '6':
322  case '7':
323  case '8':
324  case '9':
325  strcpy(fnbuf, "digits/X");
326  fnbuf[7] = str[num];
327  fn = fnbuf;
328  break;
329  }
330  if (fn && ast_fileexists(fn, NULL, lang) > 0) {
331  ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
332  }
333  num++;
334  }
335 
336  return filenames;
337 }
338 
339 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
340 {
341  const char *fn;
342  int res = 0;
343 
344  struct ast_str *filenames = ast_get_digit_str(str, lang);
345  char *files = ast_str_buffer(filenames);
346 
347  while ((fn = strsep(&files, "&"))) {
348  res = ast_streamfile(chan, fn, lang);
349  if (!res) {
350  if ((audiofd > -1) && (ctrlfd > -1))
351  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
352  else
353  res = ast_waitstream(chan, ints);
354  }
355  ast_stopstream(chan);
356  }
357 
358  ast_free(filenames);
359 
360  return res;
361 }
362 
363 static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)
364 {
365  const char *fnr;
366 
367  double dollars = 0;
368  int amt, cents;
369  struct ast_str *fnrecurse;
370 
371  struct ast_str *filenames = ast_str_create(20);
372  ast_str_reset(filenames);
373 
374  if (sscanf(str, "%30lf", &dollars) != 1) {
375  amt = 0;
376  } else { /* convert everything to cents */
377  amt = dollars * 100;
378  }
379 
380  /* Just the cents after the dollar decimal point */
381  cents = amt - (((int) dollars) * 100);
382  ast_debug(1, "Cents is %d, amount is %d\n", cents, amt);
383 
384  if (amt >= 100) {
385  fnrecurse = ast_get_number_str((amt / 100), lang);
386  fnr = ast_str_buffer(fnrecurse);
387  ast_str_append(&filenames, 0, "%s", fnr);
388 
389  /* If this is it, end on a down pitch, otherwise up pitch */
390  if (amt < 200) {
391  ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");
392  } else {
393  ast_str_append(&filenames, 0, "&%s", "dollars");
394  }
395 
396  /* If dollars and cents, add "and" in the middle */
397  if (cents > 0) {
398  ast_str_append(&filenames, 0, "&%s", "and");
399  }
400  }
401 
402  if (cents > 0) {
403  ast_debug(1, "Entered cents block\n");
404  fnrecurse = ast_get_number_str(cents, lang);
405  fnr = ast_str_buffer(fnrecurse);
406  ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);
407  ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");
408  } else if (amt == 0) {
409  fnrecurse = ast_get_digit_str("0", lang);
410  fnr = ast_str_buffer(fnrecurse);
411  ast_str_append(&filenames, 0, "%s", fnr);
412  ast_str_append(&filenames, 0, "&%s", "cents");
413  }
414 
415  return filenames;
416 }
417 
418 /*! \brief ast_get_money_str: call language-specific functions */
419 struct ast_str* ast_get_money_str(const char *str, const char *lang)
420 {
421  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
422  return ast_get_money_en_dollars_str(str, lang);
423  }
424 
425  ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);
426  /* Default to english */
427  return ast_get_money_en_dollars_str(str, lang);
428 }
429 
430 static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
431 {
432  const char *fn;
433  int res = 0;
434 
435  struct ast_str *filenames = ast_get_money_str(str, lang);
436  char *files = ast_str_buffer(filenames);
437 
438  while ((fn = strsep(&files, "&"))) {
439  res = ast_streamfile(chan, fn, lang);
440  if (!res) {
441  if ((audiofd > -1) && (ctrlfd > -1))
442  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
443  else
444  res = ast_waitstream(chan, ints);
445  }
446  ast_stopstream(chan);
447  }
448 
449  ast_free(filenames);
450 
451  return res;
452 }
453 
454 static struct ast_str* get_number_str_en(int num, const char *lang)
455 {
456  const char *fnr;
457  int loops = 0;
458 
459  int res = 0;
460  int playh = 0;
461  char fn[256] = "";
462 
463  struct ast_str *filenames;
464 
465  if (!num)
466  return ast_get_digit_str("0", lang);
467 
468  filenames = ast_str_create(20);
469  ast_str_reset(filenames);
470 
471  while (!res && (num || playh)) {
472  if (num < 0) {
473  ast_copy_string(fn, "digits/minus", sizeof(fn));
474  if ( num > INT_MIN ) {
475  num = -num;
476  } else {
477  num = 0;
478  }
479  } else if (playh) {
480  ast_copy_string(fn, "digits/hundred", sizeof(fn));
481  playh = 0;
482  } else if (num < 20) {
483  snprintf(fn, sizeof(fn), "digits/%d", num);
484  num = 0;
485  } else if (num < 100) {
486  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
487  num %= 10;
488  } else {
489  if (num < 1000){
490  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
491  playh++;
492  num %= 100;
493  } else {
494  struct ast_str *fnrecurse;
495  if (num < 1000000) { /* 1,000,000 */
496  fnrecurse = ast_get_number_str((num / 1000), lang);
497  fnr = ast_str_buffer(fnrecurse);
498  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
499  num %= 1000;
500  snprintf(fn, sizeof(fn), "&digits/thousand");
501  } else {
502  if (num < 1000000000) { /* 1,000,000,000 */
503  fnrecurse = ast_get_number_str((num / 1000000), lang);
504  fnr = ast_str_buffer(fnrecurse);
505  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
506  num %= 1000000;
507  ast_copy_string(fn, "&digits/million", sizeof(fn));
508  } else {
509  ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
510  res = -1;
511  }
512  }
513  }
514  }
515  if (!res) {
516  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
517  loops++;
518  }
519  }
520 
521  return filenames;
522 }
523 
524 /*! \brief ast_get_number_str: call language-specific functions */
525 struct ast_str* ast_get_number_str(int num, const char *lang)
526 {
527  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
528  return get_number_str_en(num, lang);
529  }
530 
531  ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
532  /* Default to english */
533  return get_number_str_en(num, lang);
534 }
535 
536 /* Forward declarations */
537 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
538  \note Not really language codes.
539  For these language codes, Asterisk will change the syntax when
540  saying numbers (and in some cases dates and voicemail messages
541  as well)
542  \arg \b da - Danish
543  \arg \b de - German
544  \arg \b en - English (US)
545  \arg \b en_GB - English (British)
546  \arg \b es - Spanish, Mexican
547  \arg \b fr - French
548  \arg \b he - Hebrew
549  \arg \b is - Icelandic
550  \arg \b it - Italian
551  \arg \b nl - Dutch
552  \arg \b no - Norwegian
553  \arg \b pl - Polish
554  \arg \b pt - Portuguese
555  \arg \b pt_BR - Portuguese (Brazil)
556  \arg \b se - Swedish
557  \arg \b zh - Taiwanese / Chinese
558  \arg \b ru - Russian
559  \arg \b ka - Georgian
560  \arg \b hu - Hungarian
561 
562  \par Gender:
563  For some languages the numbers differ for gender of the countable object.
564  Commonly for "one", like "un"/"une" in French. Note that the interface
565  is somewhat peculiar, as differing languages can have conflicting
566  genders.
567  \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
568  \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
569 
570  Date/Time functions currently have less languages supported than saynumber().
571 
572  \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
573 
574  See contrib/i18n.testsuite.conf for some examples of the different syntaxes
575 
576  \par Portuguese
577  Portuguese sound files needed for Time/Date functions:
578  pt-ah
579  pt-ao
580  pt-de
581  pt-e
582  pt-ora
583  pt-meianoite
584  pt-meiodia
585  pt-sss
586 
587  \par Spanish
588  Spanish sound files needed for Time/Date functions:
589  es-de
590  es-el
591 
592  \par Italian
593  Italian sound files needed for Time/Date functions:
594  ore-una
595  ore-mezzanotte
596 
597 */
598 
599 /* Forward declarations of language specific variants of ast_say_number_full */
600 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
601 static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
602 static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
603 static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
604 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
605 static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
606 static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
607 static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
608 static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
609 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
610 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
611 static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
612 static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
613 static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
614 static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
615 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
616 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
617 static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
618 static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
619 static int ast_say_number_full_ka(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
620 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
621 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
622 static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
623 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
624 
625 /* Forward declarations of language specific variants of ast_say_enumeration_full */
626 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
627 static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
628 static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
629 static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
630 static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
631 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
632 
633 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
634 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
635 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
636 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
637 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
638 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
639 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
640 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
641 static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
642 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
643 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
644 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
645 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
646 static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
647 
648 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
649 static int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
650 static int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
651 static int ast_say_date_with_format_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
652 static int ast_say_date_with_format_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
653 static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
654 static int ast_say_date_with_format_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
655 static int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
656 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
657 static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
658 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
659 static int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
660 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
661 static int ast_say_date_with_format_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
662 static int ast_say_date_with_format_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
663 static int ast_say_date_with_format_vi(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
664 
665 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
666 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
667 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
668 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
669 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
670 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
671 static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
672 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
673 static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
674 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
675 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
676 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
677 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
678 
679 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
680 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
681 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
682 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
683 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
684 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
685 static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
686 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
687 static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
688 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
689 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
690 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
691 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
692 
693 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
694 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
695 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
696 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
697 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
698 
699 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
700 {
701  int res;
702  if ((res = ast_streamfile(chan, file, lang))) {
703  ast_log(LOG_WARNING, "Unable to play message %s\n", file);
704  }
705  if (!res) {
706  res = ast_waitstream(chan, ints);
707  }
708  return res;
709 }
710 
711 /*! \brief ast_say_number_full: call language-specific functions
712  \note Called from AGI */
713 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
714 {
715  ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
716  if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
717  return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
718  } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
719  return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
720  } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
721  return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
722  } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
723  return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
724  } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
725  return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
726  } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
727  return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
728  } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
729  return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
730  } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
731  return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
732  } else if (!strncasecmp(language, "ja", 2)) { /* Japanese syntax */
733  return ast_say_number_full_ja(chan, num, ints, language, audiofd, ctrlfd);
734  } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
735  return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
736  } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
737  return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
738  } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
739  return ast_say_number_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
740  } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
741  return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
742  } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
743  return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
744  } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
745  return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
746  } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
747  return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
748  } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
749  return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
750  } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
751  return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
752  } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
753  return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
754  } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
755  return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
756  } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
757  return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
758  } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
759  return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
760  } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
761  return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
762  } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
763  return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
764  }
765 
766  /* Default to english */
767  return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
768 }
769 
770 /*! \brief ast_say_number_full_en: English syntax
771  \note This is the default syntax, if no other syntax defined in this file is used */
772 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
773 {
774  const char *fn;
775  int res = 0;
776 
777  struct ast_str *filenames = ast_get_number_str(num, language);
778  char *files = ast_str_buffer(filenames);
779 
780  while ((fn = strsep(&files, "&"))) {
781  res = ast_streamfile(chan, fn, language);
782  if (!res) {
783  if ((audiofd > -1) && (ctrlfd > -1))
784  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
785  else
786  res = ast_waitstream(chan, ints);
787  }
788  ast_stopstream(chan);
789  }
790 
791  ast_free(filenames);
792 
793  return res;
794 }
795 
796 static int exp10_int(int power)
797 {
798  int x, res= 1;
799  for (x=0;x<power;x++)
800  res *= 10;
801  return res;
802 }
803 
804 /*! \brief ast_say_number_full_cs: Czech syntax
805  *
806  * files needed:
807  * - 1m,2m - gender male
808  * - 1w,2w - gender female
809  * - 3,4,...,20
810  * - 30,40,...,90
811  *
812  * - hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
813  *
814  * for each number 10^(3n + 3) exist 3 files represented as:
815  * 1 tousand = jeden tisic = 1_E3
816  * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
817  * 5,6,... tousands = pet,sest,... tisic = 5_E3
818  *
819  * million = _E6
820  * miliard = _E9
821  * etc...
822  *
823  * tousand, milion are gender male, so 1 and 2 is 1m 2m
824  * miliard is gender female, so 1 and 2 is 1w 2w
825  */
826 static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
827 {
828  int res = 0;
829  int playh = 0;
830  char fn[256] = "";
831 
832  int hundered = 0;
833  int left = 0;
834  int length = 0;
835 
836  /* options - w = woman, m = man, n = neutral. Defaultl is woman */
837  if (!options)
838  options = "w";
839 
840  if (!num)
841  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
842 
843  while (!res && (num || playh)) {
844  if (num < 0) {
845  ast_copy_string(fn, "digits/minus", sizeof(fn));
846  if ( num > INT_MIN ) {
847  num = -num;
848  } else {
849  num = 0;
850  }
851  } else if (num < 3 ) {
852  snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
853  playh = 0;
854  num = 0;
855  } else if (num < 20) {
856  snprintf(fn, sizeof(fn), "digits/%d", num);
857  playh = 0;
858  num = 0;
859  } else if (num < 100) {
860  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
861  num %= 10;
862  } else if (num < 1000) {
863  hundered = num / 100;
864  if ( hundered == 1 ) {
865  ast_copy_string(fn, "digits/1sto", sizeof(fn));
866  } else if ( hundered == 2 ) {
867  ast_copy_string(fn, "digits/2ste", sizeof(fn));
868  } else {
869  res = ast_say_number_full_cs(chan, hundered, ints, language, options, audiofd, ctrlfd);
870  if (res)
871  return res;
872  if (hundered == 3 || hundered == 4) {
873  ast_copy_string(fn, "digits/sta", sizeof(fn));
874  } else if ( hundered > 4 ) {
875  ast_copy_string(fn, "digits/set", sizeof(fn));
876  }
877  }
878  num -= (hundered * 100);
879  } else { /* num > 1000 */
880  length = (int)log10(num)+1;
881  while ( (length % 3 ) != 1 ) {
882  length--;
883  }
884  left = num / (exp10_int(length-1));
885  if ( left == 2 ) {
886  switch (length-1) {
887  case 9: options = "w"; /* 1,000,000,000 gender female */
888  break;
889  default : options = "m"; /* others are male */
890  }
891  }
892  if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
893  res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
894  if (res)
895  return res;
896  }
897  if ( left >= 5 ) { /* >= 5 have the same declesion */
898  snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
899  } else if ( left >= 2 && left <= 4 ) {
900  snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
901  } else { /* left == 1 */
902  snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
903  }
904  num -= left * (exp10_int(length-1));
905  }
906  if (!res) {
907  if (!ast_streamfile(chan, fn, language)) {
908  if ((audiofd > -1) && (ctrlfd > -1)) {
909  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
910  } else {
911  res = ast_waitstream(chan, ints);
912  }
913  }
914  ast_stopstream(chan);
915  }
916  }
917  return res;
918 }
919 
920 /*! \brief ast_say_number_full_da: Danish syntax
921  New files:
922  - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
923  */
924 static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
925 {
926  int res = 0;
927  int playh = 0;
928  int playa = 0;
929  int cn = 1; /* +1 = commune; -1 = neuter */
930  char fn[256] = "";
931  if (!num)
932  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
933 
934  if (options && !strncasecmp(options, "n", 1)) cn = -1;
935 
936  while (!res && (num || playh || playa )) {
937  /* The grammar for Danish numbers is the same as for English except
938  * for the following:
939  * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
940  * - numbers 20 through 99 are said in reverse order, i.e. 21 is
941  * "one-and twenty" and 68 is "eight-and sixty".
942  * - "million" is different in singular and plural form
943  * - numbers > 1000 with zero as the third digit from last have an
944  * "and" before the last two digits, i.e. 2034 is "two thousand and
945  * four-and thirty" and 1000012 is "one million and twelve".
946  */
947  if (num < 0) {
948  ast_copy_string(fn, "digits/minus", sizeof(fn));
949  if ( num > INT_MIN ) {
950  num = -num;
951  } else {
952  num = 0;
953  }
954  } else if (playh) {
955  ast_copy_string(fn, "digits/hundred", sizeof(fn));
956  playh = 0;
957  } else if (playa) {
958  ast_copy_string(fn, "digits/and", sizeof(fn));
959  playa = 0;
960  } else if (num == 1 && cn == -1) {
961  ast_copy_string(fn, "digits/1N", sizeof(fn));
962  num = 0;
963  } else if (num < 20) {
964  snprintf(fn, sizeof(fn), "digits/%d", num);
965  num = 0;
966  } else if (num < 100) {
967  int ones = num % 10;
968  if (ones) {
969  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
970  num -= ones;
971  } else {
972  snprintf(fn, sizeof(fn), "digits/%d", num);
973  num = 0;
974  }
975  } else {
976  if (num < 1000) {
977  int hundreds = num / 100;
978  if (hundreds == 1)
979  ast_copy_string(fn, "digits/1N", sizeof(fn));
980  else
981  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
982 
983  playh++;
984  num -= 100 * hundreds;
985  if (num)
986  playa++;
987 
988  } else {
989  if (num < 1000000) {
990  res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
991  if (res)
992  return res;
993  num = num % 1000;
994  ast_copy_string(fn, "digits/thousand", sizeof(fn));
995  } else {
996  if (num < 1000000000) {
997  int millions = num / 1000000;
998  res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
999  if (res)
1000  return res;
1001  if (millions == 1)
1002  ast_copy_string(fn, "digits/million", sizeof(fn));
1003  else
1004  ast_copy_string(fn, "digits/millions", sizeof(fn));
1005  num = num % 1000000;
1006  } else {
1007  ast_debug(1, "Number '%d' is too big for me\n", num);
1008  res = -1;
1009  }
1010  }
1011  if (num && num < 100)
1012  playa++;
1013  }
1014  }
1015  if (!res) {
1016  if (!ast_streamfile(chan, fn, language)) {
1017  if ((audiofd > -1) && (ctrlfd > -1))
1018  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1019  else
1020  res = ast_waitstream(chan, ints);
1021  }
1022  ast_stopstream(chan);
1023  }
1024  }
1025  return res;
1026 }
1027 
1028 /*! \brief ast_say_number_full_de: German syntax
1029 
1030  New files:
1031  In addition to English, the following sounds are required:
1032  - "millions"
1033  - "1-and" through "9-and"
1034  - "1F" (eine)
1035  - "1N" (ein)
1036  - NB "1" is recorded as 'eins'
1037  */
1038 static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1039 {
1040  int res = 0, t = 0;
1041  int mf = 1; /* +1 = male and neuter; -1 = female */
1042  char fn[256] = "";
1043  char fna[256] = "";
1044  if (!num)
1045  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1046 
1047  if (options && (!strncasecmp(options, "f", 1)))
1048  mf = -1;
1049 
1050  while (!res && num) {
1051  /* The grammar for German numbers is the same as for English except
1052  * for the following:
1053  * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1054  * "one-and twenty" and 68 is "eight-and sixty".
1055  * - "one" varies according to gender
1056  * - 100 is 'hundert', however all other instances are 'ein hundert'
1057  * - 1000 is 'tausend', however all other instances are 'ein tausend'
1058  * - 1000000 is always 'eine million'
1059  * - "million" is different in singular and plural form
1060  * - 'and' should not go between a hundreds place value and any
1061  * tens/ones place values that follows it. i.e 136 is ein hundert
1062  * sechs und dreizig, not ein hundert und sechs und dreizig.
1063  */
1064  if (num < 0) {
1065  ast_copy_string(fn, "digits/minus", sizeof(fn));
1066  if ( num > INT_MIN ) {
1067  num = -num;
1068  } else {
1069  num = 0;
1070  }
1071  } else if (num == 1 && mf == -1) {
1072  snprintf(fn, sizeof(fn), "digits/%dF", num);
1073  num = 0;
1074  } else if (num < 20) {
1075  snprintf(fn, sizeof(fn), "digits/%d", num);
1076  num = 0;
1077  } else if (num < 100) {
1078  int ones = num % 10;
1079  if (ones) {
1080  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1081  num -= ones;
1082  } else {
1083  snprintf(fn, sizeof(fn), "digits/%d", num);
1084  num = 0;
1085  }
1086  } else if (num == 100 && t == 0) {
1087  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1088  num = 0;
1089  } else if (num < 1000) {
1090  int hundreds = num / 100;
1091  num = num % 100;
1092  if (hundreds == 1) {
1093  ast_copy_string(fn, "digits/1N", sizeof(fn));
1094  } else {
1095  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1096  }
1097  ast_copy_string(fna, "digits/hundred", sizeof(fna));
1098  } else if (num == 1000 && t == 0) {
1099  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1100  num = 0;
1101  } else if (num < 1000000) {
1102  int thousands = num / 1000;
1103  num = num % 1000;
1104  t = 1;
1105  if (thousands == 1) {
1106  ast_copy_string(fn, "digits/1N", sizeof(fn));
1107  ast_copy_string(fna, "digits/thousand", sizeof(fna));
1108  } else {
1109  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
1110  if (res)
1111  return res;
1112  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1113  }
1114  } else if (num < 1000000000) {
1115  int millions = num / 1000000;
1116  num = num % 1000000;
1117  t = 1;
1118  if (millions == 1) {
1119  ast_copy_string(fn, "digits/1F", sizeof(fn));
1120  ast_copy_string(fna, "digits/million", sizeof(fna));
1121  } else {
1122  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
1123  if (res)
1124  return res;
1125  ast_copy_string(fn, "digits/millions", sizeof(fn));
1126  }
1127  } else if (num <= INT_MAX) {
1128  int billions = num / 1000000000;
1129  num = num % 1000000000;
1130  t = 1;
1131  if (billions == 1) {
1132  ast_copy_string(fn, "digits/1F", sizeof(fn));
1133  ast_copy_string(fna, "digits/milliard", sizeof(fna));
1134  } else {
1135  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
1136  if (res) {
1137  return res;
1138  }
1139  ast_copy_string(fn, "digits/milliards", sizeof(fn));
1140  }
1141  } else {
1142  ast_debug(1, "Number '%d' is too big for me\n", num);
1143  res = -1;
1144  }
1145  if (!res) {
1146  if (!ast_streamfile(chan, fn, language)) {
1147  if ((audiofd > -1) && (ctrlfd > -1))
1148  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1149  else
1150  res = ast_waitstream(chan, ints);
1151  }
1152  ast_stopstream(chan);
1153  if (!res) {
1154  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
1155  if ((audiofd > -1) && (ctrlfd > -1))
1156  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1157  else
1158  res = ast_waitstream(chan, ints);
1159  }
1160  ast_stopstream(chan);
1161  strcpy(fna, "");
1162  }
1163  }
1164  }
1165  return res;
1166 }
1167 
1168 /*! \brief ast_say_number_full_en_GB: British syntax
1169  New files:
1170  - In addition to American English, the following sounds are required: "vm-and"
1171  */
1172 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1173 {
1174  int res = 0;
1175  int playh = 0;
1176  int playa = 0;
1177  char fn[256] = "";
1178  if (!num)
1179  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1180 
1181  while (!res && (num || playh || playa )) {
1182  if (num < 0) {
1183  ast_copy_string(fn, "digits/minus", sizeof(fn));
1184  if ( num > INT_MIN ) {
1185  num = -num;
1186  } else {
1187  num = 0;
1188  }
1189  } else if (playh) {
1190  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1191  playh = 0;
1192  } else if (playa) {
1193  ast_copy_string(fn, "vm-and", sizeof(fn));
1194  playa = 0;
1195  } else if (num < 20) {
1196  snprintf(fn, sizeof(fn), "digits/%d", num);
1197  num = 0;
1198  } else if (num < 100) {
1199  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1200  num %= 10;
1201  } else if (num < 1000) {
1202  int hundreds = num / 100;
1203  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1204 
1205  playh++;
1206  num -= 100 * hundreds;
1207  if (num)
1208  playa++;
1209  } else if (num < 1000000) {
1210  res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
1211  if (res)
1212  return res;
1213  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1214  num %= 1000;
1215  if (num && num < 100)
1216  playa++;
1217  } else if (num < 1000000000) {
1218  int millions = num / 1000000;
1219  res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1220  if (res)
1221  return res;
1222  ast_copy_string(fn, "digits/million", sizeof(fn));
1223  num %= 1000000;
1224  if (num && num < 100)
1225  playa++;
1226  } else {
1227  ast_debug(1, "Number '%d' is too big for me\n", num);
1228  res = -1;
1229  }
1230 
1231  if (!res) {
1232  if (!ast_streamfile(chan, fn, language)) {
1233  if ((audiofd > -1) && (ctrlfd > -1))
1234  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1235  else
1236  res = ast_waitstream(chan, ints);
1237  }
1238  ast_stopstream(chan);
1239  }
1240  }
1241  return res;
1242 }
1243 
1244 /*! \brief ast_say_number_full_es: Spanish syntax
1245 
1246  New files:
1247  Requires a few new audios:
1248  1F.gsm: feminine 'una'
1249  21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm
1250  */
1251 static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1252 {
1253  int res = 0;
1254  int playa = 0;
1255  int mf = 0; /* +1 = male; -1 = female */
1256  char fn[256] = "";
1257  if (!num)
1258  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1259 
1260  if (options) {
1261  if (!strncasecmp(options, "f", 1))
1262  mf = -1;
1263  else if (!strncasecmp(options, "m", 1))
1264  mf = 1;
1265  }
1266 
1267  while (!res && num) {
1268  if (num < 0) {
1269  ast_copy_string(fn, "digits/minus", sizeof(fn));
1270  if ( num > INT_MIN ) {
1271  num = -num;
1272  } else {
1273  num = 0;
1274  }
1275  } else if (playa) {
1276  ast_copy_string(fn, "digits/and", sizeof(fn));
1277  playa = 0;
1278  } else if (num == 1) {
1279  if (mf < 0)
1280  snprintf(fn, sizeof(fn), "digits/%dF", num);
1281  else if (mf > 0)
1282  snprintf(fn, sizeof(fn), "digits/%dM", num);
1283  else
1284  snprintf(fn, sizeof(fn), "digits/%d", num);
1285  num = 0;
1286  } else if (num < 31) {
1287  snprintf(fn, sizeof(fn), "digits/%d", num);
1288  num = 0;
1289  } else if (num < 100) {
1290  snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1291  num %= 10;
1292  if (num)
1293  playa++;
1294  } else if (num == 100) {
1295  ast_copy_string(fn, "digits/100", sizeof(fn));
1296  num = 0;
1297  } else if (num < 200) {
1298  ast_copy_string(fn, "digits/100-and", sizeof(fn));
1299  num -= 100;
1300  } else {
1301  if (num < 1000) {
1302  snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1303  num %= 100;
1304  } else if (num < 2000) {
1305  num %= 1000;
1306  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1307  } else {
1308  if (num < 1000000) {
1309  res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1310  if (res)
1311  return res;
1312  num %= 1000;
1313  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1314  } else {
1315  if (num < 2147483640) {
1316  if ((num/1000000) == 1) {
1317  res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1318  if (res)
1319  return res;
1320  ast_copy_string(fn, "digits/million", sizeof(fn));
1321  } else {
1322  res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1323  if (res)
1324  return res;
1325  ast_copy_string(fn, "digits/millions", sizeof(fn));
1326  }
1327  num %= 1000000;
1328  } else {
1329  ast_debug(1, "Number '%d' is too big for me\n", num);
1330  res = -1;
1331  }
1332  }
1333  }
1334  }
1335 
1336  if (!res) {
1337  if (!ast_streamfile(chan, fn, language)) {
1338  if ((audiofd > -1) && (ctrlfd > -1))
1339  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1340  else
1341  res = ast_waitstream(chan, ints);
1342  }
1343  ast_stopstream(chan);
1344 
1345  }
1346 
1347  }
1348  return res;
1349 }
1350 
1351 /*! \brief ast_say_number_full_fr: French syntax
1352  Extra sounds needed:
1353  1F: feminin 'une'
1354  et: 'and' */
1355 static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1356 {
1357  int res = 0;
1358  int playh = 0;
1359  int playa = 0;
1360  int mf = 1; /* +1 = male; -1 = female */
1361  char fn[256] = "";
1362  if (!num)
1363  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1364 
1365  if (options && !strncasecmp(options, "f", 1))
1366  mf = -1;
1367 
1368  while (!res && (num || playh || playa)) {
1369  if (num < 0) {
1370  ast_copy_string(fn, "digits/minus", sizeof(fn));
1371  if ( num > INT_MIN ) {
1372  num = -num;
1373  } else {
1374  num = 0;
1375  }
1376  } else if (playh) {
1377  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1378  playh = 0;
1379  } else if (playa) {
1380  ast_copy_string(fn, "digits/et", sizeof(fn));
1381  playa = 0;
1382  } else if (num == 1) {
1383  if (mf < 0)
1384  snprintf(fn, sizeof(fn), "digits/%dF", num);
1385  else
1386  snprintf(fn, sizeof(fn), "digits/%d", num);
1387  num = 0;
1388  } else if (num < 21) {
1389  snprintf(fn, sizeof(fn), "digits/%d", num);
1390  num = 0;
1391  } else if (num < 70) {
1392  snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1393  if ((num % 10) == 1) playa++;
1394  num = num % 10;
1395  } else if (num < 80) {
1396  ast_copy_string(fn, "digits/60", sizeof(fn));
1397  if ((num % 10) == 1) playa++;
1398  num -= 60;
1399  } else if (num < 100) {
1400  ast_copy_string(fn, "digits/80", sizeof(fn));
1401  num = num - 80;
1402  } else if (num < 200) {
1403  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1404  num = num - 100;
1405  } else if (num < 1000) {
1406  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1407  playh++;
1408  num = num % 100;
1409  } else if (num < 2000) {
1410  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1411  num = num - 1000;
1412  } else if (num < 1000000) {
1413  res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1414  if (res)
1415  return res;
1416  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1417  num = num % 1000;
1418  } else if (num < 1000000000) {
1419  res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1420  if (res)
1421  return res;
1422  ast_copy_string(fn, "digits/million", sizeof(fn));
1423  num = num % 1000000;
1424  } else {
1425  ast_debug(1, "Number '%d' is too big for me\n", num);
1426  res = -1;
1427  }
1428  if (!res) {
1429  if (!ast_streamfile(chan, fn, language)) {
1430  if ((audiofd > -1) && (ctrlfd > -1))
1431  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1432  else
1433  res = ast_waitstream(chan, ints);
1434  }
1435  ast_stopstream(chan);
1436  }
1437  }
1438  return res;
1439 }
1440 
1441 
1442 
1443 /* Hebrew syntax
1444  * Check doc/lang/hebrew-digits.txt for information about the various
1445  * recordings required to make this translation work properly */
1446 #define SAY_NUM_BUF_SIZE 256
1447 static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1448 {
1449  int res = 0;
1450  int state = 0; /* no need to save anything */
1451  int mf = -1; /* +1 = Masculin; -1 = Feminin */
1452  int tmpnum = 0;
1453 
1454  char fn[SAY_NUM_BUF_SIZE] = "";
1455 
1456  ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1457 
1458  if (!num) {
1459  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1460  }
1461  if (options && !strncasecmp(options, "m", 1)) {
1462  mf = 1;
1463  }
1464  ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1465 
1466  /* Do we have work to do? */
1467  while (!res && (num || (state > 0))) {
1468  /* first type of work: play a second sound. In this loop
1469  * we can only play one sound file at a time. Thus playing
1470  * a second one requires repeating the loop just for the
1471  * second file. The variable 'state' remembers where we were.
1472  * state==0 is the normal mode and it means that we continue
1473  * to check if the number num has yet anything left.
1474  */
1475  ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1476 
1477  if (state == 1) {
1478  state = 0;
1479  } else if (state == 2) {
1480  if ((num >= 11) && (num < 21)) {
1481  if (mf < 0) {
1482  snprintf(fn, sizeof(fn), "digits/ve");
1483  } else {
1484  snprintf(fn, sizeof(fn), "digits/uu");
1485  }
1486  } else {
1487  switch (num) {
1488  case 1:
1489  snprintf(fn, sizeof(fn), "digits/ve");
1490  break;
1491  case 2:
1492  snprintf(fn, sizeof(fn), "digits/uu");
1493  break;
1494  case 3:
1495  if (mf < 0) {
1496  snprintf(fn, sizeof(fn), "digits/ve");
1497  } else {
1498  snprintf(fn, sizeof(fn), "digits/uu");
1499  }
1500  break;
1501  case 4:
1502  snprintf(fn, sizeof(fn), "digits/ve");
1503  break;
1504  case 5:
1505  snprintf(fn, sizeof(fn), "digits/ve");
1506  break;
1507  case 6:
1508  snprintf(fn, sizeof(fn), "digits/ve");
1509  break;
1510  case 7:
1511  snprintf(fn, sizeof(fn), "digits/ve");
1512  break;
1513  case 8:
1514  snprintf(fn, sizeof(fn), "digits/uu");
1515  break;
1516  case 9:
1517  snprintf(fn, sizeof(fn), "digits/ve");
1518  break;
1519  case 10:
1520  snprintf(fn, sizeof(fn), "digits/ve");
1521  break;
1522  }
1523  }
1524  state = 0;
1525  } else if (state == 3) {
1526  snprintf(fn, sizeof(fn), "digits/1k");
1527  state = 0;
1528  } else if (num < 0) {
1529  snprintf(fn, sizeof(fn), "digits/minus");
1530  num = (-1) * num;
1531  } else if (num < 20) {
1532  if (mf < 0) {
1533  snprintf(fn, sizeof(fn), "digits/%d", num);
1534  } else {
1535  snprintf(fn, sizeof(fn), "digits/%dm", num);
1536  }
1537  num = 0;
1538  } else if ((num < 100) && (num >= 20)) {
1539  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1540  num = num % 10;
1541  if (num > 0) {
1542  state = 2;
1543  }
1544  } else if ((num >= 100) && (num < 1000)) {
1545  tmpnum = num / 100;
1546  snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1547  num = num - (tmpnum * 100);
1548  if ((num > 0) && (num < 11)) {
1549  state = 2;
1550  }
1551  } else if ((num >= 1000) && (num < 10000)) {
1552  tmpnum = num / 1000;
1553  snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1554  num = num - (tmpnum * 1000);
1555  if ((num > 0) && (num < 11)) {
1556  state = 2;
1557  }
1558  } else if (num < 20000) {
1559  snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1560  num = num % 1000;
1561  state = 3;
1562  } else if (num < 1000000) {
1563  res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1564  if (res) {
1565  return res;
1566  }
1567  snprintf(fn, sizeof(fn), "digits/1k");
1568  num = num % 1000;
1569  if ((num > 0) && (num < 11)) {
1570  state = 2;
1571  }
1572  } else if (num < 2000000) {
1573  snprintf(fn, sizeof(fn), "digits/million");
1574  num = num % 1000000;
1575  if ((num > 0) && (num < 11)) {
1576  state = 2;
1577  }
1578  } else if (num < 3000000) {
1579  snprintf(fn, sizeof(fn), "digits/twomillion");
1580  num = num - 2000000;
1581  if ((num > 0) && (num < 11)) {
1582  state = 2;
1583  }
1584  } else if (num < 1000000000) {
1585  res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1586  if (res) {
1587  return res;
1588  }
1589  snprintf(fn, sizeof(fn), "digits/million");
1590  num = num % 1000000;
1591  if ((num > 0) && (num < 11)) {
1592  state = 2;
1593  }
1594  } else {
1595  ast_debug(1, "Number '%d' is too big for me\n", num);
1596  res = -1;
1597  }
1598  tmpnum = 0;
1599  if (!res) {
1600  if (!ast_streamfile(chan, fn, language)) {
1601  if ((audiofd > -1) && (ctrlfd > -1)) {
1602  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1603  } else {
1604  res = ast_waitstream(chan, ints);
1605  }
1606  }
1607  ast_stopstream(chan);
1608  }
1609  }
1610  return res;
1611 }
1612 
1613 /*! \brief ast_say_number_full_hu: Hungarian syntax
1614 
1615  Extra sounds needed:
1616  10en: "tizen"
1617  20on: "huszon"
1618 */
1619 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1620 {
1621  int res = 0;
1622  int playh = 0;
1623  char fn[256] = "";
1624  if (!num)
1625  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1626 
1627  /*
1628  Hungarian support
1629  like english, except numbers up to 29 are from 2 words.
1630  10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1631  */
1632 
1633  while(!res && (num || playh)) {
1634  if (num < 0) {
1635  ast_copy_string(fn, "digits/minus", sizeof(fn));
1636  if ( num > INT_MIN ) {
1637  num = -num;
1638  } else {
1639  num = 0;
1640  }
1641  } else if (playh) {
1642  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1643  playh = 0;
1644  } else if (num < 11 || num == 20) {
1645  snprintf(fn, sizeof(fn), "digits/%d", num);
1646  num = 0;
1647  } else if (num < 20) {
1648  ast_copy_string(fn, "digits/10en", sizeof(fn));
1649  num -= 10;
1650  } else if (num < 30) {
1651  ast_copy_string(fn, "digits/20on", sizeof(fn));
1652  num -= 20;
1653  } else if (num < 100) {
1654  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1655  num %= 10;
1656  } else {
1657  if (num < 1000){
1658  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1659  playh++;
1660  num %= 100;
1661  } else {
1662  if (num < 1000000) { /* 1,000,000 */
1663  res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1664  if (res)
1665  return res;
1666  num %= 1000;
1667  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1668  } else {
1669  if (num < 1000000000) { /* 1,000,000,000 */
1670  res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1671  if (res)
1672  return res;
1673  num %= 1000000;
1674  ast_copy_string(fn, "digits/million", sizeof(fn));
1675  } else {
1676  ast_debug(1, "Number '%d' is too big for me\n", num);
1677  res = -1;
1678  }
1679  }
1680  }
1681  }
1682  if (!res) {
1683  if(!ast_streamfile(chan, fn, language)) {
1684  if ((audiofd > -1) && (ctrlfd > -1))
1685  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1686  else
1687  res = ast_waitstream(chan, ints);
1688  }
1689  ast_stopstream(chan);
1690  }
1691  }
1692  return res;
1693 }
1694 
1695 /*! \brief ast_say_number_full_is: Icelandic syntax */
1696 /* New files:
1697  In addition to American English, the following sounds are required: "hundreds", "millions", "1kvk", "1hk", "2kvk", "2hk", "3kvk", "3hk", "4kvk", "4hk"
1698  */
1699 static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1700 {
1701  int res = 0;
1702  int playh = 0;
1703  int playa = 0;
1704  int cn = 1; /* 1 = masc; 2 = fem; 3 = neut */
1705  char fn[256] = "";
1706 
1707  if (!num)
1708  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1709 
1710  if (options && !strncasecmp(options, "f", 1)) cn = 2;
1711  if (options && !strncasecmp(options, "c", 1)) cn = 3;
1712  /* It seems that sometimes people are using c and sometimes n. */
1713  if (options && !strncasecmp(options, "n", 1)) cn = 3;
1714 
1715  while (!res && (num || playh || playa )) {
1716  if (num < 0) {
1717  ast_copy_string(fn, "digits/minus", sizeof(fn));
1718  if ( num > INT_MIN ) {
1719  num = -num;
1720  } else {
1721  num = 0;
1722  }
1723  } else if (playh) {
1724  if (playh > 1)
1725  ast_copy_string(fn, "digits/hundreds", sizeof(fn));
1726  else
1727  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1728  playh = 0;
1729  } else if (playa) {
1730  ast_copy_string(fn, "digits/and", sizeof(fn));
1731  playa = 0;
1732  } else if (num < 5 && cn == 2) {
1733  snprintf(fn, sizeof(fn), "digits/%dkvk", num);
1734  num = 0;
1735  } else if (num < 5 && cn == 3) {
1736  snprintf(fn, sizeof(fn), "digits/%dhk", num);
1737  num = 0;
1738  } else if (num < 20) {
1739  snprintf(fn, sizeof(fn), "digits/%d", num);
1740  num = 0;
1741  } else if (num < 100) {
1742  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1743  num %= 10;
1744  if (num)
1745  playa++;
1746  } else if (num < 1000) {
1747  int hundreds = num / 100;
1748  /* The number prepending hundreds are in neutral */
1749  if (hundreds < 5)
1750  snprintf(fn, sizeof(fn), "digits/%dhk", hundreds);
1751  else
1752  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1753 
1754  playh = hundreds;
1755  num -= 100 * hundreds;
1756  if (num && num < 20)
1757  playa++;
1758  /* The 'and' moves forward on even tens. */
1759  if (num && (num % 10) == 0)
1760  playa++;
1761  } else if (num < 1000000) {
1762  res = ast_say_number_full_is(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1763  /* Play 'and' if it's an even hundred. */
1764  if ((num % 100) == 0 && (num % 1000 != 0)) {
1765  playa++;
1766  }
1767  if (res)
1768  return res;
1769  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1770  num %= 1000;
1771  if (num && (num < 20 || (num % 10 == 0)))
1772  playa++;
1773  } else if (num < 1000000000) {
1774  int millions = num / 1000000;
1775  /* The number of millions is feminine */
1776  res = ast_say_number_full_is(chan, millions, ints, language, "f", audiofd, ctrlfd);
1777  if (res)
1778  return res;
1779  if (millions > 1)
1780  ast_copy_string(fn, "digits/millions", sizeof(fn));
1781  else
1782  ast_copy_string(fn, "digits/million", sizeof(fn));
1783  num %= 1000000;
1784  if (num && num < 100)
1785  playa++;
1786  } else if (num < INT_MAX) {
1787  int milliards = num / 1000000000;
1788  /* The number of milliards is masculine */
1789  res = ast_say_number_full_is(chan, milliards, ints, language, "m", audiofd, ctrlfd);
1790  if (res)
1791  return res;
1792  if (milliards > 1)
1793  ast_copy_string(fn, "digits/milliards", sizeof(fn));
1794  else
1795  ast_copy_string(fn, "digits/milliard", sizeof(fn));
1796  num %= 1000000000;
1797  if (num && num < 100)
1798  playa++;
1799  } else {
1800  ast_debug(1, "Number '%d' is too big for me\n", num);
1801  res = -1;
1802  }
1803 
1804  if (!res) {
1805  if (!ast_streamfile(chan, fn, language)) {
1806  if ((audiofd > -1) && (ctrlfd > -1))
1807  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1808  else
1809  res = ast_waitstream(chan, ints);
1810  }
1811  ast_stopstream(chan);
1812  }
1813  }
1814  return res;
1815 }
1816 
1817 
1818 /*! \brief ast_say_number_full_it: Italian */
1819 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1820 {
1821  int res = 0;
1822  int playh = 0;
1823  int tempnum = 0;
1824  char fn[256] = "";
1825 
1826  if (!num)
1827  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1828 
1829  /*
1830  Italian support
1831 
1832  Like english, numbers up to 20 are a single 'word', and others
1833  compound, but with exceptions.
1834  For example 21 is not twenty-one, but there is a single word in 'it'.
1835  Idem for 28 (ie when a the 2nd part of a compund number
1836  starts with a vowel)
1837 
1838  There are exceptions also for hundred, thousand and million.
1839  In english 100 = one hundred, 200 is two hundred.
1840  In italian 100 = cento , like to say hundred (without one),
1841  200 and more are like english.
1842 
1843  Same applies for thousand:
1844  1000 is one thousand in en, 2000 is two thousand.
1845  In it we have 1000 = mille , 2000 = 2 mila
1846 
1847  For million(s) we use the plural, if more than one
1848  Also, one million is abbreviated in it, like on-million,
1849  or 'un milione', not 'uno milione'.
1850  So the right file is provided.
1851  */
1852 
1853  while (!res && (num || playh)) {
1854  if (num < 0) {
1855  ast_copy_string(fn, "digits/minus", sizeof(fn));
1856  if ( num > INT_MIN ) {
1857  num = -num;
1858  } else {
1859  num = 0;
1860  }
1861  } else if (playh) {
1862  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1863  playh = 0;
1864  } else if (num < 20) {
1865  snprintf(fn, sizeof(fn), "digits/%d", num);
1866  num = 0;
1867  } else if (num == 21) {
1868  snprintf(fn, sizeof(fn), "digits/%d", num);
1869  num = 0;
1870  } else if (num == 28) {
1871  snprintf(fn, sizeof(fn), "digits/%d", num);
1872  num = 0;
1873  } else if (num == 31) {
1874  snprintf(fn, sizeof(fn), "digits/%d", num);
1875  num = 0;
1876  } else if (num == 38) {
1877  snprintf(fn, sizeof(fn), "digits/%d", num);
1878  num = 0;
1879  } else if (num == 41) {
1880  snprintf(fn, sizeof(fn), "digits/%d", num);
1881  num = 0;
1882  } else if (num == 48) {
1883  snprintf(fn, sizeof(fn), "digits/%d", num);
1884  num = 0;
1885  } else if (num == 51) {
1886  snprintf(fn, sizeof(fn), "digits/%d", num);
1887  num = 0;
1888  } else if (num == 58) {
1889  snprintf(fn, sizeof(fn), "digits/%d", num);
1890  num = 0;
1891  } else if (num == 61) {
1892  snprintf(fn, sizeof(fn), "digits/%d", num);
1893  num = 0;
1894  } else if (num == 68) {
1895  snprintf(fn, sizeof(fn), "digits/%d", num);
1896  num = 0;
1897  } else if (num == 71) {
1898  snprintf(fn, sizeof(fn), "digits/%d", num);
1899  num = 0;
1900  } else if (num == 78) {
1901  snprintf(fn, sizeof(fn), "digits/%d", num);
1902  num = 0;
1903  } else if (num == 81) {
1904  snprintf(fn, sizeof(fn), "digits/%d", num);
1905  num = 0;
1906  } else if (num == 88) {
1907  snprintf(fn, sizeof(fn), "digits/%d", num);
1908  num = 0;
1909  } else if (num == 91) {
1910  snprintf(fn, sizeof(fn), "digits/%d", num);
1911  num = 0;
1912  } else if (num == 98) {
1913  snprintf(fn, sizeof(fn), "digits/%d", num);
1914  num = 0;
1915  } else if (num < 100) {
1916  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1917  num %= 10;
1918  } else {
1919  if (num < 1000) {
1920  if ((num / 100) > 1) {
1921  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1922  playh++;
1923  } else {
1924  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1925  }
1926  num %= 100;
1927  } else {
1928  if (num < 1000000) { /* 1,000,000 */
1929  if ((num/1000) > 1)
1930  res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1931  if (res)
1932  return res;
1933  tempnum = num;
1934  num %= 1000;
1935  if ((tempnum / 1000) < 2)
1936  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1937  else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1938  ast_copy_string(fn, "digits/thousands", sizeof(fn));
1939  } else {
1940  if (num < 1000000000) { /* 1,000,000,000 */
1941  if ((num / 1000000) > 1)
1942  res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1943  if (res)
1944  return res;
1945  tempnum = num;
1946  num %= 1000000;
1947  if ((tempnum / 1000000) < 2)
1948  ast_copy_string(fn, "digits/million", sizeof(fn));
1949  else
1950  ast_copy_string(fn, "digits/millions", sizeof(fn));
1951  } else {
1952  ast_debug(1, "Number '%d' is too big for me\n", num);
1953  res = -1;
1954  }
1955  }
1956  }
1957  }
1958  if (!res) {
1959  if (!ast_streamfile(chan, fn, language)) {
1960  if ((audiofd > -1) && (ctrlfd > -1))
1961  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1962  else
1963  res = ast_waitstream(chan, ints);
1964  }
1965  ast_stopstream(chan);
1966  }
1967  }
1968  return res;
1969 }
1970 
1971 /*! \brief ast_say_number_full_nl: dutch syntax
1972  * New files: digits/nl-en
1973  */
1974 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1975 {
1976  int res = 0;
1977  int playh = 0;
1978  int units = 0;
1979  char fn[256] = "";
1980  if (!num)
1981  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1982  while (!res && (num || playh )) {
1983  if (num < 0) {
1984  ast_copy_string(fn, "digits/minus", sizeof(fn));
1985  if ( num > INT_MIN ) {
1986  num = -num;
1987  } else {
1988  num = 0;
1989  }
1990  } else if (playh) {
1991  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1992  playh = 0;
1993  } else if (num < 20) {
1994  snprintf(fn, sizeof(fn), "digits/%d", num);
1995  num = 0;
1996  } else if (num < 100) {
1997  units = num % 10;
1998  if (units > 0) {
1999  res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
2000  if (res)
2001  return res;
2002  num = num - units;
2003  ast_copy_string(fn, "digits/nl-en", sizeof(fn));
2004  } else {
2005  snprintf(fn, sizeof(fn), "digits/%d", num - units);
2006  num = 0;
2007  }
2008  } else if (num < 200) {
2009  /* hundred, not one-hundred */
2010  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2011  num %= 100;
2012  } else if (num < 1000) {
2013  snprintf(fn, sizeof(fn), "digits/%d", num / 100);
2014  playh++;
2015  num %= 100;
2016  } else {
2017  if (num < 1100) {
2018  /* thousand, not one-thousand */
2019  num %= 1000;
2020  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2021  } else if (num < 10000) { /* 1,100 to 9,9999 */
2022  res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
2023  if (res)
2024  return res;
2025  num %= 100;
2026  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2027  } else {
2028  if (num < 1000000) { /* 1,000,000 */
2029  res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
2030  if (res)
2031  return res;
2032  num %= 1000;
2033  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2034  } else {
2035  if (num < 1000000000) { /* 1,000,000,000 */
2036  res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2037  if (res)
2038  return res;
2039  num %= 1000000;
2040  ast_copy_string(fn, "digits/million", sizeof(fn));
2041  } else {
2042  ast_debug(1, "Number '%d' is too big for me\n", num);
2043  res = -1;
2044  }
2045  }
2046  }
2047  }
2048 
2049  if (!res) {
2050  if (!ast_streamfile(chan, fn, language)) {
2051  if ((audiofd > -1) && (ctrlfd > -1))
2052  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2053  else
2054  res = ast_waitstream(chan, ints);
2055  }
2056  ast_stopstream(chan);
2057  }
2058  }
2059  return res;
2060 }
2061 
2062 /*! \brief ast_say_number_full_no: Norwegian syntax
2063  * New files:
2064  * In addition to American English, the following sounds are required: "and", "1N"
2065  *
2066  * The grammar for Norwegian numbers is the same as for English except
2067  * for the following:
2068  * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
2069  * "and" before the last two digits, i.e. 2034 is "two thousand and
2070  * thirty-four" and 1000012 is "one million and twelve".
2071  */
2072 static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2073 {
2074  int res = 0;
2075  int playh = 0;
2076  int playa = 0;
2077  int cn = 1; /* +1 = commune; -1 = neuter */
2078  char fn[256] = "";
2079 
2080  if (!num)
2081  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2082 
2083  if (options && !strncasecmp(options, "n", 1)) cn = -1;
2084 
2085  while (!res && (num || playh || playa )) {
2086  if (num < 0) {
2087  ast_copy_string(fn, "digits/minus", sizeof(fn));
2088  if ( num > INT_MIN ) {
2089  num = -num;
2090  } else {
2091  num = 0;
2092  }
2093  } else if (playh) {
2094  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2095  playh = 0;
2096  } else if (playa) {
2097  ast_copy_string(fn, "digits/and", sizeof(fn));
2098  playa = 0;
2099  } else if (num == 1 && cn == -1) {
2100  ast_copy_string(fn, "digits/1N", sizeof(fn));
2101  num = 0;
2102  } else if (num < 20) {
2103  snprintf(fn, sizeof(fn), "digits/%d", num);
2104  num = 0;
2105  } else if (num < 100) {
2106  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2107  num %= 10;
2108  } else if (num < 1000) {
2109  int hundreds = num / 100;
2110  if (hundreds == 1)
2111  ast_copy_string(fn, "digits/1N", sizeof(fn));
2112  else
2113  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2114 
2115  playh++;
2116  num -= 100 * hundreds;
2117  if (num)
2118  playa++;
2119  } else if (num < 1000000) {
2120  res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
2121  if (res)
2122  return res;
2123  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2124  num %= 1000;
2125  if (num && num < 100)
2126  playa++;
2127  } else if (num < 1000000000) {
2128  int millions = num / 1000000;
2129  res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
2130  if (res)
2131  return res;
2132  ast_copy_string(fn, "digits/million", sizeof(fn));
2133  num %= 1000000;
2134  if (num && num < 100)
2135  playa++;
2136  } else {
2137  ast_debug(1, "Number '%d' is too big for me\n", num);
2138  res = -1;
2139  }
2140 
2141  if (!res) {
2142  if (!ast_streamfile(chan, fn, language)) {
2143  if ((audiofd > -1) && (ctrlfd > -1))
2144  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2145  else
2146  res = ast_waitstream(chan, ints);
2147  }
2148  ast_stopstream(chan);
2149  }
2150  }
2151  return res;
2152 }
2153 
2154 typedef struct {
2156  char *cyfry[10];
2157  char *cyfry2[10];
2158  char *setki[10];
2159  char *dziesiatki[10];
2160  char *nastki[10];
2161  char *rzedy[3][3];
2162 } odmiana;
2163 
2164 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
2165 {
2166  if (rzad==0)
2167  return "";
2168 
2169  if (i==1)
2170  return odm->rzedy[rzad - 1][0];
2171  if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
2172  return odm->rzedy[rzad - 1][1];
2173  else
2174  return odm->rzedy[rzad - 1][2];
2175 }
2176 
2177 static char* pl_append(char* buffer, char* str)
2178 {
2179  strcpy(buffer, str);
2180  buffer += strlen(str);
2181  return buffer;
2182 }
2183 
2184 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
2185 {
2186  char file_name[255] = "digits/";
2187  strcat(file_name, fn);
2188  ast_debug(1, "Trying to play: %s\n", file_name);
2189  if (!ast_streamfile(chan, file_name, language)) {
2190  if ((audiofd > -1) && (ctrlfd > -1))
2191  ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2192  else
2193  ast_waitstream(chan, ints);
2194  }
2195  ast_stopstream(chan);
2196 }
2197 
2198 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
2199 {
2200  /* Initialise variables to allow compilation on Debian-stable, etc */
2201  int m1000E6 = 0;
2202  int i1000E6 = 0;
2203  int m1000E3 = 0;
2204  int i1000E3 = 0;
2205  int m1000 = 0;
2206  int i1000 = 0;
2207  int m100 = 0;
2208  int i100 = 0;
2209 
2210  if (i == 0 && rzad > 0) {
2211  return;
2212  }
2213  if (i == 0) {
2214  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
2215  return;
2216  }
2217 
2218  m1000E6 = i % 1000000000;
2219  i1000E6 = i / 1000000000;
2220 
2221  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
2222 
2223  m1000E3 = m1000E6 % 1000000;
2224  i1000E3 = m1000E6 / 1000000;
2225 
2226  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
2227 
2228  m1000 = m1000E3 % 1000;
2229  i1000 = m1000E3 / 1000;
2230 
2231  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
2232 
2233  m100 = m1000 % 100;
2234  i100 = m1000 / 100;
2235 
2236  if (i100>0)
2237  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
2238 
2239  if (m100 > 0 && m100 <= 9) {
2240  if (m1000 > 0)
2241  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
2242  else
2243  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
2244  } else if (m100 % 10 == 0 && m100 != 0) {
2245  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2246  } else if (m100 > 10 && m100 <= 19) {
2247  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
2248  } else if (m100 > 20) {
2249  if (odm->separator_dziesiatek[0] == ' ') {
2250  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2251  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
2252  } else {
2253  char buf[10];
2254  char *b = buf;
2255  b = pl_append(b, odm->dziesiatki[m100 / 10]);
2256  b = pl_append(b, odm->separator_dziesiatek);
2257  pl_append(b, odm->cyfry2[m100 % 10]);
2258  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
2259  }
2260  }
2261 
2262  if (rzad > 0) {
2263  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
2264  }
2265 }
2266 
2267 /* ast_say_number_full_pl: Polish syntax
2268 
2269 Sounds needed:
2270 0 zero
2271 1 jeden
2272 10 dziesiec
2273 100 sto
2274 1000 tysiac
2275 1000000 milion
2276 1000000000 miliard
2277 1000000000.2 miliardy
2278 1000000000.5 miliardow
2279 1000000.2 miliony
2280 1000000.5 milionow
2281 1000.2 tysiace
2282 1000.5 tysiecy
2283 100m stu
2284 10m dziesieciu
2285 11 jedenascie
2286 11m jedenastu
2287 12 dwanascie
2288 12m dwunastu
2289 13 trzynascie
2290 13m trzynastu
2291 14 czternascie
2292 14m czternastu
2293 15 pietnascie
2294 15m pietnastu
2295 16 szesnascie
2296 16m szesnastu
2297 17 siedemnascie
2298 17m siedemnastu
2299 18 osiemnascie
2300 18m osiemnastu
2301 19 dziewietnascie
2302 19m dziewietnastu
2303 1z jedna
2304 2 dwa
2305 20 dwadziescia
2306 200 dwiescie
2307 200m dwustu
2308 20m dwudziestu
2309 2-1m dwaj
2310 2-2m dwoch
2311 2z dwie
2312 3 trzy
2313 30 trzydziesci
2314 300 trzysta
2315 300m trzystu
2316 30m trzydziestu
2317 3-1m trzej
2318 3-2m trzech
2319 4 cztery
2320 40 czterdziesci
2321 400 czterysta
2322 400m czterystu
2323 40m czterdziestu
2324 4-1m czterej
2325 4-2m czterech
2326 5 piec
2327 50 piecdziesiat
2328 500 piecset
2329 500m pieciuset
2330 50m piedziesieciu
2331 5m pieciu
2332 6 szesc
2333 60 szescdziesiat
2334 600 szescset
2335 600m szesciuset
2336 60m szescdziesieciu
2337 6m szesciu
2338 7 siedem
2339 70 siedemdziesiat
2340 700 siedemset
2341 700m siedmiuset
2342 70m siedemdziesieciu
2343 7m siedmiu
2344 8 osiem
2345 80 osiemdziesiat
2346 800 osiemset
2347 800m osmiuset
2348 80m osiemdziesieciu
2349 8m osmiu
2350 9 dziewiec
2351 90 dziewiecdziesiat
2352 900 dziewiecset
2353 900m dziewieciuset
2354 90m dziewiedziesieciu
2355 9m dziewieciu
2356 and combinations of eg.: 20_1, 30m_3m, etc...
2357 
2358 */
2359 static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2360 {
2361  char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2362 
2363  char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2364 
2365  char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2366 
2367  char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2368 
2369  char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2370 
2371  char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2372 
2373  char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2374 
2375  char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2376 
2377  char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2378 
2379  char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2380 
2381  char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2382 
2383  char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2384 
2385  char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2386 
2387  /* Initialise variables to allow compilation on Debian-stable, etc */
2388  odmiana *o;
2389 
2390  static odmiana *odmiana_nieosobowa = NULL;
2391  static odmiana *odmiana_meska = NULL;
2392  static odmiana *odmiana_zenska = NULL;
2393 
2394  if (odmiana_nieosobowa == NULL) {
2395  odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2396 
2397  odmiana_nieosobowa->separator_dziesiatek = " ";
2398 
2399  memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2400  memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2401  memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2402  memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2403  memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2404  memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2405  }
2406 
2407  if (odmiana_zenska == NULL) {
2408  odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2409 
2410  odmiana_zenska->separator_dziesiatek = " ";
2411 
2412  memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2413  memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2414  memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2415  memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2416  memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2417  memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2418  }
2419 
2420  if (odmiana_meska == NULL) {
2421  odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2422 
2423  odmiana_meska->separator_dziesiatek = " ";
2424 
2425  memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2426  memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2427  memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2428  memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2429  memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2430  memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2431  }
2432 
2433  if (options) {
2434  if (strncasecmp(options, "f", 1) == 0)
2435  o = odmiana_zenska;
2436  else if (strncasecmp(options, "m", 1) == 0)
2437  o = odmiana_meska;
2438  else
2439  o = odmiana_nieosobowa;
2440  } else
2441  o = odmiana_nieosobowa;
2442 
2443  powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2444  return 0;
2445 }
2446 
2447 /* ast_say_number_full_pt: Portuguese syntax
2448 
2449  * Extra sounds needed:
2450  * For feminin all sound files ends with F
2451  * 100E for 100+ something
2452  * 1000000S for plural
2453  * pt-e for 'and'
2454  */
2455 static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2456 {
2457  int res = 0;
2458  int playh = 0;
2459  int mf = 1; /* +1 = male; -1 = female */
2460  char fn[256] = "";
2461 
2462  if (!num)
2463  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2464 
2465  if (options && !strncasecmp(options, "f", 1))
2466  mf = -1;
2467 
2468  while (!res && num ) {
2469  if (num < 0) {
2470  ast_copy_string(fn, "digits/minus", sizeof(fn));
2471  if ( num > INT_MIN ) {
2472  num = -num;
2473  } else {
2474  num = 0;
2475  }
2476  } else if (num < 20) {
2477  if ((num == 1 || num == 2) && (mf < 0))
2478  snprintf(fn, sizeof(fn), "digits/%dF", num);
2479  else
2480  snprintf(fn, sizeof(fn), "digits/%d", num);
2481  num = 0;
2482  } else if (num < 100) {
2483  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2484  if (num % 10)
2485  playh = 1;
2486  num = num % 10;
2487  } else if (num < 1000) {
2488  if (num == 100)
2489  ast_copy_string(fn, "digits/100", sizeof(fn));
2490  else if (num < 200)
2491  ast_copy_string(fn, "digits/100E", sizeof(fn));
2492  else {
2493  if (mf < 0 && num > 199)
2494  snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2495  else
2496  snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2497  if (num % 100)
2498  playh = 1;
2499  }
2500  num = num % 100;
2501  } else if (num < 1000000) {
2502  if (num > 1999) {
2503  res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2504  if (res)
2505  return res;
2506  }
2507  ast_copy_string(fn, "digits/1000", sizeof(fn));
2508  if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2509  playh = 1;
2510  num = num % 1000;
2511  } else if (num < 1000000000) {
2512  res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2513  if (res)
2514  return res;
2515  if (num < 2000000)
2516  ast_copy_string(fn, "digits/1000000", sizeof(fn));
2517  else
2518  ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2519 
2520  if ((num % 1000000) &&
2521  /* no thousands */
2522  ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2523  /* no hundreds and below */
2524  (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2525  playh = 1;
2526  num = num % 1000000;
2527  } else {
2528  /* number is too big */
2529  ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2530  res = -1;
2531  }
2532  if (!res) {
2533  if (!ast_streamfile(chan, fn, language)) {
2534  if ((audiofd > -1) && (ctrlfd > -1))
2535  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2536  else
2537  res = ast_waitstream(chan, ints);
2538  }
2539  ast_stopstream(chan);
2540  }
2541  if (!res && playh) {
2542  res = wait_file(chan, ints, "digits/pt-e", language);
2543  ast_stopstream(chan);
2544  playh = 0;
2545  }
2546  }
2547  return res;
2548 }
2549 
2550 /*! \brief ast_say_number_full_se: Swedish syntax
2551 
2552  Sound files needed
2553  - 1N
2554 */
2555 static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2556 {
2557  int playh = 0;
2558  int start = 1;
2559  char fn[256] = "";
2560  int cn = 1; /* +1 = commune; -1 = neuter */
2561  int res = 0;
2562 
2563  if (!num) {
2564  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2565  }
2566  if (options && !strncasecmp(options, "n", 1)) cn = -1;
2567 
2568  while (num || playh) {
2569  if (num < 0) {
2570  ast_copy_string(fn, "digits/minus", sizeof(fn));
2571  if ( num > INT_MIN ) {
2572  num = -num;
2573  } else {
2574  num = 0;
2575  }
2576  } else if (playh) {
2577  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2578  playh = 0;
2579  } else if (start && num < 200 && num > 99 && cn == -1) {
2580  /* Don't say "en hundra" just say "hundra". */
2581  snprintf(fn, sizeof(fn), "digits/hundred");
2582  num -= 100;
2583  } else if (num == 1 && cn == -1) { /* En eller ett? */
2584  ast_copy_string(fn, "digits/1N", sizeof(fn));
2585  num = 0;
2586  } else if (num < 20) {
2587  snprintf(fn, sizeof(fn), "digits/%d", num);
2588  num = 0;
2589  } else if (num < 100) { /* Below hundreds - teens and tens */
2590  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2591  num %= 10;
2592  } else if (num < 1000) {
2593  /* Hundreds */
2594  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2595  playh++;
2596  num %= 100;
2597  } else if (num < 1000000) { /* 1,000,000 */
2598  /* Always say "ett hundra tusen", not "en hundra tusen" */
2599  res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2600  if (res) {
2601  return res;
2602  }
2603  num %= 1000;
2604  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2605  } else if (num < 1000000000) { /* 1,000,000,000 */
2606  /* Always say "en miljon", not "ett miljon" */
2607  res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2608  if (res) {
2609  return res;
2610  }
2611  num %= 1000000;
2612  ast_copy_string(fn, "digits/million", sizeof(fn));
2613  } else { /* Miljarder - Billions */
2614  ast_debug(1, "Number '%d' is too big for me\n", num);
2615  return -1;
2616  }
2617 
2618  if (!ast_streamfile(chan, fn, language)) {
2619  if ((audiofd > -1) && (ctrlfd > -1)) {
2620  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2621  } else {
2622  res = ast_waitstream(chan, ints);
2623  }
2624  ast_stopstream(chan);
2625  if (res) {
2626  return res;
2627  }
2628  }
2629  start = 0;
2630  }
2631  return 0;
2632 }
2633 
2634 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2635 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2636 {
2637  int res = 0;
2638  int playh = 0;
2639  int playt = 0;
2640  int playz = 0;
2641  int last_length = 0;
2642  char buf[20] = "";
2643  char fn[256] = "";
2644  if (!num)
2645  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2646 
2647  while (!res && (num || playh || playt || playz)) {
2648  if (num < 0) {
2649  ast_copy_string(fn, "digits/minus", sizeof(fn));
2650  if ( num > INT_MIN ) {
2651  num = -num;
2652  } else {
2653  num = 0;
2654  }
2655  } else if (playz) {
2656  snprintf(fn, sizeof(fn), "digits/0");
2657  last_length = 0;
2658  playz = 0;
2659  } else if (playh) {
2660  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2661  playh = 0;
2662  } else if (playt) {
2663  snprintf(fn, sizeof(fn), "digits/thousand");
2664  playt = 0;
2665  } else if (num < 10) {
2666  snprintf(buf, 12, "%d", num);
2667  if (last_length - strlen(buf) > 1 && last_length != 0) {
2668  last_length = strlen(buf);
2669  playz++;
2670  continue;
2671  }
2672  snprintf(fn, sizeof(fn), "digits/%d", num);
2673  num = 0;
2674  } else if (num < 100) {
2675  snprintf(buf, 10, "%d", num);
2676  if (last_length - strlen(buf) > 1 && last_length != 0) {
2677  last_length = strlen(buf);
2678  playz++;
2679  continue;
2680  }
2681  last_length = strlen(buf);
2682  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2683  num %= 10;
2684  } else {
2685  if (num < 1000){
2686  snprintf(buf, 10, "%d", num);
2687  if (last_length - strlen(buf) > 1 && last_length != 0) {
2688  last_length = strlen(buf);
2689  playz++;
2690  continue;
2691  }
2692  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2693  playh++;
2694  snprintf(buf, 10, "%d", num);
2695  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2696  last_length = strlen(buf);
2697  num -= ((num / 100) * 100);
2698  } else if (num < 10000){
2699  snprintf(buf, 10, "%d", num);
2700  snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2701  playt++;
2702  snprintf(buf, 10, "%d", num);
2703  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2704  last_length = strlen(buf);
2705  num -= ((num / 1000) * 1000);
2706  } else if (num < 100000000) { /* 100,000,000 */
2707  res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2708  if (res)
2709  return res;
2710  snprintf(buf, 10, "%d", num);
2711  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2712  num -= ((num / 10000) * 10000);
2713  last_length = strlen(buf);
2714  snprintf(fn, sizeof(fn), "digits/wan");
2715  } else {
2716  if (num < 1000000000) { /* 1,000,000,000 */
2717  res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2718  if (res)
2719  return res;
2720  snprintf(buf, 10, "%d", num);
2721  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2722  last_length = strlen(buf);
2723  num -= ((num / 100000000) * 100000000);
2724  snprintf(fn, sizeof(fn), "digits/yi");
2725  } else {
2726  ast_debug(1, "Number '%d' is too big for me\n", num);
2727  res = -1;
2728  }
2729  }
2730  }
2731  if (!res) {
2732  if (!ast_streamfile(chan, fn, language)) {
2733  if ((audiofd > -1) && (ctrlfd > -1))
2734  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2735  else
2736  res = ast_waitstream(chan, ints);
2737  }
2738  ast_stopstream(chan);
2739  }
2740  }
2741  return res;
2742 }
2743 
2744 /*!\internal
2745  * \brief Counting in Urdu, the national language of Pakistan
2746  * \since 1.8
2747  */
2748 static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2749 {
2750  int res = 0;
2751  int playh = 0;
2752  char fn[256] = "";
2753 
2754  if (!num) {
2755  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2756  }
2757 
2758  while (!res && (num || playh)) {
2759  if (playh) {
2760  snprintf(fn, sizeof(fn), "digits/hundred");
2761  playh = 0;
2762  } else if (num < 100) {
2763  snprintf(fn, sizeof(fn), "digits/%d", num);
2764  num = 0;
2765  } else if (num < 1000) {
2766  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2767  playh++;
2768  num -= ((num / 100) * 100);
2769  } else if (num < 100000) { /* 1,00,000 */
2770  if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2771  return res;
2772  }
2773  num = num % 1000;
2774  snprintf(fn, sizeof(fn), "digits/thousand");
2775  } else if (num < 10000000) { /* 1,00,00,000 */
2776  if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2777  return res;
2778  }
2779  num = num % 100000;
2780  snprintf(fn, sizeof(fn), "digits/lac");
2781  } else if (num < 1000000000) { /* 1,00,00,00,000 */
2782  if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2783  return res;
2784  }
2785  num = num % 10000000;
2786  snprintf(fn, sizeof(fn), "digits/crore");
2787  } else {
2788  ast_debug(1, "Number '%d' is too big for me\n", num);
2789  res = -1;
2790  }
2791 
2792  if (!res) {
2793  if (!ast_streamfile(chan, fn, language)) {
2794  if ((audiofd > -1) && (ctrlfd > -1)) {
2795  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2796  } else {
2797  res = ast_waitstream(chan, ints);
2798  }
2799  }
2800  ast_stopstream(chan);
2801  }
2802  }
2803  return res;
2804 }
2805 
2806 /*! \brief determine last digits for thousands/millions (ru) */
2807 static int get_lastdigits_ru(int num) {
2808  if (num < 20) {
2809  return num;
2810  } else if (num < 100) {
2811  return get_lastdigits_ru(num % 10);
2812  } else if (num < 1000) {
2813  return get_lastdigits_ru(num % 100);
2814  }
2815  return 0; /* number too big */
2816 }
2817 
2818 
2819 /*! \brief ast_say_number_full_ru: Russian syntax
2820 
2821  additional files:
2822  n00.gsm (one hundred, two hundred, ...)
2823  thousand.gsm
2824  million.gsm
2825  thousands-i.gsm (tisyachi)
2826  million-a.gsm (milliona)
2827  thousands.gsm
2828  millions.gsm
2829  1f.gsm (odna)
2830  2f.gsm (dve)
2831 
2832  where 'n' from 1 to 9
2833 */
2834 static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2835 {
2836  int res = 0;
2837  int lastdigits = 0;
2838  char fn[256] = "";
2839  if (!num)
2840  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2841 
2842  while (!res && (num)) {
2843  if (num < 0) {
2844  ast_copy_string(fn, "digits/minus", sizeof(fn));
2845  if ( num > INT_MIN ) {
2846  num = -num;
2847  } else {
2848  num = 0;
2849  }
2850  } else if (num < 20) {
2851  if (options && strlen(options) == 1 && num < 3) {
2852  snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2853  } else {
2854  snprintf(fn, sizeof(fn), "digits/%d", num);
2855  }
2856  num = 0;
2857  } else if (num < 100) {
2858  snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2859  num %= 10;
2860  } else if (num < 1000){
2861  snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2862  num %= 100;
2863  } else if (num < 1000000) { /* 1,000,000 */
2864  lastdigits = get_lastdigits_ru(num / 1000);
2865  /* say thousands */
2866  if (lastdigits < 3) {
2867  res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2868  } else {
2869  res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2870  }
2871  if (res)
2872  return res;
2873  if (lastdigits == 1) {
2874  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2875  } else if (lastdigits > 1 && lastdigits < 5) {
2876  ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2877  } else {
2878  ast_copy_string(fn, "digits/thousands", sizeof(fn));
2879  }
2880  num %= 1000;
2881  } else if (num < 1000000000) { /* 1,000,000,000 */
2882  lastdigits = get_lastdigits_ru(num / 1000000);
2883  /* say millions */
2884  res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2885  if (res)
2886  return res;
2887  if (lastdigits == 1) {
2888  ast_copy_string(fn, "digits/million", sizeof(fn));
2889  } else if (lastdigits > 1 && lastdigits < 5) {
2890  ast_copy_string(fn, "digits/million-a", sizeof(fn));
2891  } else {
2892  ast_copy_string(fn, "digits/millions", sizeof(fn));
2893  }
2894  num %= 1000000;
2895  } else {
2896  ast_debug(1, "Number '%d' is too big for me\n", num);
2897  res = -1;
2898  }
2899  if (!res) {
2900  if (!ast_streamfile(chan, fn, language)) {
2901  if ((audiofd > -1) && (ctrlfd > -1))
2902  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2903  else
2904  res = ast_waitstream(chan, ints);
2905  }
2906  ast_stopstream(chan);
2907  }
2908  }
2909  return res;
2910 }
2911 
2912 /*! \brief Thai syntax */
2913 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2914 {
2915  int res = 0;
2916  int playh = 0;
2917  char fn[256] = "";
2918  if (!num)
2919  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2920 
2921  while(!res && (num || playh)) {
2922  if (num < 0) {
2923  ast_copy_string(fn, "digits/lop", sizeof(fn));
2924  if ( num > INT_MIN ) {
2925  num = -num;
2926  } else {
2927  num = 0;
2928  }
2929  } else if (playh) {
2930  ast_copy_string(fn, "digits/roi", sizeof(fn));
2931  playh = 0;
2932  } else if (num < 100) {
2933  if ((num <= 20) || ((num % 10) == 1)) {
2934  snprintf(fn, sizeof(fn), "digits/%d", num);
2935  num = 0;
2936  } else {
2937  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2938  num %= 10;
2939  }
2940  } else if (num < 1000) {
2941  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2942  playh++;
2943  num %= 100;
2944  } else if (num < 10000) { /* 10,000 */
2945  res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2946  if (res)
2947  return res;
2948  num %= 1000;
2949  ast_copy_string(fn, "digits/pan", sizeof(fn));
2950  } else if (num < 100000) { /* 100,000 */
2951  res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2952  if (res)
2953  return res;
2954  num %= 10000;
2955  ast_copy_string(fn, "digits/muan", sizeof(fn));
2956  } else if (num < 1000000) { /* 1,000,000 */
2957  res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2958  if (res)
2959  return res;
2960  num %= 100000;
2961  ast_copy_string(fn, "digits/san", sizeof(fn));
2962  } else {
2963  res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2964  if (res)
2965  return res;
2966  num %= 1000000;
2967  ast_copy_string(fn, "digits/larn", sizeof(fn));
2968  }
2969  if (!res) {
2970  if(!ast_streamfile(chan, fn, language)) {
2971  if ((audiofd > -1) && (ctrlfd > -1))
2972  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2973  else
2974  res = ast_waitstream(chan, ints);
2975  }
2976  ast_stopstream(chan);
2977  }
2978  }
2979  return res;
2980 }
2981 
2982 /*! \brief ast_say_number_full_vi: Vietnamese syntax */
2983 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2984 {
2985  int res = 0;
2986  int playh = 0;
2987  int playoh = 0;
2988  int playohz = 0;
2989  int playz = 0;
2990  int playl = 0;
2991  char fn[256] = "";
2992  if (!num)
2993  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2994  while (!res && (num || playh)) {
2995  if (num < 0) {
2996  ast_copy_string(fn, "digits/minus", sizeof(fn));
2997  if ( num > INT_MIN ) {
2998  num = -num;
2999  } else {
3000  num = 0;
3001  }
3002  } else if (playl) {
3003  snprintf(fn, sizeof(fn), "digits/%da", num);
3004  playl = 0;
3005  num = 0;
3006  } else if (playh) {
3007  ast_copy_string(fn, "digits/hundred", sizeof(fn));
3008  playh = 0;
3009  } else if (playz) {
3010  ast_copy_string(fn, "digits/odd", sizeof(fn));
3011  playz = 0;
3012  } else if (playoh) {
3013  ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
3014  playoh = 0;
3015  } else if (playohz) {
3016  ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
3017  playohz = 0;
3018  } else if (num < 20) {
3019  snprintf(fn, sizeof(fn), "digits/%d", num);
3020  num = 0;
3021  } else if (num < 100) {
3022  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
3023  num %= 10;
3024  if ((num == 5) || (num == 4) || (num == 1)) playl++;
3025  } else {
3026  if (num < 1000) {
3027  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3028  num %= 100;
3029  if (num && (num < 10)) {
3030  playz++;
3031  playh++;
3032  } else {
3033  playh++;
3034  }
3035  } else {
3036  if (num < 1000000) { /* 1,000,000 */
3037  res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
3038  if (res)
3039  return res;
3040  num %= 1000;
3041  snprintf(fn, sizeof(fn), "digits/thousand");
3042  if (num && (num < 10)) {
3043  playohz++;
3044  } else if (num && (num < 100)){
3045  playoh++;
3046  } else {
3047  playh = 0;
3048  playohz = 0;
3049  playoh = 0;
3050  }
3051  } else {
3052  if (num < 1000000000) { /* 1,000,000,000 */
3053  res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3054  if (res)
3055  return res;
3056  num %= 1000000;
3057  ast_copy_string(fn, "digits/million", sizeof(fn));
3058  } else {
3059  res = -1;
3060  }
3061  }
3062  }
3063  }
3064  if (!res) {
3065  if (!ast_streamfile(chan, fn, language)) {
3066  if ((audiofd > -1) && (ctrlfd > -1))
3067  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3068  else
3069  res = ast_waitstream(chan, ints);
3070  }
3071  ast_stopstream(chan);
3072  }
3073  }
3074  return res;
3075 }
3076 
3077 /*! \brief ast_say_enumeration_full: call language-specific functions
3078  * \note Called from AGI */
3079 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3080 {
3081  if (!strncasecmp(language, "en", 2)) { /* English syntax */
3082  return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3083  } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
3084  return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
3085  } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
3086  return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
3087  } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
3088  return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
3089  } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
3090  return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
3091  } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
3092  return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3093  }
3094 
3095  /* Default to english */
3096  return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3097 }
3098 
3099 /*! \brief ast_say_enumeration_full_en: English syntax
3100  \note This is the default syntax, if no other syntax defined in this file is used */
3101 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3102 {
3103  int res = 0, t = 0;
3104  char fn[256] = "";
3105 
3106  while (!res && num) {
3107  if (num < 0) {
3108  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3109  if ( num > INT_MIN ) {
3110  num = -num;
3111  } else {
3112  num = 0;
3113  }
3114  } else if (num < 20) {
3115  snprintf(fn, sizeof(fn), "digits/h-%d", num);
3116  num = 0;
3117  } else if (num < 100) {
3118  int tens = num / 10;
3119  num = num % 10;
3120  if (num == 0) {
3121  snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3122  } else {
3123  snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3124  }
3125  } else if (num < 1000) {
3126  int hundreds = num / 100;
3127  num = num % 100;
3128  if (hundreds > 1 || t == 1) {
3129  res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3130  }
3131  if (res)
3132  return res;
3133  if (num) {
3134  ast_copy_string(fn, "digits/hundred", sizeof(fn));
3135  } else {
3136  ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3137  }
3138  } else if (num < 1000000) {
3139  int thousands = num / 1000;
3140  num = num % 1000;
3141  if (thousands > 1 || t == 1) {
3142  res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3143  }
3144  if (res)
3145  return res;
3146  if (num) {
3147  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3148  } else {
3149  ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3150  }
3151  t = 1;
3152  } else if (num < 1000000000) {
3153  int millions = num / 1000000;
3154  num = num % 1000000;
3155  t = 1;
3156  res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3157  if (res)
3158  return res;
3159  if (num) {
3160  ast_copy_string(fn, "digits/million", sizeof(fn));
3161  } else {
3162  ast_copy_string(fn, "digits/h-million", sizeof(fn));
3163  }
3164  } else if (num < INT_MAX) {
3165  int billions = num / 1000000000;
3166  num = num % 1000000000;
3167  t = 1;
3168  res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3169  if (res)
3170  return res;
3171  if (num) {
3172  ast_copy_string(fn, "digits/billion", sizeof(fn));
3173  } else {
3174  ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3175  }
3176  } else if (num == INT_MAX) {
3177  ast_copy_string(fn, "digits/h-last", sizeof(fn));
3178  num = 0;
3179  } else {
3180  ast_debug(1, "Number '%d' is too big for me\n", num);
3181  res = -1;
3182  }
3183 
3184  if (!res) {
3185  if (!ast_streamfile(chan, fn, language)) {
3186  if ((audiofd > -1) && (ctrlfd > -1)) {
3187  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3188  } else {
3189  res = ast_waitstream(chan, ints);
3190  }
3191  }
3192  ast_stopstream(chan);
3193  }
3194  }
3195  return res;
3196 }
3197 
3198 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3199 {
3200  int res = 0;
3201  char fn[256] = "";
3202  ast_copy_string(fn, "digits/h", sizeof(fn));
3203  if (!res) {
3204  if (!ast_streamfile(chan, fn, language)) {
3205  if ((audiofd > -1) && (ctrlfd > -1)) {
3206  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3207  } else {
3208  res = ast_waitstream(chan, ints);
3209  }
3210  }
3211  ast_stopstream(chan);
3212  }
3213 
3214  return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3215 }
3216 
3217 /*! \brief ast_say_enumeration_full_da: Danish syntax */
3218 static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3219 {
3220  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3221  int res = 0, t = 0;
3222  char fn[256] = "", fna[256] = "";
3223  char *gender;
3224 
3225  if (options && !strncasecmp(options, "f", 1)) {
3226  gender = "F";
3227  } else if (options && !strncasecmp(options, "n", 1)) {
3228  gender = "N";
3229  } else {
3230  gender = "";
3231  }
3232 
3233  if (!num)
3234  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3235 
3236  while (!res && num) {
3237  if (num < 0) {
3238  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3239  if ( num > INT_MIN ) {
3240  num = -num;
3241  } else {
3242  num = 0;
3243  }
3244  } else if (num < 100 && t) {
3245  ast_copy_string(fn, "digits/and", sizeof(fn));
3246  t = 0;
3247  } else if (num < 20) {
3248  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3249  num = 0;
3250  } else if (num < 100) {
3251  int ones = num % 10;
3252  if (ones) {
3253  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3254  num -= ones;
3255  } else {
3256  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3257  num = 0;
3258  }
3259  } else if (num == 100 && t == 0) {
3260  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3261  num = 0;
3262  } else if (num < 1000) {
3263  int hundreds = num / 100;
3264  num = num % 100;
3265  if (hundreds == 1) {
3266  ast_copy_string(fn, "digits/1N", sizeof(fn));
3267  } else {
3268  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3269  }
3270  if (num) {
3271  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3272  } else {
3273  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3274  }
3275  t = 1;
3276  } else if (num < 1000000) {
3277  int thousands = num / 1000;
3278  num = num % 1000;
3279  if (thousands == 1) {
3280  if (num) {
3281  ast_copy_string(fn, "digits/1N", sizeof(fn));
3282  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3283  } else {
3284  if (t) {
3285  ast_copy_string(fn, "digits/1N", sizeof(fn));
3286  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3287  } else {
3288  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3289  }
3290  }
3291  } else {
3292  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3293  if (res) {
3294  return res;
3295  }
3296  if (num) {
3297  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3298  } else {
3299  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3300  }
3301  }
3302  t = 1;
3303  } else if (num < 1000000000) {
3304  int millions = num / 1000000;
3305  num = num % 1000000;
3306  if (millions == 1) {
3307  if (num) {
3308  ast_copy_string(fn, "digits/1F", sizeof(fn));
3309  ast_copy_string(fna, "digits/million", sizeof(fna));
3310  } else {
3311  ast_copy_string(fn, "digits/1N", sizeof(fn));
3312  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3313  }
3314  } else {
3315  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3316  if (res) {
3317  return res;
3318  }
3319  if (num) {
3320  ast_copy_string(fn, "digits/millions", sizeof(fn));
3321  } else {
3322  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3323  }
3324  }
3325  t = 1;
3326  } else if (num < INT_MAX) {
3327  int billions = num / 1000000000;
3328  num = num % 1000000000;
3329  if (billions == 1) {
3330  if (num) {
3331  ast_copy_string(fn, "digits/1F", sizeof(fn));
3332  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3333  } else {
3334  ast_copy_string(fn, "digits/1N", sizeof(fn));
3335  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3336  }
3337  } else {
3338  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3339  if (res)
3340  return res;
3341  if (num) {
3342  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3343  } else {
3344  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3345  }
3346  }
3347  t = 1;
3348  } else if (num == INT_MAX) {
3349  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3350  num = 0;
3351  } else {
3352  ast_debug(1, "Number '%d' is too big for me\n", num);
3353  res = -1;
3354  }
3355 
3356  if (!res) {
3357  if (!ast_streamfile(chan, fn, language)) {
3358  if ((audiofd > -1) && (ctrlfd > -1))
3359  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3360  else
3361  res = ast_waitstream(chan, ints);
3362  }
3363  ast_stopstream(chan);
3364  if (!res) {
3365  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3366  if ((audiofd > -1) && (ctrlfd > -1)) {
3367  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3368  } else {
3369  res = ast_waitstream(chan, ints);
3370  }
3371  }
3372  ast_stopstream(chan);
3373  strcpy(fna, "");
3374  }
3375  }
3376  }
3377  return res;
3378 }
3379 
3380 /*! \brief ast_say_enumeration_full_de: German syntax */
3381 static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3382 {
3383  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3384  int res = 0, t = 0;
3385  char fn[256] = "", fna[256] = "";
3386  char *gender;
3387 
3388  if (options && !strncasecmp(options, "f", 1)) {
3389  gender = "F";
3390  } else if (options && !strncasecmp(options, "n", 1)) {
3391  gender = "N";
3392  } else {
3393  gender = "";
3394  }
3395 
3396  if (!num)
3397  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3398 
3399  while (!res && num) {
3400  if (num < 0) {
3401  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3402  if ( num > INT_MIN ) {
3403  num = -num;
3404  } else {
3405  num = 0;
3406  }
3407  } else if (num < 100 && t) {
3408  ast_copy_string(fn, "digits/and", sizeof(fn));
3409  t = 0;
3410  } else if (num < 20) {
3411  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3412  num = 0;
3413  } else if (num < 100) {
3414  int ones = num % 10;
3415  if (ones) {
3416  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3417  num -= ones;
3418  } else {
3419  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3420  num = 0;
3421  }
3422  } else if (num == 100 && t == 0) {
3423  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3424  num = 0;
3425  } else if (num < 1000) {
3426  int hundreds = num / 100;
3427  num = num % 100;
3428  if (hundreds == 1) {
3429  ast_copy_string(fn, "digits/1N", sizeof(fn));
3430  } else {
3431  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3432  }
3433  if (num) {
3434  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3435  } else {
3436  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3437  }
3438  t = 1;
3439  } else if (num < 1000000) {
3440  int thousands = num / 1000;
3441  num = num % 1000;
3442  if (thousands == 1) {
3443  if (num) {
3444  ast_copy_string(fn, "digits/1N", sizeof(fn));
3445  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3446  } else {
3447  if (t) {
3448  ast_copy_string(fn, "digits/1N", sizeof(fn));
3449  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3450  } else {
3451  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3452  }
3453  }
3454  } else {
3455  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3456  if (res) {
3457  return res;
3458  }
3459  if (num) {
3460  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3461  } else {
3462  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3463  }
3464  }
3465  t = 1;
3466  } else if (num < 1000000000) {
3467  int millions = num / 1000000;
3468  num = num % 1000000;
3469  if (millions == 1) {
3470  if (num) {
3471  ast_copy_string(fn, "digits/1F", sizeof(fn));
3472  ast_copy_string(fna, "digits/million", sizeof(fna));
3473  } else {
3474  ast_copy_string(fn, "digits/1N", sizeof(fn));
3475  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3476  }
3477  } else {
3478  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3479  if (res) {
3480  return res;
3481  }
3482  if (num) {
3483  ast_copy_string(fn, "digits/millions", sizeof(fn));
3484  } else {
3485  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3486  }
3487  }
3488  t = 1;
3489  } else if (num < INT_MAX) {
3490  int billions = num / 1000000000;
3491  num = num % 1000000000;
3492  if (billions == 1) {
3493  if (num) {
3494  ast_copy_string(fn, "digits/1F", sizeof(fn));
3495  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3496  } else {
3497  ast_copy_string(fn, "digits/1N", sizeof(fn));
3498  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3499  }
3500  } else {
3501  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3502  if (res)
3503  return res;
3504  if (num) {
3505  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3506  } else {
3507  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3508  }
3509  }
3510  t = 1;
3511  } else if (num == INT_MAX) {
3512  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3513  num = 0;
3514  } else {
3515  ast_debug(1, "Number '%d' is too big for me\n", num);
3516  res = -1;
3517  }
3518 
3519  if (!res) {
3520  if (!ast_streamfile(chan, fn, language)) {
3521  if ((audiofd > -1) && (ctrlfd > -1))
3522  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3523  else
3524  res = ast_waitstream(chan, ints);
3525  }
3526  ast_stopstream(chan);
3527  if (!res) {
3528  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3529  if ((audiofd > -1) && (ctrlfd > -1)) {
3530  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3531  } else {
3532  res = ast_waitstream(chan, ints);
3533  }
3534  }
3535  ast_stopstream(chan);
3536  strcpy(fna, "");
3537  }
3538  }
3539  }
3540  return res;
3541 }
3542 
3543 static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3544 {
3545  int res = 0;
3546  char fn[256] = "";
3547  int mf = -1; /* +1 = Masculin; -1 = Feminin */
3548  ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
3549 
3550  if (options && !strncasecmp(options, "m", 1)) {
3551  mf = -1;
3552  }
3553 
3554  ast_verb(3, "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
3555 
3556  while (!res && num) {
3557  if (num < 0) {
3558  snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
3559  if (num > INT_MIN) {
3560  num = -num;
3561  } else {
3562  num = 0;
3563  }
3564  } else if (num < 21) {
3565  if (mf < 0) {
3566  if (num < 10) {
3567  snprintf(fn, sizeof(fn), "digits/f-0%d", num);
3568  } else {
3569  snprintf(fn, sizeof(fn), "digits/f-%d", num);
3570  }
3571  } else {
3572  if (num < 10) {
3573  snprintf(fn, sizeof(fn), "digits/m-0%d", num);
3574  } else {
3575  snprintf(fn, sizeof(fn), "digits/m-%d", num);
3576  }
3577  }
3578  num = 0;
3579  } else if ((num < 100) && num >= 20) {
3580  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3581  num = num % 10;
3582  } else if ((num >= 100) && (num < 1000)) {
3583  int tmpnum = num / 100;
3584  snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
3585  num = num - (tmpnum * 100);
3586  } else if ((num >= 1000) && (num < 10000)) {
3587  int tmpnum = num / 1000;
3588  snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
3589  num = num - (tmpnum * 1000);
3590  } else if (num < 20000) {
3591  snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
3592  num = num % 1000;
3593  } else if (num < 1000000) {
3594  res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
3595  if (res) {
3596  return res;
3597  }
3598  snprintf(fn, sizeof(fn), "digits/1k");
3599  num = num % 1000;
3600  } else if (num < 2000000) {
3601  snprintf(fn, sizeof(fn), "digits/1m");
3602  num = num % 1000000;
3603  } else if (num < 3000000) {
3604  snprintf(fn, sizeof(fn), "digits/2m");
3605  num = num - 2000000;
3606  } else if (num < 1000000000) {
3607  res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
3608  if (res) {
3609  return res;
3610  }
3611  snprintf(fn, sizeof(fn), "digits/1m");
3612  num = num % 1000000;
3613  } else {
3614  ast_debug(1, "Number '%d' is too big for me\n", num);
3615  res = -1;
3616  }
3617  if (!res) {
3618  if (!ast_streamfile(chan, fn, language)) {
3619  if ((audiofd > -1) && (ctrlfd > -1)) {
3620  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3621  } else {
3622  res = ast_waitstream(chan, ints);
3623  }
3624  }
3625  ast_stopstream(chan);
3626  }
3627  }
3628  return res;
3629 }
3630 
3631 /*! \brief ast_say_enumeration_full_is: Icelandic syntax */
3632 static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3633 {
3634  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3635  int res = 0, t = 0;
3636  char fn[256] = "", fna[256] = "";
3637  char *gender;
3638 
3639  if (options && !strncasecmp(options, "f", 1)) {
3640  gender = "F";
3641  } else if (options && !strncasecmp(options, "n", 1)) {
3642  gender = "N";
3643  } else {
3644  gender = "";
3645  }
3646 
3647  if (!num)
3648  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3649 
3650  while (!res && num) {
3651  if (num < 0) {
3652  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3653  if ( num > INT_MIN ) {
3654  num = -num;
3655  } else {
3656  num = 0;
3657  }
3658  } else if (num < 100 && t) {
3659  ast_copy_string(fn, "digits/and", sizeof(fn));
3660  t = 0;
3661  } else if (num < 20) {
3662  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3663  num = 0;
3664  } else if (num < 100) {
3665  int ones = num % 10;
3666  if (ones) {
3667  int tens = num - ones;
3668  snprintf(fn, sizeof(fn), "digits/h-%d%s", tens, gender);
3669  num = ones;
3670  t++;
3671  }
3672  else if (t) {
3673  snprintf(fn, sizeof(fn), "digits/and");
3674  t = 0;
3675  }
3676  else {
3677  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3678  num = 0;
3679  }
3680 
3681  } else if (num == 100 && t == 0) {
3682  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3683  num = 0;
3684  } else if (num < 1000) {
3685  int hundreds = num / 100;
3686  num = num % 100;
3687  if (hundreds == 1) {
3688  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3689  } else {
3690  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3691  }
3692  if (num) {
3693  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3694  } else {
3695  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3696  }
3697  t = 1;
3698  } else if (num < 1000000) {
3699  int thousands = num / 1000;
3700  num = num % 1000;
3701  if (thousands == 1) {
3702  if (num) {
3703  /* Thousand is a neutral word, so use the neutral recording */
3704  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3705  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3706  } else {
3707  if (t) {
3708  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3709  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3710  } else {
3711  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3712  }
3713  }
3714  } else {
3715  res = ast_say_number_full_is(chan, thousands, ints, language, options, audiofd, ctrlfd);
3716  if (res) {
3717  return res;
3718  }
3719  if (num) {
3720  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3721  } else {
3722  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3723  }
3724  }
3725  if (num)
3726  t = 1;
3727  } else if (num < 1000000000) {
3728  int millions = num / 1000000;
3729  num = num % 1000000;
3730  if (millions == 1) {
3731  if (num) {
3732  /* Million is a feminine word, so use the female form */
3733  ast_copy_string(fn, "digits/1kvk", sizeof(fn));
3734  ast_copy_string(fna, "digits/million", sizeof(fna));
3735  } else {
3736  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3737  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3738  }
3739  } else {
3740  res = ast_say_number_full_is(chan, millions, ints, language, options, audiofd, ctrlfd);
3741  if (res) {
3742  return res;
3743  }
3744  if (num) {
3745  ast_copy_string(fn, "digits/millions", sizeof(fn));
3746  } else {
3747  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3748  }
3749  }
3750  if (num)
3751  t = 1;
3752  } else if (num < INT_MAX) {
3753  int billions = num / 1000000000;
3754  num = num % 1000000000;
3755  if (billions == 1) {
3756  if (num) {
3757  ast_copy_string(fn, "digits/1", sizeof(fn));
3758  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3759  } else {
3760  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3761  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3762  }
3763  } else {
3764  res = ast_say_number_full_is(chan, billions, ints, language, options, audiofd, ctrlfd);
3765  if (res)
3766  return res;
3767  if (num) {
3768  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3769  } else {
3770  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3771  }
3772  }
3773  if (num)
3774  t = 1;
3775  } else if (num == INT_MAX) {
3776  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3777  num = 0;
3778  } else {
3779  ast_debug(1, "Number '%d' is too big for me\n", num);
3780  res = -1;
3781  }
3782 
3783  if (!res) {
3784  if (!ast_streamfile(chan, fn, language)) {
3785  if ((audiofd > -1) && (ctrlfd > -1))
3786  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3787  else
3788  res = ast_waitstream(chan, ints);
3789  }
3790  ast_stopstream(chan);
3791  if (!res) {
3792  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3793  if ((audiofd > -1) && (ctrlfd > -1)) {
3794  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3795  } else {
3796  res = ast_waitstream(chan, ints);
3797  }
3798  }
3799  ast_stopstream(chan);
3800  strcpy(fna, "");
3801  }
3802  }
3803  }
3804  return res;
3805 }
3806 
3807 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3808 {
3809  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
3810  return ast_say_date_en(chan, t, ints, lang);
3811  } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
3812  return ast_say_date_da(chan, t, ints, lang);
3813  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
3814  return ast_say_date_de(chan, t, ints, lang);
3815  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
3816  return ast_say_date_fr(chan, t, ints, lang);
3817  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
3818  return ast_say_date_gr(chan, t, ints, lang);
3819  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
3820  return ast_say_date_ja(chan, t, ints, lang);
3821  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
3822  return ast_say_date_he(chan, t, ints, lang);
3823  } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
3824  return ast_say_date_hu(chan, t, ints, lang);
3825  } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
3826  return ast_say_date_is(chan, t, ints, lang);
3827  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
3828  return ast_say_date_ka(chan, t, ints, lang);
3829  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
3830  return ast_say_date_nl(chan, t, ints, lang);
3831  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
3832  return ast_say_date_pt(chan, t, ints, lang);
3833  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
3834  return ast_say_date_th(chan, t, ints, lang);
3835  }
3836 
3837  /* Default to English */
3838  return ast_say_date_en(chan, t, ints, lang);
3839 }
3840 
3841 /*! \brief English syntax */
3842 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3843 {
3844  struct ast_tm tm;
3845  struct timeval when = { t, 0 };
3846  char fn[256];
3847  int res = 0;
3848  ast_localtime(&when, &tm, NULL);
3849  if (!res) {
3850  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3851  res = ast_streamfile(chan, fn, lang);
3852  if (!res)
3853  res = ast_waitstream(chan, ints);
3854  }
3855  if (!res) {
3856  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3857  res = ast_streamfile(chan, fn, lang);
3858  if (!res)
3859  res = ast_waitstream(chan, ints);
3860  }
3861  if (!res)
3862  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3863  if (!res)
3864  res = ast_waitstream(chan, ints);
3865  if (!res)
3866  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3867  return res;
3868 }
3869 
3870 /*! \brief Danish syntax */
3871 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3872 {
3873  struct timeval when = { t, 0 };
3874  struct ast_tm tm;
3875  char fn[256];
3876  int res = 0;
3877  ast_localtime(&when, &tm, NULL);
3878  if (!res) {
3879  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3880  res = ast_streamfile(chan, fn, lang);
3881  if (!res)
3882  res = ast_waitstream(chan, ints);
3883  }
3884  if (!res)
3885  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3886  if (!res)
3887  res = ast_waitstream(chan, ints);
3888  if (!res) {
3889  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3890  res = ast_streamfile(chan, fn, lang);
3891  if (!res)
3892  res = ast_waitstream(chan, ints);
3893  }
3894  if (!res) {
3895  /* Year */
3896  int year = tm.tm_year + 1900;
3897  if (year > 1999) { /* year 2000 and later */
3898  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3899  } else {
3900  if (year < 1100) {
3901  /* I'm not going to handle 1100 and prior */
3902  /* We'll just be silent on the year, instead of bombing out. */
3903  } else {
3904  /* year 1100 to 1999. will anybody need this?!? */
3905  snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
3906  res = wait_file(chan, ints, fn, lang);
3907  if (!res) {
3908  res = wait_file(chan, ints, "digits/hundred", lang);
3909  if (!res && year % 100 != 0) {
3910  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3911  }
3912  }
3913  }
3914  }
3915  }
3916  return res;
3917 }
3918 
3919 /*! \brief German syntax */
3920 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3921 {
3922  struct timeval when = { t, 0 };
3923  struct ast_tm tm;
3924  char fn[256];
3925  int res = 0;
3926  ast_localtime(&when, &tm, NULL);
3927  if (!res) {
3928  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3929  res = ast_streamfile(chan, fn, lang);
3930  if (!res)
3931  res = ast_waitstream(chan, ints);
3932  }
3933  if (!res)
3934  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3935  if (!res)
3936  res = ast_waitstream(chan, ints);
3937  if (!res) {
3938  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3939  res = ast_streamfile(chan, fn, lang);
3940  if (!res)
3941  res = ast_waitstream(chan, ints);
3942  }
3943  if (!res) {
3944  /* Year */
3945  int year = tm.tm_year + 1900;
3946  if (year > 1999) { /* year 2000 and later */
3947  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3948  } else {
3949  if (year < 1100) {
3950  /* I'm not going to handle 1100 and prior */
3951  /* We'll just be silent on the year, instead of bombing out. */
3952  } else {
3953  /* year 1100 to 1999. will anybody need this?!? */
3954  /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3955  snprintf(fn, sizeof(fn), "digits/%d", (year / 100) );
3956  res = wait_file(chan, ints, fn, lang);
3957  if (!res) {
3958  res = wait_file(chan, ints, "digits/hundred", lang);
3959  if (!res && year % 100 != 0) {
3960  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3961  }
3962  }
3963  }
3964  }
3965  }
3966  return res;
3967 }
3968 
3969 /*! \brief Hungarian syntax */
3970 int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3971 {
3972  struct timeval when = { t, 0 };
3973  struct ast_tm tm;
3974  char fn[256];
3975  int res = 0;
3976  ast_localtime(&when, &tm, NULL);
3977 
3978  if (!res)
3979  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3980  if (!res)
3981  res = ast_waitstream(chan, ints);
3982  if (!res) {
3983  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3984  res = ast_streamfile(chan, fn, lang);
3985  if (!res)
3986  res = ast_waitstream(chan, ints);
3987  }
3988  if (!res)
3989  ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
3990  if (!res)
3991  res = ast_waitstream(chan, ints);
3992  if (!res) {
3993  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3994  res = ast_streamfile(chan, fn, lang);
3995  if (!res)
3996  res = ast_waitstream(chan, ints);
3997  }
3998  return res;
3999 }
4000 
4001 /*! \brief French syntax */
4002 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4003 {
4004  struct timeval when = { t, 0 };
4005  struct ast_tm tm;
4006  char fn[256];
4007  int res = 0;
4008  ast_localtime(&when, &tm, NULL);
4009  if (!res) {
4010  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4011  res = ast_streamfile(chan, fn, lang);
4012  if (!res)
4013  res = ast_waitstream(chan, ints);
4014  }
4015  if (!res)
4016  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4017  if (!res)
4018  res = ast_waitstream(chan, ints);
4019  if (!res) {
4020  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4021  res = ast_streamfile(chan, fn, lang);
4022  if (!res)
4023  res = ast_waitstream(chan, ints);
4024  }
4025  if (!res)
4026  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4027  return res;
4028 }
4029 
4030 /*! \brief Dutch syntax */
4031 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4032 {
4033  struct timeval when = { t, 0 };
4034  struct ast_tm tm;
4035  char fn[256];
4036  int res = 0;
4037  ast_localtime(&when, &tm, NULL);
4038  if (!res) {
4039  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4040  res = ast_streamfile(chan, fn, lang);
4041  if (!res)
4042  res = ast_waitstream(chan, ints);
4043  }
4044  if (!res)
4045  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4046  if (!res) {
4047  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4048  res = ast_streamfile(chan, fn, lang);
4049  if (!res)
4050  res = ast_waitstream(chan, ints);
4051  }
4052  if (!res)
4053  res = ast_waitstream(chan, ints);
4054  if (!res)
4055  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4056  return res;
4057 }
4058 
4059 /*! \brief Thai syntax */
4060 int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4061 {
4062  struct timeval when = { t, 0 };
4063  struct ast_tm tm;
4064  char fn[256];
4065  int res = 0;
4066  ast_localtime(&when, &tm, NULL);
4067  if (!res) {
4068  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4069  res = ast_streamfile(chan, fn, lang);
4070  ast_copy_string(fn, "digits/tee", sizeof(fn));
4071  res = ast_streamfile(chan, fn, lang);
4072  if (!res)
4073  res = ast_waitstream(chan, ints);
4074  }
4075  if (!res)
4076  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4077  if (!res)
4078  res = ast_waitstream(chan, ints);
4079  if (!res) {
4080  ast_copy_string(fn, "digits/duan", sizeof(fn));
4081  res = ast_streamfile(chan, fn, lang);
4082  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4083  res = ast_streamfile(chan, fn, lang);
4084  if (!res)
4085  res = ast_waitstream(chan, ints);
4086  }
4087  if (!res){
4088  ast_copy_string(fn, "digits/posor", sizeof(fn));
4089  res = ast_streamfile(chan, fn, lang);
4090  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4091  }
4092  return res;
4093 }
4094 
4095 /*! \brief Portuguese syntax */
4096 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4097 {
4098  struct timeval when = { t, 0 };
4099  struct ast_tm tm;
4100  char fn[256];
4101  int res = 0;
4102 
4103  ast_localtime(&when, &tm, NULL);
4104  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4105  if (!res)
4106  res = wait_file(chan, ints, fn, lang);
4107  if (!res)
4108  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4109  if (!res)
4110  res = wait_file(chan, ints, "digits/pt-de", lang);
4111  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4112  if (!res)
4113  res = wait_file(chan, ints, fn, lang);
4114  if (!res)
4115  res = wait_file(chan, ints, "digits/pt-de", lang);
4116  if (!res)
4117  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4118 
4119  return res;
4120 }
4121 
4122 /*! \brief Hebrew syntax */
4123 int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4124 {
4125  struct timeval when = { t, 0 };
4126  struct ast_tm tm;
4127  char fn[256];
4128  int res = 0;
4129  ast_localtime(&when, &tm, NULL);
4130  if (!res) {
4131  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4132  res = ast_streamfile(chan, fn, lang);
4133  if (!res) {
4134  res = ast_waitstream(chan, ints);
4135  }
4136  }
4137  if (!res) {
4138  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4139  res = ast_streamfile(chan, fn, lang);
4140  if (!res) {
4141  res = ast_waitstream(chan, ints);
4142  }
4143  }
4144  if (!res) {
4145  res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
4146  }
4147  if (!res) {
4148  res = ast_waitstream(chan, ints);
4149  }
4150  if (!res) {
4151  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
4152  }
4153  return res;
4154 }
4155 
4156 /* Icelandic syntax */
4157 int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4158 {
4159  struct timeval when = { t, 0 };
4160  struct ast_tm tm;
4161  char fn[256];
4162  int res = 0;
4163  ast_localtime(&when, &tm, NULL);
4164  if (!res) {
4165  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4166  res = ast_streamfile(chan, fn, lang);
4167  if (!res)
4168  res = ast_waitstream(chan, ints);
4169  }
4170  if (!res)
4171  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4172  if (!res)
4173  res = ast_waitstream(chan, ints);
4174  if (!res) {
4175  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4176  res = ast_streamfile(chan, fn, lang);
4177  if (!res)
4178  res = ast_waitstream(chan, ints);
4179  }
4180  if (!res) {
4181  /* Year */
4182  int year = tm.tm_year + 1900;
4183  if (year > 1999) { /* year 2000 and later */
4184  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4185  } else {
4186  if (year < 1100) {
4187  /* I'm not going to handle 1100 and prior */
4188  /* We'll just be silent on the year, instead of bombing out. */
4189  } else {
4190  /* year 1100 to 1999. will anybody need this?!? */
4191  snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4192  res = wait_file(chan, ints, fn, lang);
4193  if (!res) {
4194  res = wait_file(chan, ints, "digits/hundred", lang);
4195  if (!res && year % 100 != 0) {
4196  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4197  }
4198  }
4199  }
4200  }
4201  }
4202  return res;
4203 }
4204 
4205 static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4206 {
4207  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
4208  return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4209  } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
4210  return ast_say_date_with_format_da(chan, t, ints, lang, format, tzone);
4211  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
4212  return ast_say_date_with_format_de(chan, t, ints, lang, format, tzone);
4213  } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
4214  return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
4215  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
4216  return ast_say_date_with_format_he(chan, t, ints, lang, format, tzone);
4217  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
4218  return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone);
4219  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
4220  return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone);
4221  } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
4222  return ast_say_date_with_format_is(chan, t, ints, lang, format, tzone);
4223  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
4224  return ast_say_date_with_format_ja(chan, t, ints, lang, format, tzone);
4225  } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
4226  return ast_say_date_with_format_it(chan, t, ints, lang, format, tzone);
4227  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
4228  return ast_say_date_with_format_nl(chan, t, ints, lang, format, tzone);
4229  } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
4230  return ast_say_date_with_format_pl(chan, t, ints, lang, format, tzone);
4231  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
4232  return ast_say_date_with_format_pt(chan, t, ints, lang, format, tzone);
4233  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
4234  return ast_say_date_with_format_th(chan, t, ints, lang, format, tzone);
4235  } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
4236  return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
4237  } else if (!strncasecmp(lang, "vi", 2)) { /* Vietnamese syntax */
4238  return ast_say_date_with_format_vi(chan, t, ints, lang, format, tzone);
4239  }
4240 
4241  /* Default to English */
4242  return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4243 }
4244 
4245 /*! \brief English syntax */
4246 int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4247 {
4248  struct timeval when = { t, 0 };
4249  struct ast_tm tm;
4250  int res=0, offset, sndoffset;
4251  char sndfile[256], nextmsg[256];
4252 
4253  if (format == NULL)
4254  format = "ABdY 'digits/at' IMp";
4255 
4256  ast_localtime(&when, &tm, tzone);
4257 
4258  for (offset=0 ; format[offset] != '\0' ; offset++) {
4259  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4260  switch (format[offset]) {
4261  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4262  case '\'':
4263  /* Literal name of a sound file */
4264  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4265  sndfile[sndoffset] = format[offset];
4266  }
4267  sndfile[sndoffset] = '\0';
4268  res = wait_file(chan, ints, sndfile, lang);
4269  break;
4270  case 'A':
4271  case 'a':
4272  /* Sunday - Saturday */
4273  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4274  res = wait_file(chan, ints, nextmsg, lang);
4275  break;
4276  case 'B':
4277  case 'b':
4278  case 'h':
4279  /* January - December */
4280  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4281  res = wait_file(chan, ints, nextmsg, lang);
4282  break;
4283  case 'm':
4284  /* Month enumerated */
4285  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
4286  break;
4287  case 'd':
4288  case 'e':
4289  /* First - Thirtyfirst */
4290  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
4291  break;
4292  case 'Y':
4293  /* Year */
4294  if (tm.tm_year > 99) {
4295  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4296  } else if (tm.tm_year < 1) {
4297  /* I'm not going to handle 1900 and prior */
4298  /* We'll just be silent on the year, instead of bombing out. */
4299  } else {
4300  res = wait_file(chan, ints, "digits/19", lang);
4301  if (!res) {
4302  if (tm.tm_year <= 9) {
4303  /* 1901 - 1909 */
4304  res = wait_file(chan, ints, "digits/oh", lang);
4305  }
4306 
4307  res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
4308  }
4309  }
4310  break;
4311  case 'I':
4312  case 'l':
4313  /* 12-Hour */
4314  if (tm.tm_hour == 0)
4315  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4316  else if (tm.tm_hour > 12)
4317  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4318  else
4319  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4320  res = wait_file(chan, ints, nextmsg, lang);
4321  break;
4322  case 'H':
4323  case 'k':
4324  /* 24-Hour */
4325  if (format[offset] == 'H') {
4326  /* e.g. oh-eight */
4327  if (tm.tm_hour < 10) {
4328  res = wait_file(chan, ints, "digits/oh", lang);
4329  }
4330  } else {
4331  /* e.g. eight */
4332  if (tm.tm_hour == 0) {
4333  res = wait_file(chan, ints, "digits/oh", lang);
4334  }
4335  }
4336  if (!res) {
4337  if (tm.tm_hour != 0) {
4338  int remaining = tm.tm_hour;
4339  if (tm.tm_hour > 20) {
4340  res = wait_file(chan, ints, "digits/20", lang);
4341  remaining -= 20;
4342  }
4343  if (!res) {
4344  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
4345  res = wait_file(chan, ints, nextmsg, lang);
4346  }
4347  }
4348  }
4349  break;
4350  case 'M':
4351  case 'N':
4352  /* Minute */
4353  if (tm.tm_min == 0) {
4354  if (format[offset] == 'M') {
4355  res = wait_file(chan, ints, "digits/oclock", lang);
4356  } else {
4357  res = wait_file(chan, ints, "digits/hundred", lang);
4358  }
4359  } else if (tm.tm_min < 10) {
4360  res = wait_file(chan, ints, "digits/oh", lang);
4361  if (!res) {
4362  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
4363  res = wait_file(chan, ints, nextmsg, lang);
4364  }
4365  } else {
4366  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4367  }
4368  break;
4369  case 'P':
4370  case 'p':
4371  /* AM/PM */
4372  if (tm.tm_hour > 11)
4373  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4374  else
4375  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4376  res = wait_file(chan, ints, nextmsg, lang);
4377  break;
4378  case 'Q':
4379  /* Shorthand for "Today", "Yesterday", or ABdY */
4380  /* XXX As emphasized elsewhere, this should the native way in your
4381  * language to say the date, with changes in what you say, depending
4382  * upon how recent the date is. XXX */
4383  {
4384  struct timeval now = ast_tvnow();
4385  struct ast_tm tmnow;
4386  time_t beg_today;
4387 
4388  gettimeofday(&now, NULL);
4389  ast_localtime(&now, &tmnow, tzone);
4390  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4391  /* In any case, it saves not having to do ast_mktime() */
4392  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4393  if (beg_today < t) {
4394  /* Today */
4395  res = wait_file(chan, ints, "digits/today", lang);
4396  } else if (beg_today - 86400 < t) {
4397  /* Yesterday */
4398  res = wait_file(chan, ints, "digits/yesterday", lang);
4399  } else if (beg_today - 86400 * 6 < t) {
4400  /* Within the last week */
4401  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4402  } else if (beg_today - 2628000 < t) {
4403  /* Less than a month ago - "Sunday, October third" */
4404  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4405  } else if (beg_today - 15768000 < t) {
4406  /* Less than 6 months ago - "August seventh" */
4407  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4408  } else {
4409  /* More than 6 months ago - "April nineteenth two thousand three" */
4410  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4411  }
4412  }
4413  break;
4414  case 'q':
4415  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4416  /* XXX As emphasized elsewhere, this should the native way in your
4417  * language to say the date, with changes in what you say, depending
4418  * upon how recent the date is. XXX */
4419  {
4420  struct timeval now;
4421  struct ast_tm tmnow;
4422  time_t beg_today;
4423 
4424  now = ast_tvnow();
4425  ast_localtime(&now, &tmnow, tzone);
4426  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4427  /* In any case, it saves not having to do ast_mktime() */
4428  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4429  if (beg_today < t) {
4430  /* Today */
4431  } else if ((beg_today - 86400) < t) {
4432  /* Yesterday */
4433  res = wait_file(chan, ints, "digits/yesterday", lang);
4434  } else if (beg_today - 86400 * 6 < t) {
4435  /* Within the last week */
4436  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4437  } else if (beg_today - 2628000 < t) {
4438  /* Less than a month ago - "Sunday, October third" */
4439  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4440  } else if (beg_today - 15768000 < t) {
4441  /* Less than 6 months ago - "August seventh" */
4442  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4443  } else {
4444  /* More than 6 months ago - "April nineteenth two thousand three" */
4445  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4446  }
4447  }
4448  break;
4449  case 'R':
4450  res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
4451  break;
4452  case 'S':
4453  /* Seconds */
4454  if (tm.tm_sec == 0) {
4455  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4456  res = wait_file(chan, ints, nextmsg, lang);
4457  } else if (tm.tm_sec < 10) {
4458  res = wait_file(chan, ints, "digits/oh", lang);
4459  if (!res) {
4460  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4461  res = wait_file(chan, ints, nextmsg, lang);
4462  }
4463  } else {
4464  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4465  }
4466  break;
4467  case 'T':
4468  res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
4469  break;
4470  case ' ':
4471  case ' ':
4472  /* Just ignore spaces and tabs */
4473  break;
4474  default:
4475  /* Unknown character */
4476  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4477  }
4478  /* Jump out on DTMF */
4479  if (res) {
4480  break;
4481  }
4482  }
4483  return res;
4484 }
4485 
4486 static char next_item(const char *format)
4487 {
4488  const char *next = ast_skip_blanks(format);
4489  return *next;
4490 }
4491 
4492 /*! \brief Danish syntax */
4493 int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4494 {
4495  struct timeval when = { t, 0 };
4496  struct ast_tm tm;
4497  int res=0, offset, sndoffset;
4498  char sndfile[256], nextmsg[256];
4499 
4500  if (!format)
4501  format = "A dBY HMS";
4502 
4503  ast_localtime(&when, &tm, tzone);
4504 
4505  for (offset=0 ; format[offset] != '\0' ; offset++) {
4506  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4507  switch (format[offset]) {
4508  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4509  case '\'':
4510  /* Literal name of a sound file */
4511  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4512  sndfile[sndoffset] = format[offset];
4513  }
4514  sndfile[sndoffset] = '\0';
4515  res = wait_file(chan, ints, sndfile, lang);
4516  break;
4517  case 'A':
4518  case 'a':
4519  /* Sunday - Saturday */
4520  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4521  res = wait_file(chan, ints, nextmsg, lang);
4522  break;
4523  case 'B':
4524  case 'b':
4525  case 'h':
4526  /* January - December */
4527  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4528  res = wait_file(chan, ints, nextmsg, lang);
4529  break;
4530  case 'm':
4531  /* Month enumerated */
4532  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4533  break;
4534  case 'd':
4535  case 'e':
4536  /* First - Thirtyfirst */
4537  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4538  break;
4539  case 'Y':
4540  /* Year */
4541  {
4542  int year = tm.tm_year + 1900;
4543  if (year > 1999) { /* year 2000 and later */
4544  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4545  } else {
4546  if (year < 1100) {
4547  /* I'm not going to handle 1100 and prior */
4548  /* We'll just be silent on the year, instead of bombing out. */
4549  } else {
4550  /* year 1100 to 1999. will anybody need this?!? */
4551  /* say 1967 as 'nineteen hundred seven and sixty' */
4552  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4553  res = wait_file(chan, ints, nextmsg, lang);
4554  if (!res) {
4555  res = wait_file(chan, ints, "digits/hundred", lang);
4556  if (!res && year % 100 != 0) {
4557  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4558  }
4559  }
4560  }
4561  }
4562  }
4563  break;
4564  case 'I':
4565  case 'l':
4566  /* 12-Hour */
4567  res = wait_file(chan, ints, "digits/oclock", lang);
4568  if (tm.tm_hour == 0)
4569  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4570  else if (tm.tm_hour > 12)
4571  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4572  else
4573  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4574  if (!res) {
4575  res = wait_file(chan, ints, nextmsg, lang);
4576  }
4577  break;
4578  case 'H':
4579  /* 24-Hour, single digit hours preceded by "oh" (0) */
4580  if (tm.tm_hour < 10 && tm.tm_hour > 0) {
4581  res = wait_file(chan, ints, "digits/0", lang);
4582  }
4583  /* FALLTRHU */
4584  case 'k':
4585  /* 24-Hour */
4586  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4587  break;
4588  case 'M':
4589  /* Minute */
4590  if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4591  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4592  }
4593  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4594  if (tm.tm_min == 1) {
4595  res = wait_file(chan, ints, "minute", lang);
4596  } else {
4597  res = wait_file(chan, ints, "minutes", lang);
4598  }
4599  }
4600  break;
4601  case 'P':
4602  case 'p':
4603  /* AM/PM */
4604  if (tm.tm_hour > 11)
4605  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4606  else
4607  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4608  res = wait_file(chan, ints, nextmsg, lang);
4609  break;
4610  case 'Q':
4611  /* Shorthand for "Today", "Yesterday", or AdBY */
4612  /* XXX As emphasized elsewhere, this should the native way in your
4613  * language to say the date, with changes in what you say, depending
4614  * upon how recent the date is. XXX */
4615  {
4616  struct timeval now = ast_tvnow();
4617  struct ast_tm tmnow;
4618  time_t beg_today;
4619 
4620  ast_localtime(&now, &tmnow, tzone);
4621  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4622  /* In any case, it saves not having to do ast_mktime() */
4623  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4624  if (beg_today < t) {
4625  /* Today */
4626  res = wait_file(chan, ints, "digits/today", lang);
4627  } else if (beg_today - 86400 < t) {
4628  /* Yesterday */
4629  res = wait_file(chan, ints, "digits/yesterday", lang);
4630  } else {
4631  res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4632  }
4633  }
4634  break;
4635  case 'q':
4636  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4637  /* XXX As emphasized elsewhere, this should the native way in your
4638  * language to say the date, with changes in what you say, depending
4639  * upon how recent the date is. XXX */
4640  {
4641  struct timeval now = ast_tvnow();
4642  struct ast_tm tmnow;
4643  time_t beg_today;
4644 
4645  ast_localtime(&now, &tmnow, tzone);
4646  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4647  /* In any case, it saves not having to do ast_mktime() */
4648  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4649  if (beg_today < t) {
4650  /* Today */
4651  } else if ((beg_today - 86400) < t) {
4652  /* Yesterday */
4653  res = wait_file(chan, ints, "digits/yesterday", lang);
4654  } else if (beg_today - 86400 * 6 < t) {
4655  /* Within the last week */
4656  res = ast_say_date_with_format_da(chan, t, ints, lang, "A", tzone);
4657  } else {
4658  res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4659  }
4660  }
4661  break;
4662  case 'R':
4663  res = ast_say_date_with_format_da(chan, t, ints, lang, "HM", tzone);
4664  break;
4665  case 'S':
4666  /* Seconds */
4667  res = wait_file(chan, ints, "digits/and", lang);
4668  if (!res) {
4669  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
4670  if (!res) {
4671  res = wait_file(chan, ints, "seconds", lang);
4672  }
4673  }
4674  break;
4675  case 'T':
4676  res = ast_say_date_with_format_da(chan, t, ints, lang, "HMS", tzone);
4677  break;
4678  case ' ':
4679  case ' ':
4680  /* Just ignore spaces and tabs */
4681  break;
4682  default:
4683  /* Unknown character */
4684  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4685  }
4686  /* Jump out on DTMF */
4687  if (res) {
4688  break;
4689  }
4690  }
4691  return res;
4692 }
4693 
4694 /*! \brief German syntax */
4695 int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4696 {
4697  struct timeval when = { t, 0 };
4698  struct ast_tm tm;
4699  int res=0, offset, sndoffset;
4700  char sndfile[256], nextmsg[256];
4701 
4702  if (!format)
4703  format = "A dBY HMS";
4704 
4705  ast_localtime(&when, &tm, tzone);
4706 
4707  for (offset=0 ; format[offset] != '\0' ; offset++) {
4708  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4709  switch (format[offset]) {
4710  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4711  case '\'':
4712  /* Literal name of a sound file */
4713  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4714  sndfile[sndoffset] = format[offset];
4715  }
4716  sndfile[sndoffset] = '\0';
4717  res = wait_file(chan, ints, sndfile, lang);
4718  break;
4719  case 'A':
4720  case 'a':
4721  /* Sunday - Saturday */
4722  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4723  res = wait_file(chan, ints, nextmsg, lang);
4724  break;
4725  case 'B':
4726  case 'b':
4727  case 'h':
4728  /* January - December */
4729  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4730  res = wait_file(chan, ints, nextmsg, lang);
4731  break;
4732  case 'm':
4733  /* Month enumerated */
4734  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4735  break;
4736  case 'd':
4737  case 'e':
4738  /* First - Thirtyfirst */
4739  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4740  break;
4741  case 'Y':
4742  /* Year */
4743  {
4744  int year = tm.tm_year + 1900;
4745  if (year > 1999) { /* year 2000 and later */
4746  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4747  } else {
4748  if (year < 1100) {
4749  /* I'm not going to handle 1100 and prior */
4750  /* We'll just be silent on the year, instead of bombing out. */
4751  } else {
4752  /* year 1100 to 1999. will anybody need this?!? */
4753  /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4754  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4755  res = wait_file(chan, ints, nextmsg, lang);
4756  if (!res) {
4757  res = wait_file(chan, ints, "digits/hundred", lang);
4758  if (!res && year % 100 != 0) {
4759  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4760  }
4761  }
4762  }
4763  }
4764  }
4765  break;
4766  case 'I':
4767  case 'l':
4768  /* 12-Hour */
4769  if (tm.tm_hour == 0)
4770  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4771  else if (tm.tm_hour > 12)
4772  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4773  else
4774  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4775  res = wait_file(chan, ints, nextmsg, lang);
4776  if (!res) {
4777  res = wait_file(chan, ints, "digits/oclock", lang);
4778  }
4779  break;
4780  case 'H':
4781  case 'k':
4782  /* 24-Hour */
4783  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4784  if (!res) {
4785  res = wait_file(chan, ints, "digits/oclock", lang);
4786  }
4787  break;
4788  case 'M':
4789  /* Minute */
4790  if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4791  res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say minutes */
4792  } else if (tm.tm_min > 0) {
4793  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4794  }
4795 
4796  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4797  if (tm.tm_min == 1) {
4798  res = wait_file(chan, ints, "minute", lang);
4799  } else {
4800  res = wait_file(chan, ints, "minutes", lang);
4801  }
4802  }
4803  break;
4804  case 'P':
4805  case 'p':
4806  /* AM/PM */
4807  if (tm.tm_hour > 11)
4808  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4809  else
4810  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4811  res = wait_file(chan, ints, nextmsg, lang);
4812  break;
4813  case 'Q':
4814  /* Shorthand for "Today", "Yesterday", or AdBY */
4815  /* XXX As emphasized elsewhere, this should the native way in your
4816  * language to say the date, with changes in what you say, depending
4817  * upon how recent the date is. XXX */
4818  {
4819  struct timeval now = ast_tvnow();
4820  struct ast_tm tmnow;
4821  time_t beg_today;
4822 
4823  ast_localtime(&now, &tmnow, tzone);
4824  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4825  /* In any case, it saves not having to do ast_mktime() */
4826  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4827  if (beg_today < t) {
4828  /* Today */
4829  res = wait_file(chan, ints, "digits/today", lang);
4830  } else if (beg_today - 86400 < t) {
4831  /* Yesterday */
4832  res = wait_file(chan, ints, "digits/yesterday", lang);
4833  } else {
4834  res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
4835  }
4836  }
4837  break;
4838  case 'q':
4839  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4840  /* XXX As emphasized elsewhere, this should the native way in your
4841  * language to say the date, with changes in what you say, depending
4842  * upon how recent the date is. XXX */
4843  {
4844  struct timeval now = ast_tvnow();
4845  struct ast_tm tmnow;
4846  time_t beg_today;
4847 
4848  ast_localtime(&now, &tmnow, tzone);
4849  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4850  /* In any case, it saves not having to do ast_mktime() */
4851  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4852  if (beg_today < t) {
4853  /* Today */
4854  } else if ((beg_today - 86400) < t) {
4855  /* Yesterday */
4856  res = wait_file(chan, ints, "digits/yesterday", lang);
4857  } else if (beg_today - 86400 * 6 < t) {
4858  /* Within the last week */
4859  res = ast_say_date_with_format_de(chan, t, ints, lang, "A", tzone);
4860  } else {
4861  res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
4862  }
4863  }
4864  break;
4865  case 'R':
4866  res = ast_say_date_with_format_de(chan, t, ints, lang, "HM", tzone);
4867  break;
4868  case 'S':
4869  /* Seconds */
4870  res = wait_file(chan, ints, "digits/and", lang);
4871  if (!res) {
4872  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
4873  if (!res) {
4874  res = wait_file(chan, ints, tm.tm_sec == 1 ? "second" : "seconds", lang);
4875  }
4876  }
4877  break;
4878  case 'T':
4879  res = ast_say_date_with_format_de(chan, t, ints, lang, "HMS", tzone);
4880  break;
4881  case ' ':
4882  case ' ':
4883  /* Just ignore spaces and tabs */
4884  break;
4885  default:
4886  /* Unknown character */
4887  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4888  }
4889  /* Jump out on DTMF */
4890  if (res) {
4891  break;
4892  }
4893  }
4894  return res;
4895 }
4896 
4897 /* Icelandic syntax */
4898 int ast_say_date_with_format_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
4899 {
4900  struct timeval when = { t, 0 };
4901  struct ast_tm tm;
4902  int res=0, offset, sndoffset;
4903  char sndfile[256], nextmsg[256];
4904 
4905  if (!format)
4906  format = "A dBY HMS";
4907 
4908  ast_localtime(&when, &tm, tzone);
4909 
4910  for (offset=0 ; format[offset] != '\0' ; offset++) {
4911  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4912  switch (format[offset]) {
4913  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4914  case '\'':
4915  /* Literal name of a sound file */
4916  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4917  sndfile[sndoffset] = format[offset];
4918  }
4919  sndfile[sndoffset] = '\0';
4920  res = wait_file(chan, ints, sndfile, lang);
4921  break;
4922  case 'A':
4923  case 'a':
4924  /* Sunday - Saturday */
4925  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4926  res = wait_file(chan, ints, nextmsg, lang);
4927  break;
4928  case 'B':
4929  case 'b':
4930  case 'h':
4931  /* January - December */
4932  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4933  res = wait_file(chan, ints, nextmsg, lang);
4934  break;
4935  case 'm':
4936  /* Month enumerated */
4937  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4938  break;
4939  case 'd':
4940  case 'e':
4941  /* First - Thirtyfirst */
4942  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4943  break;
4944  case 'Y':
4945  /* Year */
4946  {
4947  int year = tm.tm_year + 1900;
4948  if (year > 1999) { /* year 2000 and later */
4949  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4950  } else {
4951  if (year < 1100) {
4952  /* I'm not going to handle 1100 and prior */
4953  /* We'll just be silent on the year, instead of bombing out. */
4954  } else {
4955  /* year 1100 to 1999. will anybody need this?!? */
4956  /* say 1967 as 'nineteen hundred seven and sixty' */
4957  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4958  res = wait_file(chan, ints, nextmsg, lang);
4959  if (!res) {
4960  res = wait_file(chan, ints, "digits/hundred", lang);
4961  if (!res && year % 100 != 0) {
4962  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4963  }
4964  }
4965  }
4966  }
4967  }
4968  break;
4969  case 'I':
4970  case 'l':
4971  /* 12-Hour */
4972  res = wait_file(chan, ints, "digits/oclock", lang);
4973  if (tm.tm_hour == 0)
4974  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4975  else if (tm.tm_hour > 12)
4976  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4977  else
4978  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4979  if (!res) {
4980  res = wait_file(chan, ints, nextmsg, lang);
4981  }
4982  break;
4983  case 'H':
4984  /* 24-Hour, single digit hours preceeded by "oh" (0) */
4985  if (tm.tm_hour < 10 && tm.tm_hour > 0) {
4986  res = wait_file(chan, ints, "digits/0", lang);
4987  }
4988  /* FALLTRHU */
4989  case 'k':
4990  /* 24-Hour */
4991  res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
4992  break;
4993  case 'M':
4994  /* Minute */
4995  if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4996  if (tm.tm_min < 10)
4997  res = wait_file(chan, ints, "digits/0", lang);
4998  /* Gender depends on whether or not seconds follow */
4999  if (next_item(&format[offset + 1]) == 'S')
5000  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5001  else
5002  res = ast_say_number(chan, tm.tm_min, ints, lang, "n");
5003  }
5004  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
5005  /* Say minute/minutes depending on whether minutes end in 1 */
5006  if ((tm.tm_min % 10 == 1) && (tm.tm_min != 11)) {
5007  res = wait_file(chan, ints, "minute", lang);
5008  } else {
5009  res = wait_file(chan, ints, "minutes", lang);
5010  }
5011  }
5012  break;
5013  case 'P':
5014  case 'p':
5015  /* AM/PM */
5016  if (tm.tm_hour > 11)
5017  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5018  else
5019  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5020  res = wait_file(chan, ints, nextmsg, lang);
5021  break;
5022  case 'Q':
5023  /* Shorthand for "Today", "Yesterday", or AdBY */
5024  /* XXX As emphasized elsewhere, this should the native way in your
5025  * language to say the date, with changes in what you say, depending
5026  * upon how recent the date is. XXX */
5027  {
5028  struct timeval now = ast_tvnow();
5029  struct ast_tm tmnow;
5030  time_t beg_today;
5031 
5032  ast_localtime(&now, &tmnow, tzone);
5033  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5034  /* In any case, it saves not having to do ast_mktime() */
5035  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5036  if (beg_today < t) {
5037  /* Today */
5038  res = wait_file(chan, ints, "digits/today", lang);
5039  } else if (beg_today - 86400 < t) {
5040  /* Yesterday */
5041  res = wait_file(chan, ints, "digits/yesterday", lang);
5042  } else {
5043  res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5044  }
5045  }
5046  break;
5047  case 'q':
5048  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5049  /* XXX As emphasized elsewhere, this should the native way in your
5050  * language to say the date, with changes in what you say, depending
5051  * upon how recent the date is. XXX */
5052  {
5053  struct timeval now = ast_tvnow();
5054  struct ast_tm tmnow;
5055  time_t beg_today;
5056 
5057  ast_localtime(&now, &tmnow, tzone);
5058  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5059  /* In any case, it saves not having to do ast_mktime() */
5060  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5061  if (beg_today < t) {
5062  /* Today */
5063  } else if ((beg_today - 86400) < t) {
5064  /* Yesterday */
5065  res = wait_file(chan, ints, "digits/yesterday", lang);
5066  } else if (beg_today - 86400 * 6 < t) {
5067  /* Within the last week */
5068  res = ast_say_date_with_format_is(chan, t, ints, lang, "A", tzone);
5069  } else {
5070  res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5071  }
5072  }
5073  break;
5074  case 'R':
5075  res = ast_say_date_with_format_is(chan, t, ints, lang, "HM", tzone);
5076  break;
5077  case 'S':
5078  /* Seconds */
5079  res = wait_file(chan, ints, "digits/and", lang);
5080  if (!res) {
5081  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5082  /* Say minute/minutes depending on whether seconds end in 1 */
5083  if (!res && (tm.tm_sec % 10 == 1) && (tm.tm_sec != 11)) {
5084  res = wait_file(chan, ints, "second", lang);
5085  } else {
5086  res = wait_file(chan, ints, "seconds", lang);
5087  }
5088  }
5089  break;
5090  case 'T':
5091  res = ast_say_date_with_format_is(chan, t, ints, lang, "HMS", tzone);
5092  break;
5093  case ' ':
5094  case ' ':
5095  /* Just ignore spaces and tabs */
5096  break;
5097  default:
5098  /* Unknown character */
5099  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5100  }
5101  /* Jump out on DTMF */
5102  if (res) {
5103  break;
5104  }
5105  }
5106  return res;
5107 }
5108 
5109 /*! \brief Thai syntax */
5110 int ast_say_date_with_format_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5111 {
5112  struct timeval when = { t, 0 };
5113  struct ast_tm tm;
5114  int res=0, offset, sndoffset;
5115  char sndfile[256], nextmsg[256];
5116 
5117  if (format == NULL)
5118  format = "a 'digits/tee' e 'digits/duan' hY I 'digits/naliga' M 'digits/natee'";
5119 
5120  ast_localtime(&when, &tm, tzone);
5121 
5122  for (offset=0 ; format[offset] != '\0' ; offset++) {
5123  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5124  switch (format[offset]) {
5125  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5126  case '\'':
5127  /* Literal name of a sound file */
5128  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5129  sndfile[sndoffset] = format[offset];
5130  }
5131  sndfile[sndoffset] = '\0';
5132  res = wait_file(chan, ints, sndfile, lang);
5133  break;
5134  case 'A':
5135  case 'a':
5136  /* Sunday - Saturday */
5137  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5138  res = wait_file(chan, ints, nextmsg, lang);
5139  break;
5140  case 'B':
5141  case 'b':
5142  case 'h':
5143  /* January - December */
5144  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5145  res = wait_file(chan, ints, nextmsg, lang);
5146  break;
5147  case 'm':
5148  /* Month enumerated */
5149  res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
5150  break;
5151  case 'd':
5152  case 'e':
5153  /* First - Thirtyfirst */
5154  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5155  break;
5156  case 'Y':
5157  /* Year */
5158  res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
5159  break;
5160  case 'I':
5161  case 'l':
5162  /* 12-Hour */
5163  if (tm.tm_hour == 0)
5164  ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5165  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5166  res = wait_file(chan, ints, nextmsg, lang);
5167  break;
5168  case 'H':
5169  case 'k':
5170  /* 24-Hour */
5171  if (tm.tm_hour == 0)
5172  ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5173  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5174  res = wait_file(chan, ints, nextmsg, lang);
5175  break;
5176  case 'M':
5177  case 'N':
5178  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5179  break;
5180  case 'P':
5181  case 'p':
5182  break;
5183  case 'Q':
5184  /* Shorthand for "Today", "Yesterday", or ABdY */
5185  /* XXX As emphasized elsewhere, this should the native way in your
5186  * language to say the date, with changes in what you say, depending
5187  * upon how recent the date is. XXX */
5188  {
5189  struct timeval now = ast_tvnow();
5190  struct ast_tm tmnow;
5191  time_t beg_today;
5192 
5193  ast_localtime(&now, &tmnow, tzone);
5194  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5195  /* In any case, it saves not having to do ast_mktime() */
5196  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5197  if (beg_today < t) {
5198  /* Today */
5199  res = wait_file(chan, ints, "digits/today", lang);
5200  } else if (beg_today - 86400 < t) {
5201  /* Yesterday */
5202  res = wait_file(chan, ints, "digits/yesterday", lang);
5203  } else if (beg_today - 86400 * 6 < t) {
5204  /* Within the last week */
5205  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5206  } else if (beg_today - 2628000 < t) {
5207  /* Less than a month ago - "Sunday, October third" */
5208  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5209  } else if (beg_today - 15768000 < t) {
5210  /* Less than 6 months ago - "August seventh" */
5211  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5212  } else {
5213  /* More than 6 months ago - "April nineteenth two thousand three" */
5214  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5215  }
5216  }
5217  break;
5218  case 'q':
5219  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5220  /* XXX As emphasized elsewhere, this should the native way in your
5221  * language to say the date, with changes in what you say, depending
5222  * upon how recent the date is. XXX */
5223  {
5224  struct timeval now = ast_tvnow();
5225  struct ast_tm tmnow;
5226  time_t beg_today;
5227 
5228  ast_localtime(&now, &tmnow, tzone);
5229  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5230  /* In any case, it saves not having to do ast_mktime() */
5231  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5232  if (beg_today < t) {
5233  /* Today */
5234  } else if ((beg_today - 86400) < t) {
5235  /* Yesterday */
5236  res = wait_file(chan, ints, "digits/yesterday", lang);
5237  } else if (beg_today - 86400 * 6 < t) {
5238  /* Within the last week */
5239  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5240  } else if (beg_today - 2628000 < t) {
5241  /* Less than a month ago - "Sunday, October third" */
5242  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5243  } else if (beg_today - 15768000 < t) {
5244  /* Less than 6 months ago - "August seventh" */
5245  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5246  } else {
5247  /* More than 6 months ago - "April nineteenth two thousand three" */
5248  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5249  }
5250  }
5251  break;
5252  case 'R':
5253  res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
5254  break;
5255  case 'S':
5256  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
5257  break;
5258  case 'T':
5259  res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
5260  break;
5261  case ' ':
5262  case ' ':
5263  /* Just ignore spaces and tabs */
5264  break;
5265  default:
5266  /* Unknown character */
5267  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5268  }
5269  /* Jump out on DTMF */
5270  if (res) {
5271  break;
5272  }
5273  }
5274  return res;
5275 }
5276 
5277 /*! \brief ast_say_date_with_format_he Say formatted date in Hebrew
5278  *
5279  * \ref ast_say_date_with_format_en for the details of the options
5280  *
5281  * Changes from the English version:
5282  *
5283  * - don't replicate in here the logic of ast_say_number_full_he
5284  *
5285  * - year is always 4-digit (because it's simpler)
5286  *
5287  * - added c, x, and X. Mainly for my tests
5288  *
5289  * - The standard "long" format used in Hebrew is AdBY, rather than ABdY
5290  *
5291  * \todo
5292  * - A "ha" is missing in the standard date format, before the 'd'.
5293  * - The numbers of 3000--19000 are not handled well
5294  */
5295 int ast_say_date_with_format_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5296 {
5297 #define IL_DATE_STR "AdBY"
5298 #define IL_TIME_STR "HM" /* NOTE: In Hebrew we do not support 12 hours, only 24. No AM or PM exists in the Hebrew language */
5299 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
5300  /* TODO: This whole function is cut&paste from
5301  * ast_say_date_with_format_en . Is that considered acceptable?
5302  **/
5303  struct timeval when = { t, 0 };
5304  struct ast_tm tm;
5305  int res = 0, offset, sndoffset;
5306  char sndfile[256], nextmsg[256];
5307 
5308  if (!format) {
5309  format = IL_DATE_STR_FULL;
5310  }
5311 
5312  ast_localtime(&when, &tm, tzone);
5313 
5314  for (offset = 0; format[offset] != '\0'; offset++) {
5315  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5316  switch (format[offset]) {
5317  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5318  case '\'':
5319  /* Literal name of a sound file */
5320  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5321  sndfile[sndoffset] = format[offset];
5322  }
5323  sndfile[sndoffset] = '\0';
5324  res = wait_file(chan, ints, sndfile, lang);
5325  break;
5326  case 'A':
5327  case 'a':
5328  /* Sunday - Saturday */
5329  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5330  res = wait_file(chan, ints, nextmsg, lang);
5331  break;
5332  case 'B':
5333  case 'b':
5334  case 'h':
5335  /* January - December */
5336  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5337  res = wait_file(chan, ints, nextmsg, lang);
5338  break;
5339  case 'd':
5340  case 'e': /* Day of the month */
5341  /* I'm not sure exactly what the parameters
5342  * audiofd and ctrlfd to
5343  * ast_say_number_full_he mean, but it seems
5344  * safe to pass -1 there.
5345  *
5346  * At least in one of the pathes :-(
5347  */
5348  res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
5349  break;
5350  case 'Y': /* Year */
5351  res = ast_say_number_full_he(chan, tm.tm_year + 1900, ints, lang, "f", -1, -1);
5352  break;
5353  case 'I':
5354  case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
5355  case 'H':
5356  case 'k': /* 24-Hour */
5357  res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
5358  break;
5359  case 'M': /* Minute */
5360  if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
5361  res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
5362  res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5363  break;
5364  case 'P':
5365  case 'p':
5366  /* AM/PM - There is no AM/PM in Hebrew... */
5367  break;
5368  case 'Q':
5369  /* Shorthand for "Today", "Yesterday", or "date" */
5370  case 'q':
5371  /* Shorthand for "" (today), "Yesterday", A
5372  * (weekday), or "date" */
5373  /* XXX As emphasized elsewhere, this should the native way in your
5374  * language to say the date, with changes in what you say, depending
5375  * upon how recent the date is. XXX */
5376  {
5377  struct timeval now = ast_tvnow();
5378  struct ast_tm tmnow;
5379  time_t beg_today;
5380  char todo = format[offset]; /* The letter to format*/
5381 
5382  ast_localtime(&now, &tmnow, tzone);
5383  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5384  /* In any case, it saves not having to do ast_mktime() */
5385  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5386  if (beg_today < t) {
5387  /* Today */
5388  if (todo == 'Q') {
5389  res = wait_file(chan, ints, "digits/today", lang);
5390  }
5391  } else if (beg_today - 86400 < t) {
5392  /* Yesterday */
5393  res = wait_file(chan, ints, "digits/yesterday", lang);
5394  } else if ((todo != 'Q') && (beg_today - 86400 * 6 < t)) {
5395  /* Within the last week */
5396  res = ast_say_date_with_format_he(chan, t, ints, lang, "A", tzone);
5397  } else {
5398  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5399  }
5400  }
5401  break;
5402  case 'R':
5403  res = ast_say_date_with_format_he(chan, t, ints, lang, "HM", tzone);
5404  break;
5405  case 'S': /* Seconds */
5406  res = ast_say_number_full_he(chan, tm.tm_sec,
5407  ints, lang, "f", -1, -1
5408  );
5409  break;
5410  case 'T':
5411  res = ast_say_date_with_format_he(chan, t, ints, lang, "HMS", tzone);
5412  break;
5413  /* c, x, and X seem useful for testing. Not sure
5414  * if they're good for the general public */
5415  case 'c':
5416  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR_FULL, tzone);
5417  break;
5418  case 'x':
5419  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5420  break;
5421  case 'X': /* Currently not locale-dependent...*/
5422  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_TIME_STR, tzone);
5423  break;
5424  case ' ':
5425  case ' ':
5426  /* Just ignore spaces and tabs */
5427  break;
5428  default:
5429  /* Unknown character */
5430  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5431  }
5432  /* Jump out on DTMF */
5433  if (res) {
5434  break;
5435  }
5436  }
5437  return res;
5438 }
5439 
5440 
5441 /*! \brief Spanish syntax */
5442 int ast_say_date_with_format_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5443 {
5444  struct timeval when = { t, 0 };
5445  struct ast_tm tm;
5446  int res=0, offset, sndoffset;
5447  char sndfile[256], nextmsg[256];
5448 
5449  if (format == NULL)
5450  format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
5451 
5452  ast_localtime(&when, &tm, tzone);
5453 
5454  for (offset=0 ; format[offset] != '\0' ; offset++) {
5455  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5456  switch (format[offset]) {
5457  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5458  case '\'':
5459  /* Literal name of a sound file */
5460  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5461  sndfile[sndoffset] = format[offset];
5462  }
5463  sndfile[sndoffset] = '\0';
5464  snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
5465  res = wait_file(chan, ints, nextmsg, lang);
5466  break;
5467  case 'A':
5468  case 'a':
5469  /* Sunday - Saturday */
5470  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5471  res = wait_file(chan, ints, nextmsg, lang);
5472  break;
5473  case 'B':
5474  case 'b':
5475  case 'h':
5476  /* January - December */
5477  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5478  res = wait_file(chan, ints, nextmsg, lang);
5479  break;
5480  case 'm':
5481  /* First - Twelfth */
5482  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5483  res = wait_file(chan, ints, nextmsg, lang);
5484  break;
5485  case 'd':
5486  case 'e':
5487  /* First - Thirtyfirst */
5488  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5489  break;
5490  case 'Y':
5491  /* Year */
5492  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5493  break;
5494  case 'I':
5495  case 'l':
5496  /* 12-Hour */
5497  if (tm.tm_hour == 0)
5498  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5499  else if (tm.tm_hour == 1 || tm.tm_hour == 13)
5500  snprintf(nextmsg,sizeof(nextmsg), "digits/1F");
5501  else if (tm.tm_hour > 12)
5502  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5503  else
5504  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5505  res = wait_file(chan, ints, nextmsg, lang);
5506  break;
5507  case 'H':
5508  case 'k':
5509  /* 24-Hour */
5510  res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
5511  break;
5512  case 'M':
5513  /* Minute */
5514  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5515  break;
5516  case 'P':
5517  case 'p':
5518  /* AM/PM */
5519  if (tm.tm_hour > 18)
5520  res = wait_file(chan, ints, "digits/p-m", lang);
5521  else if (tm.tm_hour > 12)
5522  res = wait_file(chan, ints, "digits/afternoon", lang);
5523  else if (tm.tm_hour)
5524  res = wait_file(chan, ints, "digits/a-m", lang);
5525  break;
5526  case 'Q':
5527  /* Shorthand for "Today", "Yesterday", or ABdY */
5528  /* XXX As emphasized elsewhere, this should the native way in your
5529  * language to say the date, with changes in what you say, depending
5530  * upon how recent the date is. XXX */
5531  {
5532  struct timeval now = ast_tvnow();
5533  struct ast_tm tmnow;
5534  time_t beg_today;
5535 
5536  ast_localtime(&now, &tmnow, tzone);
5537  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5538  /* In any case, it saves not having to do ast_mktime() */
5539  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5540  if (beg_today < t) {
5541  /* Today */
5542  res = wait_file(chan, ints, "digits/today", lang);
5543  } else if (beg_today - 86400 < t) {
5544  /* Yesterday */
5545  res = wait_file(chan, ints, "digits/yesterday", lang);
5546  } else {
5547  res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5548  }
5549  }
5550  break;
5551  case 'q':
5552  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5553  /* XXX As emphasized elsewhere, this should the native way in your
5554  * language to say the date, with changes in what you say, depending
5555  * upon how recent the date is. XXX */
5556  {
5557  struct timeval now = ast_tvnow();
5558  struct ast_tm tmnow;
5559  time_t beg_today;
5560 
5561  ast_localtime(&now, &tmnow, tzone);
5562  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5563  /* In any case, it saves not having to do ast_mktime() */
5564  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5565  if (beg_today < t) {
5566  /* Today */
5567  res = wait_file(chan, ints, "digits/today", lang);
5568  } else if ((beg_today - 86400) < t) {
5569  /* Yesterday */
5570  res = wait_file(chan, ints, "digits/yesterday", lang);
5571  } else if (beg_today - 86400 * 6 < t) {
5572  /* Within the last week */
5573  res = ast_say_date_with_format_es(chan, t, ints, lang, "A", tzone);
5574  } else {
5575  res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5576  }
5577  }
5578  break;
5579  case 'R':
5580  res = ast_say_date_with_format_es(chan, t, ints, lang, "H 'digits/y' M", tzone);
5581  break;
5582  case 'S':
5583  /* Seconds */
5584  if (tm.tm_sec == 0) {
5585  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5586  res = wait_file(chan, ints, nextmsg, lang);
5587  } else if (tm.tm_sec < 10) {
5588  res = wait_file(chan, ints, "digits/oh", lang);
5589  if (!res) {
5590  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5591  res = wait_file(chan, ints, nextmsg, lang);
5592  }
5593  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5594  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5595  res = wait_file(chan, ints, nextmsg, lang);
5596  } else {
5597  int ten, one;
5598  ten = (tm.tm_sec / 10) * 10;
5599  one = (tm.tm_sec % 10);
5600  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
5601  res = wait_file(chan, ints, nextmsg, lang);
5602  if (!res) {
5603  /* Fifty, not fifty-zero */
5604  if (one != 0) {
5605  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
5606  res = wait_file(chan, ints, nextmsg, lang);
5607  }
5608  }
5609  }
5610  break;
5611  case 'T':
5612  res = ast_say_date_with_format_es(chan, t, ints, lang, "HMS", tzone);
5613  break;
5614  case ' ':
5615  case ' ':
5616  /* Just ignore spaces and tabs */
5617  break;
5618  default:
5619  /* Unknown character */
5620  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5621  }
5622  /* Jump out on DTMF */
5623  if (res) {
5624  break;
5625  }
5626  }
5627  return res;
5628 }
5629 
5630 /*! \brief French syntax
5631 oclock = heure
5632 */
5633 int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)
5634 {
5635  struct timeval when = { t, 0 };
5636  struct ast_tm tm;
5637  int res=0, offset, sndoffset;
5638  char sndfile[256], nextmsg[256];
5639 
5640  if (format == NULL)
5641  format = "AdBY 'digits/at' IMp";
5642 
5643  ast_localtime(&when, &tm, tzone);
5644 
5645  for (offset=0 ; format[offset] != '\0' ; offset++) {
5646  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5647  switch (format[offset]) {
5648  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5649  case '\'':
5650  /* Literal name of a sound file */
5651  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5652  sndfile[sndoffset] = format[offset];
5653  }
5654  sndfile[sndoffset] = '\0';
5655  res = wait_file(chan, ints, sndfile, lang);
5656  break;
5657  case 'A':
5658  case 'a':
5659  /* Sunday - Saturday */
5660  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5661  res = wait_file(chan, ints, nextmsg, lang);
5662  break;
5663  case 'B':
5664  case 'b':
5665  case 'h':
5666  /* January - December */
5667  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5668  res = wait_file(chan, ints, nextmsg, lang);
5669  break;
5670  case 'm':
5671  /* First - Twelfth */
5672  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5673  res = wait_file(chan, ints, nextmsg, lang);
5674  break;
5675  case 'd':
5676  case 'e':
5677  /* First */
5678  if (tm.tm_mday == 1) {
5679  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5680  res = wait_file(chan, ints, nextmsg, lang);
5681  } else {
5682  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
5683  }
5684  break;
5685  case 'Y':
5686  /* Year */
5687  if (tm.tm_year > 99) {
5688  res = wait_file(chan, ints, "digits/2", lang);
5689  if (!res) {
5690  res = wait_file(chan, ints, "digits/thousand", lang);
5691  }
5692  if (tm.tm_year > 100) {
5693  if (!res) {
5694  res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
5695  }
5696  }
5697  } else {
5698  if (tm.tm_year < 1) {
5699  /* I'm not going to handle 1900 and prior */
5700  /* We'll just be silent on the year, instead of bombing out. */
5701  } else {
5702  res = wait_file(chan, ints, "digits/thousand", lang);
5703  if (!res) {
5704  wait_file(chan, ints, "digits/9", lang);
5705  wait_file(chan, ints, "digits/hundred", lang);
5706  res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
5707  }
5708  }
5709  }
5710  break;
5711  case 'I':
5712  case 'l':
5713  /* 12-Hour */
5714  if (tm.tm_hour == 0)
5715  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5716  else if (tm.tm_hour > 12)
5717  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5718  else
5719  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5720  res = wait_file(chan, ints, nextmsg, lang);
5721  if (!res)
5722  res = wait_file(chan, ints, "digits/oclock", lang);
5723  break;
5724  case 'H':
5725  case 'k':
5726  /* 24-Hour */
5727  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
5728  if (!res)
5729  res = wait_file(chan, ints, "digits/oclock", lang);
5730  break;
5731  case 'M':
5732  /* Minute */
5733  if (tm.tm_min == 0) {
5734  break;
5735  }
5736  res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
5737  break;
5738  case 'P':
5739  case 'p':
5740  /* AM/PM */
5741  if (tm.tm_hour > 11)
5742  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5743  else
5744  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5745  res = wait_file(chan, ints, nextmsg, lang);
5746  break;
5747  case 'Q':
5748  /* Shorthand for "Today", "Yesterday", or AdBY */
5749  /* XXX As emphasized elsewhere, this should the native way in your
5750  * language to say the date, with changes in what you say, depending
5751  * upon how recent the date is. XXX */
5752  {
5753  struct timeval now = ast_tvnow();
5754  struct ast_tm tmnow;
5755  time_t beg_today;
5756 
5757  ast_localtime(&now, &tmnow, tzone);
5758  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5759  /* In any case, it saves not having to do ast_mktime() */
5760  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5761  if (beg_today < t) {
5762  /* Today */
5763  res = wait_file(chan, ints, "digits/today", lang);
5764  } else if (beg_today - 86400 < t) {
5765  /* Yesterday */
5766  res = wait_file(chan, ints, "digits/yesterday", lang);
5767  } else {
5768  res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
5769  }
5770  }
5771  break;
5772  case 'q':
5773  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5774  /* XXX As emphasized elsewhere, this should the native way in your
5775  * language to say the date, with changes in what you say, depending
5776  * upon how recent the date is. XXX */
5777  {
5778  struct timeval now = ast_tvnow();
5779  struct ast_tm tmnow;
5780  time_t beg_today;
5781 
5782  ast_localtime(&now, &tmnow, tzone);
5783  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5784  /* In any case, it saves not having to do ast_mktime() */
5785  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5786  if (beg_today < t) {
5787  /* Today */
5788  } else if ((beg_today - 86400) < t) {
5789  /* Yesterday */
5790  res = wait_file(chan, ints, "digits/yesterday", lang);
5791  } else if (beg_today - 86400 * 6 < t) {
5792  /* Within the last week */
5793  res = ast_say_date_with_format_fr(chan, t, ints, lang, "A", tzone);
5794  } else {
5795  res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
5796  }
5797  }
5798  break;
5799  case 'R':
5800  res = ast_say_date_with_format_fr(chan, t, ints, lang, "HM", tzone);
5801  break;
5802  case 'S':
5803  /* Seconds */
5804  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
5805  if (!res) {
5806  res = wait_file(chan, ints, "second", lang);
5807  }
5808  break;
5809  case 'T':
5810  res = ast_say_date_with_format_fr(chan, t, ints, lang, "HMS", tzone);
5811  break;
5812  case ' ':
5813  case ' ':
5814  /* Just ignore spaces and tabs */
5815  break;
5816  default:
5817  /* Unknown character */
5818  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5819  }
5820  /* Jump out on DTMF */
5821  if (res) {
5822  break;
5823  }
5824  }
5825  return res;
5826 }
5827 
5828 /*! \brief Italian syntax */
5829 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)
5830 {
5831  struct timeval when = { t, 0 };
5832  struct ast_tm tm;
5833  int res=0, offset, sndoffset;
5834  char sndfile[256], nextmsg[256];
5835 
5836  if (format == NULL)
5837  format = "AdB 'digits/at' IMp";
5838 
5839  ast_localtime(&when, &tm, tzone);
5840 
5841  for (offset=0 ; format[offset] != '\0' ; offset++) {
5842  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5843  switch (format[offset]) {
5844  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5845  case '\'':
5846  /* Literal name of a sound file */
5847  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5848  sndfile[sndoffset] = format[offset];
5849  }
5850  sndfile[sndoffset] = '\0';
5851  res = wait_file(chan, ints, sndfile, lang);
5852  break;
5853  case 'A':
5854  case 'a':
5855  /* Sunday - Saturday */
5856  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5857  res = wait_file(chan, ints, nextmsg, lang);
5858  break;
5859  case 'B':
5860  case 'b':
5861  case 'h':
5862  /* January - December */
5863  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5864  res = wait_file(chan, ints, nextmsg, lang);
5865  break;
5866  case 'm':
5867  /* First - Twelfth */
5868  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5869  res = wait_file(chan, ints, nextmsg, lang);
5870  break;
5871  case 'd':
5872  case 'e':
5873  /* First day of the month is spelled as ordinal */
5874  if (tm.tm_mday == 1) {
5875  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5876  res = wait_file(chan, ints, nextmsg, lang);
5877  } else {
5878  if (!res) {
5879  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5880  }
5881  }
5882  break;
5883  case 'Y':
5884  /* Year */
5885  if (tm.tm_year > 99) {
5886  res = wait_file(chan, ints, "digits/ore-2000", lang);
5887  if (tm.tm_year > 100) {
5888  if (!res) {
5889  /* This works until the end of 2021 */
5890  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
5891  res = wait_file(chan, ints, nextmsg, lang);
5892  }
5893  }
5894  } else {
5895  if (tm.tm_year < 1) {
5896  /* I'm not going to handle 1900 and prior */
5897  /* We'll just be silent on the year, instead of bombing out. */
5898  } else {
5899  res = wait_file(chan, ints, "digits/ore-1900", lang);
5900  if ((!res) && (tm.tm_year != 0)) {
5901  if (tm.tm_year <= 21) {
5902  /* 1910 - 1921 */
5903  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
5904  res = wait_file(chan, ints, nextmsg, lang);
5905  } else {
5906  /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
5907  int ten, one;
5908  ten = tm.tm_year / 10;
5909  one = tm.tm_year % 10;
5910  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
5911  res = wait_file(chan, ints, nextmsg, lang);
5912  if (!res) {
5913  if (one != 0) {
5914  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
5915  res = wait_file(chan, ints, nextmsg, lang);
5916  }
5917  }
5918  }
5919  }
5920  }
5921  }
5922  break;
5923  case 'I':
5924  case 'l':
5925  /* 12-Hour */
5926  if (tm.tm_hour == 0) {
5927  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5928  } else if (tm.tm_hour > 12) {
5929  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5930  } else {
5931  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5932  }
5933  res = wait_file(chan, ints, nextmsg, lang);
5934  break;
5935  case 'H':
5936  case 'k':
5937  /* 24-Hour */
5938  if (tm.tm_hour == 0) {
5939  res = wait_file(chan, ints, "digits/ore-mezzanotte", lang);
5940  } else if (tm.tm_hour == 1) {
5941  res = wait_file(chan, ints, "digits/ore-una", lang);
5942  } else {
5943  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
5944  }
5945  break;
5946  case 'M':
5947  /* Minute */
5948  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5949  break;
5950  case 'P':
5951  case 'p':
5952  /* AM/PM */
5953  if (tm.tm_hour > 11) {
5954  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5955  } else {
5956  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5957  }
5958  res = wait_file(chan, ints, nextmsg, lang);
5959  break;
5960  case 'Q':
5961  /* Shorthand for "Today", "Yesterday", or ABdY */
5962  /* XXX As emphasized elsewhere, this should the native way in your
5963  * language to say the date, with changes in what you say, depending
5964  * upon how recent the date is. XXX */
5965  {
5966  struct timeval now = ast_tvnow();
5967  struct ast_tm tmnow;
5968  time_t beg_today;
5969 
5970  ast_localtime(&now, &tmnow, tzone);
5971  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5972  /* In any case, it saves not having to do ast_mktime() */
5973  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5974  if (beg_today < t) {
5975  /* Today */
5976  res = wait_file(chan, ints, "digits/today", lang);
5977  } else if (beg_today - 86400 < t) {
5978  /* Yesterday */
5979  res = wait_file(chan, ints, "digits/yesterday", lang);
5980  } else {
5981  res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
5982  }
5983  }
5984  break;
5985  case 'q':
5986  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5987  {
5988  struct timeval now = ast_tvnow();
5989  struct ast_tm tmnow;
5990  time_t beg_today;
5991 
5992  ast_localtime(&now, &tmnow, tzone);
5993  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5994  /* In any case, it saves not having to do ast_mktime() */
5995  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5996  if (beg_today < t) {
5997  /* Today */
5998  } else if ((beg_today - 86400) < t) {
5999  /* Yesterday */
6000  res = wait_file(chan, ints, "digits/yesterday", lang);
6001  } else if (beg_today - 86400 * 6 < t) {
6002  /* Within the last week */
6003  res = ast_say_date_with_format_it(chan, t, ints, lang, "A", tzone);
6004  } else {
6005  res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
6006  }
6007  }
6008  break;
6009  case 'R':
6010  res = ast_say_date_with_format_it(chan, t, ints, lang, "HM", tzone);
6011  break;
6012  case 'S':
6013  /* Seconds */
6014  if (tm.tm_sec == 0) {
6015  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6016  res = wait_file(chan, ints, nextmsg, lang);
6017  } else if (tm.tm_sec < 10) {
6018  res = wait_file(chan, ints, "digits/oh", lang);
6019  if (!res) {
6020  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6021  res = wait_file(chan, ints, nextmsg, lang);
6022  }
6023  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6024  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6025  res = wait_file(chan, ints, nextmsg, lang);
6026  } else {
6027  int ten, one;
6028  ten = (tm.tm_sec / 10) * 10;
6029  one = (tm.tm_sec % 10);
6030  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6031  res = wait_file(chan, ints, nextmsg, lang);
6032  if (!res) {
6033  /* Fifty, not fifty-zero */
6034  if (one != 0) {
6035  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6036  res = wait_file(chan, ints, nextmsg, lang);
6037  }
6038  }
6039  }
6040  break;
6041  case 'T':
6042  res = ast_say_date_with_format_it(chan, t, ints, lang, "HMS", tzone);
6043  break;
6044  case ' ':
6045  case ' ':
6046  /* Just ignore spaces and tabs */
6047  break;
6048  default:
6049  /* Unknown character */
6050  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6051  }
6052  /* Jump out on DTMF */
6053  if (res) {
6054  break;
6055  }
6056  }
6057  return res;
6058 }
6059 
6060 /*! \brief Dutch syntax */
6061 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)
6062 {
6063  struct timeval when = { t, 0 };
6064  struct ast_tm tm;
6065  int res=0, offset, sndoffset;
6066  char sndfile[256], nextmsg[256];
6067 
6068  if (format == NULL)
6069  format = "AdBY 'digits/at' IMp";
6070 
6071  ast_localtime(&when, &tm, tzone);
6072 
6073  for (offset=0 ; format[offset] != '\0' ; offset++) {
6074  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6075  switch (format[offset]) {
6076  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6077  case '\'':
6078  /* Literal name of a sound file */
6079  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6080  sndfile[sndoffset] = format[offset];
6081  }
6082  sndfile[sndoffset] = '\0';
6083  res = wait_file(chan, ints, sndfile, lang);
6084  break;
6085  case 'A':
6086  case 'a':
6087  /* Sunday - Saturday */
6088  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6089  res = wait_file(chan, ints, nextmsg, lang);
6090  break;
6091  case 'B':
6092  case 'b':
6093  case 'h':
6094  /* January - December */
6095  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6096  res = wait_file(chan, ints, nextmsg, lang);
6097  break;
6098  case 'm':
6099  /* First - Twelfth */
6100  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6101  res = wait_file(chan, ints, nextmsg, lang);
6102  break;
6103  case 'd':
6104  case 'e':
6105  /* First - Thirtyfirst */
6106  res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
6107  break;
6108  case 'Y':
6109  /* Year */
6110  if (tm.tm_year > 99) {
6111  res = wait_file(chan, ints, "digits/2", lang);
6112  if (!res) {
6113  res = wait_file(chan, ints, "digits/thousand", lang);
6114  }
6115  if (tm.tm_year > 100) {
6116  if (!res) {
6117  res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char *) NULL);
6118  }
6119  }
6120  } else {
6121  if (tm.tm_year < 1) {
6122  /* I'm not going to handle 1900 and prior */
6123  /* We'll just be silent on the year, instead of bombing out. */
6124  } else {
6125  res = wait_file(chan, ints, "digits/19", lang);
6126  if (!res) {
6127  if (tm.tm_year <= 9) {
6128  /* 1901 - 1909 */
6129  res = wait_file(chan, ints, "digits/oh", lang);
6130  if (!res) {
6131  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6132  res = wait_file(chan, ints, nextmsg, lang);
6133  }
6134  } else if (tm.tm_year <= 20) {
6135  /* 1910 - 1920 */
6136  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6137  res = wait_file(chan, ints, nextmsg, lang);
6138  } else {
6139  /* 1921 - 1999 */
6140  int ten, one;
6141  ten = tm.tm_year / 10;
6142  one = tm.tm_year % 10;
6143  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
6144  res = wait_file(chan, ints, nextmsg, lang);
6145  if (!res) {
6146  if (one != 0) {
6147  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6148  res = wait_file(chan, ints, nextmsg, lang);
6149  }
6150  }
6151  }
6152  }
6153  }
6154  }
6155  break;
6156  case 'I':
6157  case 'l':
6158  /* 12-Hour */
6159  if (tm.tm_hour == 0)
6160  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6161  else if (tm.tm_hour > 12)
6162  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6163  else
6164  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6165  res = wait_file(chan, ints, nextmsg, lang);
6166  break;
6167  case 'H':
6168  case 'k':
6169  /* 24-Hour */
6170  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
6171  if (!res) {
6172  res = wait_file(chan, ints, "digits/nl-uur", lang);
6173  }
6174  break;
6175  case 'M':
6176  /* Minute */
6177  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6178  break;
6179  case 'P':
6180  case 'p':
6181  /* AM/PM */
6182  if (tm.tm_hour > 11)
6183  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6184  else
6185  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6186  res = wait_file(chan, ints, nextmsg, lang);
6187  break;
6188  case 'Q':
6189  /* Shorthand for "Today", "Yesterday", or AdBY */
6190  /* XXX As emphasized elsewhere, this should the native way in your
6191  * language to say the date, with changes in what you say, depending
6192  * upon how recent the date is. XXX */
6193  {
6194  struct timeval now = ast_tvnow();
6195  struct ast_tm tmnow;
6196  time_t beg_today;
6197 
6198  ast_localtime(&now, &tmnow, tzone);
6199  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6200  /* In any case, it saves not having to do ast_mktime() */
6201  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6202  if (beg_today < t) {
6203  /* Today */
6204  res = wait_file(chan, ints, "digits/today", lang);
6205  } else if (beg_today - 86400 < t) {
6206  /* Yesterday */
6207  res = wait_file(chan, ints, "digits/yesterday", lang);
6208  } else {
6209  res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6210  }
6211  }
6212  break;
6213  case 'q':
6214  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6215  {
6216  struct timeval now = ast_tvnow();
6217  struct ast_tm tmnow;
6218  time_t beg_today;
6219 
6220  ast_localtime(&now, &tmnow, tzone);
6221  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6222  /* In any case, it saves not having to do ast_mktime() */
6223  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6224  if (beg_today < t) {
6225  /* Today */
6226  } else if ((beg_today - 86400) < t) {
6227  /* Yesterday */
6228  res = wait_file(chan, ints, "digits/yesterday", lang);
6229  } else if (beg_today - 86400 * 6 < t) {
6230  /* Within the last week */
6231  res = ast_say_date_with_format_nl(chan, t, ints, lang, "A", tzone);
6232  } else {
6233  res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6234  }
6235  }
6236  break;
6237  case 'R':
6238  res = ast_say_date_with_format_nl(chan, t, ints, lang, "HM", tzone);
6239  break;
6240  case 'S':
6241  /* Seconds */
6242  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
6243  break;
6244  case 'T':
6245  res = ast_say_date_with_format_nl(chan, t, ints, lang, "HMS", tzone);
6246  break;
6247  case ' ':
6248  case ' ':
6249  /* Just ignore spaces and tabs */
6250  break;
6251  default:
6252  /* Unknown character */
6253  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6254  }
6255  /* Jump out on DTMF */
6256  if (res) {
6257  break;
6258  }
6259  }
6260  return res;
6261 }
6262 
6263 /*! \brief Polish syntax */
6264 int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const char *ints, const char *lang, const char *format, const char *tzone)
6265 {
6266  struct timeval when = { thetime, 0 };
6267  struct ast_tm tm;
6268  int res=0, offset, sndoffset;
6269  char sndfile[256], nextmsg[256];
6270 
6271  ast_localtime(&when, &tm, tzone);
6272 
6273  for (offset = 0 ; format[offset] != '\0' ; offset++) {
6274  int remaining;
6275  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6276  switch (format[offset]) {
6277  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6278  case '\'':
6279  /* Literal name of a sound file */
6280  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6281  sndfile[sndoffset] = format[offset];
6282  }
6283  sndfile[sndoffset] = '\0';
6284  res = wait_file(chan, ints, sndfile, lang);
6285  break;
6286  case 'A':
6287  case 'a':
6288  /* Sunday - Saturday */
6289  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6290  res = wait_file(chan, ints, nextmsg, lang);
6291  break;
6292  case 'B':
6293  case 'b':
6294  case 'h':
6295  /* January - December */
6296  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6297  res = wait_file(chan, ints, nextmsg, lang);
6298  break;
6299  case 'm':
6300  /* Month enumerated */
6301  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
6302  break;
6303  case 'd':
6304  case 'e':
6305  /* First - Thirtyfirst */
6306  remaining = tm.tm_mday;
6307  if (tm.tm_mday > 30) {
6308  res = wait_file(chan, ints, "digits/h-30", lang);
6309  remaining -= 30;
6310  }
6311  if (tm.tm_mday > 20 && tm.tm_mday < 30) {
6312  res = wait_file(chan, ints, "digits/h-20", lang);
6313  remaining -= 20;
6314  }
6315  if (!res) {
6316  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remaining);
6317  res = wait_file(chan, ints, nextmsg, lang);
6318  }
6319  break;
6320  case 'Y':
6321  /* Year */
6322  if (tm.tm_year > 100) {
6323  res = wait_file(chan, ints, "digits/2", lang);
6324  if (!res)
6325  res = wait_file(chan, ints, "digits/1000.2", lang);
6326  if (tm.tm_year > 100) {
6327  if (!res)
6328  res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
6329  }
6330  } else if (tm.tm_year == 100) {
6331  res = wait_file(chan, ints, "digits/h-2000", lang);
6332  } else {
6333  if (tm.tm_year < 1) {
6334  /* I'm not going to handle 1900 and prior */
6335  /* We'll just be silent on the year, instead of bombing out. */
6336  break;
6337  } else {
6338  res = wait_file(chan, ints, "digits/1000", lang);
6339  if (!res) {
6340  wait_file(chan, ints, "digits/900", lang);
6341  res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
6342  }
6343  }
6344  }
6345  if (!res)
6346  wait_file(chan, ints, "digits/year", lang);
6347  break;
6348  case 'I':
6349  case 'l':
6350  /* 12-Hour */
6351  if (tm.tm_hour == 0)
6352  ast_copy_string(nextmsg, "digits/t-12", sizeof(nextmsg));
6353  else if (tm.tm_hour > 12)
6354  snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
6355  else
6356  snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
6357 
6358  res = wait_file(chan, ints, nextmsg, lang);
6359  break;
6360  case 'H':
6361  case 'k':
6362  /* 24-Hour */
6363  if (tm.tm_hour != 0) {
6364  snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
6365  res = wait_file(chan, ints, nextmsg, lang);
6366  } else
6367  res = wait_file(chan, ints, "digits/t-24", lang);
6368  break;
6369  case 'M':
6370  case 'N':
6371  /* Minute */
6372  if (tm.tm_min == 0) {
6373  if (format[offset] == 'M') {
6374  res = wait_file(chan, ints, "digits/oclock", lang);
6375  } else {
6376  res = wait_file(chan, ints, "digits/100", lang);
6377  }
6378  } else
6379  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
6380  break;
6381  case 'P':
6382  case 'p':
6383  /* AM/PM */
6384  if (tm.tm_hour > 11)
6385  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6386  else
6387  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6388  res = wait_file(chan, ints, nextmsg, lang);
6389  break;
6390  case 'Q':
6391  /* Shorthand for "Today", "Yesterday", or AdBY */
6392  {
6393  struct timeval now = ast_tvnow();
6394  struct ast_tm tmnow;
6395  time_t beg_today;
6396 
6397  ast_localtime(&now, &tmnow, tzone);
6398  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6399  /* In any case, it saves not having to do ast_mktime() */
6400  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6401  if (beg_today < thetime) {
6402  /* Today */
6403  res = wait_file(chan, ints, "digits/today", lang);
6404  } else if (beg_today - 86400 < thetime) {
6405  /* Yesterday */
6406  res = wait_file(chan, ints, "digits/yesterday", lang);
6407  } else {
6408  res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
6409  }
6410  }
6411  break;
6412  case 'q':
6413  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6414  {
6415  struct timeval now = ast_tvnow();
6416  struct ast_tm tmnow;
6417  time_t beg_today;
6418 
6419  ast_localtime(&now, &tmnow, tzone);
6420  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6421  /* In any case, it saves not having to do ast_mktime() */
6422  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6423  if (beg_today < thetime) {
6424  /* Today */
6425  } else if ((beg_today - 86400) < thetime) {
6426  /* Yesterday */
6427  res = wait_file(chan, ints, "digits/yesterday", lang);
6428  } else if (beg_today - 86400 * 6 < thetime) {
6429  /* Within the last week */
6430  res = ast_say_date_with_format(chan, thetime, ints, lang, "A", tzone);
6431  } else {
6432  res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
6433  }
6434  }
6435  break;
6436  case 'R':
6437  res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", tzone);
6438  break;
6439  case 'S':
6440  /* Seconds */
6441  res = wait_file(chan, ints, "digits/and", lang);
6442  if (!res) {
6443  if (tm.tm_sec == 1) {
6444  res = wait_file(chan, ints, "digits/1z", lang);
6445  if (!res)
6446  res = wait_file(chan, ints, "digits/second-a", lang);
6447  } else {
6448  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
6449  if (!res) {
6450  int ten, one;
6451  ten = tm.tm_sec / 10;
6452  one = tm.tm_sec % 10;
6453 
6454  if (one > 1 && one < 5 && ten != 1)
6455  res = wait_file(chan, ints, "seconds", lang);
6456  else
6457  res = wait_file(chan, ints, "second", lang);
6458  }
6459  }
6460  }
6461  break;
6462  case 'T':
6463  res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", tzone);
6464  break;
6465  case ' ':
6466  case ' ':
6467  /* Just ignore spaces and tabs */
6468  break;
6469  default:
6470  /* Unknown character */
6471  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6472  }
6473  /* Jump out on DTMF */
6474  if (res)
6475  break;
6476  }
6477  return res;
6478 }
6479 
6480 /*! \brief Portuguese syntax */
6481 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)
6482 {
6483  struct timeval when = { t, 0 };
6484  struct ast_tm tm;
6485  int res=0, offset, sndoffset;
6486  char sndfile[256], nextmsg[256];
6487 
6488  if (format == NULL)
6489  format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
6490 
6491  ast_localtime(&when, &tm, tzone);
6492 
6493  for (offset=0 ; format[offset] != '\0' ; offset++) {
6494  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6495  switch (format[offset]) {
6496  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6497  case '\'':
6498  /* Literal name of a sound file */
6499  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6500  sndfile[sndoffset] = format[offset];
6501  }
6502  sndfile[sndoffset] = '\0';
6503  snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
6504  res = wait_file(chan, ints, nextmsg, lang);
6505  break;
6506  case 'A':
6507  case 'a':
6508  /* Sunday - Saturday */
6509  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6510  res = wait_file(chan, ints, nextmsg, lang);
6511  break;
6512  case 'B':
6513  case 'b':
6514  case 'h':
6515  /* January - December */
6516  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6517  res = wait_file(chan, ints, nextmsg, lang);
6518  break;
6519  case 'm':
6520  /* First - Twelfth */
6521  if (!strcasecmp(lang, "pt_BR")) {
6522  res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
6523  } else {
6524  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6525  res = wait_file(chan, ints, nextmsg, lang);
6526  }
6527  break;
6528  case 'd':
6529  case 'e':
6530  /* First - Thirtyfirst */
6531  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6532  break;
6533  case 'Y':
6534  /* Year */
6535  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6536  break;
6537  case 'I':
6538  case 'l':
6539  /* 12-Hour */
6540  if (!strcasecmp(lang, "pt_BR")) {
6541  if (tm.tm_hour == 0) {
6542  if (format[offset] == 'I')
6543  res = wait_file(chan, ints, "digits/pt-a", lang);
6544  if (!res)
6545  res = wait_file(chan, ints, "digits/pt-meianoite", lang);
6546  } else if (tm.tm_hour == 12) {
6547  if (format[offset] == 'I')
6548  res = wait_file(chan, ints, "digits/pt-ao", lang);
6549  if (!res)
6550  res = wait_file(chan, ints, "digits/pt-meiodia", lang);
6551  } else {
6552  if (format[offset] == 'I') {
6553  if ((tm.tm_hour % 12) != 1)
6554  res = wait_file(chan, ints, "digits/pt-as", lang);
6555  else
6556  res = wait_file(chan, ints, "digits/pt-a", lang);
6557  }
6558  if (!res)
6559  res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
6560  }
6561  } else {
6562  if (tm.tm_hour == 0) {
6563  if (format[offset] == 'I')
6564  res = wait_file(chan, ints, "digits/pt-ah", lang);
6565  if (!res)
6566  res = wait_file(chan, ints, "digits/pt-meianoite", lang);
6567  }
6568  else if (tm.tm_hour == 12) {
6569  if (format[offset] == 'I')
6570  res = wait_file(chan, ints, "digits/pt-ao", lang);
6571  if (!res)
6572  res = wait_file(chan, ints, "digits/pt-meiodia", lang);
6573  }
6574  else {
6575  if (format[offset] == 'I') {
6576  res = wait_file(chan, ints, "digits/pt-ah", lang);
6577  if ((tm.tm_hour % 12) != 1)
6578  if (!res)
6579  res = wait_file(chan, ints, "digits/pt-sss", lang);
6580  }
6581  if (!res)
6582  res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
6583  }
6584  }
6585  break;
6586  case 'H':
6587  case 'k':
6588  /* 24-Hour */
6589  if (!strcasecmp(lang, "pt_BR")) {
6590  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
6591  if ((!res) && (format[offset] == 'H')) {
6592  if (tm.tm_hour > 1) {
6593  res = wait_file(chan, ints, "digits/hours", lang);
6594  } else {
6595  res = wait_file(chan, ints, "digits/hour", lang);
6596  }
6597  }
6598  } else {
6599  res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
6600  if (!res) {
6601  if (tm.tm_hour != 0) {
6602  int remaining = tm.tm_hour;
6603  if (tm.tm_hour > 20) {
6604  res = wait_file(chan, ints, "digits/20", lang);
6605  remaining -= 20;
6606  }
6607  if (!res) {
6608  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
6609  res = wait_file(chan, ints, nextmsg, lang);
6610  }
6611  }
6612  }
6613  }
6614  break;
6615  case 'M':
6616  /* Minute */
6617  if (!strcasecmp(lang, "pt_BR")) {
6618  res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
6619  if (!res) {
6620  if (tm.tm_min > 1) {
6621  res = wait_file(chan, ints, "minutes", lang);
6622  } else {
6623  res = wait_file(chan, ints, "minute", lang);
6624  }
6625  }
6626  } else {
6627  if (tm.tm_min == 0) {
6628  res = wait_file(chan, ints, "digits/pt-hora", lang);
6629  if (tm.tm_hour != 1)
6630  if (!res)
6631  res = wait_file(chan, ints, "digits/pt-sss", lang);
6632  } else {
6633  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6634  }
6635  }
6636  break;
6637  case 'P':
6638  case 'p':
6639  /* AM/PM */
6640  if (!strcasecmp(lang, "pt_BR")) {
6641  if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
6642  res = wait_file(chan, ints, "digits/pt-da", lang);
6643  if (!res) {
6644  if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
6645  res = wait_file(chan, ints, "digits/morning", lang);
6646  else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
6647  res = wait_file(chan, ints, "digits/afternoon", lang);
6648  else res = wait_file(chan, ints, "digits/night", lang);
6649  }
6650  }
6651  } else {
6652  if (tm.tm_hour > 12)
6653  res = wait_file(chan, ints, "digits/p-m", lang);
6654  else if (tm.tm_hour && tm.tm_hour < 12)
6655  res = wait_file(chan, ints, "digits/a-m", lang);
6656  }
6657  break;
6658  case 'Q':
6659  /* Shorthand for "Today", "Yesterday", or ABdY */
6660  /* XXX As emphasized elsewhere, this should the native way in your
6661  * language to say the date, with changes in what you say, depending
6662  * upon how recent the date is. XXX */
6663  {
6664  struct timeval now = ast_tvnow();
6665  struct ast_tm tmnow;
6666  time_t beg_today;
6667 
6668  ast_localtime(&now, &tmnow, tzone);
6669  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6670  /* In any case, it saves not having to do ast_mktime() */
6671  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6672  if (beg_today < t) {
6673  /* Today */
6674  res = wait_file(chan, ints, "digits/today", lang);
6675  } else if (beg_today - 86400 < t) {
6676  /* Yesterday */
6677  res = wait_file(chan, ints, "digits/yesterday", lang);
6678  } else {
6679  res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
6680  }
6681  }
6682  break;
6683  case 'q':
6684  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6685  /* XXX As emphasized elsewhere, this should the native way in your
6686  * language to say the date, with changes in what you say, depending
6687  * upon how recent the date is. XXX */
6688  {
6689  struct timeval now = ast_tvnow();
6690  struct ast_tm tmnow;
6691  time_t beg_today;
6692 
6693  ast_localtime(&now, &tmnow, tzone);
6694  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6695  /* In any case, it saves not having to do ast_mktime() */
6696  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6697  if (beg_today < t) {
6698  /* Today */
6699  } else if ((beg_today - 86400) < t) {
6700  /* Yesterday */
6701  res = wait_file(chan, ints, "digits/yesterday", lang);
6702  } else if (beg_today - 86400 * 6 < t) {
6703  /* Within the last week */
6704  res = ast_say_date_with_format_pt(chan, t, ints, lang, "A", tzone);
6705  } else {
6706  res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
6707  }
6708  }
6709  break;
6710  case 'R':
6711  res = ast_say_date_with_format_pt(chan, t, ints, lang, "H 'digits/pt-e' M", tzone);
6712  break;
6713  case 'S':
6714  /* Seconds */
6715  if (!strcasecmp(lang, "pt_BR")) {
6716  res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
6717  if (!res) {
6718  if (tm.tm_sec > 1) {
6719  res = wait_file(chan, ints, "seconds", lang);
6720  } else {
6721  res = wait_file(chan, ints, "second", lang);
6722  }
6723  }
6724  } else {
6725  if (tm.tm_sec == 0) {
6726  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6727  res = wait_file(chan, ints, nextmsg, lang);
6728  } else if (tm.tm_sec < 10) {
6729  res = wait_file(chan, ints, "digits/oh", lang);
6730  if (!res) {
6731  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6732  res = wait_file(chan, ints, nextmsg, lang);
6733  }
6734  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6735  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6736  res = wait_file(chan, ints, nextmsg, lang);
6737  } else {
6738  int ten, one;
6739  ten = (tm.tm_sec / 10) * 10;
6740  one = (tm.tm_sec % 10);
6741  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6742  res = wait_file(chan, ints, nextmsg, lang);
6743  if (!res) {
6744  /* Fifty, not fifty-zero */
6745  if (one != 0) {
6746  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6747  res = wait_file(chan, ints, nextmsg, lang);
6748  }
6749  }
6750  }
6751  }
6752  break;
6753  case 'T':
6754  res = ast_say_date_with_format_pt(chan, t, ints, lang, "HMS", tzone);
6755  break;
6756  case ' ':
6757  case ' ':
6758  /* Just ignore spaces and tabs */
6759  break;
6760  default:
6761  /* Unknown character */
6762  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6763  }
6764  /* Jump out on DTMF */
6765  if (res) {
6766  break;
6767  }
6768  }
6769  return res;
6770 }
6771 
6772 /*! \brief Taiwanese / Chinese syntax */
6773 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)
6774 {
6775  struct timeval when = { t, 0 };
6776  struct ast_tm tm;
6777  int res=0, offset, sndoffset;
6778  char sndfile[256], nextmsg[256];
6779 
6780  if (format == NULL)
6781  format = "YBdAkM";
6782 
6783  ast_localtime(&when, &tm, tzone);
6784 
6785  for (offset=0 ; format[offset] != '\0' ; offset++) {
6786  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6787  switch (format[offset]) {
6788  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6789  case '\'':
6790  /* Literal name of a sound file */
6791  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6792  sndfile[sndoffset] = format[offset];
6793  }
6794  sndfile[sndoffset] = '\0';
6795  res = wait_file(chan, ints, sndfile, lang);
6796  break;
6797  case 'A':
6798  case 'a':
6799  /* Sunday - Saturday */
6800  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6801  res = wait_file(chan, ints, nextmsg, lang);
6802  break;
6803  case 'B':
6804  case 'b':
6805  case 'h':
6806  case 'm':
6807  /* January - December */
6808  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6809  res = wait_file(chan, ints, nextmsg, lang);
6810  break;
6811  case 'd':
6812  case 'e':
6813  /* First - Thirtyfirst */
6814  if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
6815  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday);
6816  res = wait_file(chan, ints, nextmsg, lang);
6817  } else {
6818  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
6819  res = wait_file(chan, ints, nextmsg, lang);
6820  if (!res) {
6821  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
6822  res = wait_file(chan, ints, nextmsg, lang);
6823  }
6824  }
6825  if (!res) res = wait_file(chan, ints, "digits/day", lang);
6826  break;
6827  case 'Y':
6828  /* Year */
6829  if (tm.tm_year > 99) {
6830  res = wait_file(chan, ints, "digits/2", lang);
6831  if (!res) {
6832  res = wait_file(chan, ints, "digits/thousand", lang);
6833  }
6834  if (tm.tm_year > 100) {
6835  if (!res) {
6836  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
6837  res = wait_file(chan, ints, nextmsg, lang);
6838  if (!res) {
6839  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
6840  res = wait_file(chan, ints, nextmsg, lang);
6841  }
6842  }
6843  }
6844  if (!res) {
6845  res = wait_file(chan, ints, "digits/year", lang);
6846  }
6847  } else {
6848  if (tm.tm_year < 1) {
6849  /* I'm not going to handle 1900 and prior */
6850  /* We'll just be silent on the year, instead of bombing out. */
6851  } else {
6852  res = wait_file(chan, ints, "digits/1", lang);
6853  if (!res) {
6854  res = wait_file(chan, ints, "digits/9", lang);
6855  }
6856  if (!res) {
6857  if (tm.tm_year <= 9) {
6858  /* 1901 - 1909 */
6859  res = wait_file(chan, ints, "digits/0", lang);
6860  if (!res) {
6861  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6862  res = wait_file(chan, ints, nextmsg, lang);
6863  }
6864  } else {
6865  /* 1910 - 1999 */
6866  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
6867  res = wait_file(chan, ints, nextmsg, lang);
6868  if (!res) {
6869  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
6870  res = wait_file(chan, ints, nextmsg, lang);
6871  }
6872  }
6873  }
6874  }
6875  if (!res) {
6876  res = wait_file(chan, ints, "digits/year", lang);
6877  }
6878  }
6879  break;
6880  case 'I':
6881  case 'l':
6882  /* 12-Hour */
6883  if (tm.tm_hour == 0)
6884  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6885  else if (tm.tm_hour > 12)
6886  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6887  else
6888  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6889  res = wait_file(chan, ints, nextmsg, lang);
6890  if (!res) {
6891  res = wait_file(chan, ints, "digits/oclock", lang);
6892  }
6893  break;
6894  case 'H':
6895  if (tm.tm_hour < 10) {
6896  res = wait_file(chan, ints, "digits/0", lang);
6897  }
6898  /* XXX Static analysis warns of no break here. No idea if this is
6899  * correct or not
6900  */
6901  case 'k':
6902  /* 24-Hour */
6903  if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
6904  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6905  res = wait_file(chan, ints, nextmsg, lang);
6906  } else {
6907  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
6908  res = wait_file(chan, ints, nextmsg, lang);
6909  if (!res) {
6910  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
6911  res = wait_file(chan, ints, nextmsg, lang);
6912  }
6913  }
6914  if (!res) {
6915  res = wait_file(chan, ints, "digits/oclock", lang);
6916  }
6917  break;
6918  case 'M':
6919  /* Minute */
6920  if (!(tm.tm_min % 10) || tm.tm_min < 10) {
6921  if (tm.tm_min < 10) {
6922  res = wait_file(chan, ints, "digits/0", lang);
6923  }
6924  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
6925  res = wait_file(chan, ints, nextmsg, lang);
6926  } else {
6927  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
6928  res = wait_file(chan, ints, nextmsg, lang);
6929  if (!res) {
6930  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
6931  res = wait_file(chan, ints, nextmsg, lang);
6932  }
6933  }
6934  if (!res) {
6935  res = wait_file(chan, ints, "minute", lang);
6936  }
6937  break;
6938  case 'P':
6939  case 'p':
6940  /* AM/PM */
6941  if (tm.tm_hour > 11)
6942  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6943  else
6944  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6945  res = wait_file(chan, ints, nextmsg, lang);
6946  break;
6947  case 'Q':
6948  /* Shorthand for "Today", "Yesterday", or ABdY */
6949  /* XXX As emphasized elsewhere, this should the native way in your
6950  * language to say the date, with changes in what you say, depending
6951  * upon how recent the date is. XXX */
6952  {
6953  struct timeval now = ast_tvnow();
6954  struct ast_tm tmnow;
6955  time_t beg_today;
6956 
6957  ast_localtime(&now, &tmnow, tzone);
6958  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6959  /* In any case, it saves not having to do ast_mktime() */
6960  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6961  if (beg_today < t) {
6962  /* Today */
6963  res = wait_file(chan, ints, "digits/today", lang);
6964  } else if (beg_today - 86400 < t) {
6965  /* Yesterday */
6966  res = wait_file(chan, ints, "digits/yesterday", lang);
6967  } else {
6968  res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
6969  }
6970  }
6971  break;
6972  case 'q':
6973  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6974  /* XXX As emphasized elsewhere, this should the native way in your
6975  * language to say the date, with changes in what you say, depending
6976  * upon how recent the date is. XXX */
6977  {
6978  struct timeval now = ast_tvnow();
6979  struct ast_tm tmnow;
6980  time_t beg_today;
6981 
6982  ast_localtime(&now, &tmnow, tzone);
6983  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6984  /* In any case, it saves not having to do ast_mktime() */
6985  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6986  if (beg_today < t) {
6987  /* Today */
6988  } else if ((beg_today - 86400) < t) {
6989  /* Yesterday */
6990  res = wait_file(chan, ints, "digits/yesterday", lang);
6991  } else if (beg_today - 86400 * 6 < t) {
6992  /* Within the last week */
6993  res = ast_say_date_with_format_zh(chan, t, ints, lang, "A", tzone);
6994  } else {
6995  res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
6996  }
6997  }
6998  break;
6999  case 'R':
7000  res = ast_say_date_with_format_zh(chan, t, ints, lang, "kM", tzone);
7001  break;
7002  case 'S':
7003  /* Seconds */
7004  if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
7005  if (tm.tm_sec < 10) {
7006  res = wait_file(chan, ints, "digits/0", lang);
7007  }
7008  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
7009  res = wait_file(chan, ints, nextmsg, lang);
7010  } else {
7011  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
7012  res = wait_file(chan, ints, nextmsg, lang);
7013  if (!res) {
7014  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
7015  res = wait_file(chan, ints, nextmsg, lang);
7016  }
7017  }
7018  if (!res) {
7019  res = wait_file(chan, ints, "second", lang);
7020  }
7021  break;
7022  case 'T':
7023  res = ast_say_date_with_format_zh(chan, t, ints, lang, "HMS", tzone);
7024  break;
7025  case ' ':
7026  case ' ':
7027  /* Just ignore spaces and tabs */
7028  break;
7029  default:
7030  /* Unknown character */
7031  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
7032  }
7033  /* Jump out on DTMF */
7034  if (res) {
7035  break;
7036  }
7037  }
7038  return res;
7039 }
7040 
7041 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7042 {
7043  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7044  return ast_say_time_en(chan, t, ints, lang);
7045  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
7046  return ast_say_time_de(chan, t, ints, lang);
7047  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7048  return ast_say_time_fr(chan, t, ints, lang);
7049  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
7050  return ast_say_time_gr(chan, t, ints, lang);
7051  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
7052  return ast_say_time_ja(chan, t, ints, lang);
7053  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7054  return ast_say_time_he(chan, t, ints, lang);
7055  } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
7056  return(ast_say_time_hu(chan, t, ints, lang));
7057  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7058  return ast_say_time_ka(chan, t, ints, lang);
7059  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
7060  return ast_say_time_nl(chan, t, ints, lang);
7061  } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
7062  return ast_say_time_pt_BR(chan, t, ints, lang);
7063  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7064  return ast_say_time_pt(chan, t, ints, lang);
7065  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
7066  return(ast_say_time_th(chan, t, ints, lang));
7067  } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
7068  return ast_say_time_zh(chan, t, ints, lang);
7069  }
7070 
7071  /* Default to English */
7072  return ast_say_time_en(chan, t, ints, lang);
7073 }
7074 
7075 /*! \brief English syntax */
7076 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7077 {
7078  struct timeval when = { t, 0 };
7079  struct ast_tm tm;
7080  int res = 0;
7081  int hour, pm=0;
7082 
7083  ast_localtime(&when, &tm, NULL);
7084  hour = tm.tm_hour;
7085  if (!hour)
7086  hour = 12;
7087  else if (hour == 12)
7088  pm = 1;
7089  else if (hour > 12) {
7090  hour -= 12;
7091  pm = 1;
7092  }
7093  if (!res)
7094  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7095 
7096  if (tm.tm_min > 9) {
7097  if (!res)
7098  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7099  } else if (tm.tm_min) {
7100  if (!res)
7101  res = ast_streamfile(chan, "digits/oh", lang);
7102  if (!res)
7103  res = ast_waitstream(chan, ints);
7104  if (!res)
7105  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7106  } else {
7107  if (!res)
7108  res = ast_streamfile(chan, "digits/oclock", lang);
7109  if (!res)
7110  res = ast_waitstream(chan, ints);
7111  }
7112  if (pm) {
7113  if (!res)
7114  res = ast_streamfile(chan, "digits/p-m", lang);
7115  } else {
7116  if (!res)
7117  res = ast_streamfile(chan, "digits/a-m", lang);
7118  }
7119  if (!res)
7120  res = ast_waitstream(chan, ints);
7121  return res;
7122 }
7123 
7124 /*! \brief German syntax */
7125 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7126 {
7127  struct timeval when = { t, 0 };
7128  struct ast_tm tm;
7129  int res = 0;
7130 
7131  ast_localtime(&when, &tm, NULL);
7132  if (!res)
7133  res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7134  if (!res)
7135  res = ast_streamfile(chan, "digits/oclock", lang);
7136  if (!res)
7137  res = ast_waitstream(chan, ints);
7138  if (!res)
7139  if (tm.tm_min > 0)
7140  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7141  return res;
7142 }
7143 
7144 /*! \brief Hungarian syntax */
7145 int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7146 {
7147  struct timeval when = { t, 0 };
7148  struct ast_tm tm;
7149  int res = 0;
7150 
7151  ast_localtime(&when, &tm, NULL);
7152  if (!res)
7153  res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7154  if (!res)
7155  res = ast_streamfile(chan, "digits/oclock", lang);
7156  if (!res)
7157  res = ast_waitstream(chan, ints);
7158  if (!res)
7159  if (tm.tm_min > 0) {
7160  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7161  if (!res)
7162  res = ast_streamfile(chan, "minute", lang);
7163  }
7164  return res;
7165 }
7166 
7167 /*! \brief French syntax */
7168 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7169 {
7170  struct timeval when = { t, 0 };
7171  struct ast_tm tm;
7172  int res = 0;
7173 
7174  ast_localtime(&when, &tm, NULL);
7175 
7176  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7177  if (!res)
7178  res = ast_streamfile(chan, "digits/oclock", lang);
7179  if (tm.tm_min) {
7180  if (!res)
7181  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7182  }
7183  return res;
7184 }
7185 
7186 /*! \brief Dutch syntax */
7187 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7188 {
7189  struct timeval when = { t, 0 };
7190  struct ast_tm tm;
7191  int res = 0;
7192 
7193  ast_localtime(&when, &tm, NULL);
7194  if (!res)
7195  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
7196  if (!res)
7197  res = ast_streamfile(chan, "digits/nl-uur", lang);
7198  if (!res)
7199  res = ast_waitstream(chan, ints);
7200  if (!res)
7201  if (tm.tm_min > 0)
7202  res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
7203  return res;
7204 }
7205 
7206 /*! \brief Portuguese syntax */
7207 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7208 {
7209  struct timeval when = { t, 0 };
7210  struct ast_tm tm;
7211  int res = 0;
7212  int hour;
7213 
7214  ast_localtime(&when, &tm, NULL);
7215  hour = tm.tm_hour;
7216  if (!res)
7217  res = ast_say_number(chan, hour, ints, lang, "f");
7218  if (tm.tm_min) {
7219  if (!res)
7220  res = wait_file(chan, ints, "digits/pt-e", lang);
7221  if (!res)
7222  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7223  } else {
7224  if (!res)
7225  res = wait_file(chan, ints, "digits/pt-hora", lang);
7226  if (tm.tm_hour != 1)
7227  if (!res)
7228  res = wait_file(chan, ints, "digits/pt-sss", lang);
7229  }
7230  if (!res)
7231  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7232  return res;
7233 }
7234 
7235 /*! \brief Brazilian Portuguese syntax */
7236 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7237 {
7238  struct timeval when = { t, 0 };
7239  struct ast_tm tm;
7240  int res = 0;
7241 
7242  ast_localtime(&when, &tm, NULL);
7243 
7244  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7245  if (!res) {
7246  if (tm.tm_hour > 1)
7247  res = wait_file(chan, ints, "digits/hours", lang);
7248  else
7249  res = wait_file(chan, ints, "digits/hour", lang);
7250  }
7251  if ((!res) && (tm.tm_min)) {
7252  res = wait_file(chan, ints, "digits/pt-e", lang);
7253  if (!res)
7254  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7255  if (!res) {
7256  if (tm.tm_min > 1)
7257  res = wait_file(chan, ints, "minutes", lang);
7258  else
7259  res = wait_file(chan, ints, "minute", lang);
7260  }
7261  }
7262  return res;
7263 }
7264 
7265 /*! \brief Thai syntax */
7266 int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7267 {
7268  struct timeval when = { t, 0 };
7269  struct ast_tm tm;
7270  int res = 0;
7271  int hour;
7272  ast_localtime(&when, &tm, NULL);
7273  hour = tm.tm_hour;
7274  if (!hour)
7275  hour = 24;
7276  if (!res)
7277  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7278  if (!res)
7279  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7280  return res;
7281 }
7282 
7283 /*! \brief Taiwanese / Chinese syntax */
7284 int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7285 {
7286  struct timeval when = { t, 0 };
7287  struct ast_tm tm;
7288  int res = 0;
7289  int hour, pm=0;
7290 
7291  ast_localtime(&when, &tm, NULL);
7292  hour = tm.tm_hour;
7293  if (!hour)
7294  hour = 12;
7295  else if (hour == 12)
7296  pm = 1;
7297  else if (hour > 12) {
7298  hour -= 12;
7299  pm = 1;
7300  }
7301  if (pm) {
7302  if (!res)
7303  res = ast_streamfile(chan, "digits/p-m", lang);
7304  } else {
7305  if (!res)
7306  res = ast_streamfile(chan, "digits/a-m", lang);
7307  }
7308  if (!res)
7309  res = ast_waitstream(chan, ints);
7310  if (!res)
7311  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7312  if (!res)
7313  res = ast_streamfile(chan, "digits/oclock", lang);
7314  if (!res)
7315  res = ast_waitstream(chan, ints);
7316  if (!res)
7317  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7318  if (!res)
7319  res = ast_streamfile(chan, "minute", lang);
7320  if (!res)
7321  res = ast_waitstream(chan, ints);
7322  return res;
7323 }
7324 
7325 /*! \brief Hebrew syntax */
7326 int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7327 {
7328  struct timeval when = { t, 0 };
7329  struct ast_tm tm;
7330  int res = 0;
7331  int hour;
7332 
7333  ast_localtime(&when, &tm, NULL);
7334  hour = tm.tm_hour;
7335  if (!hour)
7336  hour = 12;
7337 
7338  if (!res)
7339  res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
7340 
7341  if (tm.tm_min > 9) {
7342  if (!res)
7343  res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7344  } else if (tm.tm_min) {
7345  if (!res) { /* say a leading zero if needed */
7346  res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
7347  }
7348  if (!res)
7349  res = ast_waitstream(chan, ints);
7350  if (!res)
7351  res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7352  } else {
7353  if (!res)
7354  res = ast_waitstream(chan, ints);
7355  }
7356  if (!res)
7357  res = ast_waitstream(chan, ints);
7358  return res;
7359 }
7360 
7361 
7362 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7363 {
7364  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7365  return ast_say_datetime_en(chan, t, ints, lang);
7366  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
7367  return ast_say_datetime_de(chan, t, ints, lang);
7368  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7369  return ast_say_datetime_fr(chan, t, ints, lang);
7370  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
7371  return ast_say_datetime_gr(chan, t, ints, lang);
7372  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
7373  return ast_say_datetime_ja(chan, t, ints, lang);
7374  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7375  return ast_say_datetime_he(chan, t, ints, lang);
7376  } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
7377  return ast_say_datetime_hu(chan, t, ints, lang);
7378  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7379  return ast_say_datetime_ka(chan, t, ints, lang);
7380  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
7381  return ast_say_datetime_nl(chan, t, ints, lang);
7382  } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
7383  return ast_say_datetime_pt_BR(chan, t, ints, lang);
7384  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7385  return ast_say_datetime_pt(chan, t, ints, lang);
7386  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
7387  return ast_say_datetime_th(chan, t, ints, lang);
7388  } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
7389  return ast_say_datetime_zh(chan, t, ints, lang);
7390  }
7391 
7392  /* Default to English */
7393  return ast_say_datetime_en(chan, t, ints, lang);
7394 }
7395 
7396 /*! \brief English syntax */
7397 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7398 {
7399  struct timeval when = { t, 0 };
7400  struct ast_tm tm;
7401  char fn[256];
7402  int res = 0;
7403  int hour, pm=0;
7404 
7405  ast_localtime(&when, &tm, NULL);
7406  if (!res) {
7407  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7408  res = ast_streamfile(chan, fn, lang);
7409  if (!res)
7410  res = ast_waitstream(chan, ints);
7411  }
7412  if (!res) {
7413  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7414  res = ast_streamfile(chan, fn, lang);
7415  if (!res)
7416  res = ast_waitstream(chan, ints);
7417  }
7418  if (!res)
7419  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7420 
7421  hour = tm.tm_hour;
7422  if (!hour)
7423  hour = 12;
7424  else if (hour == 12)
7425  pm = 1;
7426  else if (hour > 12) {
7427  hour -= 12;
7428  pm = 1;
7429  }
7430  if (!res)
7431  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7432 
7433  if (tm.tm_min > 9) {
7434  if (!res)
7435  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7436  } else if (tm.tm_min) {
7437  if (!res)
7438  res = ast_streamfile(chan, "digits/oh", lang);
7439  if (!res)
7440  res = ast_waitstream(chan, ints);
7441  if (!res)
7442  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7443  } else {
7444  if (!res)
7445  res = ast_streamfile(chan, "digits/oclock", lang);
7446  if (!res)
7447  res = ast_waitstream(chan, ints);
7448  }
7449  if (pm) {
7450  if (!res)
7451  res = ast_streamfile(chan, "digits/p-m", lang);
7452  } else {
7453  if (!res)
7454  res = ast_streamfile(chan, "digits/a-m", lang);
7455  }
7456  if (!res)
7457  res = ast_waitstream(chan, ints);
7458  if (!res)
7459  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7460  return res;
7461 }
7462 
7463 /*! \brief German syntax */
7464 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7465 {
7466  struct timeval when = { t, 0 };
7467  struct ast_tm tm;
7468  int res = 0;
7469 
7470  ast_localtime(&when, &tm, NULL);
7471  res = ast_say_date(chan, t, ints, lang);
7472  if (!res)
7473  ast_say_time(chan, t, ints, lang);
7474  return res;
7475 
7476 }
7477 
7478 /*! \brief Hungarian syntax */
7479 int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7480 {
7481  struct timeval when = { t, 0 };
7482  struct ast_tm tm;
7483  int res = 0;
7484 
7485  ast_localtime(&when, &tm, NULL);
7486  res = ast_say_date(chan, t, ints, lang);
7487  if (!res)
7488  ast_say_time(chan, t, ints, lang);
7489  return res;
7490 }
7491 
7492 /*! \brief French syntax */
7493 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7494 {
7495  struct timeval when = { t, 0 };
7496  struct ast_tm tm;
7497  char fn[256];
7498  int res = 0;
7499 
7500  ast_localtime(&when, &tm, NULL);
7501 
7502  if (!res)
7503  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7504 
7505  if (!res) {
7506  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7507  res = ast_streamfile(chan, fn, lang);
7508  if (!res)
7509  res = ast_waitstream(chan, ints);
7510  }
7511  if (!res) {
7512  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7513  res = ast_streamfile(chan, fn, lang);
7514  if (!res)
7515  res = ast_waitstream(chan, ints);
7516  }
7517 
7518  if (!res)
7519  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7520  if (!res)
7521  res = ast_streamfile(chan, "digits/oclock", lang);
7522  if (tm.tm_min > 0) {
7523  if (!res)
7524  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7525  }
7526  if (!res)
7527  res = ast_waitstream(chan, ints);
7528  if (!res)
7529  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7530  return res;
7531 }
7532 
7533 /*! \brief Dutch syntax */
7534 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7535 {
7536  struct timeval when = { t, 0 };
7537  struct ast_tm tm;
7538  int res = 0;
7539 
7540  ast_localtime(&when, &tm, NULL);
7541  res = ast_say_date(chan, t, ints, lang);
7542  if (!res) {
7543  res = ast_streamfile(chan, "digits/nl-om", lang);
7544  if (!res)
7545  res = ast_waitstream(chan, ints);
7546  }
7547  if (!res)
7548  ast_say_time(chan, t, ints, lang);
7549  return res;
7550 }
7551 
7552 /*! \brief Portuguese syntax */
7553 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7554 {
7555  struct timeval when = { t, 0 };
7556  struct ast_tm tm;
7557  char fn[256];
7558  int res = 0;
7559  int hour, pm=0;
7560 
7561  ast_localtime(&when, &tm, NULL);
7562  if (!res) {
7563  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7564  res = ast_streamfile(chan, fn, lang);
7565  if (!res)
7566  res = ast_waitstream(chan, ints);
7567  }
7568  if (!res) {
7569  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7570  res = ast_streamfile(chan, fn, lang);
7571  if (!res)
7572  res = ast_waitstream(chan, ints);
7573  }
7574  if (!res)
7575  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7576 
7577  hour = tm.tm_hour;
7578  if (!hour)
7579  hour = 12;
7580  else if (hour == 12)
7581  pm = 1;
7582  else if (hour > 12) {
7583  hour -= 12;
7584  pm = 1;
7585  }
7586  if (!res)
7587  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7588 
7589  if (tm.tm_min > 9) {
7590  if (!res)
7591  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7592  } else if (tm.tm_min) {
7593  if (!res)
7594  res = ast_streamfile(chan, "digits/oh", lang);
7595  if (!res)
7596  res = ast_waitstream(chan, ints);
7597  if (!res)
7598  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7599  } else {
7600  if (!res)
7601  res = ast_streamfile(chan, "digits/oclock", lang);
7602  if (!res)
7603  res = ast_waitstream(chan, ints);
7604  }
7605  if (pm) {
7606  if (!res)
7607  res = ast_streamfile(chan, "digits/p-m", lang);
7608  } else {
7609  if (!res)
7610  res = ast_streamfile(chan, "digits/a-m", lang);
7611  }
7612  if (!res)
7613  res = ast_waitstream(chan, ints);
7614  if (!res)
7615  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7616  return res;
7617 }
7618 
7619 /*! \brief Brazilian Portuguese syntax */
7620 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7621 {
7622  struct timeval when = { t, 0 };
7623  struct ast_tm tm;
7624  int res = 0;
7625 
7626  ast_localtime(&when, &tm, NULL);
7627  res = ast_say_date(chan, t, ints, lang);
7628  if (!res)
7629  res = ast_say_time(chan, t, ints, lang);
7630  return res;
7631 }
7632 
7633 /*! \brief Thai syntax */
7634 int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7635 {
7636  struct timeval when = { t, 0 };
7637  struct ast_tm tm;
7638  char fn[256];
7639  int res = 0;
7640  int hour;
7641  ast_localtime(&when, &tm, NULL);
7642  if (!res) {
7643  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7644  res = ast_streamfile(chan, fn, lang);
7645  if (!res)
7646  res = ast_waitstream(chan, ints);
7647  }
7648  if (!res) {
7649  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7650  res = ast_streamfile(chan, fn, lang);
7651  if (!res)
7652  res = ast_waitstream(chan, ints);
7653  }
7654  if (!res){
7655  ast_copy_string(fn, "digits/posor", sizeof(fn));
7656  res = ast_streamfile(chan, fn, lang);
7657  res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
7658  }
7659  if (!res)
7660  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7661 
7662  hour = tm.tm_hour;
7663  if (!hour)
7664  hour = 24;
7665  if (!res){
7666  ast_copy_string(fn, "digits/wela", sizeof(fn));
7667  res = ast_streamfile(chan, fn, lang);
7668  }
7669  if (!res)
7670  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7671  if (!res)
7672  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7673  return res;
7674 }
7675 
7676 /*! \brief Taiwanese / Chinese syntax */
7677 int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7678 {
7679  struct timeval when = { t, 0 };
7680  struct ast_tm tm;
7681  char fn[256];
7682  int res = 0;
7683  int hour, pm=0;
7684 
7685  ast_localtime(&when, &tm, NULL);
7686  if (!res)
7687  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7688  if (!res) {
7689  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7690  res = ast_streamfile(chan, fn, lang);
7691  if (!res)
7692  res = ast_waitstream(chan, ints);
7693  }
7694  if (!res)
7695  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7696  if (!res) {
7697  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7698  res = ast_streamfile(chan, fn, lang);
7699  if (!res)
7700  res = ast_waitstream(chan, ints);
7701  }
7702 
7703  hour = tm.tm_hour;
7704  if (!hour)
7705  hour = 12;
7706  else if (hour == 12)
7707  pm = 1;
7708  else if (hour > 12) {
7709  hour -= 12;
7710  pm = 1;
7711  }
7712  if (pm) {
7713  if (!res)
7714  res = ast_streamfile(chan, "digits/p-m", lang);
7715  } else {
7716  if (!res)
7717  res = ast_streamfile(chan, "digits/a-m", lang);
7718  }
7719  if (!res)
7720  res = ast_waitstream(chan, ints);
7721  if (!res)
7722  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7723  if (!res)
7724  res = ast_streamfile(chan, "digits/oclock", lang);
7725  if (!res)
7726  res = ast_waitstream(chan, ints);
7727  if (!res)
7728  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7729  if (!res)
7730  res = ast_streamfile(chan, "minute", lang);
7731  if (!res)
7732  res = ast_waitstream(chan, ints);
7733  return res;
7734 }
7735 
7736 /*! \brief Hebrew syntax */
7737 int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7738 {
7739  struct timeval when = { t, 0 };
7740  struct ast_tm tm;
7741  char fn[256];
7742  int res = 0;
7743  int hour;
7744 
7745  ast_localtime(&when, &tm, NULL);
7746  if (!res) {
7747  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7748  res = ast_streamfile(chan, fn, lang);
7749  if (!res) {
7750  res = ast_waitstream(chan, ints);
7751  }
7752  }
7753  if (!res) {
7754  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7755  res = ast_streamfile(chan, fn, lang);
7756  if (!res) {
7757  res = ast_waitstream(chan, ints);
7758  }
7759  }
7760  if (!res) {
7761  res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
7762  }
7763 
7764  hour = tm.tm_hour;
7765  if (!hour) {
7766  hour = 12;
7767  }
7768 
7769  if (!res) {
7770  res = ast_say_number(chan, hour, ints, lang, "f");
7771  }
7772 
7773  if (tm.tm_min > 9) {
7774  if (!res) {
7775  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7776  }
7777  } else if (tm.tm_min) {
7778  if (!res) {
7779  /* say a leading zero if needed */
7780  res = ast_say_number(chan, 0, ints, lang, "f");
7781  }
7782  if (!res) {
7783  res = ast_waitstream(chan, ints);
7784  }
7785  if (!res) {
7786  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7787  }
7788  } else {
7789  if (!res) {
7790  res = ast_waitstream(chan, ints);
7791  }
7792  }
7793  if (!res) {
7794  res = ast_waitstream(chan, ints);
7795  }
7796  if (!res) {
7797  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
7798  }
7799  return res;
7800 }
7801 
7802 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7803 {
7804  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7805  return ast_say_datetime_from_now_en(chan, t, ints, lang);
7806  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7807  return ast_say_datetime_from_now_fr(chan, t, ints, lang);
7808  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7809  return ast_say_datetime_from_now_he(chan, t, ints, lang);
7810  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7811  return ast_say_datetime_from_now_ka(chan, t, ints, lang);
7812  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7813  return ast_say_datetime_from_now_pt(chan, t, ints, lang);
7814  }
7815 
7816  /* Default to English */
7817  return ast_say_datetime_from_now_en(chan, t, ints, lang);
7818 }
7819 
7820 /*! \brief English syntax */
7821 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7822 {
7823  int res=0;
7824  struct timeval nowtv = ast_tvnow(), when = { t, 0 };
7825  int daydiff;
7826  struct ast_tm tm;
7827  struct ast_tm now;
7828  char fn[256];
7829 
7830  ast_localtime(&when, &tm, NULL);
7831  ast_localtime(&nowtv, &now, NULL);
7832  daydiff = now.tm_yday - tm.tm_yday;
7833  if ((daydiff < 0) || (daydiff > 6)) {
7834  /* Day of month and month */
7835  if (!res) {
7836  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7837  res = ast_streamfile(chan, fn, lang);
7838  if (!res)
7839  res = ast_waitstream(chan, ints);
7840  }
7841  if (!res)
7842  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7843 
7844  } else if (daydiff) {
7845  /* Just what day of the week */
7846  if (!res) {
7847  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7848  res = ast_streamfile(chan, fn, lang);
7849  if (!res)
7850  res = ast_waitstream(chan, ints);
7851  }
7852  } /* Otherwise, it was today */
7853  if (!res)
7854  res = ast_say_time(chan, t, ints, lang);
7855  return res;
7856 }
7857 
7858 /*! \brief French syntax */
7859 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7860 {
7861  int res=0;
7862  struct timeval nowtv = ast_tvnow(), when = { t, 0 };
7863  int daydiff;
7864  struct ast_tm tm;
7865  struct ast_tm now;
7866  char fn[256];
7867 
7868  ast_localtime(&when, &tm, NULL);
7869  ast_localtime(&nowtv, &now, NULL);
7870  daydiff = now.tm_yday - tm.tm_yday;
7871  if ((daydiff < 0) || (daydiff > 6)) {
7872  /* Day of month and month */
7873  if (!res) {
7874  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7875  res = ast_streamfile(chan, fn, lang);
7876  if (!res)
7877  res = ast_waitstream(chan, ints);
7878  }
7879  if (!res)
7880  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7881 
7882  } else if (daydiff) {
7883  /* Just what day of the week */
7884  if (!res) {
7885  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7886  res = ast_streamfile(chan, fn, lang);
7887  if (!res)
7888  res = ast_waitstream(chan, ints);
7889  }
7890  } /* Otherwise, it was today */
7891  if (!res)
7892  res = ast_say_time(chan, t, ints, lang);
7893  return res;
7894 }
7895 
7896 /*! \brief Portuguese syntax */
7897 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7898 {
7899  int res=0;
7900  int daydiff;
7901  struct ast_tm tm;
7902  struct ast_tm now;
7903  struct timeval nowtv = ast_tvnow(), when = { t, 0 };
7904  char fn[256];
7905 
7906  ast_localtime(&when, &tm, NULL);
7907  ast_localtime(&nowtv, &now, NULL);
7908  daydiff = now.tm_yday - tm.tm_yday;
7909  if ((daydiff < 0) || (daydiff > 6)) {
7910  /* Day of month and month */
7911  if (!res)
7912  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7913  if (!res)
7914  res = wait_file(chan, ints, "digits/pt-de", lang);
7915  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7916  if (!res)
7917  res = wait_file(chan, ints, fn, lang);
7918 
7919  } else if (daydiff) {
7920  /* Just what day of the week */
7921  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7922  if (!res)
7923  res = wait_file(chan, ints, fn, lang);
7924  } /* Otherwise, it was today */
7925  if (!strcasecmp(lang, "pt_BR")) {
7926  if (tm.tm_hour > 1) {
7927  ast_copy_string(fn, "digits/pt-as", sizeof(fn));
7928  } else {
7929  ast_copy_string(fn, "digits/pt-a", sizeof(fn));
7930  }
7931  if (!res)
7932  res = wait_file(chan, ints, fn, lang);
7933  } else {
7934  ast_copy_string(fn, "digits/pt-ah", sizeof(fn));
7935  if (!res)
7936  res = wait_file(chan, ints, fn, lang);
7937  if (tm.tm_hour != 1)
7938  if (!res)
7939  res = wait_file(chan, ints, "digits/pt-sss", lang);
7940  if (!res)
7941  res = ast_say_time(chan, t, ints, lang);
7942  }
7943  return res;
7944 }
7945 
7946 /*! \brief Hebrew syntax */
7947 int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7948 {
7949  int res = 0;
7950  struct timeval nowt = ast_tvnow(), when = { t, 0 };
7951  int daydiff;
7952  struct ast_tm tm;
7953  struct ast_tm now;
7954  char fn[256];
7955 
7956  ast_localtime(&when, &tm, NULL);
7957  ast_localtime(&nowt, &now, NULL);
7958  daydiff = now.tm_yday - tm.tm_yday;
7959  if ((daydiff < 0) || (daydiff > 6)) {
7960  /* Day of month and month */
7961  if (!res) {
7962  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7963  res = ast_streamfile(chan, fn, lang);
7964  if (!res)
7965  res = ast_waitstream(chan, ints);
7966  }
7967  if (!res) {
7968  res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
7969  }
7970  } else if (daydiff) {
7971  /* Just what day of the week */
7972  if (!res) {
7973  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7974  res = ast_streamfile(chan, fn, lang);
7975  if (!res) {
7976  res = ast_waitstream(chan, ints);
7977  }
7978  }
7979  } /* Otherwise, it was today */
7980  if (!res) {
7981  res = ast_say_time(chan, t, ints, lang);
7982  }
7983  return res;
7984 }
7985 
7986 
7987 
7988 /*! \brief Greek
7989  * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
7990  */
7991 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
7992  int tmp;
7993  int left;
7994  int res;
7995  char fn[256] = "";
7996 
7997  /* ast_debug(1, "\n\n Saying number female %s %d \n\n", lang, num); */
7998  if (num < 5) {
7999  snprintf(fn, sizeof(fn), "digits/female-%d", num);
8000  res = wait_file(chan, ints, fn, lang);
8001  } else if (num < 13) {
8002  res = ast_say_number(chan, num, ints, lang, (char *) NULL);
8003  } else if (num <100 ) {
8004  tmp = (num/10) * 10;
8005  left = num - tmp;
8006  snprintf(fn, sizeof(fn), "digits/%d", tmp);
8007  res = ast_streamfile(chan, fn, lang);
8008  if (!res)
8009  res = ast_waitstream(chan, ints);
8010  if (left)
8011  gr_say_number_female(left, chan, ints, lang);
8012 
8013  } else {
8014  return -1;
8015  }
8016  return res;
8017 }
8018 
8019 
8020 
8021 /*! \brief Greek support
8022  * A list of the files that you need to create
8023  -> digits/xilia = "xilia"
8024  -> digits/myrio = "ekatomyrio"
8025  -> digits/thousands = "xiliades"
8026  -> digits/millions = "ektatomyria"
8027  -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
8028  -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
8029  e.g. 80 = "ogdonta"
8030  Here we must note that we use digits/tens/100 to utter "ekato"
8031  and digits/hundred-100 to utter "ekaton"
8032  -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
8033  "terakosia". Here again we use hundreds/1000 for "xilia"
8034  and digits/thousnds for "xiliades"
8035 */
8036 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8037 {
8038  int res = 0;
8039  char fn[256] = "";
8040  int i=0;
8041 
8042 
8043  if (!num) {
8044  ast_copy_string(fn, "digits/0", sizeof(fn));
8045  res = ast_streamfile(chan, fn, ast_channel_language(chan));
8046  if (!res)
8047  return ast_waitstream(chan, ints);
8048  }
8049 
8050  while (!res && num ) {
8051  i++;
8052  if (num < 13) {
8053  snprintf(fn, sizeof(fn), "digits/%d", num);
8054  num = 0;
8055  } else if (num <= 100) {
8056  /* 13 < num <= 100 */
8057  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8058  num %= 10;
8059  } else if (num < 200) {
8060  /* 100 < num < 200 */
8061  snprintf(fn, sizeof(fn), "digits/hundred-100");
8062  num %= 100;
8063  } else if (num < 1000) {
8064  /* 200 < num < 1000 */
8065  snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
8066  num %= 100;
8067  } else if (num < 2000){
8068  snprintf(fn, sizeof(fn), "digits/xilia");
8069  num %= 1000;
8070  } else {
8071  /* num > 1000 */
8072  if (num < 1000000) {
8073  res = ast_say_number_full_gr(chan, (num / 1000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8074  if (res)
8075  return res;
8076  num %= 1000;
8077  snprintf(fn, sizeof(fn), "digits/thousands");
8078  } else {
8079  if (num < 1000000000) { /* 1,000,000,000 */
8080  res = ast_say_number_full_gr(chan, (num / 1000000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8081  if (res)
8082  return res;
8083  num %= 1000000;
8084  snprintf(fn, sizeof(fn), "digits/millions");
8085  } else {
8086  ast_debug(1, "Number '%d' is too big for me\n", num);
8087  res = -1;
8088  }
8089  }
8090  }
8091  if (!res) {
8092  if (!ast_streamfile(chan, fn, language)) {
8093  if ((audiofd > -1) && (ctrlfd > -1))
8094  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8095  else
8096  res = ast_waitstream(chan, ints);
8097  }
8098  ast_stopstream(chan);
8099  }
8100  }
8101  return res;
8102 }
8103 
8104 /* Japanese syntax */
8105 static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8106 {
8107  int res = 0;
8108  int playh = 0;
8109  char fn[256] = "";
8110 
8111  if (!num)
8112  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
8113 
8114  while (!res && (num || playh)) {
8115  if (num < 0) {
8116  ast_copy_string(fn, "digits/minus", sizeof(fn));
8117  if ( num > INT_MIN ) {
8118  num = -num;
8119  } else {
8120  num = 0;
8121  }
8122  } else if (playh) {
8123  ast_copy_string(fn, "digits/hundred", sizeof(fn));
8124  playh = 0;
8125  } else if (num < 20) {
8126  snprintf(fn, sizeof(fn), "digits/%d", num);
8127  num = 0;
8128  } else if (num < 100) {
8129  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8130  num %= 10;
8131  } else {
8132  if (num < 1000){
8133  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
8134  playh++;
8135  num %= 100;
8136  } else {
8137  if (num < 1000000) { /* 1,000,000 */
8138  res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
8139  if (res)
8140  return res;
8141  num %= 1000;
8142  snprintf(fn, sizeof(fn), "digits/thousand");
8143  } else {
8144  if (num < 1000000000) { /* 1,000,000,000 */
8145  res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
8146  if (res)
8147  return res;
8148  num %= 1000000;
8149  ast_copy_string(fn, "digits/million", sizeof(fn));
8150  } else {
8151  ast_debug(1, "Number '%d' is too big for me\n", num);
8152  res = -1;
8153  }
8154  }
8155  }
8156  }
8157  if (!res) {
8158  if (!ast_streamfile(chan, fn, language)) {
8159  if ((audiofd > -1) && (ctrlfd > -1))
8160  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8161  else
8162  res = ast_waitstream(chan, ints);
8163  }
8164  ast_stopstream(chan);
8165  }
8166  }
8167  return res;
8168 }
8169 
8170 
8171 /*! \brief Greek support
8172  *
8173  * The format is weekday - day - month -year
8174  *
8175  * A list of the files that you need to create
8176  * digits/day-[1..7] : "Deytera .. Paraskeyh"
8177  * digits/months/1..12 : "Ianouariou .. Dekembriou"
8178  Attention the months are in "gekinh klhsh"
8179  */
8180 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8181 {
8182  struct ast_tm tm;
8183  struct timeval when = { t, 0 };
8184 
8185  char fn[256];
8186  int res = 0;
8187 
8188 
8189  ast_localtime(&when, &tm, NULL);
8190  /* W E E K - D A Y */
8191  if (!res) {
8192  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8193  res = ast_streamfile(chan, fn, lang);
8194  if (!res)
8195  res = ast_waitstream(chan, ints);
8196  }
8197  /* D A Y */
8198  if (!res) {
8199  gr_say_number_female(tm.tm_mday, chan, ints, lang);
8200  }
8201  /* M O N T H */
8202  if (!res) {
8203  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8204  res = ast_streamfile(chan, fn, lang);
8205  if (!res)
8206  res = ast_waitstream(chan, ints);
8207  }
8208  /* Y E A R */
8209  if (!res)
8210  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8211  return res;
8212 }
8213 
8214 
8215 /* Japanese syntax */
8216 int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8217 {
8218  struct ast_tm tm;
8219  struct timeval tv = { t, 0 };
8220  char fn[256];
8221  int res = 0;
8222 
8223  ast_localtime(&tv, &tm, NULL);
8224 
8225  if (!res)
8226  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8227  if (!res)
8228  res = ast_waitstream(chan, ints);
8229  if (!res)
8230  res = ast_streamfile(chan, "digits/nen", lang);
8231  if (!res) {
8232  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8233  res = ast_streamfile(chan, fn, lang);
8234  if (!res)
8235  res = ast_waitstream(chan, ints);
8236  }
8237  if (!res)
8238  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
8239  if (!res)
8240  res = ast_streamfile(chan, "digits/nichi", lang);
8241  if (!res) {
8242  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8243  res = ast_streamfile(chan, fn, lang);
8244  if (!res)
8245  res = ast_waitstream(chan, ints);
8246  }
8247  return res;
8248 }
8249 
8250 
8251 /*! \brief Greek support
8252  *
8253  * A list of the files that you need to create
8254  * - digits/female/1..4 : "Mia, dyo , treis, tesseris "
8255  * - digits/kai : "KAI"
8256  * - didgits : "h wra"
8257  * - digits/p-m : "meta meshmbrias"
8258  * - digits/a-m : "pro meshmbrias"
8259  */
8260 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8261 {
8262 
8263  struct timeval when = { t, 0 };
8264  struct ast_tm tm;
8265  int res = 0;
8266  int hour, pm=0;
8267 
8268  ast_localtime(&when, &tm, NULL);
8269  hour = tm.tm_hour;
8270 
8271  if (!hour)
8272  hour = 12;
8273  else if (hour == 12)
8274  pm = 1;
8275  else if (hour > 12) {
8276  hour -= 12;
8277  pm = 1;
8278  }
8279 
8280  res = gr_say_number_female(hour, chan, ints, lang);
8281  if (tm.tm_min) {
8282  if (!res)
8283  res = ast_streamfile(chan, "digits/kai", lang);
8284  if (!res)
8285  res = ast_waitstream(chan, ints);
8286  if (!res)
8287  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8288  } else {
8289  if (!res)
8290  res = ast_streamfile(chan, "digits/hwra", lang);
8291  if (!res)
8292  res = ast_waitstream(chan, ints);
8293  }
8294  if (pm) {
8295  if (!res)
8296  res = ast_streamfile(chan, "digits/p-m", lang);
8297  } else {
8298  if (!res)
8299  res = ast_streamfile(chan, "digits/a-m", lang);
8300  }
8301  if (!res)
8302  res = ast_waitstream(chan, ints);
8303  return res;
8304 }
8305 
8306 
8307 /* Japanese */
8308 static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8309 {
8310  struct timeval tv = { t, 0 };
8311  struct ast_tm tm;
8312  int res = 0;
8313  int hour, pm=0;
8314 
8315  ast_localtime(&tv, &tm, NULL);
8316  hour = tm.tm_hour;
8317 
8318  if (!hour)
8319  hour = 12;
8320  else if (hour == 12)
8321  pm = 1;
8322  else if (hour > 12) {
8323  hour -= 12;
8324  pm = 1;
8325  }
8326 
8327  if (pm) {
8328  if (!res)
8329  res = ast_streamfile(chan, "digits/p-m", lang);
8330  } else {
8331  if (!res)
8332  res = ast_streamfile(chan, "digits/a-m", lang);
8333  }
8334  if (hour == 9 || hour == 21) {
8335  if (!res)
8336  res = ast_streamfile(chan, "digits/9_2", lang);
8337  } else {
8338  if (!res)
8339  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8340  }
8341  if (!res)
8342  res = ast_streamfile(chan, "digits/ji", lang);
8343  if (!res)
8344  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8345  if (!res)
8346  res = ast_streamfile(chan, "digits/fun", lang);
8347  if (!res)
8348  res = ast_waitstream(chan, ints);
8349  return res;
8350 }
8351 
8352 
8353 /*! \brief Greek support
8354  */
8355 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8356 {
8357  struct timeval when = { t, 0 };
8358  struct ast_tm tm;
8359  char fn[256];
8360  int res = 0;
8361 
8362  ast_localtime(&when, &tm, NULL);
8363 
8364  /* W E E K - D A Y */
8365  if (!res) {
8366  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8367  res = ast_streamfile(chan, fn, lang);
8368  if (!res)
8369  res = ast_waitstream(chan, ints);
8370  }
8371  /* D A Y */
8372  if (!res) {
8373  gr_say_number_female(tm.tm_mday, chan, ints, lang);
8374  }
8375  /* M O N T H */
8376  if (!res) {
8377  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8378  res = ast_streamfile(chan, fn, lang);
8379  if (!res)
8380  res = ast_waitstream(chan, ints);
8381  }
8382 
8383  res = ast_say_time_gr(chan, t, ints, lang);
8384  return res;
8385 }
8386 
8387 /* Japanese syntax */
8388 int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8389 {
8390  struct timeval tv = { t, 0 };
8391  struct ast_tm tm;
8392  char fn[256];
8393  int res = 0;
8394  int hour, pm = 0;
8395 
8396  ast_localtime(&tv, &tm, NULL);
8397 
8398  if (!res)
8399  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8400  if (!res)
8401  res = ast_streamfile(chan, "digits/nen", lang);
8402  if (!res) {
8403  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8404  res = ast_streamfile(chan, fn, lang);
8405  if (!res)
8406  res = ast_waitstream(chan, ints);
8407  }
8408  if (!res)
8409  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8410  if (!res)
8411  res = ast_streamfile(chan, "digits/nichi", lang);
8412  if (!res) {
8413  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8414  res = ast_streamfile(chan, fn, lang);
8415  if (!res)
8416  res = ast_waitstream(chan, ints);
8417  }
8418 
8419  hour = tm.tm_hour;
8420  if (!hour)
8421  hour = 12;
8422  else if (hour == 12)
8423  pm = 1;
8424  else if (hour > 12) {
8425  hour -= 12;
8426  pm = 1;
8427  }
8428  if (pm) {
8429  if (!res)
8430  res = ast_streamfile(chan, "digits/p-m", lang);
8431  } else {
8432  if (!res)
8433  res = ast_streamfile(chan, "digits/a-m", lang);
8434  }
8435  if (hour == 9 || hour == 21) {
8436  if (!res)
8437  res = ast_streamfile(chan, "digits/9_2", lang);
8438  } else {
8439  if (!res)
8440  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8441  }
8442  if (!res)
8443  res = ast_streamfile(chan, "digits/ji", lang);
8444  if (!res)
8445  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8446  if (!res)
8447  res = ast_streamfile(chan, "digits/fun", lang);
8448  if (!res)
8449  res = ast_waitstream(chan, ints);
8450  return res;
8451 }
8452 
8453 /*! \brief Greek support
8454  */
8455 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)
8456 {
8457  struct timeval when = { t, 0 };
8458  struct ast_tm tm;
8459  int res=0, offset, sndoffset;
8460  char sndfile[256], nextmsg[256];
8461 
8462  if (!format)
8463  format = "AdBY 'digits/at' IMp";
8464 
8465  ast_localtime(&when, &tm, tzone);
8466 
8467  for (offset=0 ; format[offset] != '\0' ; offset++) {
8468  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
8469  switch (format[offset]) {
8470  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
8471  case '\'':
8472  /* Literal name of a sound file */
8473  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
8474  sndfile[sndoffset] = format[offset];
8475  }
8476  sndfile[sndoffset] = '\0';
8477  res = wait_file(chan, ints, sndfile, lang);
8478  break;
8479  case 'A':
8480  case 'a':
8481  /* Sunday - Saturday */
8482  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
8483  res = wait_file(chan, ints, nextmsg, lang);
8484  break;
8485  case 'B':
8486  case 'b':
8487  case 'h':
8488  /* January - December */
8489  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
8490  res = wait_file(chan, ints, nextmsg, lang);
8491  break;
8492  case 'd':
8493  case 'e':
8494  /* first - thirtyfirst */
8495  gr_say_number_female(tm.tm_mday, chan, ints, lang);
8496  break;
8497  case 'Y':
8498  /* Year */
8499 
8500  ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, ast_channel_language(chan), -1, -1);
8501  break;
8502  case 'I':
8503  case 'l':
8504  /* 12-Hour */
8505  if (tm.tm_hour == 0)
8506  gr_say_number_female(12, chan, ints, lang);
8507  else if (tm.tm_hour > 12)
8508  gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
8509  else
8510  gr_say_number_female(tm.tm_hour, chan, ints, lang);
8511  break;
8512  case 'H':
8513  case 'k':
8514  /* 24-Hour */
8515  gr_say_number_female(tm.tm_hour, chan, ints, lang);
8516  break;
8517  case 'M':
8518  /* Minute */
8519  if (tm.tm_min) {
8520  if (!res)
8521  res = ast_streamfile(chan, "digits/kai", lang);
8522  if (!res)
8523  res = ast_waitstream(chan, ints);
8524  if (!res)
8525  res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
8526  } else {
8527  if (!res)
8528  res = ast_streamfile(chan, "digits/oclock", lang);
8529  if (!res)
8530  res = ast_waitstream(chan, ints);
8531  }
8532  break;
8533  case 'P':
8534  case 'p':
8535  /* AM/PM */
8536  if (tm.tm_hour > 11)
8537  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
8538  else
8539  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
8540  res = wait_file(chan, ints, nextmsg, lang);
8541  break;
8542  case 'Q':
8543  /* Shorthand for "Today", "Yesterday", or ABdY */
8544  /* XXX As emphasized elsewhere, this should the native way in your
8545  * language to say the date, with changes in what you say, depending
8546  * upon how recent the date is. XXX */
8547  {
8548  struct timeval now = ast_tvnow();
8549  struct ast_tm tmnow;
8550  time_t beg_today;
8551 
8552  ast_localtime(&now, &tmnow, tzone);
8553  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8554  /* In any case, it saves not having to do ast_mktime() */
8555  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8556  if (beg_today < t) {
8557  /* Today */
8558  res = wait_file(chan, ints, "digits/today", lang);
8559  } else if (beg_today - 86400 < t) {
8560  /* Yesterday */
8561  res = wait_file(chan, ints, "digits/yesterday", lang);
8562  } else {
8563  res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
8564  }
8565  }
8566  break;
8567  case 'q':
8568  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
8569  /* XXX As emphasized elsewhere, this should the native way in your
8570  * language to say the date, with changes in what you say, depending
8571  * upon how recent the date is. XXX */
8572  {
8573  struct timeval now = ast_tvnow();
8574  struct ast_tm tmnow;
8575  time_t beg_today;
8576 
8577  ast_localtime(&now, &tmnow, tzone);
8578  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8579  /* In any case, it saves not having to do ast_mktime() */
8580  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8581  if (beg_today < t) {
8582  /* Today */
8583  } else if ((beg_today - 86400) < t) {
8584  /* Yesterday */
8585  res = wait_file(chan, ints, "digits/yesterday", lang);
8586  } else if (beg_today - 86400 * 6 < t) {
8587  /* Within the last week */
8588  res = ast_say_date_with_format_gr(chan, t, ints, lang, "A", tzone);
8589  } else {
8590  res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
8591  }
8592  }
8593  break;
8594  case 'R':
8595  res = ast_say_date_with_format_gr(chan, t, ints, lang, "HM", tzone);
8596  break;
8597  case 'S':
8598  /* Seconds */
8599  ast_copy_string(nextmsg, "digits/kai", sizeof(nextmsg));
8600  res = wait_file(chan, ints, nextmsg, lang);
8601  if (!res)
8602  res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
8603  if (!res)
8604  ast_copy_string(nextmsg, "seconds", sizeof(nextmsg));
8605  res = wait_file(chan, ints, nextmsg, lang);
8606  break;
8607  case 'T':
8608  res = ast_say_date_with_format_gr(chan, t, ints, lang, "HMS", tzone);
8609  break;
8610  case ' ':
8611  case ' ':
8612  /* Just ignore spaces and tabs */
8613  break;
8614  default:
8615  /* Unknown character */
8616  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
8617  }
8618  /* Jump out on DTMF */
8619  if (res) {
8620  break;
8621  }
8622  }
8623  return res;
8624 }
8625 
8626 /* Japanese syntax */
8627 int ast_say_date_with_format_ja(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
8628 {
8629  struct timeval tv = { time, 0 };
8630  struct ast_tm tm;
8631  int res = 0, offset, sndoffset;
8632  char sndfile[256], nextmsg[256];
8633 
8634  if (!format)
8635  format = "YbdAPIMS";
8636 
8637  ast_localtime(&tv, &tm, timezone);
8638 
8639  for (offset = 0; format[offset] != '\0'; offset++) {
8640  ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
8641  switch (format[offset]) {
8642  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
8643  case '\'':
8644  /* Literal name of a sound file */
8645  for (sndoffset = 0; (format[++offset] != '\'') && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
8646  sndfile[sndoffset] = format[offset];
8647  }
8648  sndfile[sndoffset] = '\0';
8649  res = wait_file(chan,ints,sndfile,lang);
8650  break;
8651  case 'A':
8652  case 'a':
8653  /* Sunday - Saturday */
8654  snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
8655  res = wait_file(chan,ints,nextmsg,lang);
8656  break;
8657  case 'B':
8658  case 'b':
8659  case 'h':
8660  /* January - December */
8661  snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
8662  res = wait_file(chan,ints,nextmsg,lang);
8663  break;
8664  case 'd':
8665  case 'e':
8666  /* First - Thirtyfirst */
8667  if (tm.tm_mday < 21) {
8668  snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d_2", tm.tm_mday);
8669  res = wait_file(chan,ints,nextmsg,lang);
8670  } else if (tm.tm_mday < 30) {
8671  /* Between 21 and 29 - two sounds */
8672  res = wait_file(chan,ints, "digits/20",lang);
8673  if (!res) {
8674  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
8675  res = wait_file(chan,ints,nextmsg,lang);
8676  }
8677  res = wait_file(chan,ints, "digits/nichi",lang);
8678  } else if (tm.tm_mday == 30) {
8679  /* 30 */
8680  res = wait_file(chan,ints, "digits/h-30_2",lang);
8681  } else {
8682  /* 31 */
8683  res = wait_file(chan,ints, "digits/30",lang);
8684  res = wait_file(chan,ints, "digits/1",lang);
8685  res = wait_file(chan,ints, "digits/nichi",lang);
8686  }
8687  break;
8688  case 'Y':
8689  /* Year */
8690  if (tm.tm_year > 99) {
8691  res = wait_file(chan,ints, "digits/2",lang);
8692  if (!res) {
8693  res = wait_file(chan,ints, "digits/thousand",lang);
8694  }
8695  if (tm.tm_year > 100) {
8696  if (!res) {
8697  /* This works until the end of 2020 */
8698  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
8699  res = wait_file(chan,ints,nextmsg,lang);
8700  }
8701  }
8702  } else {
8703  if (tm.tm_year < 1) {
8704  /* I'm not going to handle 1900 and prior */
8705  /* We'll just be silent on the year, instead of bombing out. */
8706  } else {
8707  res = wait_file(chan,ints, "digits/19",lang);
8708  if (!res) {
8709  if (tm.tm_year <= 9) {
8710  /* 1901 - 1909 */
8711  res = wait_file(chan,ints, "digits/oh",lang);
8712  if (!res) {
8713  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
8714  res = wait_file(chan,ints,nextmsg,lang);
8715  }
8716  } else if (tm.tm_year <= 20) {
8717  /* 1910 - 1920 */
8718  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
8719  res = wait_file(chan,ints,nextmsg,lang);
8720  } else {
8721  /* 1921 - 1999 */
8722  int ten, one;
8723  ten = tm.tm_year / 10;
8724  one = tm.tm_year % 10;
8725  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
8726  res = wait_file(chan,ints,nextmsg,lang);
8727  if (!res) {
8728  if (one != 0) {
8729  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
8730  res = wait_file(chan,ints,nextmsg,lang);
8731  }
8732  }
8733  }
8734  }
8735  }
8736  }
8737  res = wait_file(chan,ints, "digits/nen",lang);
8738  break;
8739  case 'P':
8740  case 'p':
8741  /* AM/PM */
8742  if (tm.tm_hour > 11)
8743  snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
8744  else
8745  snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
8746  res = wait_file(chan,ints,nextmsg,lang);
8747  break;
8748  case 'I':
8749  case 'l':
8750  /* 12-Hour */
8751  if (tm.tm_hour == 0)
8752  snprintf(nextmsg,sizeof(nextmsg), "digits/12");
8753  else if (tm.tm_hour == 9 || tm.tm_hour == 21)
8754  snprintf(nextmsg,sizeof(nextmsg), "digits/9_2");
8755  else if (tm.tm_hour > 12)
8756  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
8757  else
8758  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
8759  res = wait_file(chan,ints,nextmsg,lang);
8760  if(!res) res = wait_file(chan,ints, "digits/ji",lang);
8761  break;
8762  case 'H':
8763  case 'k':
8764  if (!res) {
8765  if (tm.tm_hour != 0) {
8766  int remainder = tm.tm_hour;
8767  if (tm.tm_hour > 20) {
8768  res = wait_file(chan,ints, "digits/20",lang);
8769  remainder -= 20;
8770  }
8771  if (!res) {
8772  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
8773  res = wait_file(chan,ints,nextmsg,lang);
8774  }
8775  }
8776  }
8777  res = wait_file(chan,ints, "digits/ji",lang);
8778  break;
8779  case 'M':
8780  /* Minute */
8781  if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
8782  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
8783  res = wait_file(chan,ints,nextmsg,lang);
8784  } else {
8785  int ten, one;
8786  ten = (tm.tm_min / 10) * 10;
8787  one = (tm.tm_min % 10);
8788  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
8789  res = wait_file(chan,ints,nextmsg,lang);
8790  if (!res) {
8791  /* Fifty, not fifty-zero */
8792  if (one != 0) {
8793  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
8794  res = wait_file(chan,ints,nextmsg,lang);
8795  }
8796  }
8797  }
8798  res = wait_file(chan,ints, "digits/fun",lang);
8799  break;
8800  case 'Q':
8801  /* Shorthand for "Today", "Yesterday", or ABdY */
8802  {
8803  struct timeval now;
8804  struct ast_tm tmnow;
8805  time_t beg_today;
8806 
8807  gettimeofday(&now,NULL);
8808  ast_localtime(&now,&tmnow,timezone);
8809  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8810  /* In any case, it saves not having to do ast_mktime() */
8811  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8812  if (beg_today < time) {
8813  /* Today */
8814  res = wait_file(chan,ints, "digits/today",lang);
8815  } else if (beg_today - 86400 < time) {
8816  /* Yesterday */
8817  res = wait_file(chan,ints, "digits/yesterday",lang);
8818  } else {
8819  res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
8820  }
8821  }
8822  break;
8823  case 'q':
8824  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
8825  {
8826  struct timeval now;
8827  struct ast_tm tmnow;
8828  time_t beg_today;
8829 
8830  gettimeofday(&now,NULL);
8831  ast_localtime(&now,&tmnow,timezone);
8832  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8833  /* In any case, it saves not having to do ast_mktime() */
8834  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8835  if (beg_today < time) {
8836  /* Today */
8837  } else if ((beg_today - 86400) < time) {
8838  /* Yesterday */
8839  res = wait_file(chan,ints, "digits/yesterday",lang);
8840  } else if (beg_today - 86400 * 6 < time) {
8841  /* Within the last week */
8842  res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
8843  } else {
8844  res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
8845  }
8846  }
8847  break;
8848  case 'R':
8849  res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
8850  break;
8851  case 'S':
8852  /* Seconds */
8853  if (tm.tm_sec == 0) {
8854  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
8855  res = wait_file(chan,ints,nextmsg,lang);
8856  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
8857  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
8858  res = wait_file(chan,ints,nextmsg,lang);
8859  } else {
8860  int ten, one;
8861  ten = (tm.tm_sec / 10) * 10;
8862  one = (tm.tm_sec % 10);
8863  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
8864  res = wait_file(chan,ints,nextmsg,lang);
8865  if (!res) {
8866  /* Fifty, not fifty-zero */
8867  if (one != 0) {
8868  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
8869  res = wait_file(chan,ints,nextmsg,lang);
8870  }
8871  }
8872  }
8873  res = wait_file(chan,ints, "digits/byou",lang);
8874  break;
8875  case 'T':
8876  res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
8877  break;
8878  case ' ':
8879  case ' ':
8880  /* Just ignore spaces and tabs */
8881  break;
8882  default:
8883  /* Unknown character */
8884  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
8885  }
8886  /* Jump out on DTMF */
8887  if (res) {
8888  break;
8889  }
8890  }
8891  return res;
8892 }
8893 
8894 /*! \brief Vietnamese syntax */
8895 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)
8896 {
8897  struct timeval when = { t, 0 };
8898  struct ast_tm tm;
8899  int res = 0, offset, sndoffset;
8900  char sndfile[256], nextmsg[256];
8901 
8902  if (format == NULL)
8903  format = "A 'digits/day' eB 'digits/year' Y 'digits/at' k 'hours' M 'minutes' p";
8904 
8905  ast_localtime(&when, &tm, tzone);
8906 
8907  for (offset=0 ; format[offset] != '\0' ; offset++) {
8908  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
8909  switch (format[offset]) {
8910  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
8911  case '\'':
8912  /* Literal name of a sound file */
8913  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
8914  sndfile[sndoffset] = format[offset];
8915  }
8916  sndfile[sndoffset] = '\0';
8917  res = wait_file(chan, ints, sndfile, lang);
8918  break;
8919  case 'A':
8920  case 'a':
8921  /* Sunday - Saturday */
8922  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
8923  res = wait_file(chan, ints, nextmsg, lang);
8924  break;
8925  case 'B':
8926  case 'b':
8927  case 'h':
8928  /* January - December */
8929  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
8930  res = wait_file(chan, ints, nextmsg, lang);
8931  break;
8932  case 'm':
8933  /* Month enumerated */
8934  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
8935  break;
8936  case 'd':
8937  case 'e':
8938  /* 1 - 31 */
8939  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8940  break;
8941  case 'Y':
8942  /* Year */
8943  if (tm.tm_year > 99) {
8944  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8945  } else if (tm.tm_year < 1) {
8946  /* I'm not going to handle 1900 and prior */
8947  /* We'll just be silent on the year, instead of bombing out. */
8948  } else {
8949  res = wait_file(chan, ints, "digits/19", lang);
8950  if (!res) {
8951  if (tm.tm_year <= 9) {
8952  /* 1901 - 1909 */
8953  res = wait_file(chan, ints, "digits/odd", lang);
8954  }
8955 
8956  res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
8957  }
8958  }
8959  break;
8960  case 'I':
8961  case 'l':
8962  /* 12-Hour */
8963  if (tm.tm_hour == 0)
8964  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
8965  else if (tm.tm_hour > 12)
8966  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
8967  else
8968  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
8969  res = wait_file(chan, ints, nextmsg, lang);
8970  break;
8971  case 'H':
8972  case 'k':
8973  /* 24-Hour */
8974  if (format[offset] == 'H') {
8975  /* e.g. oh-eight */
8976  if (tm.tm_hour < 10) {
8977  res = wait_file(chan, ints, "digits/0", lang);
8978  }
8979  } else {
8980  /* e.g. eight */
8981  if (tm.tm_hour == 0) {
8982  res = wait_file(chan, ints, "digits/0", lang);
8983  }
8984  }
8985  if (!res) {
8986  if (tm.tm_hour != 0) {
8987  int remaining = tm.tm_hour;
8988  if (tm.tm_hour > 20) {
8989  res = wait_file(chan, ints, "digits/20", lang);
8990  remaining -= 20;
8991  }
8992  if (!res) {
8993  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
8994  res = wait_file(chan, ints, nextmsg, lang);
8995  }
8996  }
8997  }
8998  break;
8999  case 'M':
9000  case 'N':
9001  /* Minute */
9002  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
9003  break;
9004  case 'P':
9005  case 'p':
9006  /* AM/PM */
9007  if (tm.tm_hour > 11)
9008  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
9009  else
9010  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
9011  res = wait_file(chan, ints, nextmsg, lang);
9012  break;
9013  case 'Q':
9014  /* Shorthand for "Today", "Yesterday", or ABdY */
9015  /* XXX As emphasized elsewhere, this should the native way in your
9016  * language to say the date, with changes in what you say, depending
9017  * upon how recent the date is. XXX */
9018  {
9019  struct timeval now = ast_tvnow();
9020  struct ast_tm tmnow;
9021  time_t beg_today;
9022 
9023  gettimeofday(&now, NULL);
9024  ast_localtime(&now, &tmnow, tzone);
9025  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9026  /* In any case, it saves not having to do ast_mktime() */
9027  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9028  if (beg_today < t) {
9029  /* Today */
9030  res = wait_file(chan, ints, "digits/today", lang);
9031  } else if (beg_today - 86400 < t) {
9032  /* Yesterday */
9033  res = wait_file(chan, ints, "digits/yesterday", lang);
9034  } else if (beg_today - 86400 * 6 < t) {
9035  /* Within the last week */
9036  res = ast_say_date_with_format_vi(chan, t, ints, lang, "A", tzone);
9037  } else if (beg_today - 2628000 < t) {
9038  /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9039  res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9040  } else if (beg_today - 15768000 < t) {
9041  /* Less than 6 months ago - "August seventh" */
9042  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9043  } else {
9044  /* More than 6 months ago - "April nineteenth two thousand three" */
9045  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9046  }
9047  }
9048  break;
9049  case 'q':
9050  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
9051  /* XXX As emphasized elsewhere, this should the native way in your
9052  * language to say the date, with changes in what you say, depending
9053  * upon how recent the date is. XXX */
9054  {
9055  struct timeval now;
9056  struct ast_tm tmnow;
9057  time_t beg_today;
9058 
9059  now = ast_tvnow();
9060  ast_localtime(&now, &tmnow, tzone);
9061  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9062  /* In any case, it saves not having to do ast_mktime() */
9063  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9064  if (beg_today < t) {
9065  /* Today */
9066  } else if ((beg_today - 86400) < t) {
9067  /* Yesterday */
9068  res = wait_file(chan, ints, "digits/yesterday", lang);
9069  } else if (beg_today - 86400 * 6 < t) {
9070  /* Within the last week */
9071  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
9072  } else if (beg_today - 2628000 < t) {
9073  /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9074  res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9075  } else if (beg_today - 15768000 < t) {
9076  /* Less than 6 months ago - "August seventh" */
9077  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9078  } else {
9079  /* More than 6 months ago - "April nineteenth two thousand three" */
9080  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9081  }
9082  }
9083  break;
9084  case 'R':
9085  res = ast_say_date_with_format_vi(chan, t, ints, lang, "HM", tzone);
9086  break;
9087  case 'S':
9088  /* Seconds */
9089  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
9090  break;
9091  case 'T':
9092  res = ast_say_date_with_format_vi(chan, t, ints, lang, "H 'hours' M 'minutes' S 'seconds'", tzone);
9093  break;
9094  case ' ':
9095  case ' ':
9096  /* Just ignore spaces and tabs */
9097  break;
9098  default:
9099  /* Unknown character */
9100  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9101  }
9102  /* Jump out on DTMF */
9103  if (res) {
9104  break;
9105  }
9106  }
9107  return res;
9108 }
9109 
9110 /*! \brief Georgian support
9111 
9112  Convert a number into a semi-localized string. Only for Georgian.
9113  res must be of at least 256 bytes, preallocated.
9114  The output corresponds to Georgian spoken numbers, so
9115  it may be either converted to real words by applying a direct conversion
9116  table, or played just by substituting the entities with played files.
9117 
9118  Output may consist of the following tokens (separated by spaces):
9119  0, minus.
9120  1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
9121  10-19.
9122  20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
9123  100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
9124  1000, 1000_. (atasi, atas).
9125  1000000, 1000000_. (milioni, milion).
9126  1000000000, 1000000000_. (miliardi, miliard).
9127 
9128  To be able to play the sounds, each of the above tokens needs
9129  a corresponding sound file. (e.g. 200_.gsm).
9130 */
9131 static char* ast_translate_number_ka(int num, char* res, int res_len)
9132 {
9133  char buf[256];
9134  int digit = 0;
9135  int remaining = 0;
9136 
9137 
9138  if (num < 0) {
9139  strncat(res, "minus ", res_len - strlen(res) - 1);
9140  if ( num > INT_MIN ) {
9141  num = -num;
9142  } else {
9143  num = 0;
9144  }
9145  }
9146 
9147 
9148  /* directly read the numbers */
9149  if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
9150  snprintf(buf, sizeof(buf), "%d", num);
9151  strncat(res, buf, res_len - strlen(res) - 1);
9152  return res;
9153  }
9154 
9155 
9156  if (num < 40) { /* ocda... */
9157  strncat(res, "20_ ", res_len - strlen(res) - 1);
9158  return ast_translate_number_ka(num - 20, res, res_len);
9159  }
9160 
9161  if (num < 60) { /* ormocda... */
9162  strncat(res, "40_ ", res_len - strlen(res) - 1);
9163  return ast_translate_number_ka(num - 40, res, res_len);
9164  }
9165 
9166  if (num < 80) { /* samocda... */
9167  strncat(res, "60_ ", res_len - strlen(res) - 1);
9168  return ast_translate_number_ka(num - 60, res, res_len);
9169  }
9170 
9171  if (num < 100) { /* otxmocda... */
9172  strncat(res, "80_ ", res_len - strlen(res) - 1);
9173  return ast_translate_number_ka(num - 80, res, res_len);
9174  }
9175 
9176 
9177  if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
9178  remaining = num % 100;
9179  digit = (num - remaining) / 100;
9180 
9181  if (remaining == 0) {
9182  snprintf(buf, sizeof(buf), "%d", num);
9183  strncat(res, buf, res_len - strlen(res) - 1);
9184  return res;
9185  } else {
9186  snprintf(buf, sizeof(buf), "%d_ ", digit*100);
9187  strncat(res, buf, res_len - strlen(res) - 1);
9188  return ast_translate_number_ka(remaining, res, res_len);
9189  }
9190  }
9191 
9192 
9193  if (num == 1000) {
9194  strncat(res, "1000", res_len - strlen(res) - 1);
9195  return res;
9196  }
9197 
9198 
9199  if (num < 1000000) {
9200  remaining = num % 1000;
9201  digit = (num - remaining) / 1000;
9202 
9203  if (remaining == 0) {
9204  ast_translate_number_ka(digit, res, res_len);
9205  strncat(res, " 1000", res_len - strlen(res) - 1);
9206  return res;
9207  }
9208 
9209  if (digit == 1) {
9210  strncat(res, "1000_ ", res_len - strlen(res) - 1);
9211  return ast_translate_number_ka(remaining, res, res_len);
9212  }
9213 
9214  ast_translate_number_ka(digit, res, res_len);
9215  strncat(res, " 1000_ ", res_len - strlen(res) - 1);
9216  return ast_translate_number_ka(remaining, res, res_len);
9217  }
9218 
9219 
9220  if (num == 1000000) {
9221  strncat(res, "1 1000000", res_len - strlen(res) - 1);
9222  return res;
9223  }
9224 
9225 
9226  if (num < 1000000000) {
9227  remaining = num % 1000000;
9228  digit = (num - remaining) / 1000000;
9229 
9230  if (remaining == 0) {
9231  ast_translate_number_ka(digit, res, res_len);
9232  strncat(res, " 1000000", res_len - strlen(res) - 1);
9233  return res;
9234  }
9235 
9236  ast_translate_number_ka(digit, res, res_len);
9237  strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
9238  return ast_translate_number_ka(remaining, res, res_len);
9239  }
9240 
9241 
9242  if (num == 1000000000) {
9243  strncat(res, "1 1000000000", res_len - strlen(res) - 1);
9244  return res;
9245  }
9246 
9247 
9248  if (num > 1000000000) {
9249  remaining = num % 1000000000;
9250  digit = (num - remaining) / 1000000000;
9251 
9252  if (remaining == 0) {
9253  ast_translate_number_ka(digit, res, res_len);
9254  strncat(res, " 1000000000", res_len - strlen(res) - 1);
9255  return res;
9256  }
9257 
9258  ast_translate_number_ka(digit, res, res_len);
9259  strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
9260  return ast_translate_number_ka(remaining, res, res_len);
9261  }
9262 
9263  return res;
9264 
9265 }
9266 
9267 
9268 
9269 /*! \brief ast_say_number_full_ka: Georgian syntax */
9270 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)
9271 {
9272  int res = 0;
9273  char fn[512] = "";
9274  char* s = 0;
9275  const char* remaining = fn;
9276 
9277  if (!num) {
9278  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
9279  }
9280 
9281 
9282  ast_translate_number_ka(num, fn, 512);
9283 
9284 
9285 
9286  while (res == 0 && (s = strstr(remaining, " "))) {
9287  size_t len = s - remaining;
9288  char* new_string = ast_malloc(len + 1 + strlen("digits/"));
9289 
9290  sprintf(new_string, "digits/");
9291  strncat(new_string, remaining, len); /* we can't sprintf() it, it's not null-terminated. */
9292 /* new_string[len + strlen("digits/")] = '\0'; */
9293 
9294  if (!ast_streamfile(chan, new_string, language)) {
9295  if ((audiofd > -1) && (ctrlfd > -1)) {
9296  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9297  } else {
9298  res = ast_waitstream(chan, ints);
9299  }
9300  }
9301  ast_stopstream(chan);
9302 
9303  ast_free(new_string);
9304 
9305  remaining = s + 1; /* position just after the found space char. */
9306  while (*remaining == ' ') { /* skip multiple spaces */
9307  remaining++;
9308  }
9309  }
9310 
9311 
9312  /* the last chunk. */
9313  if (res == 0 && *remaining) {
9314 
9315  char* new_string = ast_malloc(strlen(remaining) + 1 + strlen("digits/"));
9316  sprintf(new_string, "digits/%s", remaining);
9317 
9318  if (!ast_streamfile(chan, new_string, language)) {
9319  if ((audiofd > -1) && (ctrlfd > -1)) {
9320  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9321  } else {
9322  res = ast_waitstream(chan, ints);
9323  }
9324  }
9325  ast_stopstream(chan);
9326 
9327  ast_free(new_string);
9328 
9329  }
9330 
9331 
9332  return res;
9333 
9334 }
9335 
9336 
9337 
9338 /*! \brief Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi".
9339 
9340 Georgian support for date/time requires the following files (*.gsm):
9341 
9342  - mon-1, mon-2, ... (ianvari, tebervali, ...)
9343  - day-1, day-2, ... (orshabati, samshabati, ...)
9344  - saati_da
9345  - tsuti
9346  - tslis
9347 */
9348 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9349 {
9350  struct timeval when = { t, 0 };
9351  struct ast_tm tm;
9352  char fn[256];
9353  int res = 0;
9354  ast_localtime(&when, &tm, NULL);
9355 
9356  if (!res) {
9357  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
9358  }
9359 
9360  if (!res) {
9361  snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
9362  res = ast_streamfile(chan, fn, lang);
9363  if (!res) {
9364  res = ast_waitstream(chan, ints);
9365  }
9366  }
9367 
9368  if (!res) {
9369  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
9370 /* if (!res)
9371  res = ast_waitstream(chan, ints);
9372 */
9373  }
9374 
9375  if (!res) {
9376  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9377  res = ast_streamfile(chan, fn, lang);
9378  if (!res) {
9379  res = ast_waitstream(chan, ints);
9380  }
9381  }
9382  return res;
9383 
9384 }
9385 
9386 
9387 
9388 
9389 
9390 /*! \brief Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
9391 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9392 {
9393  struct timeval when = { t, 0 };
9394  struct ast_tm tm;
9395  int res = 0;
9396 
9397  ast_localtime(&when, &tm, NULL);
9398 
9399  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
9400  if (!res) {
9401  res = ast_streamfile(chan, "digits/saati_da", lang);
9402  if (!res) {
9403  res = ast_waitstream(chan, ints);
9404  }
9405  }
9406 
9407  if (tm.tm_min) {
9408  if (!res) {
9409  res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
9410 
9411  if (!res) {
9412  res = ast_streamfile(chan, "digits/tsuti", lang);
9413  if (!res) {
9414  res = ast_waitstream(chan, ints);
9415  }
9416  }
9417  }
9418  }
9419  return res;
9420 }
9421 
9422 
9423 
9424 /*! \brief Georgian syntax. Say date, then say time. */
9425 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9426 {
9427  struct timeval when = { t, 0 };
9428  struct ast_tm tm;
9429  int res = 0;
9430 
9431  ast_localtime(&when, &tm, NULL);
9432  res = ast_say_date(chan, t, ints, lang);
9433  if (!res) {
9434  ast_say_time(chan, t, ints, lang);
9435  }
9436  return res;
9437 
9438 }
9439 
9440 
9441 
9442 
9443 /*! \brief Georgian syntax */
9444 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9445 {
9446  int res=0;
9447  int daydiff;
9448  struct ast_tm tm;
9449  struct ast_tm now;
9450  struct timeval when = { t, 0 }, nowt = ast_tvnow();
9451  char fn[256];
9452 
9453  ast_localtime(&when, &tm, NULL);
9454  ast_localtime(&nowt, &now, NULL);
9455  daydiff = now.tm_yday - tm.tm_yday;
9456  if ((daydiff < 0) || (daydiff > 6)) {
9457  /* Day of month and month */
9458  if (!res) {
9459  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
9460  }
9461  if (!res) {
9462  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9463  res = ast_streamfile(chan, fn, lang);
9464  if (!res) {
9465  res = ast_waitstream(chan, ints);
9466  }
9467  }
9468 
9469  } else if (daydiff) {
9470  /* Just what day of the week */
9471  if (!res) {
9472  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
9473  res = ast_streamfile(chan, fn, lang);
9474  if (!res) {
9475  res = ast_waitstream(chan, ints);
9476  }
9477  }
9478  } /* Otherwise, it was today */
9479  if (!res) {
9480  res = ast_say_time(chan, t, ints, lang);
9481  }
9482 
9483  return res;
9484 }
9485 
9486 /*! \brief
9487  * In English, we use the plural for everything but one. For example:
9488  * - 1 degree
9489  * - 2 degrees
9490  * - 5 degrees
9491  * The filename for the plural form is generated by appending "s". Note that
9492  * purpose is to generate a unique filename, not to implement irregular
9493  * declensions. Thus:
9494  * - 1 man
9495  * - 2 mans (the "mans" soundfile will of course say "men")
9496  */
9497 static const char *counted_noun_ending_en(int num)
9498 {
9499  if (num == 1 || num == -1) {
9500  return "";
9501  } else {
9502  return "s";
9503  }
9504 }
9505 
9506 /*! \brief
9507  * Counting of objects in slavic languages such as Russian and Ukrainian the
9508  * rules are more complicated. There are two plural forms used in counting.
9509  * They are the genative singular which we represent with the suffix "x1" and
9510  * the genative plural which we represent with the suffix "x2". The base names
9511  * of the soundfiles remain in English. For example:
9512  * - 1 degree (soudfile says "gradus")
9513  * - 2 degreex1 (soundfile says "gradusa")
9514  * - 5 degreex2 (soundfile says "gradusov")
9515  */
9516 static const char *counted_noun_ending_slavic(int num)
9517 {
9518  if (num < 0) {
9519  num *= -1;
9520  }
9521  num %= 100; /* never pay attention to more than two digits */
9522  if (num >= 20) { /* for numbers 20 and above, pay attention to only last digit */
9523  num %= 10;
9524  }
9525  if (num == 1) { /* singular */
9526  return "";
9527  }
9528  if (num > 0 && num < 5) { /* 2--4 get genative singular */
9529  return "x1";
9530  } else { /* 5--19 get genative plural */
9531  return "x2";
9532  }
9533 }
9534 
9535 int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
9536 {
9537  char *temp;
9538  int temp_len;
9539  const char *ending;
9540  if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
9541  ending = counted_noun_ending_slavic(num);
9542  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
9543  ending = counted_noun_ending_slavic(num);
9544  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
9545  ending = counted_noun_ending_slavic(num);
9546  } else { /* English and default */
9547  ending = counted_noun_ending_en(num);
9548  }
9549  temp = ast_alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
9550  snprintf(temp, temp_len, "%s%s", noun, ending);
9551  return ast_play_and_wait(chan, temp);
9552 }
9553 
9554 /*! \brief
9555  * In slavic languages such as Russian and Ukrainian the rules for declining
9556  * adjectives are simpler than those for nouns. When counting we use only
9557  * the singular (to which we give no suffix) and the genative plural (which
9558  * we represent by adding an "x"). Oh, an in the singular gender matters
9559  * so we append the supplied gender suffix ("m", "f", "n").
9560  */
9561 static const char *counted_adjective_ending_ru(int num, const char gender[])
9562 {
9563  if (num < 0) {
9564  num *= -1;
9565  }
9566  num %= 100; /* never pay attention to more than two digits */
9567  if (num >= 20) { /* at 20 and beyond only the last digit matters */
9568  num %= 10;
9569  }
9570  if (num == 1) {
9571  return gender ? gender : "";
9572  } else { /* all other numbers get the genative plural */
9573  return "x";
9574  }
9575 }
9576 
9577 int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
9578 {
9579  char *temp;
9580  int temp_len;
9581  const char *ending;
9582  if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
9583  ending = counted_adjective_ending_ru(num, gender);
9584  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
9585  ending = counted_adjective_ending_ru(num, gender);
9586  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
9587  ending = counted_adjective_ending_ru(num, gender);
9588  } else { /* English and default */
9589  ending = "";
9590  }
9591  temp = ast_alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
9592  snprintf(temp, temp_len, "%s%s", adjective, ending);
9593  return ast_play_and_wait(chan, temp);
9594 }
9595 
9596 
9597 
9598 /*! \brief
9599  * remap the 'say' functions to use those in this file
9600  */
9601 static void __attribute__((constructor)) __say_init(void)
9602 {
9614 }
static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Definition: say.c:3198
static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:7947
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)
Definition: say.c:171
struct ast_str * ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity)
Returns an ast_str of files for SayAlpha playback.
Definition: say.c:63
char digit
Main Channel structure associated with a channel.
SAY_EXTERN int(* ast_say_number_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_number_full)
Same as ast_say_number() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: say.h:86
#define IL_DATE_STR_FULL
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)
Definition: say.c:2748
static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_number_full: call language-specific functions
Definition: say.c:713
static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
Definition: say.c:699
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1250
Asterisk locking-related definitions:
static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Brazilian Portuguese syntax.
Definition: say.c:7620
Asterisk main include file. File version handling, generic pbx functions.
SAY_EXTERN int(* ast_say_time)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_time)
Definition: say.h:183
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)
ast_say_number_full_se: Swedish syntax
Definition: say.c:2555
SAY_EXTERN int(* ast_say_enumeration_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_enumeration_full)
Same as ast_say_enumeration() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: say.h:106
static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_hu: Hungarian syntax
Definition: say.c:1619
static char * ast_translate_number_ka(int num, char *res, int res_len)
Georgian support.
Definition: say.c:9131
static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:7634
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)
Definition: say.c:3543
static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:4002
static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax. e.g. "otxi saati da eqvsi tsuti".
Definition: say.c:9391
char * setki[10]
Definition: say.c:2158
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)
ast_say_number_full_ru: Russian syntax
Definition: say.c:2834
Time-related functions and macros.
static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:7326
static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:7534
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
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)
Dutch syntax.
Definition: say.c:6061
struct ast_str * ast_get_number_str(int num, const char *lang)
ast_get_number_str: call language-specific functions
Definition: say.c:525
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)
Taiwanese / Chinese syntax.
Definition: say.c:6773
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:4060
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)
English syntax.
Definition: say.c:4246
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)
ast_say_enumeration_full_is: Icelandic syntax
Definition: say.c:3632
#define SAY_NUM_BUF_SIZE
Definition: say.c:1446
static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax. Say date, then say time.
Definition: say.c:9425
static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_enumeration_full_en: English syntax
Definition: say.c:3101
SAY_EXTERN int(* ast_say_datetime_from_now)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_datetime_from_now)
Definition: say.h:187
static int tmp()
Definition: bt_open.c:389
char * nastki[10]
Definition: say.c:2160
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_say_digits_full(struct ast_channel *chan, int num, const char *ints, const char *lang, int audiofd, int ctrlfd)
Same as ast_say_digits() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: channel.c:8379
static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:7076
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)
ast_say_enumeration_full_da: Danish syntax
Definition: say.c:3218
static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:7802
char * cyfry2[10]
Definition: say.c:2157
static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8180
static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:7493
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)
Spanish syntax.
Definition: say.c:5442
Test Framework API.
static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:7362
static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:3842
static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:7859
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Greek support A list of the files that you need to create -> digits/xilia = "xilia" -> digits/myrio =...
Definition: say.c:8036
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_nl: dutch syntax New files: digits/nl-en
Definition: say.c:1974
const char * str
Definition: app_jack.c:147
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
#define NULL
Definition: resample.c:96
#define LOG_DEBUG
Definition: logger.h:241
static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_it: Italian
Definition: say.c:1819
struct ast_str * ast_get_money_str(const char *str, const char *lang)
ast_get_money_str: call language-specific functions
Definition: say.c:419
static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:7397
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)
Definition: say.c:1447
int tm_year
Definition: localtime.h:41
#define ast_verb(level,...)
Definition: logger.h:463
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)
ast_say_number_full_is: Icelandic syntax
Definition: say.c:1699
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)
Greek support.
Definition: say.c:8455
static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:4031
SAY_EXTERN int(* ast_say_money_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_money_str_full)
Definition: say.h:151
static int exp10_int(int power)
Definition: say.c:796
Utility functions.
static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:7897
static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi".
Definition: say.c:9348
char * cyfry[10]
Definition: say.c:2156
Custom localtime functions for multiple timezones.
#define IL_DATE_STR
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)
ast_say_number_full_fr: French syntax Extra sounds needed: 1F: feminin &#39;une&#39; et: &#39;and&#39; ...
Definition: say.c:1355
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)
Vietnamese syntax.
Definition: say.c:8895
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)
ast_say_number_full_no: Norwegian syntax New files: In addition to American English, the following sounds are required: "and", "1N"
Definition: say.c:2072
char * rzedy[3][3]
Definition: say.c:2161
static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:7553
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:3970
struct ast_str * ast_get_digit_str(const char *str, const char *lang)
Returns an ast_str of files for SayDigits playback.
Definition: say.c:294
static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:7266
General Asterisk PBX channel definitions.
SAY_EXTERN int(* ast_say_digit_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_digit_str_full)
Same as ast_say_digit_str() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: say.h:143
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)
ast_say_number_full_cs: Czech syntax
Definition: say.c:826
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)
ast_say_number_full_de: German syntax
Definition: say.c:1038
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)
Definition: say.c:2455
static char * pl_append(char *buffer, char *str)
Definition: say.c:2177
static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:4157
static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Definition: say.c:8105
int tm_mon
Definition: localtime.h:40
static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:3807
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
Play a stream and wait for a digit, returning the digit that was pressed.
Definition: main/app.c:1470
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)
ast_say_date_with_format_he Say formatted date in Hebrew
Definition: say.c:5295
static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:7125
static const char * counted_adjective_ending_ru(int num, const char gender[])
In slavic languages such as Russian and Ukrainian the rules for declining adjectives are simpler than...
Definition: say.c:9561
static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
Definition: say.c:430
static struct ast_str * ast_get_money_en_dollars_str(const char *str, const char *lang)
Definition: say.c:363
char * separator_dziesiatek
Definition: say.c:2155
static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
ast_say_enumeration_full: call language-specific functions
Definition: say.c:3079
static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:8388
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:117
static char next_item(const char *format)
Definition: say.c:4486
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
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)
German syntax.
Definition: say.c:4695
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)
Definition: say.c:8627
int tm_mday
Definition: localtime.h:39
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)
Thai syntax.
Definition: say.c:5110
SAY_EXTERN int(* ast_say_date)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_date)
Definition: say.h:185
static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_en_GB: British syntax New files:
Definition: say.c:1172
static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Taiwanese / Chinese syntax.
Definition: say.c:7677
ast_say_case_sensitivity
Controls how ast_say_character_str denotes the case of characters in a string.
Definition: say.h:162
static int get_lastdigits_ru(int num)
determine last digits for thousands/millions (ru)
Definition: say.c:2807
static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:4123
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:196
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:7187
static void __say_init(void)
remap the &#39;say&#39; functions to use those in this file
Definition: say.c:9601
SAY_EXTERN int(* ast_say_date_with_format)(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *timezone) SAY_INIT(ast_say_date_with_format)
Definition: say.h:189
static char * pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
Definition: say.c:2164
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
Definition: say.c:2198
static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:7464
static const char * counted_noun_ending_slavic(int num)
Counting of objects in slavic languages such as Russian and Ukrainian the rules are more complicated...
Definition: say.c:9516
Definition: say.c:2154
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
Definition: say.c:9577
static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:8308
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:157
char * dziesiatki[10]
Definition: say.c:2159
int tm_wday
Definition: localtime.h:42
#define ast_free(a)
Definition: astmm.h:182
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)
Definition: say.c:4205
static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:7821
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)
ast_say_number_full_es: Spanish syntax
Definition: say.c:1251
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)
ast_say_number_full_da: Danish syntax New files:
Definition: say.c:924
static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:3920
int tm_hour
Definition: localtime.h:38
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)
ast_say_enumeration_full_de: German syntax
Definition: say.c:3381
SAY_EXTERN int(* ast_say_datetime)(struct ast_channel *chan, time_t t, const char *ints, const char *lang) SAY_INIT(ast_say_datetime)
Definition: say.h:182
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)
Definition: say.c:4898
int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
Definition: say.c:9535
static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:4096
static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_vi: Vietnamese syntax
Definition: say.c:2983
static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Danish syntax.
Definition: say.c:3871
static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:7207
int tm_sec
Definition: localtime.h:36
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:653
static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:7168
static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Brazilian Portuguese syntax.
Definition: say.c:7236
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)
Italian syntax.
Definition: say.c:5829
int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int monfd)
Definition: file.c:1785
char * strsep(char **str, const char *delims)
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)
Polish syntax.
Definition: say.c:6264
static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
Thai syntax.
Definition: say.c:2913
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)
Portuguese syntax.
Definition: say.c:6481
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition: channel.c:8337
struct ast_str * ast_get_phonetic_str(const char *str, const char *lang)
Returns an ast_str of files for SayPhonetic playback.
Definition: say.c:195
static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
Definition: say.c:339
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:7145
const char * ast_channel_name(const struct ast_channel *chan)
static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:7041
#define IL_TIME_STR
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1776
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1086
static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
Definition: say.c:270
static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:7479
static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Definition: say.c:8216
static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_zh: Taiwanese / Chinese syntax
Definition: say.c:2635
static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Taiwanese / Chinese syntax.
Definition: say.c:7284
const char * ast_channel_language(const struct ast_channel *chan)
static const char * counted_noun_ending_en(int num)
In English, we use the plural for everything but one. For example:
Definition: say.c:9497
SAY_EXTERN int(* ast_say_character_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd) SAY_INIT(ast_say_character_str_full)
Definition: say.h:175
static struct test_val b
static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
Definition: say.c:2184
static struct test_options options
int tm_yday
Definition: localtime.h:43
static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Georgian syntax.
Definition: say.c:9444
static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8355
int ast_say_enumeration(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says an enumeration
Definition: channel.c:8343
static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8260
Say numbers and dates (maybe words one day too)
static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
ast_say_number_full_en: English syntax
Definition: say.c:772
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)
ast_say_number_full_ka: Georgian syntax
Definition: say.c:9270
SAY_EXTERN int(* ast_say_phonetic_str_full)(struct ast_channel *chan, const char *num, const char *ints, const char *lang, int audiofd, int ctrlfd) SAY_INIT(ast_say_phonetic_str_full)
Definition: say.h:180
static snd_pcm_format_t format
Definition: chan_alsa.c:102
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:7737
static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang)
Greek digits/female-[1..4] : "Mia, dyo , treis, tessereis".
Definition: say.c:7991
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)
Definition: say.c:2359
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:187
int tm_min
Definition: localtime.h:37
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)
Danish syntax.
Definition: say.c:4493
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
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)
French syntax oclock = heure.
Definition: say.c:5633
static struct ast_str * get_number_str_en(int num, const char *lang)
Definition: say.c:454