Asterisk - The Open Source Telephony Project  21.4.1
say.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  * George Konstantoulakis <gkon@inaccessnetworks.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*! \file
21  *
22  * \brief Say numbers and dates (maybe words one day too)
23  *
24  * \author Mark Spencer <markster@digium.com>
25  *
26  * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
27  *
28  * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
29  * Next Generation Networks (NGN).
30  * \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,
31  * IP Crossing Co., Ltd.
32  * \note 2021-07-26 : Refactoring to separate string buildup and playback
33  * by Naveen Albert <asterisk@phreaknet.org>
34  */
35 
36 /*** MODULEINFO
37  <support_level>core</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 #include <netinet/in.h>
43 #include <time.h>
44 #include <ctype.h>
45 #include <math.h>
46 
47 #ifdef SOLARIS
48 #include <iso/limits_iso.h>
49 #endif
50 
51 #include "asterisk/file.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/say.h"
54 #include "asterisk/lock.h"
55 #include "asterisk/localtime.h"
56 #include "asterisk/utils.h"
57 #include "asterisk/app.h"
58 #include "asterisk/test.h"
59 #include "asterisk/cli.h" /* use ESS */
60 
61 /* Forward declaration */
62 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
63 
64 struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {
65  const char *fn;
66  char fnbuf[10], asciibuf[20] = "letters/ascii";
67  char ltr;
68  int num = 0;
69  int res = 0;
70  int upper = 0;
71  int lower = 0;
72 
73  struct ast_str *filenames = ast_str_create(20);
74  if (!filenames) {
75  return NULL;
76  }
77  ast_str_reset(filenames);
78 
79  while (str[num] && !res) {
80  fn = NULL;
81  switch (str[num]) {
82  case ('*'):
83  fn = "digits/star";
84  break;
85  case ('#'):
86  fn = "digits/pound";
87  break;
88  case ('!'):
89  fn = "letters/exclaimation-point";
90  break;
91  case ('@'):
92  fn = "letters/at";
93  break;
94  case ('$'):
95  fn = "letters/dollar";
96  break;
97  case ('-'):
98  fn = "letters/dash";
99  break;
100  case ('.'):
101  fn = "letters/dot";
102  break;
103  case ('='):
104  fn = "letters/equals";
105  break;
106  case ('+'):
107  fn = "letters/plus";
108  break;
109  case ('/'):
110  fn = "letters/slash";
111  break;
112  case (' '):
113  fn = "letters/space";
114  break;
115  case ('0'):
116  case ('1'):
117  case ('2'):
118  case ('3'):
119  case ('4'):
120  case ('5'):
121  case ('6'):
122  case ('7'):
123  case ('8'):
124  case ('9'):
125  strcpy(fnbuf, "digits/X");
126  fnbuf[7] = str[num];
127  fn = fnbuf;
128  break;
129  default:
130  ltr = str[num];
131  if ('A' <= ltr && ltr <= 'Z') {
132  ltr += 'a' - 'A'; /* file names are all lower-case */
133  switch (sensitivity) {
134  case AST_SAY_CASE_UPPER:
135  case AST_SAY_CASE_ALL:
136  upper = !upper;
137  case AST_SAY_CASE_LOWER:
138  case AST_SAY_CASE_NONE:
139  break;
140  }
141  } else if ('a' <= ltr && ltr <= 'z') {
142  switch (sensitivity) {
143  case AST_SAY_CASE_LOWER:
144  case AST_SAY_CASE_ALL:
145  lower = !lower;
146  case AST_SAY_CASE_UPPER:
147  case AST_SAY_CASE_NONE:
148  break;
149  }
150  }
151 
152  if (upper) {
153  strcpy(fnbuf, "uppercase");
154  } else if (lower) {
155  strcpy(fnbuf, "lowercase");
156  } else {
157  strcpy(fnbuf, "letters/X");
158  fnbuf[8] = ltr;
159  }
160  fn = fnbuf;
161  }
162  if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
163  (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
164  ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
165  }
166  if (upper || lower) {
167  continue;
168  }
169  num++;
170  }
171 
172  return filenames;
173 }
174 
175 static int say_filenames(struct ast_channel *chan, const char *ints, const char *lang, int audiofd, int ctrlfd, struct ast_str *filenames)
176 {
177  int res = 0;
178  char *files;
179  const char *fn;
180 
181  if (!filenames) {
182  return -1;
183  }
184 
185  /* No filenames to play? Return success so we don't hang up erroneously */
186  if (ast_str_strlen(filenames) == 0) {
187  ast_free(filenames);
188  return 0;
189  }
190 
191  files = ast_str_buffer(filenames);
192 
193  while (!res && (fn = strsep(&files, "&"))) {
194  res = ast_streamfile(chan, fn, lang);
195  if (!res) {
196  if ((audiofd > -1) && (ctrlfd > -1)) {
197  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
198  } else {
199  res = ast_waitstream(chan, ints);
200  }
201  }
202  ast_stopstream(chan);
203  }
204 
205  ast_free(filenames);
206 
207  return res;
208 }
209 
210 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)
211 {
212  struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);
213  return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
214 }
215 
216 struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)
217 {
218  const char *fn;
219  char fnbuf[256];
220  char ltr;
221  int num = 0;
222 
223  struct ast_str *filenames = ast_str_create(20);
224  if (!filenames) {
225  return NULL;
226  }
227  ast_str_reset(filenames);
228 
229  while (str[num]) {
230  fn = NULL;
231  switch (str[num]) {
232  case ('*'):
233  fn = "digits/star";
234  break;
235  case ('#'):
236  fn = "digits/pound";
237  break;
238  case ('!'):
239  fn = "letters/exclaimation-point";
240  break;
241  case ('@'):
242  fn = "letters/at";
243  break;
244  case ('$'):
245  fn = "letters/dollar";
246  break;
247  case ('-'):
248  fn = "letters/dash";
249  break;
250  case ('.'):
251  fn = "letters/dot";
252  break;
253  case ('='):
254  fn = "letters/equals";
255  break;
256  case ('+'):
257  fn = "letters/plus";
258  break;
259  case ('/'):
260  fn = "letters/slash";
261  break;
262  case (' '):
263  fn = "letters/space";
264  break;
265  case ('0'):
266  case ('1'):
267  case ('2'):
268  case ('3'):
269  case ('4'):
270  case ('5'):
271  case ('6'):
272  case ('7'):
273  case ('8'):
274  strcpy(fnbuf, "digits/X");
275  fnbuf[7] = str[num];
276  fn = fnbuf;
277  break;
278  default: /* '9' falls here... */
279  ltr = str[num];
280  if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
281  strcpy(fnbuf, "phonetic/X_p");
282  fnbuf[9] = ltr;
283  fn = fnbuf;
284  }
285  if (fn && ast_fileexists(fn, NULL, lang) > 0) {
286  ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
287  }
288  num++;
289  }
290 
291  return filenames;
292 }
293 
294 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
295 {
296  struct ast_str *filenames = ast_get_phonetic_str(str, lang);
297  return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
298 }
299 
300 struct ast_str* ast_get_digit_str(const char *str, const char *lang)
301 {
302  const char *fn;
303  char fnbuf[256];
304  int num = 0;
305 
306  struct ast_str *filenames = ast_str_create(20);
307  if (!filenames) {
308  return NULL;
309  }
310  ast_str_reset(filenames);
311 
312  while (str[num]) {
313  fn = NULL;
314  switch (str[num]) {
315  case ('*'):
316  fn = "digits/star";
317  break;
318  case ('#'):
319  fn = "digits/pound";
320  break;
321  case ('-'):
322  fn = "digits/minus";
323  break;
324  case '0':
325  case '1':
326  case '2':
327  case '3':
328  case '4':
329  case '5':
330  case '6':
331  case '7':
332  case '8':
333  case '9':
334  strcpy(fnbuf, "digits/X");
335  fnbuf[7] = str[num];
336  fn = fnbuf;
337  break;
338  }
339  if (fn && ast_fileexists(fn, NULL, lang) > 0) {
340  ast_str_append(&filenames, 0, "%s%s", ast_str_strlen(filenames) ? "&" : "", fn);
341  }
342  num++;
343  }
344 
345  return filenames;
346 }
347 
348 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
349 {
350  struct ast_str *filenames = ast_get_digit_str(str, lang);
351  return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
352 }
353 
354 static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)
355 {
356  const char *fnr;
357  int amt, dollars = 0, cents = 0;
358  struct ast_str *fnrecurse = NULL;
359  struct ast_str *filenames;
360 
361  if (ast_strlen_zero(str)) {
362  return NULL;
363  }
364 
365  filenames = ast_str_create(20);
366  if (!filenames) {
367  return NULL;
368  }
369  ast_str_reset(filenames);
370 
371  /* Don't use %f because floating point rounding
372  * could distort the cents units. Just parse as string. */
373  if (str && *str == '.') {
374  if (sscanf(str, ".%02u", &cents) < 1) {
375  dollars = cents = 0;
376  } else {
377  /* If we have a space instead of numbers after '.',
378  * then it's not quite valid. */
379  const char *period = strchr(str, '.');
380  if (period && !isdigit(*(period + 1))) {
381  cents = 0;
382  }
383  }
384  } else {
385  int res = sscanf(str, "%d.%02u", &dollars, &cents);
386  if (res < 1) {
387  dollars = cents = 0;
388  } else if (res == 2) {
389  const char *period = strchr(str, '.');
390  if (period && !isdigit(*(period + 1))) {
391  cents = 0;
392  }
393  }
394  }
395  amt = dollars * 100 + cents; /* convert everything to cents */
396 
397  ast_debug(1, "Amount is %d (%d dollar%s, %d cent%s)\n", amt, dollars, ESS(dollars), cents, ESS(cents));
398 
399  if (amt >= 100) {
400  fnrecurse = ast_get_number_str((amt / 100), lang);
401  if (!fnrecurse) {
402  ast_log(LOG_WARNING, "Couldn't get string for dollars\n");
403  } else {
404  fnr = ast_str_buffer(fnrecurse);
405  ast_str_append(&filenames, 0, "%s", fnr);
406  }
407 
408  /* If this is it, end on a down pitch, otherwise up pitch */
409  if (amt < 200) {
410  ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");
411  } else {
412  ast_str_append(&filenames, 0, "&%s", "dollars");
413  }
414 
415  /* If dollars and cents, add "and" in the middle */
416  if (cents > 0) {
417  ast_str_append(&filenames, 0, "&%s", "and");
418  }
419  }
420 
421  if (cents > 0) {
422  fnrecurse = ast_get_number_str(cents, lang);
423  if (!fnrecurse) {
424  ast_log(LOG_ERROR, "Couldn't get string for cents\n");
425  } else {
426  fnr = ast_str_buffer(fnrecurse);
427  ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);
428  }
429  ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");
430  } else if (amt == 0) {
431  fnrecurse = ast_get_digit_str("0", lang);
432  if (!fnrecurse) {
433  ast_log(LOG_ERROR, "Couldn't get string for cents\n");
434  } else {
435  fnr = ast_str_buffer(fnrecurse);
436  ast_str_append(&filenames, 0, "%s", fnr);
437  }
438  ast_str_append(&filenames, 0, "&%s", "cents");
439  }
440 
441  if (fnrecurse) {
442  ast_free(fnrecurse);
443  }
444 
445  return filenames;
446 }
447 
448 /*! \brief ast_get_money_str: call language-specific functions */
449 struct ast_str* ast_get_money_str(const char *str, const char *lang)
450 {
451  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
452  return ast_get_money_en_dollars_str(str, lang);
453  }
454 
455  ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);
456  /* Default to english */
457  return ast_get_money_en_dollars_str(str, lang);
458 }
459 
460 static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
461 {
462  struct ast_str *filenames = ast_get_money_str(str, lang);
463  return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
464 }
465 
466 static struct ast_str* get_number_str_en(int num, const char *lang)
467 {
468  const char *fnr;
469  int loops = 0;
470 
471  int res = 0;
472  int playh = 0;
473  char fn[256] = "";
474 
475  struct ast_str *filenames;
476 
477  if (!num) {
478  return ast_get_digit_str("0", lang);
479  }
480 
481  filenames = ast_str_create(20);
482  if (!filenames) {
483  return NULL;
484  }
485  ast_str_reset(filenames);
486 
487  while (!res && (num || playh)) {
488  if (num < 0) {
489  ast_copy_string(fn, "digits/minus", sizeof(fn));
490  if ( num > INT_MIN ) {
491  num = -num;
492  } else {
493  num = 0;
494  }
495  } else if (playh) {
496  ast_copy_string(fn, "digits/hundred", sizeof(fn));
497  playh = 0;
498  } else if (num < 20) {
499  snprintf(fn, sizeof(fn), "digits/%d", num);
500  num = 0;
501  } else if (num < 100) {
502  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
503  num %= 10;
504  } else {
505  if (num < 1000){
506  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
507  playh++;
508  num %= 100;
509  } else {
510  struct ast_str *fnrecurse = NULL;
511  if (num < 1000000) { /* 1,000,000 */
512  fnrecurse = get_number_str_en((num / 1000), lang);
513  if (!fnrecurse) {
514  ast_log(LOG_ERROR, "Couldn't get string for num\n");
515  } else {
516  fnr = ast_str_buffer(fnrecurse);
517  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
518  }
519  num %= 1000;
520  snprintf(fn, sizeof(fn), "&digits/thousand");
521  } else {
522  if (num < 1000000000) { /* 1,000,000,000 */
523  fnrecurse = get_number_str_en((num / 1000000), lang);
524  if (!fnrecurse) {
525  ast_log(LOG_ERROR, "Couldn't get string for num\n");
526  } else {
527  fnr = ast_str_buffer(fnrecurse);
528  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
529  }
530  num %= 1000000;
531  ast_copy_string(fn, "&digits/million", sizeof(fn));
532  } else {
533  if (num < INT_MAX) {
534  fnrecurse = get_number_str_en((num / 1000000000), lang);
535  if (!fnrecurse) {
536  ast_log(LOG_ERROR, "Couldn't get string for num\n");
537  } else {
538  fnr = ast_str_buffer(fnrecurse);
539  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
540  }
541  num %= 1000000000;
542  ast_copy_string(fn, "&digits/billion", sizeof(fn));
543  } else {
544  ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
545  res = -1;
546  }
547  }
548  }
549  if (fnrecurse) {
550  ast_free(fnrecurse);
551  }
552  /* we already decided whether or not to add an &, don't add another one immediately */
553  loops = 0;
554  }
555  }
556  if (!res) {
557  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
558  loops++;
559  }
560  }
561 
562  return filenames;
563 }
564 
565 /*! \brief ast_get_number_str: call language-specific functions */
566 struct ast_str* ast_get_number_str(int num, const char *lang)
567 {
568  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
569  return get_number_str_en(num, lang);
570  }
571 
572  ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
573  /* Default to english */
574  return get_number_str_en(num, lang);
575 }
576 
577 static struct ast_str* get_ordinal_str_en(int num, const char *lang)
578 {
579  const char *fnr;
580  int loops = 0;
581 
582  int res = 0;
583  int playh = 0;
584  char fn[256] = "";
585 
586  struct ast_str *filenames;
587 
588  if (!num) {
589  num = 0;
590  }
591 
592  filenames = ast_str_create(20);
593  if (!filenames) {
594  return NULL;
595  }
596  ast_str_reset(filenames);
597 
598  while (!res && (num || playh)) {
599  if (num < 0) {
600  ast_copy_string(fn, "digits/minus", sizeof(fn));
601  if ( num > INT_MIN ) {
602  num = -num;
603  } else {
604  num = 0;
605  }
606  } else if (playh) {
607  ast_copy_string(fn, (num % 100 == 0) ? "digits/h-hundred" : "digits/hundred", sizeof(fn));
608  playh = 0;
609  } else if (num < 20) {
610  if (num > 0) {
611  snprintf(fn, sizeof(fn), "digits/h-%d", num);
612  } else {
613  ast_log(LOG_ERROR, "Unsupported ordinal number: %d\n", num);
614  }
615  num = 0;
616  } else if (num < 100) {
617  int base = (num / 10) * 10;
618  if (base != num) {
619  snprintf(fn, sizeof(fn), "digits/%d", base);
620  } else {
621  snprintf(fn, sizeof(fn), "digits/h-%d", base);
622  }
623  num %= 10;
624  } else {
625  if (num < 1000){
626  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
627  playh++;
628  num %= 100;
629  } else {
630  struct ast_str *fnrecurse = NULL;
631  if (num < 1000000) { /* 1,000,000 */
632  fnrecurse = get_number_str_en((num / 1000), lang);
633  if (!fnrecurse) {
634  ast_log(LOG_ERROR, "Couldn't get string for num\n");
635  } else {
636  fnr = ast_str_buffer(fnrecurse);
637  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
638  }
639  num %= 1000;
640  snprintf(fn, sizeof(fn), (num % 1000 == 0) ? "&digits/h-thousand" : "&digits/thousand");
641  } else {
642  if (num < 1000000000) { /* 1,000,000,000 */
643  fnrecurse = get_number_str_en((num / 1000000), lang);
644  if (!fnrecurse) {
645  ast_log(LOG_ERROR, "Couldn't get string for num\n");
646  } else {
647  fnr = ast_str_buffer(fnrecurse);
648  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
649  }
650  num %= 1000000;
651  ast_copy_string(fn, (num % 1000000 == 0) ? "&digits/h-million" : "&digits/million", sizeof(fn));
652  } else {
653  if (num < INT_MAX) {
654  fnrecurse = get_number_str_en((num / 1000000000), lang);
655  if (!fnrecurse) {
656  ast_log(LOG_ERROR, "Couldn't get string for num\n");
657  } else {
658  fnr = ast_str_buffer(fnrecurse);
659  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
660  }
661  num %= 1000000000;
662  ast_copy_string(fn, (num % 1000000000 == 0) ? "&digits/h-billion" : "&digits/billion", sizeof(fn));
663  } else {
664  ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
665  res = -1;
666  }
667  }
668  }
669  if (fnrecurse) {
670  ast_free(fnrecurse);
671  }
672  /* we already decided whether or not to add an &, don't add another one immediately */
673  loops = 0;
674  }
675  }
676  if (!res) {
677  ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
678  loops++;
679  }
680  }
681 
682  return filenames;
683 }
684 
685 /*! \brief ast_get_ordinal_str: call language-specific functions */
686 struct ast_str* ast_get_ordinal_str(int num, const char *lang)
687 {
688  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
689  return get_ordinal_str_en(num, lang);
690  }
691 
692  ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
693  /* Default to english */
694  return get_ordinal_str_en(num, lang);
695 }
696 
697 /* Forward declarations */
698 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
699  \note Not really language codes.
700  For these language codes, Asterisk will change the syntax when
701  saying numbers (and in some cases dates and voicemail messages
702  as well)
703  \arg \b da - Danish
704  \arg \b de - German
705  \arg \b en - English (US)
706  \arg \b en_GB - English (British)
707  \arg \b es - Spanish, Mexican
708  \arg \b fr - French
709  \arg \b he - Hebrew
710  \arg \b is - Icelandic
711  \arg \b it - Italian
712  \arg \b nl - Dutch
713  \arg \b no - Norwegian
714  \arg \b pl - Polish
715  \arg \b pt - Portuguese
716  \arg \b pt_BR - Portuguese (Brazil)
717  \arg \b se - Swedish
718  \arg \b zh - Taiwanese / Chinese
719  \arg \b ru - Russian
720  \arg \b ka - Georgian
721  \arg \b hu - Hungarian
722 
723  \par Gender:
724  For some languages the numbers differ for gender of the countable object.
725  Commonly for "one", like "un"/"une" in French. Note that the interface
726  is somewhat peculiar, as differing languages can have conflicting
727  genders.
728  \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
729  \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
730 
731  Date/Time functions currently have less languages supported than saynumber().
732 
733  \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
734 
735  See contrib/i18n.testsuite.conf for some examples of the different syntaxes
736 
737  \par Portuguese
738  Portuguese sound files needed for Time/Date functions:
739  pt-ah
740  pt-ao
741  pt-de
742  pt-e
743  pt-ora
744  pt-meianoite
745  pt-meiodia
746  pt-sss
747 
748  \par Spanish
749  Spanish sound files needed for Time/Date functions:
750  es-de
751  es-el
752 
753  \par Italian
754  Italian sound files needed for Time/Date functions:
755  ore-una
756  ore-mezzanotte
757 
758 */
759 
760 /* Forward declarations of language specific variants of ast_say_number_full */
761 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
762 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);
763 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);
764 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);
765 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
766 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);
767 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);
768 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);
769 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);
770 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
771 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
772 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);
773 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);
774 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);
775 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);
776 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
777 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
778 static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
779 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);
780 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);
781 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
782 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
783 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);
784 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
785 
786 /* Forward declarations of language specific variants of ast_say_enumeration_full */
787 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
788 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);
789 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);
790 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);
791 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);
792 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
793 
794 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
795 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
796 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
797 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
798 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
799 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
800 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
801 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
802 static int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
803 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
804 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
805 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
806 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
807 static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
808 
809 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);
810 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);
811 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);
812 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);
813 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);
814 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);
815 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);
816 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);
817 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);
818 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);
819 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);
820 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);
821 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);
822 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);
823 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);
824 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);
825 
826 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
827 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
828 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
829 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
830 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
831 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
832 static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
833 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
834 static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
835 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
836 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
837 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
838 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
839 
840 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
841 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
842 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
843 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
844 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
845 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
846 static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
847 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
848 static int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
849 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
850 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
851 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
852 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
853 
854 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
855 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
856 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
857 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
858 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
859 
860 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
861 {
862  int res;
863  if ((res = ast_streamfile(chan, file, lang))) {
864  ast_log(LOG_WARNING, "Unable to play message %s\n", file);
865  }
866  if (!res) {
867  res = ast_waitstream(chan, ints);
868  }
869  return res;
870 }
871 
872 /*! \brief ast_say_number_full: call language-specific functions
873  \note Called from AGI */
874 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
875 {
876  ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
877  if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
878  return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
879  } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
880  return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
881  } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
882  return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
883  } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
884  return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
885  } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
886  return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
887  } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
888  return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
889  } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
890  return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
891  } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
892  return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
893  } else if (!strncasecmp(language, "ja", 2)) { /* Japanese syntax */
894  return ast_say_number_full_ja(chan, num, ints, language, audiofd, ctrlfd);
895  } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
896  return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
897  } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
898  return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
899  } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
900  return ast_say_number_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
901  } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
902  return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
903  } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
904  return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
905  } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
906  return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
907  } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
908  return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
909  } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
910  return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
911  } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
912  return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
913  } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
914  return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
915  } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
916  return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
917  } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
918  return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
919  } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
920  return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
921  } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
922  return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
923  } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
924  return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
925  }
926 
927  /* Default to english */
928  return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
929 }
930 
931 /*! \brief ast_say_number_full_en: English syntax
932  \note This is the default syntax, if no other syntax defined in this file is used */
933 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
934 {
935  struct ast_str *filenames = ast_get_number_str(num, language);
936  return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
937 }
938 
939 /*! \brief say_ordinal_full */
940 static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
941 {
942  struct ast_str *filenames = ast_get_ordinal_str(num, language);
943  return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
944 }
945 
946 static int exp10_int(int power)
947 {
948  int x, res= 1;
949  for (x=0;x<power;x++)
950  res *= 10;
951  return res;
952 }
953 
954 /*! \brief ast_say_number_full_cs: Czech syntax
955  *
956  * files needed:
957  * - 1m,2m - gender male
958  * - 1w,2w - gender female
959  * - 3,4,...,20
960  * - 30,40,...,90
961  *
962  * - hundreds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
963  *
964  * for each number 10^(3n + 3) exist 3 files represented as:
965  * 1 thousand = jeden tisic = 1_E3
966  * 2,3,4 thousands = dva,tri,ctyri tisice = 2-3_E3
967  * 5,6,... thousands = pet,sest,... tisic = 5_E3
968  *
969  * million = _E6
970  * miliard = _E9
971  * etc...
972  *
973  * thousand, milion are gender male, so 1 and 2 is 1m 2m
974  * miliard is gender female, so 1 and 2 is 1w 2w
975  */
976 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)
977 {
978  int res = 0;
979  int playh = 0;
980  char fn[256] = "";
981 
982  int hundred = 0;
983  int left = 0;
984  int length = 0;
985 
986  /* options - w = woman, m = man, n = neutral. Defaultl is woman */
987  if (!options)
988  options = "w";
989 
990  if (!num)
991  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
992 
993  while (!res && (num || playh)) {
994  if (num < 0) {
995  ast_copy_string(fn, "digits/minus", sizeof(fn));
996  if ( num > INT_MIN ) {
997  num = -num;
998  } else {
999  num = 0;
1000  }
1001  } else if (num < 3 ) {
1002  snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
1003  playh = 0;
1004  num = 0;
1005  } else if (num < 20) {
1006  snprintf(fn, sizeof(fn), "digits/%d", num);
1007  playh = 0;
1008  num = 0;
1009  } else if (num < 100) {
1010  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1011  num %= 10;
1012  } else if (num < 1000) {
1013  hundred = num / 100;
1014  if ( hundred == 1 ) {
1015  ast_copy_string(fn, "digits/1sto", sizeof(fn));
1016  } else if ( hundred == 2 ) {
1017  ast_copy_string(fn, "digits/2ste", sizeof(fn));
1018  } else {
1019  res = ast_say_number_full_cs(chan, hundred, ints, language, options, audiofd, ctrlfd);
1020  if (res)
1021  return res;
1022  if (hundred == 3 || hundred == 4) {
1023  ast_copy_string(fn, "digits/sta", sizeof(fn));
1024  } else if ( hundred > 4 ) {
1025  ast_copy_string(fn, "digits/set", sizeof(fn));
1026  }
1027  }
1028  num -= (hundred * 100);
1029  } else { /* num > 1000 */
1030  length = (int)log10(num)+1;
1031  while ( (length % 3 ) != 1 ) {
1032  length--;
1033  }
1034  left = num / (exp10_int(length-1));
1035  if ( left == 2 ) {
1036  switch (length-1) {
1037  case 9: options = "w"; /* 1,000,000,000 gender female */
1038  break;
1039  default : options = "m"; /* others are male */
1040  }
1041  }
1042  if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
1043  res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
1044  if (res)
1045  return res;
1046  }
1047  if ( left >= 5 ) { /* >= 5 have the same declension */
1048  snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
1049  } else if ( left >= 2 && left <= 4 ) {
1050  snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
1051  } else { /* left == 1 */
1052  snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
1053  }
1054  num -= left * (exp10_int(length-1));
1055  }
1056  if (!res) {
1057  if (!ast_streamfile(chan, fn, language)) {
1058  if ((audiofd > -1) && (ctrlfd > -1)) {
1059  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1060  } else {
1061  res = ast_waitstream(chan, ints);
1062  }
1063  }
1064  ast_stopstream(chan);
1065  }
1066  }
1067  return res;
1068 }
1069 
1070 /*! \brief ast_say_number_full_da: Danish syntax
1071  New files:
1072  - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
1073  */
1074 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)
1075 {
1076  int res = 0;
1077  int playh = 0;
1078  int playa = 0;
1079  int cn = 1; /* +1 = commune; -1 = neuter */
1080  char fn[256] = "";
1081  if (!num)
1082  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1083 
1084  if (options && !strncasecmp(options, "n", 1)) cn = -1;
1085 
1086  while (!res && (num || playh || playa )) {
1087  /* The grammar for Danish numbers is the same as for English except
1088  * for the following:
1089  * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
1090  * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1091  * "one-and twenty" and 68 is "eight-and sixty".
1092  * - "million" is different in singular and plural form
1093  * - numbers > 1000 with zero as the third digit from last have an
1094  * "and" before the last two digits, i.e. 2034 is "two thousand and
1095  * four-and thirty" and 1000012 is "one million and twelve".
1096  */
1097  if (num < 0) {
1098  ast_copy_string(fn, "digits/minus", sizeof(fn));
1099  if ( num > INT_MIN ) {
1100  num = -num;
1101  } else {
1102  num = 0;
1103  }
1104  } else if (playh) {
1105  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1106  playh = 0;
1107  } else if (playa) {
1108  ast_copy_string(fn, "digits/and", sizeof(fn));
1109  playa = 0;
1110  } else if (num == 1 && cn == -1) {
1111  ast_copy_string(fn, "digits/1N", sizeof(fn));
1112  num = 0;
1113  } else if (num < 20) {
1114  snprintf(fn, sizeof(fn), "digits/%d", num);
1115  num = 0;
1116  } else if (num < 100) {
1117  int ones = num % 10;
1118  if (ones) {
1119  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1120  num -= ones;
1121  } else {
1122  snprintf(fn, sizeof(fn), "digits/%d", num);
1123  num = 0;
1124  }
1125  } else {
1126  if (num < 1000) {
1127  int hundreds = num / 100;
1128  if (hundreds == 1)
1129  ast_copy_string(fn, "digits/1N", sizeof(fn));
1130  else
1131  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1132 
1133  playh++;
1134  num -= 100 * hundreds;
1135  if (num)
1136  playa++;
1137 
1138  } else {
1139  if (num < 1000000) {
1140  res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1141  if (res)
1142  return res;
1143  num = num % 1000;
1144  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1145  } else {
1146  if (num < 1000000000) {
1147  int millions = num / 1000000;
1148  res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
1149  if (res)
1150  return res;
1151  if (millions == 1)
1152  ast_copy_string(fn, "digits/million", sizeof(fn));
1153  else
1154  ast_copy_string(fn, "digits/millions", sizeof(fn));
1155  num = num % 1000000;
1156  } else {
1157  ast_debug(1, "Number '%d' is too big for me\n", num);
1158  res = -1;
1159  }
1160  }
1161  if (num && num < 100)
1162  playa++;
1163  }
1164  }
1165  if (!res) {
1166  if (!ast_streamfile(chan, fn, language)) {
1167  if ((audiofd > -1) && (ctrlfd > -1))
1168  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1169  else
1170  res = ast_waitstream(chan, ints);
1171  }
1172  ast_stopstream(chan);
1173  }
1174  }
1175  return res;
1176 }
1177 
1178 /*! \brief ast_say_number_full_de: German syntax
1179 
1180  New files:
1181  In addition to English, the following sounds are required:
1182  - "millions"
1183  - "1-and" through "9-and"
1184  - "1F" (eine)
1185  - "1N" (ein)
1186  - NB "1" is recorded as 'eins'
1187  */
1188 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)
1189 {
1190  int res = 0, t = 0;
1191  int mf = 1; /* +1 = male and neuter; -1 = female */
1192  char fn[256] = "";
1193  char fna[256] = "";
1194  if (!num)
1195  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1196 
1197  if (options && (!strncasecmp(options, "f", 1)))
1198  mf = -1;
1199 
1200  while (!res && num) {
1201  /* The grammar for German numbers is the same as for English except
1202  * for the following:
1203  * - numbers 20 through 99 are said in reverse order, i.e. 21 is
1204  * "one-and twenty" and 68 is "eight-and sixty".
1205  * - "one" varies according to gender
1206  * - 100 is 'hundert', however all other instances are 'ein hundert'
1207  * - 1000 is 'tausend', however all other instances are 'ein tausend'
1208  * - 1000000 is always 'eine million'
1209  * - "million" is different in singular and plural form
1210  * - 'and' should not go between a hundreds place value and any
1211  * tens/ones place values that follows it. i.e 136 is ein hundert
1212  * sechs und dreizig, not ein hundert und sechs und dreizig.
1213  */
1214  if (num < 0) {
1215  ast_copy_string(fn, "digits/minus", sizeof(fn));
1216  if ( num > INT_MIN ) {
1217  num = -num;
1218  } else {
1219  num = 0;
1220  }
1221  } else if (num == 1 && mf == -1) {
1222  snprintf(fn, sizeof(fn), "digits/%dF", num);
1223  num = 0;
1224  } else if (num < 20) {
1225  snprintf(fn, sizeof(fn), "digits/%d", num);
1226  num = 0;
1227  } else if (num < 100) {
1228  int ones = num % 10;
1229  if (ones) {
1230  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
1231  num -= ones;
1232  } else {
1233  snprintf(fn, sizeof(fn), "digits/%d", num);
1234  num = 0;
1235  }
1236  } else if (num == 100 && t == 0) {
1237  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1238  num = 0;
1239  } else if (num < 1000) {
1240  int hundreds = num / 100;
1241  num = num % 100;
1242  if (hundreds == 1) {
1243  ast_copy_string(fn, "digits/1N", sizeof(fn));
1244  } else {
1245  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
1246  }
1247  ast_copy_string(fna, "digits/hundred", sizeof(fna));
1248  } else if (num == 1000 && t == 0) {
1249  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1250  num = 0;
1251  } else if (num < 1000000) {
1252  int thousands = num / 1000;
1253  num = num % 1000;
1254  t = 1;
1255  if (thousands == 1) {
1256  ast_copy_string(fn, "digits/1N", sizeof(fn));
1257  ast_copy_string(fna, "digits/thousand", sizeof(fna));
1258  } else {
1259  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
1260  if (res)
1261  return res;
1262  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1263  }
1264  } else if (num < 1000000000) {
1265  int millions = num / 1000000;
1266  num = num % 1000000;
1267  t = 1;
1268  if (millions == 1) {
1269  ast_copy_string(fn, "digits/1F", sizeof(fn));
1270  ast_copy_string(fna, "digits/million", sizeof(fna));
1271  } else {
1272  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
1273  if (res)
1274  return res;
1275  ast_copy_string(fn, "digits/millions", sizeof(fn));
1276  }
1277  } else if (num <= INT_MAX) {
1278  int billions = num / 1000000000;
1279  num = num % 1000000000;
1280  t = 1;
1281  if (billions == 1) {
1282  ast_copy_string(fn, "digits/1F", sizeof(fn));
1283  ast_copy_string(fna, "digits/milliard", sizeof(fna));
1284  } else {
1285  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
1286  if (res) {
1287  return res;
1288  }
1289  ast_copy_string(fn, "digits/milliards", sizeof(fn));
1290  }
1291  } else {
1292  ast_debug(1, "Number '%d' is too big for me\n", num);
1293  res = -1;
1294  }
1295  if (!res) {
1296  if (!ast_streamfile(chan, fn, language)) {
1297  if ((audiofd > -1) && (ctrlfd > -1))
1298  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1299  else
1300  res = ast_waitstream(chan, ints);
1301  }
1302  ast_stopstream(chan);
1303  if (!res) {
1304  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
1305  if ((audiofd > -1) && (ctrlfd > -1))
1306  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1307  else
1308  res = ast_waitstream(chan, ints);
1309  }
1310  ast_stopstream(chan);
1311  strcpy(fna, "");
1312  }
1313  }
1314  }
1315  return res;
1316 }
1317 
1318 /*! \brief ast_say_number_full_en_GB: British syntax
1319  New files:
1320  - In addition to American English, the following sounds are required: "vm-and"
1321  */
1322 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1323 {
1324  int res = 0;
1325  int playh = 0;
1326  int playa = 0;
1327  char fn[256] = "";
1328  if (!num)
1329  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1330 
1331  while (!res && (num || playh || playa )) {
1332  if (num < 0) {
1333  ast_copy_string(fn, "digits/minus", sizeof(fn));
1334  if ( num > INT_MIN ) {
1335  num = -num;
1336  } else {
1337  num = 0;
1338  }
1339  } else if (playh) {
1340  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1341  playh = 0;
1342  } else if (playa) {
1343  ast_copy_string(fn, "vm-and", sizeof(fn));
1344  playa = 0;
1345  } else if (num < 20) {
1346  snprintf(fn, sizeof(fn), "digits/%d", num);
1347  num = 0;
1348  } else if (num < 100) {
1349  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1350  num %= 10;
1351  } else if (num < 1000) {
1352  int hundreds = num / 100;
1353  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1354 
1355  playh++;
1356  num -= 100 * hundreds;
1357  if (num)
1358  playa++;
1359  } else if (num < 1000000) {
1360  res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
1361  if (res)
1362  return res;
1363  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1364  num %= 1000;
1365  if (num && num < 100)
1366  playa++;
1367  } else if (num < 1000000000) {
1368  int millions = num / 1000000;
1369  res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1370  if (res)
1371  return res;
1372  ast_copy_string(fn, "digits/million", sizeof(fn));
1373  num %= 1000000;
1374  if (num && num < 100)
1375  playa++;
1376  } else {
1377  ast_debug(1, "Number '%d' is too big for me\n", num);
1378  res = -1;
1379  }
1380 
1381  if (!res) {
1382  if (!ast_streamfile(chan, fn, language)) {
1383  if ((audiofd > -1) && (ctrlfd > -1))
1384  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1385  else
1386  res = ast_waitstream(chan, ints);
1387  }
1388  ast_stopstream(chan);
1389  }
1390  }
1391  return res;
1392 }
1393 
1394 /*! \brief ast_say_number_full_es: Spanish syntax
1395 
1396  New files:
1397  Requires a few new audios:
1398  1F.gsm: feminine 'una'
1399  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
1400  */
1401 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)
1402 {
1403  int res = 0;
1404  int playa = 0;
1405  int mf = 0; /* +1 = male; -1 = female */
1406  char fn[256] = "";
1407  if (!num)
1408  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1409 
1410  if (options) {
1411  if (!strncasecmp(options, "f", 1))
1412  mf = -1;
1413  else if (!strncasecmp(options, "m", 1))
1414  mf = 1;
1415  }
1416 
1417  while (!res && num) {
1418  if (num < 0) {
1419  ast_copy_string(fn, "digits/minus", sizeof(fn));
1420  if ( num > INT_MIN ) {
1421  num = -num;
1422  } else {
1423  num = 0;
1424  }
1425  } else if (playa) {
1426  ast_copy_string(fn, "digits/and", sizeof(fn));
1427  playa = 0;
1428  } else if (num == 1) {
1429  if (mf < 0)
1430  snprintf(fn, sizeof(fn), "digits/%dF", num);
1431  else if (mf > 0)
1432  snprintf(fn, sizeof(fn), "digits/%dM", num);
1433  else
1434  snprintf(fn, sizeof(fn), "digits/%d", num);
1435  num = 0;
1436  } else if (num < 31) {
1437  snprintf(fn, sizeof(fn), "digits/%d", num);
1438  num = 0;
1439  } else if (num < 100) {
1440  snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1441  num %= 10;
1442  if (num)
1443  playa++;
1444  } else if (num == 100) {
1445  ast_copy_string(fn, "digits/100", sizeof(fn));
1446  num = 0;
1447  } else if (num < 200) {
1448  ast_copy_string(fn, "digits/100-and", sizeof(fn));
1449  num -= 100;
1450  } else {
1451  if (num < 1000) {
1452  snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1453  num %= 100;
1454  } else if (num < 2000) {
1455  num %= 1000;
1456  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1457  } else {
1458  if (num < 1000000) {
1459  res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1460  if (res)
1461  return res;
1462  num %= 1000;
1463  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1464  } else {
1465  if (num < 2147483640) {
1466  if ((num/1000000) == 1) {
1467  res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1468  if (res)
1469  return res;
1470  ast_copy_string(fn, "digits/million", sizeof(fn));
1471  } else {
1472  res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1473  if (res)
1474  return res;
1475  ast_copy_string(fn, "digits/millions", sizeof(fn));
1476  }
1477  num %= 1000000;
1478  } else {
1479  ast_debug(1, "Number '%d' is too big for me\n", num);
1480  res = -1;
1481  }
1482  }
1483  }
1484  }
1485 
1486  if (!res) {
1487  if (!ast_streamfile(chan, fn, language)) {
1488  if ((audiofd > -1) && (ctrlfd > -1))
1489  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1490  else
1491  res = ast_waitstream(chan, ints);
1492  }
1493  ast_stopstream(chan);
1494 
1495  }
1496 
1497  }
1498  return res;
1499 }
1500 
1501 /*! \brief ast_say_number_full_fr: French syntax
1502  Extra sounds needed:
1503  1F: feminin 'une'
1504  et: 'and' */
1505 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)
1506 {
1507  int res = 0;
1508  int playh = 0;
1509  int playa = 0;
1510  int mf = 1; /* +1 = male; -1 = female */
1511  char fn[256] = "";
1512  if (!num)
1513  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1514 
1515  if (options && !strncasecmp(options, "f", 1))
1516  mf = -1;
1517 
1518  while (!res && (num || playh || playa)) {
1519  if (num < 0) {
1520  ast_copy_string(fn, "digits/minus", sizeof(fn));
1521  if ( num > INT_MIN ) {
1522  num = -num;
1523  } else {
1524  num = 0;
1525  }
1526  } else if (playh) {
1527  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1528  playh = 0;
1529  } else if (playa) {
1530  ast_copy_string(fn, "digits/et", sizeof(fn));
1531  playa = 0;
1532  } else if (num == 1) {
1533  if (mf < 0)
1534  snprintf(fn, sizeof(fn), "digits/%dF", num);
1535  else
1536  snprintf(fn, sizeof(fn), "digits/%d", num);
1537  num = 0;
1538  } else if (num < 21) {
1539  snprintf(fn, sizeof(fn), "digits/%d", num);
1540  num = 0;
1541  } else if (num < 70) {
1542  snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1543  if ((num % 10) == 1) playa++;
1544  num = num % 10;
1545  } else if (num < 80) {
1546  ast_copy_string(fn, "digits/60", sizeof(fn));
1547  if ((num % 10) == 1) playa++;
1548  num -= 60;
1549  } else if (num < 100) {
1550  ast_copy_string(fn, "digits/80", sizeof(fn));
1551  num = num - 80;
1552  } else if (num < 200) {
1553  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1554  num = num - 100;
1555  } else if (num < 1000) {
1556  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1557  playh++;
1558  num = num % 100;
1559  } else if (num < 2000) {
1560  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1561  num = num - 1000;
1562  } else if (num < 1000000) {
1563  res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1564  if (res)
1565  return res;
1566  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1567  num = num % 1000;
1568  } else if (num < 1000000000) {
1569  res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1570  if (res)
1571  return res;
1572  ast_copy_string(fn, "digits/million", sizeof(fn));
1573  num = num % 1000000;
1574  } else {
1575  ast_debug(1, "Number '%d' is too big for me\n", num);
1576  res = -1;
1577  }
1578  if (!res) {
1579  if (!ast_streamfile(chan, fn, language)) {
1580  if ((audiofd > -1) && (ctrlfd > -1))
1581  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1582  else
1583  res = ast_waitstream(chan, ints);
1584  }
1585  ast_stopstream(chan);
1586  }
1587  }
1588  return res;
1589 }
1590 
1591 
1592 
1593 /* Hebrew syntax
1594  * Check doc/lang/hebrew-digits.txt for information about the various
1595  * recordings required to make this translation work properly */
1596 #define SAY_NUM_BUF_SIZE 256
1597 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)
1598 {
1599  int res = 0;
1600  int state = 0; /* no need to save anything */
1601  int mf = -1; /* +1 = Masculin; -1 = Feminin */
1602  int tmpnum = 0;
1603 
1604  char fn[SAY_NUM_BUF_SIZE] = "";
1605 
1606  ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1607 
1608  if (!num) {
1609  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1610  }
1611  if (options && !strncasecmp(options, "m", 1)) {
1612  mf = 1;
1613  }
1614  ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1615 
1616  /* Do we have work to do? */
1617  while (!res && (num || (state > 0))) {
1618  /* first type of work: play a second sound. In this loop
1619  * we can only play one sound file at a time. Thus playing
1620  * a second one requires repeating the loop just for the
1621  * second file. The variable 'state' remembers where we were.
1622  * state==0 is the normal mode and it means that we continue
1623  * to check if the number num has yet anything left.
1624  */
1625  ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
1626 
1627  if (state == 1) {
1628  state = 0;
1629  } else if (state == 2) {
1630  if ((num >= 11) && (num < 21)) {
1631  if (mf < 0) {
1632  snprintf(fn, sizeof(fn), "digits/ve");
1633  } else {
1634  snprintf(fn, sizeof(fn), "digits/uu");
1635  }
1636  } else {
1637  switch (num) {
1638  case 1:
1639  snprintf(fn, sizeof(fn), "digits/ve");
1640  break;
1641  case 2:
1642  snprintf(fn, sizeof(fn), "digits/uu");
1643  break;
1644  case 3:
1645  if (mf < 0) {
1646  snprintf(fn, sizeof(fn), "digits/ve");
1647  } else {
1648  snprintf(fn, sizeof(fn), "digits/uu");
1649  }
1650  break;
1651  case 4:
1652  snprintf(fn, sizeof(fn), "digits/ve");
1653  break;
1654  case 5:
1655  snprintf(fn, sizeof(fn), "digits/ve");
1656  break;
1657  case 6:
1658  snprintf(fn, sizeof(fn), "digits/ve");
1659  break;
1660  case 7:
1661  snprintf(fn, sizeof(fn), "digits/ve");
1662  break;
1663  case 8:
1664  snprintf(fn, sizeof(fn), "digits/uu");
1665  break;
1666  case 9:
1667  snprintf(fn, sizeof(fn), "digits/ve");
1668  break;
1669  case 10:
1670  snprintf(fn, sizeof(fn), "digits/ve");
1671  break;
1672  }
1673  }
1674  state = 0;
1675  } else if (state == 3) {
1676  snprintf(fn, sizeof(fn), "digits/1k");
1677  state = 0;
1678  } else if (num < 0) {
1679  snprintf(fn, sizeof(fn), "digits/minus");
1680  num = (-1) * num;
1681  } else if (num < 20) {
1682  if (mf < 0) {
1683  snprintf(fn, sizeof(fn), "digits/%d", num);
1684  } else {
1685  snprintf(fn, sizeof(fn), "digits/%dm", num);
1686  }
1687  num = 0;
1688  } else if ((num < 100) && (num >= 20)) {
1689  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1690  num = num % 10;
1691  if (num > 0) {
1692  state = 2;
1693  }
1694  } else if ((num >= 100) && (num < 1000)) {
1695  tmpnum = num / 100;
1696  snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1697  num = num - (tmpnum * 100);
1698  if ((num > 0) && (num < 11)) {
1699  state = 2;
1700  }
1701  } else if ((num >= 1000) && (num < 10000)) {
1702  tmpnum = num / 1000;
1703  snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1704  num = num - (tmpnum * 1000);
1705  if ((num > 0) && (num < 11)) {
1706  state = 2;
1707  }
1708  } else if (num < 20000) {
1709  snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1710  num = num % 1000;
1711  state = 3;
1712  } else if (num < 1000000) {
1713  res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1714  if (res) {
1715  return res;
1716  }
1717  snprintf(fn, sizeof(fn), "digits/1k");
1718  num = num % 1000;
1719  if ((num > 0) && (num < 11)) {
1720  state = 2;
1721  }
1722  } else if (num < 2000000) {
1723  snprintf(fn, sizeof(fn), "digits/million");
1724  num = num % 1000000;
1725  if ((num > 0) && (num < 11)) {
1726  state = 2;
1727  }
1728  } else if (num < 3000000) {
1729  snprintf(fn, sizeof(fn), "digits/twomillion");
1730  num = num - 2000000;
1731  if ((num > 0) && (num < 11)) {
1732  state = 2;
1733  }
1734  } else if (num < 1000000000) {
1735  res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1736  if (res) {
1737  return res;
1738  }
1739  snprintf(fn, sizeof(fn), "digits/million");
1740  num = num % 1000000;
1741  if ((num > 0) && (num < 11)) {
1742  state = 2;
1743  }
1744  } else {
1745  ast_debug(1, "Number '%d' is too big for me\n", num);
1746  res = -1;
1747  }
1748  tmpnum = 0;
1749  if (!res) {
1750  if (!ast_streamfile(chan, fn, language)) {
1751  if ((audiofd > -1) && (ctrlfd > -1)) {
1752  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1753  } else {
1754  res = ast_waitstream(chan, ints);
1755  }
1756  }
1757  ast_stopstream(chan);
1758  }
1759  }
1760  return res;
1761 }
1762 
1763 /*! \brief ast_say_number_full_hu: Hungarian syntax
1764 
1765  Extra sounds needed:
1766  10en: "tizen"
1767  20on: "huszon"
1768 */
1769 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1770 {
1771  int res = 0;
1772  int playh = 0;
1773  char fn[256] = "";
1774  if (!num)
1775  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1776 
1777  /*
1778  Hungarian support
1779  like english, except numbers up to 29 are from 2 words.
1780  10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1781  */
1782 
1783  while(!res && (num || playh)) {
1784  if (num < 0) {
1785  ast_copy_string(fn, "digits/minus", sizeof(fn));
1786  if ( num > INT_MIN ) {
1787  num = -num;
1788  } else {
1789  num = 0;
1790  }
1791  } else if (playh) {
1792  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1793  playh = 0;
1794  } else if (num < 11 || num == 20) {
1795  snprintf(fn, sizeof(fn), "digits/%d", num);
1796  num = 0;
1797  } else if (num < 20) {
1798  ast_copy_string(fn, "digits/10en", sizeof(fn));
1799  num -= 10;
1800  } else if (num < 30) {
1801  ast_copy_string(fn, "digits/20on", sizeof(fn));
1802  num -= 20;
1803  } else if (num < 100) {
1804  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1805  num %= 10;
1806  } else {
1807  if (num < 1000){
1808  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1809  playh++;
1810  num %= 100;
1811  } else {
1812  if (num < 1000000) { /* 1,000,000 */
1813  res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1814  if (res)
1815  return res;
1816  num %= 1000;
1817  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1818  } else {
1819  if (num < 1000000000) { /* 1,000,000,000 */
1820  res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1821  if (res)
1822  return res;
1823  num %= 1000000;
1824  ast_copy_string(fn, "digits/million", sizeof(fn));
1825  } else {
1826  ast_debug(1, "Number '%d' is too big for me\n", num);
1827  res = -1;
1828  }
1829  }
1830  }
1831  }
1832  if (!res) {
1833  if(!ast_streamfile(chan, fn, language)) {
1834  if ((audiofd > -1) && (ctrlfd > -1))
1835  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1836  else
1837  res = ast_waitstream(chan, ints);
1838  }
1839  ast_stopstream(chan);
1840  }
1841  }
1842  return res;
1843 }
1844 
1845 /*! \brief ast_say_number_full_is: Icelandic syntax */
1846 /* New files:
1847  In addition to American English, the following sounds are required: "hundreds", "millions", "1kvk", "1hk", "2kvk", "2hk", "3kvk", "3hk", "4kvk", "4hk"
1848  */
1849 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)
1850 {
1851  int res = 0;
1852  int playh = 0;
1853  int playa = 0;
1854  int cn = 1; /* 1 = masc; 2 = fem; 3 = neut */
1855  char fn[256] = "";
1856 
1857  if (!num)
1858  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1859 
1860  if (options && !strncasecmp(options, "f", 1)) cn = 2;
1861  if (options && !strncasecmp(options, "c", 1)) cn = 3;
1862  /* It seems that sometimes people are using c and sometimes n. */
1863  if (options && !strncasecmp(options, "n", 1)) cn = 3;
1864 
1865  while (!res && (num || playh || playa )) {
1866  if (num < 0) {
1867  ast_copy_string(fn, "digits/minus", sizeof(fn));
1868  if ( num > INT_MIN ) {
1869  num = -num;
1870  } else {
1871  num = 0;
1872  }
1873  } else if (playh) {
1874  if (playh > 1)
1875  ast_copy_string(fn, "digits/hundreds", sizeof(fn));
1876  else
1877  ast_copy_string(fn, "digits/hundred", sizeof(fn));
1878  playh = 0;
1879  } else if (playa) {
1880  ast_copy_string(fn, "digits/and", sizeof(fn));
1881  playa = 0;
1882  } else if (num < 5 && cn == 2) {
1883  snprintf(fn, sizeof(fn), "digits/%dkvk", num);
1884  num = 0;
1885  } else if (num < 5 && cn == 3) {
1886  snprintf(fn, sizeof(fn), "digits/%dhk", num);
1887  num = 0;
1888  } else if (num < 20) {
1889  snprintf(fn, sizeof(fn), "digits/%d", num);
1890  num = 0;
1891  } else if (num < 100) {
1892  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1893  num %= 10;
1894  if (num)
1895  playa++;
1896  } else if (num < 1000) {
1897  int hundreds = num / 100;
1898  /* The number prepending hundreds are in neutral */
1899  if (hundreds < 5)
1900  snprintf(fn, sizeof(fn), "digits/%dhk", hundreds);
1901  else
1902  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1903 
1904  playh = hundreds;
1905  num -= 100 * hundreds;
1906  if (num && num < 20)
1907  playa++;
1908  /* The 'and' moves forward on even tens. */
1909  if (num && (num % 10) == 0)
1910  playa++;
1911  } else if (num < 1000000) {
1912  res = ast_say_number_full_is(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1913  /* Play 'and' if it's an even hundred. */
1914  if ((num % 100) == 0 && (num % 1000 != 0)) {
1915  playa++;
1916  }
1917  if (res)
1918  return res;
1919  ast_copy_string(fn, "digits/thousand", sizeof(fn));
1920  num %= 1000;
1921  if (num && (num < 20 || (num % 10 == 0)))
1922  playa++;
1923  } else if (num < 1000000000) {
1924  int millions = num / 1000000;
1925  /* The number of millions is feminine */
1926  res = ast_say_number_full_is(chan, millions, ints, language, "f", audiofd, ctrlfd);
1927  if (res)
1928  return res;
1929  if (millions > 1)
1930  ast_copy_string(fn, "digits/millions", sizeof(fn));
1931  else
1932  ast_copy_string(fn, "digits/million", sizeof(fn));
1933  num %= 1000000;
1934  if (num && num < 100)
1935  playa++;
1936  } else if (num < INT_MAX) {
1937  int milliards = num / 1000000000;
1938  /* The number of milliards is masculine */
1939  res = ast_say_number_full_is(chan, milliards, ints, language, "m", audiofd, ctrlfd);
1940  if (res)
1941  return res;
1942  if (milliards > 1)
1943  ast_copy_string(fn, "digits/milliards", sizeof(fn));
1944  else
1945  ast_copy_string(fn, "digits/milliard", sizeof(fn));
1946  num %= 1000000000;
1947  if (num && num < 100)
1948  playa++;
1949  } else {
1950  ast_debug(1, "Number '%d' is too big for me\n", num);
1951  res = -1;
1952  }
1953 
1954  if (!res) {
1955  if (!ast_streamfile(chan, fn, language)) {
1956  if ((audiofd > -1) && (ctrlfd > -1))
1957  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1958  else
1959  res = ast_waitstream(chan, ints);
1960  }
1961  ast_stopstream(chan);
1962  }
1963  }
1964  return res;
1965 }
1966 
1967 
1968 /*! \brief ast_say_number_full_it: Italian */
1969 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1970 {
1971  int res = 0;
1972  int playh = 0;
1973  int tempnum = 0;
1974  char fn[256] = "";
1975 
1976  if (!num)
1977  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1978 
1979  /*
1980  Italian support
1981 
1982  Like english, numbers up to 20 are a single 'word', and others
1983  compound, but with exceptions.
1984  For example 21 is not twenty-one, but there is a single word in 'it'.
1985  Idem for 28 (ie when a the 2nd part of a compound number
1986  starts with a vowel)
1987 
1988  There are exceptions also for hundred, thousand and million.
1989  In english 100 = one hundred, 200 is two hundred.
1990  In italian 100 = cento , like to say hundred (without one),
1991  200 and more are like english.
1992 
1993  Same applies for thousand:
1994  1000 is one thousand in en, 2000 is two thousand.
1995  In it we have 1000 = mille , 2000 = 2 mila
1996 
1997  For million(s) we use the plural, if more than one
1998  Also, one million is abbreviated in it, like on-million,
1999  or 'un milione', not 'uno milione'.
2000  So the right file is provided.
2001  */
2002 
2003  while (!res && (num || playh)) {
2004  if (num < 0) {
2005  ast_copy_string(fn, "digits/minus", sizeof(fn));
2006  if ( num > INT_MIN ) {
2007  num = -num;
2008  } else {
2009  num = 0;
2010  }
2011  } else if (playh) {
2012  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2013  playh = 0;
2014  } else if (num < 20) {
2015  snprintf(fn, sizeof(fn), "digits/%d", num);
2016  num = 0;
2017  } else if (num == 21) {
2018  snprintf(fn, sizeof(fn), "digits/%d", num);
2019  num = 0;
2020  } else if (num == 28) {
2021  snprintf(fn, sizeof(fn), "digits/%d", num);
2022  num = 0;
2023  } else if (num == 31) {
2024  snprintf(fn, sizeof(fn), "digits/%d", num);
2025  num = 0;
2026  } else if (num == 38) {
2027  snprintf(fn, sizeof(fn), "digits/%d", num);
2028  num = 0;
2029  } else if (num == 41) {
2030  snprintf(fn, sizeof(fn), "digits/%d", num);
2031  num = 0;
2032  } else if (num == 48) {
2033  snprintf(fn, sizeof(fn), "digits/%d", num);
2034  num = 0;
2035  } else if (num == 51) {
2036  snprintf(fn, sizeof(fn), "digits/%d", num);
2037  num = 0;
2038  } else if (num == 58) {
2039  snprintf(fn, sizeof(fn), "digits/%d", num);
2040  num = 0;
2041  } else if (num == 61) {
2042  snprintf(fn, sizeof(fn), "digits/%d", num);
2043  num = 0;
2044  } else if (num == 68) {
2045  snprintf(fn, sizeof(fn), "digits/%d", num);
2046  num = 0;
2047  } else if (num == 71) {
2048  snprintf(fn, sizeof(fn), "digits/%d", num);
2049  num = 0;
2050  } else if (num == 78) {
2051  snprintf(fn, sizeof(fn), "digits/%d", num);
2052  num = 0;
2053  } else if (num == 81) {
2054  snprintf(fn, sizeof(fn), "digits/%d", num);
2055  num = 0;
2056  } else if (num == 88) {
2057  snprintf(fn, sizeof(fn), "digits/%d", num);
2058  num = 0;
2059  } else if (num == 91) {
2060  snprintf(fn, sizeof(fn), "digits/%d", num);
2061  num = 0;
2062  } else if (num == 98) {
2063  snprintf(fn, sizeof(fn), "digits/%d", num);
2064  num = 0;
2065  } else if (num < 100) {
2066  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2067  num %= 10;
2068  } else {
2069  if (num < 1000) {
2070  if ((num / 100) > 1) {
2071  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2072  playh++;
2073  } else {
2074  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2075  }
2076  num %= 100;
2077  } else {
2078  if (num < 1000000) { /* 1,000,000 */
2079  if ((num/1000) > 1)
2080  res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
2081  if (res)
2082  return res;
2083  tempnum = num;
2084  num %= 1000;
2085  if ((tempnum / 1000) < 2)
2086  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2087  else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
2088  ast_copy_string(fn, "digits/thousands", sizeof(fn));
2089  } else {
2090  if (num < 1000000000) { /* 1,000,000,000 */
2091  if ((num / 1000000) > 1)
2092  res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2093  if (res)
2094  return res;
2095  tempnum = num;
2096  num %= 1000000;
2097  if ((tempnum / 1000000) < 2)
2098  ast_copy_string(fn, "digits/million", sizeof(fn));
2099  else
2100  ast_copy_string(fn, "digits/millions", sizeof(fn));
2101  } else {
2102  ast_debug(1, "Number '%d' is too big for me\n", num);
2103  res = -1;
2104  }
2105  }
2106  }
2107  }
2108  if (!res) {
2109  if (!ast_streamfile(chan, fn, language)) {
2110  if ((audiofd > -1) && (ctrlfd > -1))
2111  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2112  else
2113  res = ast_waitstream(chan, ints);
2114  }
2115  ast_stopstream(chan);
2116  }
2117  }
2118  return res;
2119 }
2120 
2121 /*! \brief ast_say_number_full_nl: dutch syntax
2122  * New files: digits/nl-en
2123  */
2124 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2125 {
2126  int res = 0;
2127  int playh = 0;
2128  int units = 0;
2129  char fn[256] = "";
2130  if (!num)
2131  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2132  while (!res && (num || playh )) {
2133  if (num < 0) {
2134  ast_copy_string(fn, "digits/minus", sizeof(fn));
2135  if ( num > INT_MIN ) {
2136  num = -num;
2137  } else {
2138  num = 0;
2139  }
2140  } else if (playh) {
2141  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2142  playh = 0;
2143  } else if (num < 20) {
2144  snprintf(fn, sizeof(fn), "digits/%d", num);
2145  num = 0;
2146  } else if (num < 100) {
2147  units = num % 10;
2148  if (units > 0) {
2149  res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
2150  if (res)
2151  return res;
2152  num = num - units;
2153  ast_copy_string(fn, "digits/nl-en", sizeof(fn));
2154  } else {
2155  snprintf(fn, sizeof(fn), "digits/%d", num - units);
2156  num = 0;
2157  }
2158  } else if (num < 200) {
2159  /* hundred, not one-hundred */
2160  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2161  num %= 100;
2162  } else if (num < 1000) {
2163  snprintf(fn, sizeof(fn), "digits/%d", num / 100);
2164  playh++;
2165  num %= 100;
2166  } else {
2167  if (num < 1100) {
2168  /* thousand, not one-thousand */
2169  num %= 1000;
2170  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2171  } else if (num < 10000) { /* 1,100 to 9,9999 */
2172  res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
2173  if (res)
2174  return res;
2175  num %= 100;
2176  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2177  } else {
2178  if (num < 1000000) { /* 1,000,000 */
2179  res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
2180  if (res)
2181  return res;
2182  num %= 1000;
2183  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2184  } else {
2185  if (num < 1000000000) { /* 1,000,000,000 */
2186  res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2187  if (res)
2188  return res;
2189  num %= 1000000;
2190  ast_copy_string(fn, "digits/million", sizeof(fn));
2191  } else {
2192  ast_debug(1, "Number '%d' is too big for me\n", num);
2193  res = -1;
2194  }
2195  }
2196  }
2197  }
2198 
2199  if (!res) {
2200  if (!ast_streamfile(chan, fn, language)) {
2201  if ((audiofd > -1) && (ctrlfd > -1))
2202  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2203  else
2204  res = ast_waitstream(chan, ints);
2205  }
2206  ast_stopstream(chan);
2207  }
2208  }
2209  return res;
2210 }
2211 
2212 /*! \brief ast_say_number_full_no: Norwegian syntax
2213  * New files:
2214  * In addition to American English, the following sounds are required: "and", "1N"
2215  *
2216  * The grammar for Norwegian numbers is the same as for English except
2217  * for the following:
2218  * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
2219  * "and" before the last two digits, i.e. 2034 is "two thousand and
2220  * thirty-four" and 1000012 is "one million and twelve".
2221  */
2222 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)
2223 {
2224  int res = 0;
2225  int playh = 0;
2226  int playa = 0;
2227  int cn = 1; /* +1 = commune; -1 = neuter */
2228  char fn[256] = "";
2229 
2230  if (!num)
2231  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2232 
2233  if (options && !strncasecmp(options, "n", 1)) cn = -1;
2234 
2235  while (!res && (num || playh || playa )) {
2236  if (num < 0) {
2237  ast_copy_string(fn, "digits/minus", sizeof(fn));
2238  if ( num > INT_MIN ) {
2239  num = -num;
2240  } else {
2241  num = 0;
2242  }
2243  } else if (playh) {
2244  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2245  playh = 0;
2246  } else if (playa) {
2247  ast_copy_string(fn, "digits/and", sizeof(fn));
2248  playa = 0;
2249  } else if (num == 1 && cn == -1) {
2250  ast_copy_string(fn, "digits/1N", sizeof(fn));
2251  num = 0;
2252  } else if (num < 20) {
2253  snprintf(fn, sizeof(fn), "digits/%d", num);
2254  num = 0;
2255  } else if (num < 100) {
2256  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2257  num %= 10;
2258  } else if (num < 1000) {
2259  int hundreds = num / 100;
2260  if (hundreds == 1)
2261  ast_copy_string(fn, "digits/1N", sizeof(fn));
2262  else
2263  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2264 
2265  playh++;
2266  num -= 100 * hundreds;
2267  if (num)
2268  playa++;
2269  } else if (num < 1000000) {
2270  res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
2271  if (res)
2272  return res;
2273  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2274  num %= 1000;
2275  if (num && num < 100)
2276  playa++;
2277  } else if (num < 1000000000) {
2278  int millions = num / 1000000;
2279  res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
2280  if (res)
2281  return res;
2282  ast_copy_string(fn, "digits/million", sizeof(fn));
2283  num %= 1000000;
2284  if (num && num < 100)
2285  playa++;
2286  } else {
2287  ast_debug(1, "Number '%d' is too big for me\n", num);
2288  res = -1;
2289  }
2290 
2291  if (!res) {
2292  if (!ast_streamfile(chan, fn, language)) {
2293  if ((audiofd > -1) && (ctrlfd > -1))
2294  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2295  else
2296  res = ast_waitstream(chan, ints);
2297  }
2298  ast_stopstream(chan);
2299  }
2300  }
2301  return res;
2302 }
2303 
2304 typedef struct {
2305  char *separator_dziesiatek;
2306  char *cyfry[10];
2307  char *cyfry2[10];
2308  char *setki[10];
2309  char *dziesiatki[10];
2310  char *nastki[10];
2311  char *rzedy[3][3];
2312 } odmiana;
2313 
2314 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
2315 {
2316  if (rzad==0)
2317  return "";
2318 
2319  if (i==1)
2320  return odm->rzedy[rzad - 1][0];
2321  if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
2322  return odm->rzedy[rzad - 1][1];
2323  else
2324  return odm->rzedy[rzad - 1][2];
2325 }
2326 
2327 static char* pl_append(char* buffer, char* str)
2328 {
2329  strcpy(buffer, str);
2330  buffer += strlen(str);
2331  return buffer;
2332 }
2333 
2334 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
2335 {
2336  char file_name[255] = "digits/";
2337  strcat(file_name, fn);
2338  ast_debug(1, "Trying to play: %s\n", file_name);
2339  if (!ast_streamfile(chan, file_name, language)) {
2340  if ((audiofd > -1) && (ctrlfd > -1))
2341  ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2342  else
2343  ast_waitstream(chan, ints);
2344  }
2345  ast_stopstream(chan);
2346 }
2347 
2348 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
2349 {
2350  /* Initialise variables to allow compilation on Debian-stable, etc */
2351  int m1000E6 = 0;
2352  int i1000E6 = 0;
2353  int m1000E3 = 0;
2354  int i1000E3 = 0;
2355  int m1000 = 0;
2356  int i1000 = 0;
2357  int m100 = 0;
2358  int i100 = 0;
2359 
2360  if (i == 0 && rzad > 0) {
2361  return;
2362  }
2363  if (i == 0) {
2364  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
2365  return;
2366  }
2367 
2368  m1000E6 = i % 1000000000;
2369  i1000E6 = i / 1000000000;
2370 
2371  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
2372 
2373  m1000E3 = m1000E6 % 1000000;
2374  i1000E3 = m1000E6 / 1000000;
2375 
2376  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
2377 
2378  m1000 = m1000E3 % 1000;
2379  i1000 = m1000E3 / 1000;
2380 
2381  powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
2382 
2383  m100 = m1000 % 100;
2384  i100 = m1000 / 100;
2385 
2386  if (i100>0)
2387  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
2388 
2389  if (m100 > 0 && m100 <= 9) {
2390  if (m1000 > 0)
2391  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
2392  else
2393  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
2394  } else if (m100 % 10 == 0 && m100 != 0) {
2395  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2396  } else if (m100 > 10 && m100 <= 19) {
2397  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
2398  } else if (m100 > 20) {
2399  if (odm->separator_dziesiatek[0] == ' ') {
2400  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
2401  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
2402  } else {
2403  char buf[10];
2404  char *b = buf;
2405  b = pl_append(b, odm->dziesiatki[m100 / 10]);
2406  b = pl_append(b, odm->separator_dziesiatek);
2407  pl_append(b, odm->cyfry2[m100 % 10]);
2408  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
2409  }
2410  }
2411 
2412  if (rzad > 0) {
2413  pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
2414  }
2415 }
2416 
2417 /* ast_say_number_full_pl: Polish syntax
2418 
2419 Sounds needed:
2420 0 zero
2421 1 jeden
2422 10 dziesiec
2423 100 sto
2424 1000 tysiac
2425 1000000 milion
2426 1000000000 miliard
2427 1000000000.2 miliardy
2428 1000000000.5 miliardow
2429 1000000.2 miliony
2430 1000000.5 milionow
2431 1000.2 tysiace
2432 1000.5 tysiecy
2433 100m stu
2434 10m dziesieciu
2435 11 jedenascie
2436 11m jedenastu
2437 12 dwanascie
2438 12m dwunastu
2439 13 trzynascie
2440 13m trzynastu
2441 14 czternascie
2442 14m czternastu
2443 15 pietnascie
2444 15m pietnastu
2445 16 szesnascie
2446 16m szesnastu
2447 17 siedemnascie
2448 17m siedemnastu
2449 18 osiemnascie
2450 18m osiemnastu
2451 19 dziewietnascie
2452 19m dziewietnastu
2453 1z jedna
2454 2 dwa
2455 20 dwadziescia
2456 200 dwiescie
2457 200m dwustu
2458 20m dwudziestu
2459 2-1m dwaj
2460 2-2m dwoch
2461 2z dwie
2462 3 trzy
2463 30 trzydziesci
2464 300 trzysta
2465 300m trzystu
2466 30m trzydziestu
2467 3-1m trzej
2468 3-2m trzech
2469 4 cztery
2470 40 czterdziesci
2471 400 czterysta
2472 400m czterystu
2473 40m czterdziestu
2474 4-1m czterej
2475 4-2m czterech
2476 5 piec
2477 50 piecdziesiat
2478 500 piecset
2479 500m pieciuset
2480 50m piedziesieciu
2481 5m pieciu
2482 6 szesc
2483 60 szescdziesiat
2484 600 szescset
2485 600m szesciuset
2486 60m szescdziesieciu
2487 6m szesciu
2488 7 siedem
2489 70 siedemdziesiat
2490 700 siedemset
2491 700m siedmiuset
2492 70m siedemdziesieciu
2493 7m siedmiu
2494 8 osiem
2495 80 osiemdziesiat
2496 800 osiemset
2497 800m osmiuset
2498 80m osiemdziesieciu
2499 8m osmiu
2500 9 dziewiec
2501 90 dziewiecdziesiat
2502 900 dziewiecset
2503 900m dziewieciuset
2504 90m dziewiedziesieciu
2505 9m dziewieciu
2506 and combinations of eg.: 20_1, 30m_3m, etc...
2507 
2508 */
2509 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)
2510 {
2511  char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2512 
2513  char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2514 
2515  char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2516 
2517  char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2518 
2519  char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2520 
2521  char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2522 
2523  char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2524 
2525  char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2526 
2527  char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2528 
2529  char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2530 
2531  char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2532 
2533  char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2534 
2535  char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2536 
2537  /* Initialise variables to allow compilation on Debian-stable, etc */
2538  odmiana *o;
2539 
2540  static odmiana *odmiana_nieosobowa = NULL;
2541  static odmiana *odmiana_meska = NULL;
2542  static odmiana *odmiana_zenska = NULL;
2543 
2544  if (odmiana_nieosobowa == NULL) {
2545  odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2546 
2547  odmiana_nieosobowa->separator_dziesiatek = " ";
2548 
2549  memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2550  memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2551  memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2552  memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2553  memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2554  memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2555  }
2556 
2557  if (odmiana_zenska == NULL) {
2558  odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2559 
2560  odmiana_zenska->separator_dziesiatek = " ";
2561 
2562  memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2563  memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2564  memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2565  memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2566  memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2567  memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2568  }
2569 
2570  if (odmiana_meska == NULL) {
2571  odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2572 
2573  odmiana_meska->separator_dziesiatek = " ";
2574 
2575  memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2576  memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2577  memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2578  memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2579  memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2580  memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2581  }
2582 
2583  if (options) {
2584  if (strncasecmp(options, "f", 1) == 0)
2585  o = odmiana_zenska;
2586  else if (strncasecmp(options, "m", 1) == 0)
2587  o = odmiana_meska;
2588  else
2589  o = odmiana_nieosobowa;
2590  } else
2591  o = odmiana_nieosobowa;
2592 
2593  powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2594  return 0;
2595 }
2596 
2597 /* ast_say_number_full_pt: Portuguese syntax
2598 
2599  * Extra sounds needed:
2600  * For feminin all sound files ends with F
2601  * 100E for 100+ something
2602  * 1000000S for plural
2603  * pt-e for 'and'
2604  */
2605 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)
2606 {
2607  int res = 0;
2608  int playh = 0;
2609  int mf = 1; /* +1 = male; -1 = female */
2610  char fn[256] = "";
2611 
2612  if (!num)
2613  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2614 
2615  if (options && !strncasecmp(options, "f", 1))
2616  mf = -1;
2617 
2618  while (!res && num ) {
2619  if (num < 0) {
2620  ast_copy_string(fn, "digits/minus", sizeof(fn));
2621  if ( num > INT_MIN ) {
2622  num = -num;
2623  } else {
2624  num = 0;
2625  }
2626  } else if (num < 20) {
2627  if ((num == 1 || num == 2) && (mf < 0))
2628  snprintf(fn, sizeof(fn), "digits/%dF", num);
2629  else
2630  snprintf(fn, sizeof(fn), "digits/%d", num);
2631  num = 0;
2632  } else if (num < 100) {
2633  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2634  if (num % 10)
2635  playh = 1;
2636  num = num % 10;
2637  } else if (num < 1000) {
2638  if (num == 100)
2639  ast_copy_string(fn, "digits/100", sizeof(fn));
2640  else if (num < 200)
2641  ast_copy_string(fn, "digits/100E", sizeof(fn));
2642  else {
2643  if (mf < 0 && num > 199)
2644  snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2645  else
2646  snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2647  if (num % 100)
2648  playh = 1;
2649  }
2650  num = num % 100;
2651  } else if (num < 1000000) {
2652  if (num > 1999) {
2653  res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2654  if (res)
2655  return res;
2656  }
2657  ast_copy_string(fn, "digits/1000", sizeof(fn));
2658  if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2659  playh = 1;
2660  num = num % 1000;
2661  } else if (num < 1000000000) {
2662  res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2663  if (res)
2664  return res;
2665  if (num < 2000000)
2666  ast_copy_string(fn, "digits/1000000", sizeof(fn));
2667  else
2668  ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2669 
2670  if ((num % 1000000) &&
2671  /* no thousands */
2672  ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2673  /* no hundreds and below */
2674  (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2675  playh = 1;
2676  num = num % 1000000;
2677  } else {
2678  /* number is too big */
2679  ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2680  res = -1;
2681  }
2682  if (!res) {
2683  if (!ast_streamfile(chan, fn, language)) {
2684  if ((audiofd > -1) && (ctrlfd > -1))
2685  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2686  else
2687  res = ast_waitstream(chan, ints);
2688  }
2689  ast_stopstream(chan);
2690  }
2691  if (!res && playh) {
2692  res = wait_file(chan, ints, "digits/pt-e", language);
2693  ast_stopstream(chan);
2694  playh = 0;
2695  }
2696  }
2697  return res;
2698 }
2699 
2700 /*! \brief ast_say_number_full_se: Swedish syntax
2701 
2702  Sound files needed
2703  - 1N
2704 */
2705 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)
2706 {
2707  int playh = 0;
2708  int start = 1;
2709  char fn[256] = "";
2710  int cn = 1; /* +1 = commune; -1 = neuter */
2711  int res = 0;
2712 
2713  if (!num) {
2714  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2715  }
2716  if (options && !strncasecmp(options, "n", 1)) cn = -1;
2717 
2718  while (num || playh) {
2719  if (num < 0) {
2720  ast_copy_string(fn, "digits/minus", sizeof(fn));
2721  if ( num > INT_MIN ) {
2722  num = -num;
2723  } else {
2724  num = 0;
2725  }
2726  } else if (playh) {
2727  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2728  playh = 0;
2729  } else if (start && num < 200 && num > 99 && cn == -1) {
2730  /* Don't say "en hundra" just say "hundra". */
2731  snprintf(fn, sizeof(fn), "digits/hundred");
2732  num -= 100;
2733  } else if (num == 1 && cn == -1) { /* En eller ett? */
2734  ast_copy_string(fn, "digits/1N", sizeof(fn));
2735  num = 0;
2736  } else if (num < 20) {
2737  snprintf(fn, sizeof(fn), "digits/%d", num);
2738  num = 0;
2739  } else if (num < 100) { /* Below hundreds - teens and tens */
2740  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2741  num %= 10;
2742  } else if (num < 1000) {
2743  /* Hundreds */
2744  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2745  playh++;
2746  num %= 100;
2747  } else if (num < 1000000) { /* 1,000,000 */
2748  /* Always say "ett hundra tusen", not "en hundra tusen" */
2749  res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
2750  if (res) {
2751  return res;
2752  }
2753  num %= 1000;
2754  ast_copy_string(fn, "digits/thousand", sizeof(fn));
2755  } else if (num < 1000000000) { /* 1,000,000,000 */
2756  /* Always say "en miljon", not "ett miljon" */
2757  res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
2758  if (res) {
2759  return res;
2760  }
2761  num %= 1000000;
2762  ast_copy_string(fn, "digits/million", sizeof(fn));
2763  } else { /* Miljarder - Billions */
2764  ast_debug(1, "Number '%d' is too big for me\n", num);
2765  return -1;
2766  }
2767 
2768  if (!ast_streamfile(chan, fn, language)) {
2769  if ((audiofd > -1) && (ctrlfd > -1)) {
2770  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2771  } else {
2772  res = ast_waitstream(chan, ints);
2773  }
2774  ast_stopstream(chan);
2775  if (res) {
2776  return res;
2777  }
2778  }
2779  start = 0;
2780  }
2781  return 0;
2782 }
2783 
2784 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2785 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2786 {
2787  int res = 0;
2788  int playh = 0;
2789  int playt = 0;
2790  int playz = 0;
2791  int last_length = 0;
2792  char buf[20] = "";
2793  char fn[256] = "";
2794  if (!num)
2795  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2796 
2797  while (!res && (num || playh || playt || playz)) {
2798  if (num < 0) {
2799  ast_copy_string(fn, "digits/minus", sizeof(fn));
2800  if ( num > INT_MIN ) {
2801  num = -num;
2802  } else {
2803  num = 0;
2804  }
2805  } else if (playz) {
2806  snprintf(fn, sizeof(fn), "digits/0");
2807  last_length = 0;
2808  playz = 0;
2809  } else if (playh) {
2810  ast_copy_string(fn, "digits/hundred", sizeof(fn));
2811  playh = 0;
2812  } else if (playt) {
2813  snprintf(fn, sizeof(fn), "digits/thousand");
2814  playt = 0;
2815  } else if (num < 10) {
2816  snprintf(buf, 12, "%d", num);
2817  if (last_length - strlen(buf) > 1 && last_length != 0) {
2818  last_length = strlen(buf);
2819  playz++;
2820  continue;
2821  }
2822  snprintf(fn, sizeof(fn), "digits/%d", num);
2823  num = 0;
2824  } else if (num < 100) {
2825  snprintf(buf, 10, "%d", num);
2826  if (last_length - strlen(buf) > 1 && last_length != 0) {
2827  last_length = strlen(buf);
2828  playz++;
2829  continue;
2830  }
2831  last_length = strlen(buf);
2832  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2833  num %= 10;
2834  } else {
2835  if (num < 1000){
2836  snprintf(buf, 10, "%d", num);
2837  if (last_length - strlen(buf) > 1 && last_length != 0) {
2838  last_length = strlen(buf);
2839  playz++;
2840  continue;
2841  }
2842  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2843  playh++;
2844  snprintf(buf, 10, "%d", num);
2845  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2846  last_length = strlen(buf);
2847  num -= ((num / 100) * 100);
2848  } else if (num < 10000){
2849  snprintf(buf, 10, "%d", num);
2850  snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2851  playt++;
2852  snprintf(buf, 10, "%d", num);
2853  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2854  last_length = strlen(buf);
2855  num -= ((num / 1000) * 1000);
2856  } else if (num < 100000000) { /* 100,000,000 */
2857  res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2858  if (res)
2859  return res;
2860  snprintf(buf, 10, "%d", num);
2861  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2862  num -= ((num / 10000) * 10000);
2863  last_length = strlen(buf);
2864  snprintf(fn, sizeof(fn), "digits/wan");
2865  } else {
2866  if (num < 1000000000) { /* 1,000,000,000 */
2867  res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2868  if (res)
2869  return res;
2870  snprintf(buf, 10, "%d", num);
2871  ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2872  last_length = strlen(buf);
2873  num -= ((num / 100000000) * 100000000);
2874  snprintf(fn, sizeof(fn), "digits/yi");
2875  } else {
2876  ast_debug(1, "Number '%d' is too big for me\n", num);
2877  res = -1;
2878  }
2879  }
2880  }
2881  if (!res) {
2882  if (!ast_streamfile(chan, fn, language)) {
2883  if ((audiofd > -1) && (ctrlfd > -1))
2884  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2885  else
2886  res = ast_waitstream(chan, ints);
2887  }
2888  ast_stopstream(chan);
2889  }
2890  }
2891  return res;
2892 }
2893 
2894 /*!\internal
2895  * \brief Counting in Urdu, the national language of Pakistan
2896  * \since 1.8
2897  */
2898 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)
2899 {
2900  int res = 0;
2901  int playh = 0;
2902  char fn[256] = "";
2903 
2904  if (!num) {
2905  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2906  }
2907 
2908  while (!res && (num || playh)) {
2909  if (playh) {
2910  snprintf(fn, sizeof(fn), "digits/hundred");
2911  playh = 0;
2912  } else if (num < 100) {
2913  snprintf(fn, sizeof(fn), "digits/%d", num);
2914  num = 0;
2915  } else if (num < 1000) {
2916  snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2917  playh++;
2918  num -= ((num / 100) * 100);
2919  } else if (num < 100000) { /* 1,00,000 */
2920  if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2921  return res;
2922  }
2923  num = num % 1000;
2924  snprintf(fn, sizeof(fn), "digits/thousand");
2925  } else if (num < 10000000) { /* 1,00,00,000 */
2926  if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2927  return res;
2928  }
2929  num = num % 100000;
2930  snprintf(fn, sizeof(fn), "digits/lac");
2931  } else if (num < 1000000000) { /* 1,00,00,00,000 */
2932  if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2933  return res;
2934  }
2935  num = num % 10000000;
2936  snprintf(fn, sizeof(fn), "digits/crore");
2937  } else {
2938  ast_debug(1, "Number '%d' is too big for me\n", num);
2939  res = -1;
2940  }
2941 
2942  if (!res) {
2943  if (!ast_streamfile(chan, fn, language)) {
2944  if ((audiofd > -1) && (ctrlfd > -1)) {
2945  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2946  } else {
2947  res = ast_waitstream(chan, ints);
2948  }
2949  }
2950  ast_stopstream(chan);
2951  }
2952  }
2953  return res;
2954 }
2955 
2956 /*! \brief determine last digits for thousands/millions (ru) */
2957 static int get_lastdigits_ru(int num) {
2958  if (num < 20) {
2959  return num;
2960  } else if (num < 100) {
2961  return get_lastdigits_ru(num % 10);
2962  } else if (num < 1000) {
2963  return get_lastdigits_ru(num % 100);
2964  }
2965  return 0; /* number too big */
2966 }
2967 
2968 
2969 /*! \brief ast_say_number_full_ru: Russian syntax
2970 
2971  additional files:
2972  n00.gsm (one hundred, two hundred, ...)
2973  thousand.gsm
2974  million.gsm
2975  thousands-i.gsm (tisyachi)
2976  million-a.gsm (milliona)
2977  thousands.gsm
2978  millions.gsm
2979  1f.gsm (odna)
2980  2f.gsm (dve)
2981 
2982  where 'n' from 1 to 9
2983 */
2984 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)
2985 {
2986  int res = 0;
2987  int lastdigits = 0;
2988  char fn[256] = "";
2989  if (!num)
2990  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2991 
2992  while (!res && (num)) {
2993  if (num < 0) {
2994  ast_copy_string(fn, "digits/minus", sizeof(fn));
2995  if ( num > INT_MIN ) {
2996  num = -num;
2997  } else {
2998  num = 0;
2999  }
3000  } else if (num < 20) {
3001  if (options && strlen(options) == 1 && num < 3) {
3002  snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
3003  } else {
3004  snprintf(fn, sizeof(fn), "digits/%d", num);
3005  }
3006  num = 0;
3007  } else if (num < 100) {
3008  snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
3009  num %= 10;
3010  } else if (num < 1000){
3011  snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
3012  num %= 100;
3013  } else if (num < 1000000) { /* 1,000,000 */
3014  lastdigits = get_lastdigits_ru(num / 1000);
3015  /* say thousands */
3016  if (lastdigits < 3) {
3017  res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
3018  } else {
3019  res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
3020  }
3021  if (res)
3022  return res;
3023  if (lastdigits == 1) {
3024  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3025  } else if (lastdigits > 1 && lastdigits < 5) {
3026  ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
3027  } else {
3028  ast_copy_string(fn, "digits/thousands", sizeof(fn));
3029  }
3030  num %= 1000;
3031  } else if (num < 1000000000) { /* 1,000,000,000 */
3032  lastdigits = get_lastdigits_ru(num / 1000000);
3033  /* say millions */
3034  res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
3035  if (res)
3036  return res;
3037  if (lastdigits == 1) {
3038  ast_copy_string(fn, "digits/million", sizeof(fn));
3039  } else if (lastdigits > 1 && lastdigits < 5) {
3040  ast_copy_string(fn, "digits/million-a", sizeof(fn));
3041  } else {
3042  ast_copy_string(fn, "digits/millions", sizeof(fn));
3043  }
3044  num %= 1000000;
3045  } else {
3046  ast_debug(1, "Number '%d' is too big for me\n", num);
3047  res = -1;
3048  }
3049  if (!res) {
3050  if (!ast_streamfile(chan, fn, language)) {
3051  if ((audiofd > -1) && (ctrlfd > -1))
3052  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3053  else
3054  res = ast_waitstream(chan, ints);
3055  }
3056  ast_stopstream(chan);
3057  }
3058  }
3059  return res;
3060 }
3061 
3062 /*! \brief Thai syntax */
3063 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3064 {
3065  int res = 0;
3066  int playh = 0;
3067  char fn[256] = "";
3068  if (!num)
3069  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3070 
3071  while(!res && (num || playh)) {
3072  if (num < 0) {
3073  ast_copy_string(fn, "digits/lop", sizeof(fn));
3074  if ( num > INT_MIN ) {
3075  num = -num;
3076  } else {
3077  num = 0;
3078  }
3079  } else if (playh) {
3080  ast_copy_string(fn, "digits/roi", sizeof(fn));
3081  playh = 0;
3082  } else if (num < 100) {
3083  if ((num <= 20) || ((num % 10) == 1)) {
3084  snprintf(fn, sizeof(fn), "digits/%d", num);
3085  num = 0;
3086  } else {
3087  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3088  num %= 10;
3089  }
3090  } else if (num < 1000) {
3091  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3092  playh++;
3093  num %= 100;
3094  } else if (num < 10000) { /* 10,000 */
3095  res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
3096  if (res)
3097  return res;
3098  num %= 1000;
3099  ast_copy_string(fn, "digits/pan", sizeof(fn));
3100  } else if (num < 100000) { /* 100,000 */
3101  res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
3102  if (res)
3103  return res;
3104  num %= 10000;
3105  ast_copy_string(fn, "digits/muan", sizeof(fn));
3106  } else if (num < 1000000) { /* 1,000,000 */
3107  res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
3108  if (res)
3109  return res;
3110  num %= 100000;
3111  ast_copy_string(fn, "digits/san", sizeof(fn));
3112  } else {
3113  res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3114  if (res)
3115  return res;
3116  num %= 1000000;
3117  ast_copy_string(fn, "digits/larn", sizeof(fn));
3118  }
3119  if (!res) {
3120  if(!ast_streamfile(chan, fn, language)) {
3121  if ((audiofd > -1) && (ctrlfd > -1))
3122  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3123  else
3124  res = ast_waitstream(chan, ints);
3125  }
3126  ast_stopstream(chan);
3127  }
3128  }
3129  return res;
3130 }
3131 
3132 /*! \brief ast_say_number_full_vi: Vietnamese syntax */
3133 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3134 {
3135  int res = 0;
3136  int playh = 0;
3137  int playoh = 0;
3138  int playohz = 0;
3139  int playz = 0;
3140  int playl = 0;
3141  char fn[256] = "";
3142  if (!num)
3143  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3144  while (!res && (num || playh)) {
3145  if (num < 0) {
3146  ast_copy_string(fn, "digits/minus", sizeof(fn));
3147  if ( num > INT_MIN ) {
3148  num = -num;
3149  } else {
3150  num = 0;
3151  }
3152  } else if (playl) {
3153  snprintf(fn, sizeof(fn), "digits/%da", num);
3154  playl = 0;
3155  num = 0;
3156  } else if (playh) {
3157  ast_copy_string(fn, "digits/hundred", sizeof(fn));
3158  playh = 0;
3159  } else if (playz) {
3160  ast_copy_string(fn, "digits/odd", sizeof(fn));
3161  playz = 0;
3162  } else if (playoh) {
3163  ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
3164  playoh = 0;
3165  } else if (playohz) {
3166  ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
3167  playohz = 0;
3168  } else if (num < 20) {
3169  snprintf(fn, sizeof(fn), "digits/%d", num);
3170  num = 0;
3171  } else if (num < 100) {
3172  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
3173  num %= 10;
3174  if ((num == 5) || (num == 4) || (num == 1)) playl++;
3175  } else {
3176  if (num < 1000) {
3177  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
3178  num %= 100;
3179  if (num && (num < 10)) {
3180  playz++;
3181  playh++;
3182  } else {
3183  playh++;
3184  }
3185  } else {
3186  if (num < 1000000) { /* 1,000,000 */
3187  res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
3188  if (res)
3189  return res;
3190  num %= 1000;
3191  snprintf(fn, sizeof(fn), "digits/thousand");
3192  if (num && (num < 10)) {
3193  playohz++;
3194  } else if (num && (num < 100)){
3195  playoh++;
3196  } else {
3197  playh = 0;
3198  playohz = 0;
3199  playoh = 0;
3200  }
3201  } else {
3202  if (num < 1000000000) { /* 1,000,000,000 */
3203  res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
3204  if (res)
3205  return res;
3206  num %= 1000000;
3207  ast_copy_string(fn, "digits/million", sizeof(fn));
3208  } else {
3209  res = -1;
3210  }
3211  }
3212  }
3213  }
3214  if (!res) {
3215  if (!ast_streamfile(chan, fn, language)) {
3216  if ((audiofd > -1) && (ctrlfd > -1))
3217  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3218  else
3219  res = ast_waitstream(chan, ints);
3220  }
3221  ast_stopstream(chan);
3222  }
3223  }
3224  return res;
3225 }
3226 
3227 /*! \brief ast_say_enumeration_full: call language-specific functions
3228  * \note Called from AGI */
3229 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
3230 {
3231  if (!strncasecmp(language, "en", 2)) { /* English syntax */
3232  return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3233  } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
3234  return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
3235  } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
3236  return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
3237  } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
3238  return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
3239  } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */
3240  return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd);
3241  } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
3242  return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3243  }
3244 
3245  /* Default to english */
3246  return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
3247 }
3248 
3249 /*! \brief ast_say_enumeration_full_en: English syntax
3250  \note This is the default syntax, if no other syntax defined in this file is used */
3251 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3252 {
3253  int res = 0, t = 0;
3254  char fn[256] = "";
3255 
3256  while (!res && num) {
3257  if (num < 0) {
3258  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3259  if ( num > INT_MIN ) {
3260  num = -num;
3261  } else {
3262  num = 0;
3263  }
3264  } else if (num < 20) {
3265  snprintf(fn, sizeof(fn), "digits/h-%d", num);
3266  num = 0;
3267  } else if (num < 100) {
3268  int tens = num / 10;
3269  num = num % 10;
3270  if (num == 0) {
3271  snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
3272  } else {
3273  snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
3274  }
3275  } else if (num < 1000) {
3276  int hundreds = num / 100;
3277  num = num % 100;
3278  if (hundreds > 1 || t == 1) {
3279  res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
3280  }
3281  if (res)
3282  return res;
3283  if (num) {
3284  ast_copy_string(fn, "digits/hundred", sizeof(fn));
3285  } else {
3286  ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
3287  }
3288  } else if (num < 1000000) {
3289  int thousands = num / 1000;
3290  num = num % 1000;
3291  if (thousands > 1 || t == 1) {
3292  res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
3293  }
3294  if (res)
3295  return res;
3296  if (num) {
3297  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3298  } else {
3299  ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
3300  }
3301  t = 1;
3302  } else if (num < 1000000000) {
3303  int millions = num / 1000000;
3304  num = num % 1000000;
3305  t = 1;
3306  res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
3307  if (res)
3308  return res;
3309  if (num) {
3310  ast_copy_string(fn, "digits/million", sizeof(fn));
3311  } else {
3312  ast_copy_string(fn, "digits/h-million", sizeof(fn));
3313  }
3314  } else if (num < INT_MAX) {
3315  int billions = num / 1000000000;
3316  num = num % 1000000000;
3317  t = 1;
3318  res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
3319  if (res)
3320  return res;
3321  if (num) {
3322  ast_copy_string(fn, "digits/billion", sizeof(fn));
3323  } else {
3324  ast_copy_string(fn, "digits/h-billion", sizeof(fn));
3325  }
3326  } else if (num == INT_MAX) {
3327  ast_copy_string(fn, "digits/h-last", sizeof(fn));
3328  num = 0;
3329  } else {
3330  ast_debug(1, "Number '%d' is too big for me\n", num);
3331  res = -1;
3332  }
3333 
3334  if (!res) {
3335  if (!ast_streamfile(chan, fn, language)) {
3336  if ((audiofd > -1) && (ctrlfd > -1)) {
3337  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3338  } else {
3339  res = ast_waitstream(chan, ints);
3340  }
3341  }
3342  ast_stopstream(chan);
3343  }
3344  }
3345  return res;
3346 }
3347 
3348 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
3349 {
3350  int res = 0;
3351  char fn[256] = "";
3352  ast_copy_string(fn, "digits/h", sizeof(fn));
3353  if (!res) {
3354  if (!ast_streamfile(chan, fn, language)) {
3355  if ((audiofd > -1) && (ctrlfd > -1)) {
3356  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3357  } else {
3358  res = ast_waitstream(chan, ints);
3359  }
3360  }
3361  ast_stopstream(chan);
3362  }
3363 
3364  return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
3365 }
3366 
3367 /*! \brief ast_say_enumeration_full_da: Danish syntax */
3368 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)
3369 {
3370  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3371  int res = 0, t = 0;
3372  char fn[256] = "", fna[256] = "";
3373  char *gender;
3374 
3375  if (options && !strncasecmp(options, "f", 1)) {
3376  gender = "F";
3377  } else if (options && !strncasecmp(options, "n", 1)) {
3378  gender = "N";
3379  } else {
3380  gender = "";
3381  }
3382 
3383  if (!num)
3384  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3385 
3386  while (!res && num) {
3387  if (num < 0) {
3388  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3389  if ( num > INT_MIN ) {
3390  num = -num;
3391  } else {
3392  num = 0;
3393  }
3394  } else if (num < 100 && t) {
3395  ast_copy_string(fn, "digits/and", sizeof(fn));
3396  t = 0;
3397  } else if (num < 20) {
3398  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3399  num = 0;
3400  } else if (num < 100) {
3401  int ones = num % 10;
3402  if (ones) {
3403  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3404  num -= ones;
3405  } else {
3406  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3407  num = 0;
3408  }
3409  } else if (num == 100 && t == 0) {
3410  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3411  num = 0;
3412  } else if (num < 1000) {
3413  int hundreds = num / 100;
3414  num = num % 100;
3415  if (hundreds == 1) {
3416  ast_copy_string(fn, "digits/1N", sizeof(fn));
3417  } else {
3418  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3419  }
3420  if (num) {
3421  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3422  } else {
3423  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3424  }
3425  t = 1;
3426  } else if (num < 1000000) {
3427  int thousands = num / 1000;
3428  num = num % 1000;
3429  if (thousands == 1) {
3430  if (num) {
3431  ast_copy_string(fn, "digits/1N", sizeof(fn));
3432  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3433  } else {
3434  if (t) {
3435  ast_copy_string(fn, "digits/1N", sizeof(fn));
3436  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3437  } else {
3438  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3439  }
3440  }
3441  } else {
3442  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3443  if (res) {
3444  return res;
3445  }
3446  if (num) {
3447  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3448  } else {
3449  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3450  }
3451  }
3452  t = 1;
3453  } else if (num < 1000000000) {
3454  int millions = num / 1000000;
3455  num = num % 1000000;
3456  if (millions == 1) {
3457  if (num) {
3458  ast_copy_string(fn, "digits/1F", sizeof(fn));
3459  ast_copy_string(fna, "digits/million", sizeof(fna));
3460  } else {
3461  ast_copy_string(fn, "digits/1N", sizeof(fn));
3462  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3463  }
3464  } else {
3465  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3466  if (res) {
3467  return res;
3468  }
3469  if (num) {
3470  ast_copy_string(fn, "digits/millions", sizeof(fn));
3471  } else {
3472  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3473  }
3474  }
3475  t = 1;
3476  } else if (num < INT_MAX) {
3477  int billions = num / 1000000000;
3478  num = num % 1000000000;
3479  if (billions == 1) {
3480  if (num) {
3481  ast_copy_string(fn, "digits/1F", sizeof(fn));
3482  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3483  } else {
3484  ast_copy_string(fn, "digits/1N", sizeof(fn));
3485  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3486  }
3487  } else {
3488  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3489  if (res)
3490  return res;
3491  if (num) {
3492  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3493  } else {
3494  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3495  }
3496  }
3497  t = 1;
3498  } else if (num == INT_MAX) {
3499  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3500  num = 0;
3501  } else {
3502  ast_debug(1, "Number '%d' is too big for me\n", num);
3503  res = -1;
3504  }
3505 
3506  if (!res) {
3507  if (!ast_streamfile(chan, fn, language)) {
3508  if ((audiofd > -1) && (ctrlfd > -1))
3509  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3510  else
3511  res = ast_waitstream(chan, ints);
3512  }
3513  ast_stopstream(chan);
3514  if (!res) {
3515  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3516  if ((audiofd > -1) && (ctrlfd > -1)) {
3517  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3518  } else {
3519  res = ast_waitstream(chan, ints);
3520  }
3521  }
3522  ast_stopstream(chan);
3523  strcpy(fna, "");
3524  }
3525  }
3526  }
3527  return res;
3528 }
3529 
3530 /*! \brief ast_say_enumeration_full_de: German syntax */
3531 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)
3532 {
3533  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3534  int res = 0, t = 0;
3535  char fn[256] = "", fna[256] = "";
3536  char *gender;
3537 
3538  if (options && !strncasecmp(options, "f", 1)) {
3539  gender = "F";
3540  } else if (options && !strncasecmp(options, "n", 1)) {
3541  gender = "N";
3542  } else {
3543  gender = "";
3544  }
3545 
3546  if (!num)
3547  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3548 
3549  while (!res && num) {
3550  if (num < 0) {
3551  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3552  if ( num > INT_MIN ) {
3553  num = -num;
3554  } else {
3555  num = 0;
3556  }
3557  } else if (num < 100 && t) {
3558  ast_copy_string(fn, "digits/and", sizeof(fn));
3559  t = 0;
3560  } else if (num < 20) {
3561  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3562  num = 0;
3563  } else if (num < 100) {
3564  int ones = num % 10;
3565  if (ones) {
3566  snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3567  num -= ones;
3568  } else {
3569  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3570  num = 0;
3571  }
3572  } else if (num == 100 && t == 0) {
3573  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3574  num = 0;
3575  } else if (num < 1000) {
3576  int hundreds = num / 100;
3577  num = num % 100;
3578  if (hundreds == 1) {
3579  ast_copy_string(fn, "digits/1N", sizeof(fn));
3580  } else {
3581  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3582  }
3583  if (num) {
3584  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3585  } else {
3586  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3587  }
3588  t = 1;
3589  } else if (num < 1000000) {
3590  int thousands = num / 1000;
3591  num = num % 1000;
3592  if (thousands == 1) {
3593  if (num) {
3594  ast_copy_string(fn, "digits/1N", sizeof(fn));
3595  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3596  } else {
3597  if (t) {
3598  ast_copy_string(fn, "digits/1N", sizeof(fn));
3599  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3600  } else {
3601  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3602  }
3603  }
3604  } else {
3605  res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
3606  if (res) {
3607  return res;
3608  }
3609  if (num) {
3610  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3611  } else {
3612  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3613  }
3614  }
3615  t = 1;
3616  } else if (num < 1000000000) {
3617  int millions = num / 1000000;
3618  num = num % 1000000;
3619  if (millions == 1) {
3620  if (num) {
3621  ast_copy_string(fn, "digits/1F", sizeof(fn));
3622  ast_copy_string(fna, "digits/million", sizeof(fna));
3623  } else {
3624  ast_copy_string(fn, "digits/1N", sizeof(fn));
3625  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3626  }
3627  } else {
3628  res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
3629  if (res) {
3630  return res;
3631  }
3632  if (num) {
3633  ast_copy_string(fn, "digits/millions", sizeof(fn));
3634  } else {
3635  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3636  }
3637  }
3638  t = 1;
3639  } else if (num < INT_MAX) {
3640  int billions = num / 1000000000;
3641  num = num % 1000000000;
3642  if (billions == 1) {
3643  if (num) {
3644  ast_copy_string(fn, "digits/1F", sizeof(fn));
3645  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3646  } else {
3647  ast_copy_string(fn, "digits/1N", sizeof(fn));
3648  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3649  }
3650  } else {
3651  res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3652  if (res)
3653  return res;
3654  if (num) {
3655  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3656  } else {
3657  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3658  }
3659  }
3660  t = 1;
3661  } else if (num == INT_MAX) {
3662  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3663  num = 0;
3664  } else {
3665  ast_debug(1, "Number '%d' is too big for me\n", num);
3666  res = -1;
3667  }
3668 
3669  if (!res) {
3670  if (!ast_streamfile(chan, fn, language)) {
3671  if ((audiofd > -1) && (ctrlfd > -1))
3672  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3673  else
3674  res = ast_waitstream(chan, ints);
3675  }
3676  ast_stopstream(chan);
3677  if (!res) {
3678  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3679  if ((audiofd > -1) && (ctrlfd > -1)) {
3680  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3681  } else {
3682  res = ast_waitstream(chan, ints);
3683  }
3684  }
3685  ast_stopstream(chan);
3686  strcpy(fna, "");
3687  }
3688  }
3689  }
3690  return res;
3691 }
3692 
3693 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)
3694 {
3695  int res = 0;
3696  char fn[256] = "";
3697  int mf = -1; /* +1 = Masculin; -1 = Feminin */
3698  ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
3699 
3700  if (options && !strncasecmp(options, "m", 1)) {
3701  mf = -1;
3702  }
3703 
3704  ast_verb(3, "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
3705 
3706  while (!res && num) {
3707  if (num < 0) {
3708  snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
3709  if (num > INT_MIN) {
3710  num = -num;
3711  } else {
3712  num = 0;
3713  }
3714  } else if (num < 21) {
3715  if (mf < 0) {
3716  if (num < 10) {
3717  snprintf(fn, sizeof(fn), "digits/f-0%d", num);
3718  } else {
3719  snprintf(fn, sizeof(fn), "digits/f-%d", num);
3720  }
3721  } else {
3722  if (num < 10) {
3723  snprintf(fn, sizeof(fn), "digits/m-0%d", num);
3724  } else {
3725  snprintf(fn, sizeof(fn), "digits/m-%d", num);
3726  }
3727  }
3728  num = 0;
3729  } else if ((num < 100) && num >= 20) {
3730  snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
3731  num = num % 10;
3732  } else if ((num >= 100) && (num < 1000)) {
3733  int tmpnum = num / 100;
3734  snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
3735  num = num - (tmpnum * 100);
3736  } else if ((num >= 1000) && (num < 10000)) {
3737  int tmpnum = num / 1000;
3738  snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
3739  num = num - (tmpnum * 1000);
3740  } else if (num < 20000) {
3741  snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
3742  num = num % 1000;
3743  } else if (num < 1000000) {
3744  res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
3745  if (res) {
3746  return res;
3747  }
3748  snprintf(fn, sizeof(fn), "digits/1k");
3749  num = num % 1000;
3750  } else if (num < 2000000) {
3751  snprintf(fn, sizeof(fn), "digits/1m");
3752  num = num % 1000000;
3753  } else if (num < 3000000) {
3754  snprintf(fn, sizeof(fn), "digits/2m");
3755  num = num - 2000000;
3756  } else if (num < 1000000000) {
3757  res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
3758  if (res) {
3759  return res;
3760  }
3761  snprintf(fn, sizeof(fn), "digits/1m");
3762  num = num % 1000000;
3763  } else {
3764  ast_debug(1, "Number '%d' is too big for me\n", num);
3765  res = -1;
3766  }
3767  if (!res) {
3768  if (!ast_streamfile(chan, fn, language)) {
3769  if ((audiofd > -1) && (ctrlfd > -1)) {
3770  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3771  } else {
3772  res = ast_waitstream(chan, ints);
3773  }
3774  }
3775  ast_stopstream(chan);
3776  }
3777  }
3778  return res;
3779 }
3780 
3781 /*! \brief ast_say_enumeration_full_is: Icelandic syntax */
3782 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)
3783 {
3784  /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender */
3785  int res = 0, t = 0;
3786  char fn[256] = "", fna[256] = "";
3787  char *gender;
3788 
3789  if (options && !strncasecmp(options, "f", 1)) {
3790  gender = "F";
3791  } else if (options && !strncasecmp(options, "n", 1)) {
3792  gender = "N";
3793  } else {
3794  gender = "";
3795  }
3796 
3797  if (!num)
3798  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3799 
3800  while (!res && num) {
3801  if (num < 0) {
3802  ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3803  if ( num > INT_MIN ) {
3804  num = -num;
3805  } else {
3806  num = 0;
3807  }
3808  } else if (num < 100 && t) {
3809  ast_copy_string(fn, "digits/and", sizeof(fn));
3810  t = 0;
3811  } else if (num < 20) {
3812  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3813  num = 0;
3814  } else if (num < 100) {
3815  int ones = num % 10;
3816  if (ones) {
3817  int tens = num - ones;
3818  snprintf(fn, sizeof(fn), "digits/h-%d%s", tens, gender);
3819  num = ones;
3820  t++;
3821  }
3822  else if (t) {
3823  snprintf(fn, sizeof(fn), "digits/and");
3824  t = 0;
3825  }
3826  else {
3827  snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3828  num = 0;
3829  }
3830 
3831  } else if (num == 100 && t == 0) {
3832  snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3833  num = 0;
3834  } else if (num < 1000) {
3835  int hundreds = num / 100;
3836  num = num % 100;
3837  if (hundreds == 1) {
3838  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3839  } else {
3840  snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3841  }
3842  if (num) {
3843  ast_copy_string(fna, "digits/hundred", sizeof(fna));
3844  } else {
3845  snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3846  }
3847  t = 1;
3848  } else if (num < 1000000) {
3849  int thousands = num / 1000;
3850  num = num % 1000;
3851  if (thousands == 1) {
3852  if (num) {
3853  /* Thousand is a neutral word, so use the neutral recording */
3854  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3855  ast_copy_string(fna, "digits/thousand", sizeof(fna));
3856  } else {
3857  if (t) {
3858  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3859  snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
3860  } else {
3861  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3862  }
3863  }
3864  } else {
3865  res = ast_say_number_full_is(chan, thousands, ints, language, options, audiofd, ctrlfd);
3866  if (res) {
3867  return res;
3868  }
3869  if (num) {
3870  ast_copy_string(fn, "digits/thousand", sizeof(fn));
3871  } else {
3872  snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
3873  }
3874  }
3875  if (num)
3876  t = 1;
3877  } else if (num < 1000000000) {
3878  int millions = num / 1000000;
3879  num = num % 1000000;
3880  if (millions == 1) {
3881  if (num) {
3882  /* Million is a feminine word, so use the female form */
3883  ast_copy_string(fn, "digits/1kvk", sizeof(fn));
3884  ast_copy_string(fna, "digits/million", sizeof(fna));
3885  } else {
3886  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3887  snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
3888  }
3889  } else {
3890  res = ast_say_number_full_is(chan, millions, ints, language, options, audiofd, ctrlfd);
3891  if (res) {
3892  return res;
3893  }
3894  if (num) {
3895  ast_copy_string(fn, "digits/millions", sizeof(fn));
3896  } else {
3897  snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3898  }
3899  }
3900  if (num)
3901  t = 1;
3902  } else if (num < INT_MAX) {
3903  int billions = num / 1000000000;
3904  num = num % 1000000000;
3905  if (billions == 1) {
3906  if (num) {
3907  ast_copy_string(fn, "digits/1", sizeof(fn));
3908  ast_copy_string(fna, "digits/milliard", sizeof(fna));
3909  } else {
3910  ast_copy_string(fn, "digits/1hk", sizeof(fn));
3911  snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3912  }
3913  } else {
3914  res = ast_say_number_full_is(chan, billions, ints, language, options, audiofd, ctrlfd);
3915  if (res)
3916  return res;
3917  if (num) {
3918  ast_copy_string(fn, "digits/milliards", sizeof(fna));
3919  } else {
3920  snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3921  }
3922  }
3923  if (num)
3924  t = 1;
3925  } else if (num == INT_MAX) {
3926  snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3927  num = 0;
3928  } else {
3929  ast_debug(1, "Number '%d' is too big for me\n", num);
3930  res = -1;
3931  }
3932 
3933  if (!res) {
3934  if (!ast_streamfile(chan, fn, language)) {
3935  if ((audiofd > -1) && (ctrlfd > -1))
3936  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3937  else
3938  res = ast_waitstream(chan, ints);
3939  }
3940  ast_stopstream(chan);
3941  if (!res) {
3942  if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3943  if ((audiofd > -1) && (ctrlfd > -1)) {
3944  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3945  } else {
3946  res = ast_waitstream(chan, ints);
3947  }
3948  }
3949  ast_stopstream(chan);
3950  strcpy(fna, "");
3951  }
3952  }
3953  }
3954  return res;
3955 }
3956 
3957 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3958 {
3959  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
3960  return ast_say_date_en(chan, t, ints, lang);
3961  } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
3962  return ast_say_date_da(chan, t, ints, lang);
3963  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
3964  return ast_say_date_de(chan, t, ints, lang);
3965  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
3966  return ast_say_date_fr(chan, t, ints, lang);
3967  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
3968  return ast_say_date_gr(chan, t, ints, lang);
3969  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
3970  return ast_say_date_ja(chan, t, ints, lang);
3971  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
3972  return ast_say_date_he(chan, t, ints, lang);
3973  } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
3974  return ast_say_date_hu(chan, t, ints, lang);
3975  } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
3976  return ast_say_date_is(chan, t, ints, lang);
3977  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
3978  return ast_say_date_ka(chan, t, ints, lang);
3979  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
3980  return ast_say_date_nl(chan, t, ints, lang);
3981  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
3982  return ast_say_date_pt(chan, t, ints, lang);
3983  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
3984  return ast_say_date_th(chan, t, ints, lang);
3985  }
3986 
3987  /* Default to English */
3988  return ast_say_date_en(chan, t, ints, lang);
3989 }
3990 
3991 /*! \brief English syntax */
3992 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3993 {
3994  struct ast_tm tm;
3995  struct timeval when = { t, 0 };
3996  char fn[256];
3997  int res = 0;
3998  ast_localtime(&when, &tm, NULL);
3999  if (!res) {
4000  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4001  res = ast_streamfile(chan, fn, lang);
4002  if (!res)
4003  res = ast_waitstream(chan, ints);
4004  }
4005  if (!res) {
4006  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4007  res = ast_streamfile(chan, fn, lang);
4008  if (!res)
4009  res = ast_waitstream(chan, ints);
4010  }
4011  if (!res)
4012  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4013  if (!res)
4014  res = ast_waitstream(chan, ints);
4015  if (!res)
4016  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4017  return res;
4018 }
4019 
4020 /*! \brief Danish syntax */
4021 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4022 {
4023  struct timeval when = { t, 0 };
4024  struct ast_tm tm;
4025  char fn[256];
4026  int res = 0;
4027  ast_localtime(&when, &tm, NULL);
4028  if (!res) {
4029  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4030  res = ast_streamfile(chan, fn, lang);
4031  if (!res)
4032  res = ast_waitstream(chan, ints);
4033  }
4034  if (!res)
4035  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4036  if (!res)
4037  res = ast_waitstream(chan, ints);
4038  if (!res) {
4039  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4040  res = ast_streamfile(chan, fn, lang);
4041  if (!res)
4042  res = ast_waitstream(chan, ints);
4043  }
4044  if (!res) {
4045  /* Year */
4046  int year = tm.tm_year + 1900;
4047  if (year > 1999) { /* year 2000 and later */
4048  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4049  } else {
4050  if (year < 1100) {
4051  /* I'm not going to handle 1100 and prior */
4052  /* We'll just be silent on the year, instead of bombing out. */
4053  } else {
4054  /* year 1100 to 1999. will anybody need this?!? */
4055  snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4056  res = wait_file(chan, ints, fn, lang);
4057  if (!res) {
4058  res = wait_file(chan, ints, "digits/hundred", lang);
4059  if (!res && year % 100 != 0) {
4060  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4061  }
4062  }
4063  }
4064  }
4065  }
4066  return res;
4067 }
4068 
4069 /*! \brief German syntax */
4070 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4071 {
4072  struct timeval when = { t, 0 };
4073  struct ast_tm tm;
4074  char fn[256];
4075  int res = 0;
4076  ast_localtime(&when, &tm, NULL);
4077  if (!res) {
4078  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4079  res = ast_streamfile(chan, fn, lang);
4080  if (!res)
4081  res = ast_waitstream(chan, ints);
4082  }
4083  if (!res)
4084  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4085  if (!res)
4086  res = ast_waitstream(chan, ints);
4087  if (!res) {
4088  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4089  res = ast_streamfile(chan, fn, lang);
4090  if (!res)
4091  res = ast_waitstream(chan, ints);
4092  }
4093  if (!res) {
4094  /* Year */
4095  int year = tm.tm_year + 1900;
4096  if (year > 1999) { /* year 2000 and later */
4097  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4098  } else {
4099  if (year < 1100) {
4100  /* I'm not going to handle 1100 and prior */
4101  /* We'll just be silent on the year, instead of bombing out. */
4102  } else {
4103  /* year 1100 to 1999. will anybody need this?!? */
4104  /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4105  snprintf(fn, sizeof(fn), "digits/%d", (year / 100) );
4106  res = wait_file(chan, ints, fn, lang);
4107  if (!res) {
4108  res = wait_file(chan, ints, "digits/hundred", lang);
4109  if (!res && year % 100 != 0) {
4110  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4111  }
4112  }
4113  }
4114  }
4115  }
4116  return res;
4117 }
4118 
4119 /*! \brief Hungarian syntax */
4120 int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4121 {
4122  struct timeval when = { t, 0 };
4123  struct ast_tm tm;
4124  char fn[256];
4125  int res = 0;
4126  ast_localtime(&when, &tm, NULL);
4127 
4128  if (!res)
4129  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4130  if (!res)
4131  res = ast_waitstream(chan, ints);
4132  if (!res) {
4133  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4134  res = ast_streamfile(chan, fn, lang);
4135  if (!res)
4136  res = ast_waitstream(chan, ints);
4137  }
4138  if (!res)
4139  ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
4140  if (!res)
4141  res = ast_waitstream(chan, ints);
4142  if (!res) {
4143  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4144  res = ast_streamfile(chan, fn, lang);
4145  if (!res)
4146  res = ast_waitstream(chan, ints);
4147  }
4148  return res;
4149 }
4150 
4151 /*! \brief French syntax */
4152 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4153 {
4154  struct timeval when = { t, 0 };
4155  struct ast_tm tm;
4156  char fn[256];
4157  int res = 0;
4158  ast_localtime(&when, &tm, NULL);
4159  if (!res) {
4160  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4161  res = ast_streamfile(chan, fn, lang);
4162  if (!res)
4163  res = ast_waitstream(chan, ints);
4164  }
4165  if (!res)
4166  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4167  if (!res)
4168  res = ast_waitstream(chan, ints);
4169  if (!res) {
4170  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4171  res = ast_streamfile(chan, fn, lang);
4172  if (!res)
4173  res = ast_waitstream(chan, ints);
4174  }
4175  if (!res)
4176  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4177  return res;
4178 }
4179 
4180 /*! \brief Dutch syntax */
4181 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4182 {
4183  struct timeval when = { t, 0 };
4184  struct ast_tm tm;
4185  char fn[256];
4186  int res = 0;
4187  ast_localtime(&when, &tm, NULL);
4188  if (!res) {
4189  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4190  res = ast_streamfile(chan, fn, lang);
4191  if (!res)
4192  res = ast_waitstream(chan, ints);
4193  }
4194  if (!res)
4195  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4196  if (!res) {
4197  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4198  res = ast_streamfile(chan, fn, lang);
4199  if (!res)
4200  res = ast_waitstream(chan, ints);
4201  }
4202  if (!res)
4203  res = ast_waitstream(chan, ints);
4204  if (!res)
4205  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4206  return res;
4207 }
4208 
4209 /*! \brief Thai syntax */
4210 int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4211 {
4212  struct timeval when = { t, 0 };
4213  struct ast_tm tm;
4214  char fn[256];
4215  int res = 0;
4216  ast_localtime(&when, &tm, NULL);
4217  if (!res) {
4218  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4219  res = ast_streamfile(chan, fn, lang);
4220  ast_copy_string(fn, "digits/tee", sizeof(fn));
4221  res = ast_streamfile(chan, fn, lang);
4222  if (!res)
4223  res = ast_waitstream(chan, ints);
4224  }
4225  if (!res)
4226  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4227  if (!res)
4228  res = ast_waitstream(chan, ints);
4229  if (!res) {
4230  ast_copy_string(fn, "digits/duan", sizeof(fn));
4231  res = ast_streamfile(chan, fn, lang);
4232  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4233  res = ast_streamfile(chan, fn, lang);
4234  if (!res)
4235  res = ast_waitstream(chan, ints);
4236  }
4237  if (!res){
4238  ast_copy_string(fn, "digits/posor", sizeof(fn));
4239  res = ast_streamfile(chan, fn, lang);
4240  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4241  }
4242  return res;
4243 }
4244 
4245 /*! \brief Portuguese syntax */
4246 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4247 {
4248  struct timeval when = { t, 0 };
4249  struct ast_tm tm;
4250  char fn[256];
4251  int res = 0;
4252 
4253  ast_localtime(&when, &tm, NULL);
4254  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4255  if (!res)
4256  res = wait_file(chan, ints, fn, lang);
4257  if (!res)
4258  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
4259  if (!res)
4260  res = wait_file(chan, ints, "digits/pt-de", lang);
4261  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4262  if (!res)
4263  res = wait_file(chan, ints, fn, lang);
4264  if (!res)
4265  res = wait_file(chan, ints, "digits/pt-de", lang);
4266  if (!res)
4267  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4268 
4269  return res;
4270 }
4271 
4272 /*! \brief Hebrew syntax */
4273 int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4274 {
4275  struct timeval when = { t, 0 };
4276  struct ast_tm tm;
4277  char fn[256];
4278  int res = 0;
4279  ast_localtime(&when, &tm, NULL);
4280  if (!res) {
4281  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4282  res = ast_streamfile(chan, fn, lang);
4283  if (!res) {
4284  res = ast_waitstream(chan, ints);
4285  }
4286  }
4287  if (!res) {
4288  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4289  res = ast_streamfile(chan, fn, lang);
4290  if (!res) {
4291  res = ast_waitstream(chan, ints);
4292  }
4293  }
4294  if (!res) {
4295  res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
4296  }
4297  if (!res) {
4298  res = ast_waitstream(chan, ints);
4299  }
4300  if (!res) {
4301  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
4302  }
4303  return res;
4304 }
4305 
4306 /* Icelandic syntax */
4307 int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
4308 {
4309  struct timeval when = { t, 0 };
4310  struct ast_tm tm;
4311  char fn[256];
4312  int res = 0;
4313  ast_localtime(&when, &tm, NULL);
4314  if (!res) {
4315  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
4316  res = ast_streamfile(chan, fn, lang);
4317  if (!res)
4318  res = ast_waitstream(chan, ints);
4319  }
4320  if (!res)
4321  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
4322  if (!res)
4323  res = ast_waitstream(chan, ints);
4324  if (!res) {
4325  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
4326  res = ast_streamfile(chan, fn, lang);
4327  if (!res)
4328  res = ast_waitstream(chan, ints);
4329  }
4330  if (!res) {
4331  /* Year */
4332  int year = tm.tm_year + 1900;
4333  if (year > 1999) { /* year 2000 and later */
4334  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4335  } else {
4336  if (year < 1100) {
4337  /* I'm not going to handle 1100 and prior */
4338  /* We'll just be silent on the year, instead of bombing out. */
4339  } else {
4340  /* year 1100 to 1999. will anybody need this?!? */
4341  snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
4342  res = wait_file(chan, ints, fn, lang);
4343  if (!res) {
4344  res = wait_file(chan, ints, "digits/hundred", lang);
4345  if (!res && year % 100 != 0) {
4346  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4347  }
4348  }
4349  }
4350  }
4351  }
4352  return res;
4353 }
4354 
4355 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)
4356 {
4357  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
4358  return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4359  } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
4360  return ast_say_date_with_format_da(chan, t, ints, lang, format, tzone);
4361  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
4362  return ast_say_date_with_format_de(chan, t, ints, lang, format, tzone);
4363  } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
4364  return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
4365  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
4366  return ast_say_date_with_format_he(chan, t, ints, lang, format, tzone);
4367  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
4368  return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone);
4369  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
4370  return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone);
4371  } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */
4372  return ast_say_date_with_format_is(chan, t, ints, lang, format, tzone);
4373  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
4374  return ast_say_date_with_format_ja(chan, t, ints, lang, format, tzone);
4375  } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
4376  return ast_say_date_with_format_it(chan, t, ints, lang, format, tzone);
4377  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
4378  return ast_say_date_with_format_nl(chan, t, ints, lang, format, tzone);
4379  } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
4380  return ast_say_date_with_format_pl(chan, t, ints, lang, format, tzone);
4381  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
4382  return ast_say_date_with_format_pt(chan, t, ints, lang, format, tzone);
4383  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
4384  return ast_say_date_with_format_th(chan, t, ints, lang, format, tzone);
4385  } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
4386  return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
4387  } else if (!strncasecmp(lang, "vi", 2)) { /* Vietnamese syntax */
4388  return ast_say_date_with_format_vi(chan, t, ints, lang, format, tzone);
4389  }
4390 
4391  /* Default to English */
4392  return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
4393 }
4394 
4395 /*! \brief English syntax */
4396 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)
4397 {
4398  struct timeval when = { t, 0 };
4399  struct ast_tm tm;
4400  int res=0, offset, sndoffset;
4401  char sndfile[256], nextmsg[256];
4402 
4403  if (format == NULL)
4404  format = "ABdY 'digits/at' IMp";
4405 
4406  ast_localtime(&when, &tm, tzone);
4407 
4408  for (offset=0 ; format[offset] != '\0' ; offset++) {
4409  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4410  switch (format[offset]) {
4411  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4412  case '\'':
4413  /* Literal name of a sound file */
4414  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4415  sndfile[sndoffset] = format[offset];
4416  }
4417  sndfile[sndoffset] = '\0';
4418  res = wait_file(chan, ints, sndfile, lang);
4419  break;
4420  case 'A':
4421  case 'a':
4422  /* Sunday - Saturday */
4423  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4424  res = wait_file(chan, ints, nextmsg, lang);
4425  break;
4426  case 'B':
4427  case 'b':
4428  case 'h':
4429  /* January - December */
4430  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4431  res = wait_file(chan, ints, nextmsg, lang);
4432  break;
4433  case 'm':
4434  /* Month enumerated */
4435  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
4436  break;
4437  case 'd':
4438  case 'e':
4439  /* First - Thirtyfirst */
4440  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
4441  break;
4442  case 'Y':
4443  /* Year */
4444  if (tm.tm_year > 99) {
4445  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
4446  } else if (tm.tm_year < 1) {
4447  /* I'm not going to handle 1900 and prior */
4448  /* We'll just be silent on the year, instead of bombing out. */
4449  } else {
4450  res = wait_file(chan, ints, "digits/19", lang);
4451  if (!res) {
4452  if (tm.tm_year <= 9) {
4453  /* 1901 - 1909 */
4454  res = wait_file(chan, ints, "digits/oh", lang);
4455  }
4456 
4457  res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
4458  }
4459  }
4460  break;
4461  case 'I':
4462  case 'l':
4463  /* 12-Hour */
4464  if (tm.tm_hour == 0)
4465  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4466  else if (tm.tm_hour > 12)
4467  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4468  else
4469  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4470  res = wait_file(chan, ints, nextmsg, lang);
4471  break;
4472  case 'H':
4473  case 'k':
4474  /* 24-Hour */
4475  if (format[offset] == 'H') {
4476  /* e.g. oh-eight */
4477  if (tm.tm_hour < 10) {
4478  res = wait_file(chan, ints, "digits/oh", lang);
4479  }
4480  } else {
4481  /* e.g. eight */
4482  if (tm.tm_hour == 0) {
4483  res = wait_file(chan, ints, "digits/oh", lang);
4484  }
4485  }
4486  if (!res) {
4487  if (tm.tm_hour != 0) {
4488  int remaining = tm.tm_hour;
4489  if (tm.tm_hour > 20) {
4490  res = wait_file(chan, ints, "digits/20", lang);
4491  remaining -= 20;
4492  }
4493  if (!res) {
4494  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
4495  res = wait_file(chan, ints, nextmsg, lang);
4496  }
4497  }
4498  }
4499  break;
4500  case 'M':
4501  case 'N':
4502  /* Minute */
4503  if (tm.tm_min == 0) {
4504  if (format[offset] == 'M') {
4505  res = wait_file(chan, ints, "digits/oclock", lang);
4506  } else {
4507  res = wait_file(chan, ints, "digits/hundred", lang);
4508  }
4509  } else if (tm.tm_min < 10) {
4510  res = wait_file(chan, ints, "digits/oh", lang);
4511  if (!res) {
4512  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
4513  res = wait_file(chan, ints, nextmsg, lang);
4514  }
4515  } else {
4516  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4517  }
4518  break;
4519  case 'P':
4520  case 'p':
4521  /* AM/PM */
4522  if (tm.tm_hour > 11)
4523  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4524  else
4525  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4526  res = wait_file(chan, ints, nextmsg, lang);
4527  break;
4528  case 'Q':
4529  /* Shorthand for "Today", "Yesterday", or ABdY */
4530  /* XXX As emphasized elsewhere, this should the native way in your
4531  * language to say the date, with changes in what you say, depending
4532  * upon how recent the date is. XXX */
4533  {
4534  struct timeval now = ast_tvnow();
4535  struct ast_tm tmnow;
4536  time_t beg_today;
4537 
4538  gettimeofday(&now, NULL);
4539  ast_localtime(&now, &tmnow, tzone);
4540  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4541  /* In any case, it saves not having to do ast_mktime() */
4542  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4543  if (beg_today + 15768000 < t) {
4544  /* More than 6 months from now - "April nineteenth two thousand three" */
4545  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4546  } else if (beg_today + 2628000 < t) {
4547  /* Less than 6 months from now - "August seventh" */
4548  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4549  } else if (beg_today + 86400 * 6 < t) {
4550  /* Less than a month from now - "Sunday, October third" */
4551  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4552  } else if (beg_today + 172800 < t) {
4553  /* Within the next week */
4554  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4555  } else if (beg_today + 86400 < t) {
4556  /* Tomorrow */
4557  res = wait_file(chan, ints, "digits/tomorrow", lang);
4558  } else if (beg_today < t) {
4559  /* Today */
4560  res = wait_file(chan, ints, "digits/today", lang);
4561  } else if (beg_today - 86400 < t) {
4562  /* Yesterday */
4563  res = wait_file(chan, ints, "digits/yesterday", lang);
4564  } else if (beg_today - 86400 * 6 < t) {
4565  /* Within the last week */
4566  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4567  } else if (beg_today - 2628000 < t) {
4568  /* Less than a month ago - "Sunday, October third" */
4569  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4570  } else if (beg_today - 15768000 < t) {
4571  /* Less than 6 months ago - "August seventh" */
4572  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4573  } else {
4574  /* More than 6 months ago - "April nineteenth two thousand three" */
4575  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4576  }
4577  }
4578  break;
4579  case 'q':
4580  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4581  /* XXX As emphasized elsewhere, this should the native way in your
4582  * language to say the date, with changes in what you say, depending
4583  * upon how recent the date is. XXX */
4584  {
4585  struct timeval now;
4586  struct ast_tm tmnow;
4587  time_t beg_today;
4588 
4589  now = ast_tvnow();
4590  ast_localtime(&now, &tmnow, tzone);
4591  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4592  /* In any case, it saves not having to do ast_mktime() */
4593  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4594  if (beg_today + 15768000 < t) {
4595  /* More than 6 months from now - "April nineteenth two thousand three" */
4596  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4597  } else if (beg_today + 2628000 < t) {
4598  /* Less than 6 months from now - "August seventh" */
4599  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4600  } else if (beg_today + 86400 * 6 < t) {
4601  /* Less than a month from now - "Sunday, October third" */
4602  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4603  } else if (beg_today + 172800 < t) {
4604  /* Within the next week */
4605  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4606  } else if (beg_today + 86400 < t) {
4607  /* Tomorrow */
4608  res = wait_file(chan, ints, "digits/tomorrow", lang);
4609  } else if (beg_today < t) {
4610  /* Today */
4611  res = wait_file(chan, ints, "digits/today", lang);
4612  } else if (beg_today - 86400 < t) {
4613  /* Yesterday */
4614  res = wait_file(chan, ints, "digits/yesterday", lang);
4615  } else if (beg_today - 86400 * 6 < t) {
4616  /* Within the last week */
4617  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
4618  } else if (beg_today - 2628000 < t) {
4619  /* Less than a month ago - "Sunday, October third" */
4620  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
4621  } else if (beg_today - 15768000 < t) {
4622  /* Less than 6 months ago - "August seventh" */
4623  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
4624  } else {
4625  /* More than 6 months ago - "April nineteenth two thousand three" */
4626  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
4627  }
4628  }
4629  break;
4630  case 'R':
4631  res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
4632  break;
4633  case 'S':
4634  /* Seconds */
4635  if (tm.tm_sec == 0) {
4636  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4637  res = wait_file(chan, ints, nextmsg, lang);
4638  } else if (tm.tm_sec < 10) {
4639  res = wait_file(chan, ints, "digits/oh", lang);
4640  if (!res) {
4641  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
4642  res = wait_file(chan, ints, nextmsg, lang);
4643  }
4644  } else {
4645  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
4646  }
4647  break;
4648  case 'T':
4649  res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
4650  break;
4651  case ' ':
4652  case ' ':
4653  /* Just ignore spaces and tabs */
4654  break;
4655  default:
4656  /* Unknown character */
4657  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4658  }
4659  /* Jump out on DTMF */
4660  if (res) {
4661  break;
4662  }
4663  }
4664  return res;
4665 }
4666 
4667 static char next_item(const char *format)
4668 {
4669  const char *next = ast_skip_blanks(format);
4670  return *next;
4671 }
4672 
4673 /*! \brief Danish syntax */
4674 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)
4675 {
4676  struct timeval when = { t, 0 };
4677  struct ast_tm tm;
4678  int res=0, offset, sndoffset;
4679  char sndfile[256], nextmsg[256];
4680 
4681  if (!format)
4682  format = "A dBY HMS";
4683 
4684  ast_localtime(&when, &tm, tzone);
4685 
4686  for (offset=0 ; format[offset] != '\0' ; offset++) {
4687  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4688  switch (format[offset]) {
4689  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4690  case '\'':
4691  /* Literal name of a sound file */
4692  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4693  sndfile[sndoffset] = format[offset];
4694  }
4695  sndfile[sndoffset] = '\0';
4696  res = wait_file(chan, ints, sndfile, lang);
4697  break;
4698  case 'A':
4699  case 'a':
4700  /* Sunday - Saturday */
4701  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4702  res = wait_file(chan, ints, nextmsg, lang);
4703  break;
4704  case 'B':
4705  case 'b':
4706  case 'h':
4707  /* January - December */
4708  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4709  res = wait_file(chan, ints, nextmsg, lang);
4710  break;
4711  case 'm':
4712  /* Month enumerated */
4713  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4714  break;
4715  case 'd':
4716  case 'e':
4717  /* First - Thirtyfirst */
4718  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4719  break;
4720  case 'Y':
4721  /* Year */
4722  {
4723  int year = tm.tm_year + 1900;
4724  if (year > 1999) { /* year 2000 and later */
4725  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4726  } else {
4727  if (year < 1100) {
4728  /* I'm not going to handle 1100 and prior */
4729  /* We'll just be silent on the year, instead of bombing out. */
4730  } else {
4731  /* year 1100 to 1999. will anybody need this?!? */
4732  /* say 1967 as 'nineteen hundred seven and sixty' */
4733  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4734  res = wait_file(chan, ints, nextmsg, lang);
4735  if (!res) {
4736  res = wait_file(chan, ints, "digits/hundred", lang);
4737  if (!res && year % 100 != 0) {
4738  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4739  }
4740  }
4741  }
4742  }
4743  }
4744  break;
4745  case 'I':
4746  case 'l':
4747  /* 12-Hour */
4748  res = wait_file(chan, ints, "digits/oclock", lang);
4749  if (tm.tm_hour == 0)
4750  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4751  else if (tm.tm_hour > 12)
4752  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4753  else
4754  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4755  if (!res) {
4756  res = wait_file(chan, ints, nextmsg, lang);
4757  }
4758  break;
4759  case 'H':
4760  /* 24-Hour, single digit hours preceded by "oh" (0) */
4761  if (tm.tm_hour < 10 && tm.tm_hour > 0) {
4762  res = wait_file(chan, ints, "digits/0", lang);
4763  }
4764  /* FALLTRHU */
4765  case 'k':
4766  /* 24-Hour */
4767  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4768  break;
4769  case 'M':
4770  /* Minute */
4771  if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4772  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
4773  }
4774  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4775  if (tm.tm_min == 1) {
4776  res = wait_file(chan, ints, "minute", lang);
4777  } else {
4778  res = wait_file(chan, ints, "minutes", lang);
4779  }
4780  }
4781  break;
4782  case 'P':
4783  case 'p':
4784  /* AM/PM */
4785  if (tm.tm_hour > 11)
4786  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4787  else
4788  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4789  res = wait_file(chan, ints, nextmsg, lang);
4790  break;
4791  case 'Q':
4792  /* Shorthand for "Today", "Yesterday", or AdBY */
4793  /* XXX As emphasized elsewhere, this should the native way in your
4794  * language to say the date, with changes in what you say, depending
4795  * upon how recent the date is. XXX */
4796  {
4797  struct timeval now = ast_tvnow();
4798  struct ast_tm tmnow;
4799  time_t beg_today;
4800 
4801  ast_localtime(&now, &tmnow, tzone);
4802  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4803  /* In any case, it saves not having to do ast_mktime() */
4804  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4805  if (beg_today < t) {
4806  /* Today */
4807  res = wait_file(chan, ints, "digits/today", lang);
4808  } else if (beg_today - 86400 < t) {
4809  /* Yesterday */
4810  res = wait_file(chan, ints, "digits/yesterday", lang);
4811  } else {
4812  res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4813  }
4814  }
4815  break;
4816  case 'q':
4817  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4818  /* XXX As emphasized elsewhere, this should the native way in your
4819  * language to say the date, with changes in what you say, depending
4820  * upon how recent the date is. XXX */
4821  {
4822  struct timeval now = ast_tvnow();
4823  struct ast_tm tmnow;
4824  time_t beg_today;
4825 
4826  ast_localtime(&now, &tmnow, tzone);
4827  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4828  /* In any case, it saves not having to do ast_mktime() */
4829  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
4830  if (beg_today < t) {
4831  /* Today */
4832  } else if ((beg_today - 86400) < t) {
4833  /* Yesterday */
4834  res = wait_file(chan, ints, "digits/yesterday", lang);
4835  } else if (beg_today - 86400 * 6 < t) {
4836  /* Within the last week */
4837  res = ast_say_date_with_format_da(chan, t, ints, lang, "A", tzone);
4838  } else {
4839  res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
4840  }
4841  }
4842  break;
4843  case 'R':
4844  res = ast_say_date_with_format_da(chan, t, ints, lang, "HM", tzone);
4845  break;
4846  case 'S':
4847  /* Seconds */
4848  res = wait_file(chan, ints, "digits/and", lang);
4849  if (!res) {
4850  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
4851  if (!res) {
4852  res = wait_file(chan, ints, "seconds", lang);
4853  }
4854  }
4855  break;
4856  case 'T':
4857  res = ast_say_date_with_format_da(chan, t, ints, lang, "HMS", tzone);
4858  break;
4859  case ' ':
4860  case ' ':
4861  /* Just ignore spaces and tabs */
4862  break;
4863  default:
4864  /* Unknown character */
4865  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
4866  }
4867  /* Jump out on DTMF */
4868  if (res) {
4869  break;
4870  }
4871  }
4872  return res;
4873 }
4874 
4875 /*! \brief German syntax */
4876 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)
4877 {
4878  struct timeval when = { t, 0 };
4879  struct ast_tm tm;
4880  int res=0, offset, sndoffset;
4881  char sndfile[256], nextmsg[256];
4882 
4883  if (!format)
4884  format = "A dBY HMS";
4885 
4886  ast_localtime(&when, &tm, tzone);
4887 
4888  for (offset=0 ; format[offset] != '\0' ; offset++) {
4889  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
4890  switch (format[offset]) {
4891  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4892  case '\'':
4893  /* Literal name of a sound file */
4894  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
4895  sndfile[sndoffset] = format[offset];
4896  }
4897  sndfile[sndoffset] = '\0';
4898  res = wait_file(chan, ints, sndfile, lang);
4899  break;
4900  case 'A':
4901  case 'a':
4902  /* Sunday - Saturday */
4903  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
4904  res = wait_file(chan, ints, nextmsg, lang);
4905  break;
4906  case 'B':
4907  case 'b':
4908  case 'h':
4909  /* January - December */
4910  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
4911  res = wait_file(chan, ints, nextmsg, lang);
4912  break;
4913  case 'm':
4914  /* Month enumerated */
4915  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
4916  break;
4917  case 'd':
4918  case 'e':
4919  /* First - Thirtyfirst */
4920  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
4921  break;
4922  case 'Y':
4923  /* Year */
4924  {
4925  int year = tm.tm_year + 1900;
4926  if (year > 1999) { /* year 2000 and later */
4927  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
4928  } else {
4929  if (year < 1100) {
4930  /* I'm not going to handle 1100 and prior */
4931  /* We'll just be silent on the year, instead of bombing out. */
4932  } else {
4933  /* year 1100 to 1999. will anybody need this?!? */
4934  /* say 1967 as 'neunzehn hundert sieben und sechzig' */
4935  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
4936  res = wait_file(chan, ints, nextmsg, lang);
4937  if (!res) {
4938  res = wait_file(chan, ints, "digits/hundred", lang);
4939  if (!res && year % 100 != 0) {
4940  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
4941  }
4942  }
4943  }
4944  }
4945  }
4946  break;
4947  case 'I':
4948  case 'l':
4949  /* 12-Hour */
4950  if (tm.tm_hour == 0)
4951  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
4952  else if (tm.tm_hour == 1)
4953  ast_copy_string(nextmsg, "digits/1N", sizeof(nextmsg));
4954  else if (tm.tm_hour > 12)
4955  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
4956  else
4957  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
4958  res = wait_file(chan, ints, nextmsg, lang);
4959  if (!res) {
4960  res = wait_file(chan, ints, "digits/oclock", lang);
4961  }
4962  break;
4963  case 'H':
4964  case 'k':
4965  /* 24-Hour */
4966  if (tm.tm_hour == 1) {
4967  res = wait_file(chan, ints, "digits/1N", lang);
4968  } else {
4969  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
4970  }
4971  if (!res) {
4972  res = wait_file(chan, ints, "digits/oclock", lang);
4973  }
4974  break;
4975  case 'M':
4976  /* Minute */
4977  if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
4978  res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say minutes */
4979  } else if (tm.tm_min > 0) {
4980  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
4981  }
4982 
4983  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
4984  if (tm.tm_min == 1) {
4985  res = wait_file(chan, ints, "minute", lang);
4986  } else {
4987  res = wait_file(chan, ints, "minutes", lang);
4988  }
4989  }
4990  break;
4991  case 'P':
4992  case 'p':
4993  /* AM/PM */
4994  if (tm.tm_hour > 11)
4995  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
4996  else
4997  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
4998  res = wait_file(chan, ints, nextmsg, lang);
4999  break;
5000  case 'Q':
5001  /* Shorthand for "Today", "Yesterday", or AdBY */
5002  /* XXX As emphasized elsewhere, this should the native way in your
5003  * language to say the date, with changes in what you say, depending
5004  * upon how recent the date is. XXX */
5005  {
5006  struct timeval now = ast_tvnow();
5007  struct ast_tm tmnow;
5008  time_t beg_today;
5009 
5010  ast_localtime(&now, &tmnow, tzone);
5011  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5012  /* In any case, it saves not having to do ast_mktime() */
5013  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5014  if (beg_today < t) {
5015  /* Today */
5016  res = wait_file(chan, ints, "digits/today", lang);
5017  } else if (beg_today - 86400 < t) {
5018  /* Yesterday */
5019  res = wait_file(chan, ints, "digits/yesterday", lang);
5020  } else {
5021  res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
5022  }
5023  }
5024  break;
5025  case 'q':
5026  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5027  /* XXX As emphasized elsewhere, this should the native way in your
5028  * language to say the date, with changes in what you say, depending
5029  * upon how recent the date is. XXX */
5030  {
5031  struct timeval now = ast_tvnow();
5032  struct ast_tm tmnow;
5033  time_t beg_today;
5034 
5035  ast_localtime(&now, &tmnow, tzone);
5036  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5037  /* In any case, it saves not having to do ast_mktime() */
5038  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5039  if (beg_today < t) {
5040  /* Today */
5041  } else if ((beg_today - 86400) < t) {
5042  /* Yesterday */
5043  res = wait_file(chan, ints, "digits/yesterday", lang);
5044  } else if (beg_today - 86400 * 6 < t) {
5045  /* Within the last week */
5046  res = ast_say_date_with_format_de(chan, t, ints, lang, "A", tzone);
5047  } else {
5048  res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
5049  }
5050  }
5051  break;
5052  case 'R':
5053  res = ast_say_date_with_format_de(chan, t, ints, lang, "HM", tzone);
5054  break;
5055  case 'S':
5056  /* Seconds */
5057  res = wait_file(chan, ints, "digits/and", lang);
5058  if (!res) {
5059  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5060  if (!res) {
5061  res = wait_file(chan, ints, tm.tm_sec == 1 ? "second" : "seconds", lang);
5062  }
5063  }
5064  break;
5065  case 'T':
5066  res = ast_say_date_with_format_de(chan, t, ints, lang, "HMS", tzone);
5067  break;
5068  case ' ':
5069  case ' ':
5070  /* Just ignore spaces and tabs */
5071  break;
5072  default:
5073  /* Unknown character */
5074  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5075  }
5076  /* Jump out on DTMF */
5077  if (res) {
5078  break;
5079  }
5080  }
5081  return res;
5082 }
5083 
5084 /* Icelandic syntax */
5085 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)
5086 {
5087  struct timeval when = { t, 0 };
5088  struct ast_tm tm;
5089  int res=0, offset, sndoffset;
5090  char sndfile[256], nextmsg[256];
5091 
5092  if (!format)
5093  format = "A dBY HMS";
5094 
5095  ast_localtime(&when, &tm, tzone);
5096 
5097  for (offset=0 ; format[offset] != '\0' ; offset++) {
5098  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5099  switch (format[offset]) {
5100  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5101  case '\'':
5102  /* Literal name of a sound file */
5103  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5104  sndfile[sndoffset] = format[offset];
5105  }
5106  sndfile[sndoffset] = '\0';
5107  res = wait_file(chan, ints, sndfile, lang);
5108  break;
5109  case 'A':
5110  case 'a':
5111  /* Sunday - Saturday */
5112  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5113  res = wait_file(chan, ints, nextmsg, lang);
5114  break;
5115  case 'B':
5116  case 'b':
5117  case 'h':
5118  /* January - December */
5119  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5120  res = wait_file(chan, ints, nextmsg, lang);
5121  break;
5122  case 'm':
5123  /* Month enumerated */
5124  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
5125  break;
5126  case 'd':
5127  case 'e':
5128  /* First - Thirtyfirst */
5129  res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
5130  break;
5131  case 'Y':
5132  /* Year */
5133  {
5134  int year = tm.tm_year + 1900;
5135  if (year > 1999) { /* year 2000 and later */
5136  res = ast_say_number(chan, year, ints, lang, (char *) NULL);
5137  } else {
5138  if (year < 1100) {
5139  /* I'm not going to handle 1100 and prior */
5140  /* We'll just be silent on the year, instead of bombing out. */
5141  } else {
5142  /* year 1100 to 1999. will anybody need this?!? */
5143  /* say 1967 as 'nineteen hundred seven and sixty' */
5144  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
5145  res = wait_file(chan, ints, nextmsg, lang);
5146  if (!res) {
5147  res = wait_file(chan, ints, "digits/hundred", lang);
5148  if (!res && year % 100 != 0) {
5149  res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
5150  }
5151  }
5152  }
5153  }
5154  }
5155  break;
5156  case 'I':
5157  case 'l':
5158  /* 12-Hour */
5159  res = wait_file(chan, ints, "digits/oclock", lang);
5160  if (tm.tm_hour == 0)
5161  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5162  else if (tm.tm_hour > 12)
5163  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5164  else
5165  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5166  if (!res) {
5167  res = wait_file(chan, ints, nextmsg, lang);
5168  }
5169  break;
5170  case 'H':
5171  /* 24-Hour, single digit hours preceeded by "oh" (0) */
5172  if (tm.tm_hour < 10 && tm.tm_hour > 0) {
5173  res = wait_file(chan, ints, "digits/0", lang);
5174  }
5175  /* FALLTRHU */
5176  case 'k':
5177  /* 24-Hour */
5178  res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
5179  break;
5180  case 'M':
5181  /* Minute */
5182  if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
5183  if (tm.tm_min < 10)
5184  res = wait_file(chan, ints, "digits/0", lang);
5185  /* Gender depends on whether or not seconds follow */
5186  if (next_item(&format[offset + 1]) == 'S')
5187  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
5188  else
5189  res = ast_say_number(chan, tm.tm_min, ints, lang, "n");
5190  }
5191  if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
5192  /* Say minute/minutes depending on whether minutes end in 1 */
5193  if ((tm.tm_min % 10 == 1) && (tm.tm_min != 11)) {
5194  res = wait_file(chan, ints, "minute", lang);
5195  } else {
5196  res = wait_file(chan, ints, "minutes", lang);
5197  }
5198  }
5199  break;
5200  case 'P':
5201  case 'p':
5202  /* AM/PM */
5203  if (tm.tm_hour > 11)
5204  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5205  else
5206  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5207  res = wait_file(chan, ints, nextmsg, lang);
5208  break;
5209  case 'Q':
5210  /* Shorthand for "Today", "Yesterday", or AdBY */
5211  /* XXX As emphasized elsewhere, this should the native way in your
5212  * language to say the date, with changes in what you say, depending
5213  * upon how recent the date is. XXX */
5214  {
5215  struct timeval now = ast_tvnow();
5216  struct ast_tm tmnow;
5217  time_t beg_today;
5218 
5219  ast_localtime(&now, &tmnow, tzone);
5220  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5221  /* In any case, it saves not having to do ast_mktime() */
5222  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5223  if (beg_today < t) {
5224  /* Today */
5225  res = wait_file(chan, ints, "digits/today", lang);
5226  } else if (beg_today - 86400 < t) {
5227  /* Yesterday */
5228  res = wait_file(chan, ints, "digits/yesterday", lang);
5229  } else {
5230  res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5231  }
5232  }
5233  break;
5234  case 'q':
5235  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5236  /* XXX As emphasized elsewhere, this should the native way in your
5237  * language to say the date, with changes in what you say, depending
5238  * upon how recent the date is. XXX */
5239  {
5240  struct timeval now = ast_tvnow();
5241  struct ast_tm tmnow;
5242  time_t beg_today;
5243 
5244  ast_localtime(&now, &tmnow, tzone);
5245  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5246  /* In any case, it saves not having to do ast_mktime() */
5247  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5248  if (beg_today < t) {
5249  /* Today */
5250  } else if ((beg_today - 86400) < t) {
5251  /* Yesterday */
5252  res = wait_file(chan, ints, "digits/yesterday", lang);
5253  } else if (beg_today - 86400 * 6 < t) {
5254  /* Within the last week */
5255  res = ast_say_date_with_format_is(chan, t, ints, lang, "A", tzone);
5256  } else {
5257  res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone);
5258  }
5259  }
5260  break;
5261  case 'R':
5262  res = ast_say_date_with_format_is(chan, t, ints, lang, "HM", tzone);
5263  break;
5264  case 'S':
5265  /* Seconds */
5266  res = wait_file(chan, ints, "digits/and", lang);
5267  if (!res) {
5268  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
5269  /* Say minute/minutes depending on whether seconds end in 1 */
5270  if (!res && (tm.tm_sec % 10 == 1) && (tm.tm_sec != 11)) {
5271  res = wait_file(chan, ints, "second", lang);
5272  } else {
5273  res = wait_file(chan, ints, "seconds", lang);
5274  }
5275  }
5276  break;
5277  case 'T':
5278  res = ast_say_date_with_format_is(chan, t, ints, lang, "HMS", tzone);
5279  break;
5280  case ' ':
5281  case ' ':
5282  /* Just ignore spaces and tabs */
5283  break;
5284  default:
5285  /* Unknown character */
5286  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5287  }
5288  /* Jump out on DTMF */
5289  if (res) {
5290  break;
5291  }
5292  }
5293  return res;
5294 }
5295 
5296 /*! \brief Thai syntax */
5297 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)
5298 {
5299  struct timeval when = { t, 0 };
5300  struct ast_tm tm;
5301  int res=0, offset, sndoffset;
5302  char sndfile[256], nextmsg[256];
5303 
5304  if (format == NULL)
5305  format = "a 'digits/tee' e 'digits/duan' hY I 'digits/naliga' M 'digits/natee'";
5306 
5307  ast_localtime(&when, &tm, tzone);
5308 
5309  for (offset=0 ; format[offset] != '\0' ; offset++) {
5310  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5311  switch (format[offset]) {
5312  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5313  case '\'':
5314  /* Literal name of a sound file */
5315  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5316  sndfile[sndoffset] = format[offset];
5317  }
5318  sndfile[sndoffset] = '\0';
5319  res = wait_file(chan, ints, sndfile, lang);
5320  break;
5321  case 'A':
5322  case 'a':
5323  /* Sunday - Saturday */
5324  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5325  res = wait_file(chan, ints, nextmsg, lang);
5326  break;
5327  case 'B':
5328  case 'b':
5329  case 'h':
5330  /* January - December */
5331  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5332  res = wait_file(chan, ints, nextmsg, lang);
5333  break;
5334  case 'm':
5335  /* Month enumerated */
5336  res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
5337  break;
5338  case 'd':
5339  case 'e':
5340  /* First - Thirtyfirst */
5341  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5342  break;
5343  case 'Y':
5344  /* Year */
5345  res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
5346  break;
5347  case 'I':
5348  case 'l':
5349  /* 12-Hour */
5350  if (tm.tm_hour == 0)
5351  ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5352  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5353  res = wait_file(chan, ints, nextmsg, lang);
5354  break;
5355  case 'H':
5356  case 'k':
5357  /* 24-Hour */
5358  if (tm.tm_hour == 0)
5359  ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
5360  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5361  res = wait_file(chan, ints, nextmsg, lang);
5362  break;
5363  case 'M':
5364  case 'N':
5365  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5366  break;
5367  case 'P':
5368  case 'p':
5369  break;
5370  case 'Q':
5371  /* Shorthand for "Today", "Yesterday", or ABdY */
5372  /* XXX As emphasized elsewhere, this should the native way in your
5373  * language to say the date, with changes in what you say, depending
5374  * upon how recent the date is. XXX */
5375  {
5376  struct timeval now = ast_tvnow();
5377  struct ast_tm tmnow;
5378  time_t beg_today;
5379 
5380  ast_localtime(&now, &tmnow, tzone);
5381  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5382  /* In any case, it saves not having to do ast_mktime() */
5383  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5384  if (beg_today < t) {
5385  /* Today */
5386  res = wait_file(chan, ints, "digits/today", lang);
5387  } else if (beg_today - 86400 < t) {
5388  /* Yesterday */
5389  res = wait_file(chan, ints, "digits/yesterday", lang);
5390  } else if (beg_today - 86400 * 6 < t) {
5391  /* Within the last week */
5392  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5393  } else if (beg_today - 2628000 < t) {
5394  /* Less than a month ago - "Sunday, October third" */
5395  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5396  } else if (beg_today - 15768000 < t) {
5397  /* Less than 6 months ago - "August seventh" */
5398  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5399  } else {
5400  /* More than 6 months ago - "April nineteenth two thousand three" */
5401  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5402  }
5403  }
5404  break;
5405  case 'q':
5406  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5407  /* XXX As emphasized elsewhere, this should the native way in your
5408  * language to say the date, with changes in what you say, depending
5409  * upon how recent the date is. XXX */
5410  {
5411  struct timeval now = ast_tvnow();
5412  struct ast_tm tmnow;
5413  time_t beg_today;
5414 
5415  ast_localtime(&now, &tmnow, tzone);
5416  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5417  /* In any case, it saves not having to do ast_mktime() */
5418  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5419  if (beg_today < t) {
5420  /* Today */
5421  } else if ((beg_today - 86400) < t) {
5422  /* Yesterday */
5423  res = wait_file(chan, ints, "digits/yesterday", lang);
5424  } else if (beg_today - 86400 * 6 < t) {
5425  /* Within the last week */
5426  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
5427  } else if (beg_today - 2628000 < t) {
5428  /* Less than a month ago - "Sunday, October third" */
5429  res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
5430  } else if (beg_today - 15768000 < t) {
5431  /* Less than 6 months ago - "August seventh" */
5432  res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
5433  } else {
5434  /* More than 6 months ago - "April nineteenth two thousand three" */
5435  res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
5436  }
5437  }
5438  break;
5439  case 'R':
5440  res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
5441  break;
5442  case 'S':
5443  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
5444  break;
5445  case 'T':
5446  res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
5447  break;
5448  case ' ':
5449  case ' ':
5450  /* Just ignore spaces and tabs */
5451  break;
5452  default:
5453  /* Unknown character */
5454  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5455  }
5456  /* Jump out on DTMF */
5457  if (res) {
5458  break;
5459  }
5460  }
5461  return res;
5462 }
5463 
5464 /*! \brief ast_say_date_with_format_he Say formatted date in Hebrew
5465  *
5466  * \ref ast_say_date_with_format_en for the details of the options
5467  *
5468  * Changes from the English version:
5469  *
5470  * - don't replicate in here the logic of ast_say_number_full_he
5471  *
5472  * - year is always 4-digit (because it's simpler)
5473  *
5474  * - added c, x, and X. Mainly for my tests
5475  *
5476  * - The standard "long" format used in Hebrew is AdBY, rather than ABdY
5477  *
5478  * \todo
5479  * - A "ha" is missing in the standard date format, before the 'd'.
5480  * - The numbers of 3000--19000 are not handled well
5481  */
5482 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)
5483 {
5484 #define IL_DATE_STR "AdBY"
5485 #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 */
5486 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
5487  /* TODO: This whole function is cut&paste from
5488  * ast_say_date_with_format_en . Is that considered acceptable?
5489  **/
5490  struct timeval when = { t, 0 };
5491  struct ast_tm tm;
5492  int res = 0, offset, sndoffset;
5493  char sndfile[256], nextmsg[256];
5494 
5495  if (!format) {
5496  format = IL_DATE_STR_FULL;
5497  }
5498 
5499  ast_localtime(&when, &tm, tzone);
5500 
5501  for (offset = 0; format[offset] != '\0'; offset++) {
5502  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5503  switch (format[offset]) {
5504  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5505  case '\'':
5506  /* Literal name of a sound file */
5507  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5508  sndfile[sndoffset] = format[offset];
5509  }
5510  sndfile[sndoffset] = '\0';
5511  res = wait_file(chan, ints, sndfile, lang);
5512  break;
5513  case 'A':
5514  case 'a':
5515  /* Sunday - Saturday */
5516  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5517  res = wait_file(chan, ints, nextmsg, lang);
5518  break;
5519  case 'B':
5520  case 'b':
5521  case 'h':
5522  /* January - December */
5523  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5524  res = wait_file(chan, ints, nextmsg, lang);
5525  break;
5526  case 'd':
5527  case 'e': /* Day of the month */
5528  /* I'm not sure exactly what the parameters
5529  * audiofd and ctrlfd to
5530  * ast_say_number_full_he mean, but it seems
5531  * safe to pass -1 there.
5532  *
5533  * At least in one of the paths :-(
5534  */
5535  res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
5536  break;
5537  case 'Y': /* Year */
5538  res = ast_say_number_full_he(chan, tm.tm_year + 1900, ints, lang, "f", -1, -1);
5539  break;
5540  case 'I':
5541  case 'l': /* 12-Hour -> we do not support 12 hour based languages in Hebrew */
5542  case 'H':
5543  case 'k': /* 24-Hour */
5544  res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
5545  break;
5546  case 'M': /* Minute */
5547  if (tm.tm_min >= 0 && tm.tm_min <= 9) /* say a leading zero if needed */
5548  res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
5549  res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
5550  break;
5551  case 'P':
5552  case 'p':
5553  /* AM/PM - There is no AM/PM in Hebrew... */
5554  break;
5555  case 'Q':
5556  /* Shorthand for "Today", "Yesterday", or "date" */
5557  case 'q':
5558  /* Shorthand for "" (today), "Yesterday", A
5559  * (weekday), or "date" */
5560  /* XXX As emphasized elsewhere, this should the native way in your
5561  * language to say the date, with changes in what you say, depending
5562  * upon how recent the date is. XXX */
5563  {
5564  struct timeval now = ast_tvnow();
5565  struct ast_tm tmnow;
5566  time_t beg_today;
5567  char todo = format[offset]; /* The letter to format*/
5568 
5569  ast_localtime(&now, &tmnow, tzone);
5570  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5571  /* In any case, it saves not having to do ast_mktime() */
5572  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5573  if (beg_today < t) {
5574  /* Today */
5575  if (todo == 'Q') {
5576  res = wait_file(chan, ints, "digits/today", lang);
5577  }
5578  } else if (beg_today - 86400 < t) {
5579  /* Yesterday */
5580  res = wait_file(chan, ints, "digits/yesterday", lang);
5581  } else if ((todo != 'Q') && (beg_today - 86400 * 6 < t)) {
5582  /* Within the last week */
5583  res = ast_say_date_with_format_he(chan, t, ints, lang, "A", tzone);
5584  } else {
5585  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5586  }
5587  }
5588  break;
5589  case 'R':
5590  res = ast_say_date_with_format_he(chan, t, ints, lang, "HM", tzone);
5591  break;
5592  case 'S': /* Seconds */
5593  res = ast_say_number_full_he(chan, tm.tm_sec,
5594  ints, lang, "f", -1, -1
5595  );
5596  break;
5597  case 'T':
5598  res = ast_say_date_with_format_he(chan, t, ints, lang, "HMS", tzone);
5599  break;
5600  /* c, x, and X seem useful for testing. Not sure
5601  * if they're good for the general public */
5602  case 'c':
5603  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR_FULL, tzone);
5604  break;
5605  case 'x':
5606  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
5607  break;
5608  case 'X': /* Currently not locale-dependent...*/
5609  res = ast_say_date_with_format_he(chan, t, ints, lang, IL_TIME_STR, tzone);
5610  break;
5611  case ' ':
5612  case ' ':
5613  /* Just ignore spaces and tabs */
5614  break;
5615  default:
5616  /* Unknown character */
5617  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5618  }
5619  /* Jump out on DTMF */
5620  if (res) {
5621  break;
5622  }
5623  }
5624  return res;
5625 }
5626 
5627 
5628 /*! \brief Spanish syntax */
5629 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)
5630 {
5631  struct timeval when = { t, 0 };
5632  struct ast_tm tm;
5633  int res=0, offset, sndoffset;
5634  char sndfile[256], nextmsg[256];
5635 
5636  if (format == NULL)
5637  format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
5638 
5639  ast_localtime(&when, &tm, tzone);
5640 
5641  for (offset=0 ; format[offset] != '\0' ; offset++) {
5642  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5643  switch (format[offset]) {
5644  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5645  case '\'':
5646  /* Literal name of a sound file */
5647  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5648  sndfile[sndoffset] = format[offset];
5649  }
5650  sndfile[sndoffset] = '\0';
5651  snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
5652  res = wait_file(chan, ints, nextmsg, lang);
5653  break;
5654  case 'A':
5655  case 'a':
5656  /* Sunday - Saturday */
5657  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5658  res = wait_file(chan, ints, nextmsg, lang);
5659  break;
5660  case 'B':
5661  case 'b':
5662  case 'h':
5663  /* January - December */
5664  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5665  res = wait_file(chan, ints, nextmsg, lang);
5666  break;
5667  case 'm':
5668  /* First - Twelfth */
5669  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5670  res = wait_file(chan, ints, nextmsg, lang);
5671  break;
5672  case 'd':
5673  case 'e':
5674  /* First - Thirtyfirst */
5675  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
5676  break;
5677  case 'Y':
5678  /* Year */
5679  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
5680  break;
5681  case 'I':
5682  case 'l':
5683  /* 12-Hour */
5684  if (tm.tm_hour == 0)
5685  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5686  else if (tm.tm_hour == 1 || tm.tm_hour == 13)
5687  snprintf(nextmsg,sizeof(nextmsg), "digits/1F");
5688  else if (tm.tm_hour > 12)
5689  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5690  else
5691  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5692  res = wait_file(chan, ints, nextmsg, lang);
5693  break;
5694  case 'H':
5695  case 'k':
5696  /* 24-Hour */
5697  res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
5698  break;
5699  case 'M':
5700  /* Minute */
5701  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
5702  break;
5703  case 'P':
5704  case 'p':
5705  /* AM/PM */
5706  if (tm.tm_hour > 18)
5707  res = wait_file(chan, ints, "digits/p-m", lang);
5708  else if (tm.tm_hour > 12)
5709  res = wait_file(chan, ints, "digits/afternoon", lang);
5710  else if (tm.tm_hour)
5711  res = wait_file(chan, ints, "digits/a-m", lang);
5712  break;
5713  case 'Q':
5714  /* Shorthand for "Today", "Yesterday", or ABdY */
5715  /* XXX As emphasized elsewhere, this should the native way in your
5716  * language to say the date, with changes in what you say, depending
5717  * upon how recent the date is. XXX */
5718  {
5719  struct timeval now = ast_tvnow();
5720  struct ast_tm tmnow;
5721  time_t beg_today;
5722 
5723  ast_localtime(&now, &tmnow, tzone);
5724  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5725  /* In any case, it saves not having to do ast_mktime() */
5726  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5727  if (beg_today < t) {
5728  /* Today */
5729  res = wait_file(chan, ints, "digits/today", lang);
5730  } else if (beg_today - 86400 < t) {
5731  /* Yesterday */
5732  res = wait_file(chan, ints, "digits/yesterday", lang);
5733  } else {
5734  res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5735  }
5736  }
5737  break;
5738  case 'q':
5739  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5740  /* XXX As emphasized elsewhere, this should the native way in your
5741  * language to say the date, with changes in what you say, depending
5742  * upon how recent the date is. XXX */
5743  {
5744  struct timeval now = ast_tvnow();
5745  struct ast_tm tmnow;
5746  time_t beg_today;
5747 
5748  ast_localtime(&now, &tmnow, tzone);
5749  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5750  /* In any case, it saves not having to do ast_mktime() */
5751  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5752  if (beg_today < t) {
5753  /* Today */
5754  res = wait_file(chan, ints, "digits/today", lang);
5755  } else if ((beg_today - 86400) < t) {
5756  /* Yesterday */
5757  res = wait_file(chan, ints, "digits/yesterday", lang);
5758  } else if (beg_today - 86400 * 6 < t) {
5759  /* Within the last week */
5760  res = ast_say_date_with_format_es(chan, t, ints, lang, "A", tzone);
5761  } else {
5762  res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
5763  }
5764  }
5765  break;
5766  case 'R':
5767  res = ast_say_date_with_format_es(chan, t, ints, lang, "H 'digits/y' M", tzone);
5768  break;
5769  case 'S':
5770  /* Seconds */
5771  if (tm.tm_sec == 0) {
5772  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5773  res = wait_file(chan, ints, nextmsg, lang);
5774  } else if (tm.tm_sec < 10) {
5775  res = wait_file(chan, ints, "digits/oh", lang);
5776  if (!res) {
5777  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5778  res = wait_file(chan, ints, nextmsg, lang);
5779  }
5780  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
5781  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
5782  res = wait_file(chan, ints, nextmsg, lang);
5783  } else {
5784  int ten, one;
5785  ten = (tm.tm_sec / 10) * 10;
5786  one = (tm.tm_sec % 10);
5787  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
5788  res = wait_file(chan, ints, nextmsg, lang);
5789  if (!res) {
5790  /* Fifty, not fifty-zero */
5791  if (one != 0) {
5792  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
5793  res = wait_file(chan, ints, nextmsg, lang);
5794  }
5795  }
5796  }
5797  break;
5798  case 'T':
5799  res = ast_say_date_with_format_es(chan, t, ints, lang, "HMS", tzone);
5800  break;
5801  case ' ':
5802  case ' ':
5803  /* Just ignore spaces and tabs */
5804  break;
5805  default:
5806  /* Unknown character */
5807  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
5808  }
5809  /* Jump out on DTMF */
5810  if (res) {
5811  break;
5812  }
5813  }
5814  return res;
5815 }
5816 
5817 /*! \brief French syntax
5818 oclock = heure
5819 */
5820 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)
5821 {
5822  struct timeval when = { t, 0 };
5823  struct ast_tm tm;
5824  int res=0, offset, sndoffset;
5825  char sndfile[256], nextmsg[256];
5826 
5827  if (format == NULL)
5828  format = "AdBY 'digits/at' IMp";
5829 
5830  ast_localtime(&when, &tm, tzone);
5831 
5832  for (offset=0 ; format[offset] != '\0' ; offset++) {
5833  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
5834  switch (format[offset]) {
5835  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5836  case '\'':
5837  /* Literal name of a sound file */
5838  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
5839  sndfile[sndoffset] = format[offset];
5840  }
5841  sndfile[sndoffset] = '\0';
5842  res = wait_file(chan, ints, sndfile, lang);
5843  break;
5844  case 'A':
5845  case 'a':
5846  /* Sunday - Saturday */
5847  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
5848  res = wait_file(chan, ints, nextmsg, lang);
5849  break;
5850  case 'B':
5851  case 'b':
5852  case 'h':
5853  /* January - December */
5854  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
5855  res = wait_file(chan, ints, nextmsg, lang);
5856  break;
5857  case 'm':
5858  /* First - Twelfth */
5859  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
5860  res = wait_file(chan, ints, nextmsg, lang);
5861  break;
5862  case 'd':
5863  case 'e':
5864  /* First */
5865  if (tm.tm_mday == 1) {
5866  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
5867  res = wait_file(chan, ints, nextmsg, lang);
5868  } else {
5869  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
5870  }
5871  break;
5872  case 'Y':
5873  /* Year */
5874  if (tm.tm_year > 99) {
5875  res = wait_file(chan, ints, "digits/2", lang);
5876  if (!res) {
5877  res = wait_file(chan, ints, "digits/thousand", lang);
5878  }
5879  if (tm.tm_year > 100) {
5880  if (!res) {
5881  res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
5882  }
5883  }
5884  } else {
5885  if (tm.tm_year < 1) {
5886  /* I'm not going to handle 1900 and prior */
5887  /* We'll just be silent on the year, instead of bombing out. */
5888  } else {
5889  res = wait_file(chan, ints, "digits/thousand", lang);
5890  if (!res) {
5891  wait_file(chan, ints, "digits/9", lang);
5892  wait_file(chan, ints, "digits/hundred", lang);
5893  res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
5894  }
5895  }
5896  }
5897  break;
5898  case 'I':
5899  case 'l':
5900  /* 12-Hour */
5901  if (tm.tm_hour == 0)
5902  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
5903  else if (tm.tm_hour > 12)
5904  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
5905  else
5906  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
5907  res = wait_file(chan, ints, nextmsg, lang);
5908  if (!res)
5909  res = wait_file(chan, ints, "digits/oclock", lang);
5910  break;
5911  case 'H':
5912  case 'k':
5913  /* 24-Hour */
5914  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
5915  if (!res)
5916  res = wait_file(chan, ints, "digits/oclock", lang);
5917  break;
5918  case 'M':
5919  /* Minute */
5920  if (tm.tm_min == 0) {
5921  break;
5922  }
5923  res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
5924  break;
5925  case 'P':
5926  case 'p':
5927  /* AM/PM */
5928  if (tm.tm_hour > 11)
5929  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
5930  else
5931  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
5932  res = wait_file(chan, ints, nextmsg, lang);
5933  break;
5934  case 'Q':
5935  /* Shorthand for "Today", "Yesterday", or AdBY */
5936  /* XXX As emphasized elsewhere, this should the native way in your
5937  * language to say the date, with changes in what you say, depending
5938  * upon how recent the date is. XXX */
5939  {
5940  struct timeval now = ast_tvnow();
5941  struct ast_tm tmnow;
5942  time_t beg_today;
5943 
5944  ast_localtime(&now, &tmnow, tzone);
5945  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5946  /* In any case, it saves not having to do ast_mktime() */
5947  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5948  if (beg_today < t) {
5949  /* Today */
5950  res = wait_file(chan, ints, "digits/today", lang);
5951  } else if (beg_today - 86400 < t) {
5952  /* Yesterday */
5953  res = wait_file(chan, ints, "digits/yesterday", lang);
5954  } else {
5955  res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
5956  }
5957  }
5958  break;
5959  case 'q':
5960  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
5961  /* XXX As emphasized elsewhere, this should the native way in your
5962  * language to say the date, with changes in what you say, depending
5963  * upon how recent the date is. XXX */
5964  {
5965  struct timeval now = ast_tvnow();
5966  struct ast_tm tmnow;
5967  time_t beg_today;
5968 
5969  ast_localtime(&now, &tmnow, tzone);
5970  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5971  /* In any case, it saves not having to do ast_mktime() */
5972  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
5973  if (beg_today < t) {
5974  /* Today */
5975  } else if ((beg_today - 86400) < t) {
5976  /* Yesterday */
5977  res = wait_file(chan, ints, "digits/yesterday", lang);
5978  } else if (beg_today - 86400 * 6 < t) {
5979  /* Within the last week */
5980  res = ast_say_date_with_format_fr(chan, t, ints, lang, "A", tzone);
5981  } else {
5982  res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
5983  }
5984  }
5985  break;
5986  case 'R':
5987  res = ast_say_date_with_format_fr(chan, t, ints, lang, "HM", tzone);
5988  break;
5989  case 'S':
5990  /* Seconds */
5991  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
5992  if (!res) {
5993  res = wait_file(chan, ints, "second", lang);
5994  }
5995  break;
5996  case 'T':
5997  res = ast_say_date_with_format_fr(chan, t, ints, lang, "HMS", tzone);
5998  break;
5999  case ' ':
6000  case ' ':
6001  /* Just ignore spaces and tabs */
6002  break;
6003  default:
6004  /* Unknown character */
6005  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6006  }
6007  /* Jump out on DTMF */
6008  if (res) {
6009  break;
6010  }
6011  }
6012  return res;
6013 }
6014 
6015 /*! \brief Italian syntax */
6016 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)
6017 {
6018  struct timeval when = { t, 0 };
6019  struct ast_tm tm;
6020  int res=0, offset, sndoffset;
6021  char sndfile[256], nextmsg[256];
6022 
6023  if (format == NULL)
6024  format = "AdB 'digits/at' IMp";
6025 
6026  ast_localtime(&when, &tm, tzone);
6027 
6028  for (offset=0 ; format[offset] != '\0' ; offset++) {
6029  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6030  switch (format[offset]) {
6031  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6032  case '\'':
6033  /* Literal name of a sound file */
6034  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6035  sndfile[sndoffset] = format[offset];
6036  }
6037  sndfile[sndoffset] = '\0';
6038  res = wait_file(chan, ints, sndfile, lang);
6039  break;
6040  case 'A':
6041  case 'a':
6042  /* Sunday - Saturday */
6043  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6044  res = wait_file(chan, ints, nextmsg, lang);
6045  break;
6046  case 'B':
6047  case 'b':
6048  case 'h':
6049  /* January - December */
6050  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6051  res = wait_file(chan, ints, nextmsg, lang);
6052  break;
6053  case 'm':
6054  /* First - Twelfth */
6055  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6056  res = wait_file(chan, ints, nextmsg, lang);
6057  break;
6058  case 'd':
6059  case 'e':
6060  /* First day of the month is spelled as ordinal */
6061  if (tm.tm_mday == 1) {
6062  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
6063  res = wait_file(chan, ints, nextmsg, lang);
6064  } else {
6065  if (!res) {
6066  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6067  }
6068  }
6069  break;
6070  case 'Y':
6071  /* Year */
6072  if (tm.tm_year > 99) {
6073  res = wait_file(chan, ints, "digits/ore-2000", lang);
6074  if (tm.tm_year > 100) {
6075  if (!res) {
6076  /* This works until the end of 2021 */
6077  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
6078  res = wait_file(chan, ints, nextmsg, lang);
6079  }
6080  }
6081  } else {
6082  if (tm.tm_year < 1) {
6083  /* I'm not going to handle 1900 and prior */
6084  /* We'll just be silent on the year, instead of bombing out. */
6085  } else {
6086  res = wait_file(chan, ints, "digits/ore-1900", lang);
6087  if ((!res) && (tm.tm_year != 0)) {
6088  if (tm.tm_year <= 21) {
6089  /* 1910 - 1921 */
6090  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6091  res = wait_file(chan, ints, nextmsg, lang);
6092  } else {
6093  /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
6094  int ten, one;
6095  ten = tm.tm_year / 10;
6096  one = tm.tm_year % 10;
6097  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
6098  res = wait_file(chan, ints, nextmsg, lang);
6099  if (!res) {
6100  if (one != 0) {
6101  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6102  res = wait_file(chan, ints, nextmsg, lang);
6103  }
6104  }
6105  }
6106  }
6107  }
6108  }
6109  break;
6110  case 'I':
6111  case 'l':
6112  /* 12-Hour */
6113  if (tm.tm_hour == 0) {
6114  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6115  } else if (tm.tm_hour > 12) {
6116  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6117  } else {
6118  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6119  }
6120  res = wait_file(chan, ints, nextmsg, lang);
6121  break;
6122  case 'H':
6123  case 'k':
6124  /* 24-Hour */
6125  if (tm.tm_hour == 0) {
6126  res = wait_file(chan, ints, "digits/ore-mezzanotte", lang);
6127  } else if (tm.tm_hour == 1) {
6128  res = wait_file(chan, ints, "digits/ore-una", lang);
6129  } else {
6130  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
6131  }
6132  break;
6133  case 'M':
6134  /* Minute */
6135  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6136  break;
6137  case 'P':
6138  case 'p':
6139  /* AM/PM */
6140  if (tm.tm_hour > 11) {
6141  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6142  } else {
6143  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6144  }
6145  res = wait_file(chan, ints, nextmsg, lang);
6146  break;
6147  case 'Q':
6148  /* Shorthand for "Today", "Yesterday", or ABdY */
6149  /* XXX As emphasized elsewhere, this should the native way in your
6150  * language to say the date, with changes in what you say, depending
6151  * upon how recent the date is. XXX */
6152  {
6153  struct timeval now = ast_tvnow();
6154  struct ast_tm tmnow;
6155  time_t beg_today;
6156 
6157  ast_localtime(&now, &tmnow, tzone);
6158  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6159  /* In any case, it saves not having to do ast_mktime() */
6160  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6161  if (beg_today < t) {
6162  /* Today */
6163  res = wait_file(chan, ints, "digits/today", lang);
6164  } else if (beg_today - 86400 < t) {
6165  /* Yesterday */
6166  res = wait_file(chan, ints, "digits/yesterday", lang);
6167  } else {
6168  res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
6169  }
6170  }
6171  break;
6172  case 'q':
6173  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6174  {
6175  struct timeval now = ast_tvnow();
6176  struct ast_tm tmnow;
6177  time_t beg_today;
6178 
6179  ast_localtime(&now, &tmnow, tzone);
6180  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6181  /* In any case, it saves not having to do ast_mktime() */
6182  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6183  if (beg_today < t) {
6184  /* Today */
6185  } else if ((beg_today - 86400) < t) {
6186  /* Yesterday */
6187  res = wait_file(chan, ints, "digits/yesterday", lang);
6188  } else if (beg_today - 86400 * 6 < t) {
6189  /* Within the last week */
6190  res = ast_say_date_with_format_it(chan, t, ints, lang, "A", tzone);
6191  } else {
6192  res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
6193  }
6194  }
6195  break;
6196  case 'R':
6197  res = ast_say_date_with_format_it(chan, t, ints, lang, "HM", tzone);
6198  break;
6199  case 'S':
6200  /* Seconds */
6201  if (tm.tm_sec == 0) {
6202  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6203  res = wait_file(chan, ints, nextmsg, lang);
6204  } else if (tm.tm_sec < 10) {
6205  res = wait_file(chan, ints, "digits/oh", lang);
6206  if (!res) {
6207  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6208  res = wait_file(chan, ints, nextmsg, lang);
6209  }
6210  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6211  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6212  res = wait_file(chan, ints, nextmsg, lang);
6213  } else {
6214  int ten, one;
6215  ten = (tm.tm_sec / 10) * 10;
6216  one = (tm.tm_sec % 10);
6217  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6218  res = wait_file(chan, ints, nextmsg, lang);
6219  if (!res) {
6220  /* Fifty, not fifty-zero */
6221  if (one != 0) {
6222  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6223  res = wait_file(chan, ints, nextmsg, lang);
6224  }
6225  }
6226  }
6227  break;
6228  case 'T':
6229  res = ast_say_date_with_format_it(chan, t, ints, lang, "HMS", tzone);
6230  break;
6231  case ' ':
6232  case ' ':
6233  /* Just ignore spaces and tabs */
6234  break;
6235  default:
6236  /* Unknown character */
6237  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6238  }
6239  /* Jump out on DTMF */
6240  if (res) {
6241  break;
6242  }
6243  }
6244  return res;
6245 }
6246 
6247 /*! \brief Dutch syntax */
6248 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)
6249 {
6250  struct timeval when = { t, 0 };
6251  struct ast_tm tm;
6252  int res=0, offset, sndoffset;
6253  char sndfile[256], nextmsg[256];
6254 
6255  if (format == NULL)
6256  format = "AdBY 'digits/at' IMp";
6257 
6258  ast_localtime(&when, &tm, tzone);
6259 
6260  for (offset=0 ; format[offset] != '\0' ; offset++) {
6261  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6262  switch (format[offset]) {
6263  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6264  case '\'':
6265  /* Literal name of a sound file */
6266  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6267  sndfile[sndoffset] = format[offset];
6268  }
6269  sndfile[sndoffset] = '\0';
6270  res = wait_file(chan, ints, sndfile, lang);
6271  break;
6272  case 'A':
6273  case 'a':
6274  /* Sunday - Saturday */
6275  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6276  res = wait_file(chan, ints, nextmsg, lang);
6277  break;
6278  case 'B':
6279  case 'b':
6280  case 'h':
6281  /* January - December */
6282  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6283  res = wait_file(chan, ints, nextmsg, lang);
6284  break;
6285  case 'm':
6286  /* First - Twelfth */
6287  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6288  res = wait_file(chan, ints, nextmsg, lang);
6289  break;
6290  case 'd':
6291  case 'e':
6292  /* First - Thirtyfirst */
6293  res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
6294  break;
6295  case 'Y':
6296  /* Year */
6297  if (tm.tm_year > 99) {
6298  res = wait_file(chan, ints, "digits/2", lang);
6299  if (!res) {
6300  res = wait_file(chan, ints, "digits/thousand", lang);
6301  }
6302  if (tm.tm_year > 100) {
6303  if (!res) {
6304  res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char *) NULL);
6305  }
6306  }
6307  } else {
6308  if (tm.tm_year < 1) {
6309  /* I'm not going to handle 1900 and prior */
6310  /* We'll just be silent on the year, instead of bombing out. */
6311  } else {
6312  res = wait_file(chan, ints, "digits/19", lang);
6313  if (!res) {
6314  if (tm.tm_year <= 9) {
6315  /* 1901 - 1909 */
6316  res = wait_file(chan, ints, "digits/oh", lang);
6317  if (!res) {
6318  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6319  res = wait_file(chan, ints, nextmsg, lang);
6320  }
6321  } else if (tm.tm_year <= 20) {
6322  /* 1910 - 1920 */
6323  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
6324  res = wait_file(chan, ints, nextmsg, lang);
6325  } else {
6326  /* 1921 - 1999 */
6327  int ten, one;
6328  ten = tm.tm_year / 10;
6329  one = tm.tm_year % 10;
6330  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
6331  res = wait_file(chan, ints, nextmsg, lang);
6332  if (!res) {
6333  if (one != 0) {
6334  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6335  res = wait_file(chan, ints, nextmsg, lang);
6336  }
6337  }
6338  }
6339  }
6340  }
6341  }
6342  break;
6343  case 'I':
6344  case 'l':
6345  /* 12-Hour */
6346  if (tm.tm_hour == 0)
6347  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
6348  else if (tm.tm_hour > 12)
6349  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
6350  else
6351  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
6352  res = wait_file(chan, ints, nextmsg, lang);
6353  break;
6354  case 'H':
6355  case 'k':
6356  /* 24-Hour */
6357  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
6358  if (!res) {
6359  res = wait_file(chan, ints, "digits/nl-uur", lang);
6360  }
6361  break;
6362  case 'M':
6363  /* Minute */
6364  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6365  break;
6366  case 'P':
6367  case 'p':
6368  /* AM/PM */
6369  if (tm.tm_hour > 11)
6370  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6371  else
6372  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6373  res = wait_file(chan, ints, nextmsg, lang);
6374  break;
6375  case 'Q':
6376  /* Shorthand for "Today", "Yesterday", or AdBY */
6377  /* XXX As emphasized elsewhere, this should the native way in your
6378  * language to say the date, with changes in what you say, depending
6379  * upon how recent the date is. XXX */
6380  {
6381  struct timeval now = ast_tvnow();
6382  struct ast_tm tmnow;
6383  time_t beg_today;
6384 
6385  ast_localtime(&now, &tmnow, tzone);
6386  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6387  /* In any case, it saves not having to do ast_mktime() */
6388  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6389  if (beg_today < t) {
6390  /* Today */
6391  res = wait_file(chan, ints, "digits/today", lang);
6392  } else if (beg_today - 86400 < t) {
6393  /* Yesterday */
6394  res = wait_file(chan, ints, "digits/yesterday", lang);
6395  } else {
6396  res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6397  }
6398  }
6399  break;
6400  case 'q':
6401  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6402  {
6403  struct timeval now = ast_tvnow();
6404  struct ast_tm tmnow;
6405  time_t beg_today;
6406 
6407  ast_localtime(&now, &tmnow, tzone);
6408  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6409  /* In any case, it saves not having to do ast_mktime() */
6410  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6411  if (beg_today < t) {
6412  /* Today */
6413  } else if ((beg_today - 86400) < t) {
6414  /* Yesterday */
6415  res = wait_file(chan, ints, "digits/yesterday", lang);
6416  } else if (beg_today - 86400 * 6 < t) {
6417  /* Within the last week */
6418  res = ast_say_date_with_format_nl(chan, t, ints, lang, "A", tzone);
6419  } else {
6420  res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
6421  }
6422  }
6423  break;
6424  case 'R':
6425  res = ast_say_date_with_format_nl(chan, t, ints, lang, "HM", tzone);
6426  break;
6427  case 'S':
6428  /* Seconds */
6429  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
6430  break;
6431  case 'T':
6432  res = ast_say_date_with_format_nl(chan, t, ints, lang, "HMS", tzone);
6433  break;
6434  case ' ':
6435  case ' ':
6436  /* Just ignore spaces and tabs */
6437  break;
6438  default:
6439  /* Unknown character */
6440  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6441  }
6442  /* Jump out on DTMF */
6443  if (res) {
6444  break;
6445  }
6446  }
6447  return res;
6448 }
6449 
6450 /*! \brief Polish syntax */
6451 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)
6452 {
6453  struct timeval when = { thetime, 0 };
6454  struct ast_tm tm;
6455  int res=0, offset, sndoffset;
6456  char sndfile[256], nextmsg[256];
6457 
6458  ast_localtime(&when, &tm, tzone);
6459 
6460  for (offset = 0 ; format[offset] != '\0' ; offset++) {
6461  int remaining;
6462  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6463  switch (format[offset]) {
6464  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6465  case '\'':
6466  /* Literal name of a sound file */
6467  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6468  sndfile[sndoffset] = format[offset];
6469  }
6470  sndfile[sndoffset] = '\0';
6471  res = wait_file(chan, ints, sndfile, lang);
6472  break;
6473  case 'A':
6474  case 'a':
6475  /* Sunday - Saturday */
6476  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6477  res = wait_file(chan, ints, nextmsg, lang);
6478  break;
6479  case 'B':
6480  case 'b':
6481  case 'h':
6482  /* January - December */
6483  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6484  res = wait_file(chan, ints, nextmsg, lang);
6485  break;
6486  case 'm':
6487  /* Month enumerated */
6488  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
6489  break;
6490  case 'd':
6491  case 'e':
6492  /* First - Thirtyfirst */
6493  remaining = tm.tm_mday;
6494  if (tm.tm_mday > 30) {
6495  res = wait_file(chan, ints, "digits/h-30", lang);
6496  remaining -= 30;
6497  }
6498  if (tm.tm_mday > 20 && tm.tm_mday < 30) {
6499  res = wait_file(chan, ints, "digits/h-20", lang);
6500  remaining -= 20;
6501  }
6502  if (!res) {
6503  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remaining);
6504  res = wait_file(chan, ints, nextmsg, lang);
6505  }
6506  break;
6507  case 'Y':
6508  /* Year */
6509  if (tm.tm_year > 100) {
6510  res = wait_file(chan, ints, "digits/2", lang);
6511  if (!res)
6512  res = wait_file(chan, ints, "digits/1000.2", lang);
6513  if (tm.tm_year > 100) {
6514  if (!res)
6515  res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
6516  }
6517  } else if (tm.tm_year == 100) {
6518  res = wait_file(chan, ints, "digits/h-2000", lang);
6519  } else {
6520  if (tm.tm_year < 1) {
6521  /* I'm not going to handle 1900 and prior */
6522  /* We'll just be silent on the year, instead of bombing out. */
6523  break;
6524  } else {
6525  res = wait_file(chan, ints, "digits/1000", lang);
6526  if (!res) {
6527  wait_file(chan, ints, "digits/900", lang);
6528  res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
6529  }
6530  }
6531  }
6532  if (!res)
6533  wait_file(chan, ints, "digits/year", lang);
6534  break;
6535  case 'I':
6536  case 'l':
6537  /* 12-Hour */
6538  if (tm.tm_hour == 0)
6539  ast_copy_string(nextmsg, "digits/t-12", sizeof(nextmsg));
6540  else if (tm.tm_hour > 12)
6541  snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
6542  else
6543  snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
6544 
6545  res = wait_file(chan, ints, nextmsg, lang);
6546  break;
6547  case 'H':
6548  case 'k':
6549  /* 24-Hour */
6550  if (tm.tm_hour != 0) {
6551  snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
6552  res = wait_file(chan, ints, nextmsg, lang);
6553  } else
6554  res = wait_file(chan, ints, "digits/t-24", lang);
6555  break;
6556  case 'M':
6557  case 'N':
6558  /* Minute */
6559  if (tm.tm_min == 0) {
6560  if (format[offset] == 'M') {
6561  res = wait_file(chan, ints, "digits/oclock", lang);
6562  } else {
6563  res = wait_file(chan, ints, "digits/100", lang);
6564  }
6565  } else
6566  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
6567  break;
6568  case 'P':
6569  case 'p':
6570  /* AM/PM */
6571  if (tm.tm_hour > 11)
6572  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
6573  else
6574  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
6575  res = wait_file(chan, ints, nextmsg, lang);
6576  break;
6577  case 'Q':
6578  /* Shorthand for "Today", "Yesterday", or AdBY */
6579  {
6580  struct timeval now = ast_tvnow();
6581  struct ast_tm tmnow;
6582  time_t beg_today;
6583 
6584  ast_localtime(&now, &tmnow, tzone);
6585  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6586  /* In any case, it saves not having to do ast_mktime() */
6587  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6588  if (beg_today < thetime) {
6589  /* Today */
6590  res = wait_file(chan, ints, "digits/today", lang);
6591  } else if (beg_today - 86400 < thetime) {
6592  /* Yesterday */
6593  res = wait_file(chan, ints, "digits/yesterday", lang);
6594  } else {
6595  res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
6596  }
6597  }
6598  break;
6599  case 'q':
6600  /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
6601  {
6602  struct timeval now = ast_tvnow();
6603  struct ast_tm tmnow;
6604  time_t beg_today;
6605 
6606  ast_localtime(&now, &tmnow, tzone);
6607  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6608  /* In any case, it saves not having to do ast_mktime() */
6609  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6610  if (beg_today < thetime) {
6611  /* Today */
6612  } else if ((beg_today - 86400) < thetime) {
6613  /* Yesterday */
6614  res = wait_file(chan, ints, "digits/yesterday", lang);
6615  } else if (beg_today - 86400 * 6 < thetime) {
6616  /* Within the last week */
6617  res = ast_say_date_with_format(chan, thetime, ints, lang, "A", tzone);
6618  } else {
6619  res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
6620  }
6621  }
6622  break;
6623  case 'R':
6624  res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", tzone);
6625  break;
6626  case 'S':
6627  /* Seconds */
6628  res = wait_file(chan, ints, "digits/and", lang);
6629  if (!res) {
6630  if (tm.tm_sec == 1) {
6631  res = wait_file(chan, ints, "digits/1z", lang);
6632  if (!res)
6633  res = wait_file(chan, ints, "digits/second-a", lang);
6634  } else {
6635  res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
6636  if (!res) {
6637  int ten, one;
6638  ten = tm.tm_sec / 10;
6639  one = tm.tm_sec % 10;
6640 
6641  if (one > 1 && one < 5 && ten != 1)
6642  res = wait_file(chan, ints, "seconds", lang);
6643  else
6644  res = wait_file(chan, ints, "second", lang);
6645  }
6646  }
6647  }
6648  break;
6649  case 'T':
6650  res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", tzone);
6651  break;
6652  case ' ':
6653  case ' ':
6654  /* Just ignore spaces and tabs */
6655  break;
6656  default:
6657  /* Unknown character */
6658  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6659  }
6660  /* Jump out on DTMF */
6661  if (res)
6662  break;
6663  }
6664  return res;
6665 }
6666 
6667 /*! \brief Portuguese syntax */
6668 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)
6669 {
6670  struct timeval when = { t, 0 };
6671  struct ast_tm tm;
6672  int res=0, offset, sndoffset;
6673  char sndfile[256], nextmsg[256];
6674 
6675  if (format == NULL)
6676  format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
6677 
6678  ast_localtime(&when, &tm, tzone);
6679 
6680  for (offset=0 ; format[offset] != '\0' ; offset++) {
6681  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6682  switch (format[offset]) {
6683  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6684  case '\'':
6685  /* Literal name of a sound file */
6686  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6687  sndfile[sndoffset] = format[offset];
6688  }
6689  sndfile[sndoffset] = '\0';
6690  snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
6691  res = wait_file(chan, ints, nextmsg, lang);
6692  break;
6693  case 'A':
6694  case 'a':
6695  /* Sunday - Saturday */
6696  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6697  res = wait_file(chan, ints, nextmsg, lang);
6698  break;
6699  case 'B':
6700  case 'b':
6701  case 'h':
6702  /* January - December */
6703  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6704  res = wait_file(chan, ints, nextmsg, lang);
6705  break;
6706  case 'm':
6707  /* First - Twelfth */
6708  if (!strcasecmp(lang, "pt_BR")) {
6709  res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
6710  } else {
6711  snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
6712  res = wait_file(chan, ints, nextmsg, lang);
6713  }
6714  break;
6715  case 'd':
6716  case 'e':
6717  /* First - Thirtyfirst */
6718  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
6719  break;
6720  case 'Y':
6721  /* Year */
6722  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
6723  break;
6724  case 'I':
6725  case 'l':
6726  /* 12-Hour */
6727  if (!strcasecmp(lang, "pt_BR")) {
6728  if (tm.tm_hour == 0) {
6729  if (format[offset] == 'I')
6730  res = wait_file(chan, ints, "digits/pt-a", lang);
6731  if (!res)
6732  res = wait_file(chan, ints, "digits/pt-meianoite", lang);
6733  } else if (tm.tm_hour == 12) {
6734  if (format[offset] == 'I')
6735  res = wait_file(chan, ints, "digits/pt-ao", lang);
6736  if (!res)
6737  res = wait_file(chan, ints, "digits/pt-meiodia", lang);
6738  } else {
6739  if (format[offset] == 'I') {
6740  if ((tm.tm_hour % 12) != 1)
6741  res = wait_file(chan, ints, "digits/pt-as", lang);
6742  else
6743  res = wait_file(chan, ints, "digits/pt-a", lang);
6744  }
6745  if (!res)
6746  res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
6747  }
6748  } else {
6749  if (tm.tm_hour == 0) {
6750  if (format[offset] == 'I')
6751  res = wait_file(chan, ints, "digits/pt-ah", lang);
6752  if (!res)
6753  res = wait_file(chan, ints, "digits/pt-meianoite", lang);
6754  }
6755  else if (tm.tm_hour == 12) {
6756  if (format[offset] == 'I')
6757  res = wait_file(chan, ints, "digits/pt-ao", lang);
6758  if (!res)
6759  res = wait_file(chan, ints, "digits/pt-meiodia", lang);
6760  }
6761  else {
6762  if (format[offset] == 'I') {
6763  res = wait_file(chan, ints, "digits/pt-ah", lang);
6764  if ((tm.tm_hour % 12) != 1)
6765  if (!res)
6766  res = wait_file(chan, ints, "digits/pt-sss", lang);
6767  }
6768  if (!res)
6769  res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
6770  }
6771  }
6772  break;
6773  case 'H':
6774  case 'k':
6775  /* 24-Hour */
6776  if (!strcasecmp(lang, "pt_BR")) {
6777  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
6778  if ((!res) && (format[offset] == 'H')) {
6779  if (tm.tm_hour > 1) {
6780  res = wait_file(chan, ints, "digits/hours", lang);
6781  } else {
6782  res = wait_file(chan, ints, "digits/hour", lang);
6783  }
6784  }
6785  } else {
6786  res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
6787  if (!res) {
6788  if (tm.tm_hour != 0) {
6789  int remaining = tm.tm_hour;
6790  if (tm.tm_hour > 20) {
6791  res = wait_file(chan, ints, "digits/20", lang);
6792  remaining -= 20;
6793  }
6794  if (!res) {
6795  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
6796  res = wait_file(chan, ints, nextmsg, lang);
6797  }
6798  }
6799  }
6800  }
6801  break;
6802  case 'M':
6803  /* Minute */
6804  if (!strcasecmp(lang, "pt_BR")) {
6805  res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
6806  if (!res) {
6807  if (tm.tm_min > 1) {
6808  res = wait_file(chan, ints, "minutes", lang);
6809  } else {
6810  res = wait_file(chan, ints, "minute", lang);
6811  }
6812  }
6813  } else {
6814  if (tm.tm_min == 0) {
6815  res = wait_file(chan, ints, "digits/pt-hora", lang);
6816  if (tm.tm_hour != 1)
6817  if (!res)
6818  res = wait_file(chan, ints, "digits/pt-sss", lang);
6819  } else {
6820  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
6821  }
6822  }
6823  break;
6824  case 'P':
6825  case 'p':
6826  /* AM/PM */
6827  if (!strcasecmp(lang, "pt_BR")) {
6828  if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
6829  res = wait_file(chan, ints, "digits/pt-da", lang);
6830  if (!res) {
6831  if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
6832  res = wait_file(chan, ints, "digits/morning", lang);
6833  else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
6834  res = wait_file(chan, ints, "digits/afternoon", lang);
6835  else res = wait_file(chan, ints, "digits/night", lang);
6836  }
6837  }
6838  } else {
6839  if (tm.tm_hour > 12)
6840  res = wait_file(chan, ints, "digits/p-m", lang);
6841  else if (tm.tm_hour && tm.tm_hour < 12)
6842  res = wait_file(chan, ints, "digits/a-m", lang);
6843  }
6844  break;
6845  case 'Q':
6846  /* Shorthand for "Today", "Yesterday", or ABdY */
6847  /* XXX As emphasized elsewhere, this should the native way in your
6848  * language to say the date, with changes in what you say, depending
6849  * upon how recent the date is. XXX */
6850  {
6851  struct timeval now = ast_tvnow();
6852  struct ast_tm tmnow;
6853  time_t beg_today;
6854 
6855  ast_localtime(&now, &tmnow, tzone);
6856  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6857  /* In any case, it saves not having to do ast_mktime() */
6858  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6859  if (beg_today < t) {
6860  /* Today */
6861  res = wait_file(chan, ints, "digits/today", lang);
6862  } else if (beg_today - 86400 < t) {
6863  /* Yesterday */
6864  res = wait_file(chan, ints, "digits/yesterday", lang);
6865  } else {
6866  res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
6867  }
6868  }
6869  break;
6870  case 'q':
6871  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6872  /* XXX As emphasized elsewhere, this should the native way in your
6873  * language to say the date, with changes in what you say, depending
6874  * upon how recent the date is. XXX */
6875  {
6876  struct timeval now = ast_tvnow();
6877  struct ast_tm tmnow;
6878  time_t beg_today;
6879 
6880  ast_localtime(&now, &tmnow, tzone);
6881  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6882  /* In any case, it saves not having to do ast_mktime() */
6883  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
6884  if (beg_today < t) {
6885  /* Today */
6886  } else if ((beg_today - 86400) < t) {
6887  /* Yesterday */
6888  res = wait_file(chan, ints, "digits/yesterday", lang);
6889  } else if (beg_today - 86400 * 6 < t) {
6890  /* Within the last week */
6891  res = ast_say_date_with_format_pt(chan, t, ints, lang, "A", tzone);
6892  } else {
6893  res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
6894  }
6895  }
6896  break;
6897  case 'R':
6898  res = ast_say_date_with_format_pt(chan, t, ints, lang, "H 'digits/pt-e' M", tzone);
6899  break;
6900  case 'S':
6901  /* Seconds */
6902  if (!strcasecmp(lang, "pt_BR")) {
6903  res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
6904  if (!res) {
6905  if (tm.tm_sec > 1) {
6906  res = wait_file(chan, ints, "seconds", lang);
6907  } else {
6908  res = wait_file(chan, ints, "second", lang);
6909  }
6910  }
6911  } else {
6912  if (tm.tm_sec == 0) {
6913  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6914  res = wait_file(chan, ints, nextmsg, lang);
6915  } else if (tm.tm_sec < 10) {
6916  res = wait_file(chan, ints, "digits/oh", lang);
6917  if (!res) {
6918  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6919  res = wait_file(chan, ints, nextmsg, lang);
6920  }
6921  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
6922  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
6923  res = wait_file(chan, ints, nextmsg, lang);
6924  } else {
6925  int ten, one;
6926  ten = (tm.tm_sec / 10) * 10;
6927  one = (tm.tm_sec % 10);
6928  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
6929  res = wait_file(chan, ints, nextmsg, lang);
6930  if (!res) {
6931  /* Fifty, not fifty-zero */
6932  if (one != 0) {
6933  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
6934  res = wait_file(chan, ints, nextmsg, lang);
6935  }
6936  }
6937  }
6938  }
6939  break;
6940  case 'T':
6941  res = ast_say_date_with_format_pt(chan, t, ints, lang, "HMS", tzone);
6942  break;
6943  case ' ':
6944  case ' ':
6945  /* Just ignore spaces and tabs */
6946  break;
6947  default:
6948  /* Unknown character */
6949  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
6950  }
6951  /* Jump out on DTMF */
6952  if (res) {
6953  break;
6954  }
6955  }
6956  return res;
6957 }
6958 
6959 /*! \brief Taiwanese / Chinese syntax */
6960 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)
6961 {
6962  struct timeval when = { t, 0 };
6963  struct ast_tm tm;
6964  int res=0, offset, sndoffset;
6965  char sndfile[256], nextmsg[256];
6966 
6967  if (format == NULL)
6968  format = "YBdAkM";
6969 
6970  ast_localtime(&when, &tm, tzone);
6971 
6972  for (offset=0 ; format[offset] != '\0' ; offset++) {
6973  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
6974  switch (format[offset]) {
6975  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6976  case '\'':
6977  /* Literal name of a sound file */
6978  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
6979  sndfile[sndoffset] = format[offset];
6980  }
6981  sndfile[sndoffset] = '\0';
6982  res = wait_file(chan, ints, sndfile, lang);
6983  break;
6984  case 'A':
6985  case 'a':
6986  /* Sunday - Saturday */
6987  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
6988  res = wait_file(chan, ints, nextmsg, lang);
6989  break;
6990  case 'B':
6991  case 'b':
6992  case 'h':
6993  case 'm':
6994  /* January - December */
6995  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
6996  res = wait_file(chan, ints, nextmsg, lang);
6997  break;
6998  case 'd':
6999  case 'e':
7000  /* First - Thirtyfirst */
7001  if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
7002  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday);
7003  res = wait_file(chan, ints, nextmsg, lang);
7004  } else {
7005  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
7006  res = wait_file(chan, ints, nextmsg, lang);
7007  if (!res) {
7008  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
7009  res = wait_file(chan, ints, nextmsg, lang);
7010  }
7011  }
7012  if (!res) res = wait_file(chan, ints, "digits/day", lang);
7013  break;
7014  case 'Y':
7015  /* Year */
7016  if (tm.tm_year > 99) {
7017  res = wait_file(chan, ints, "digits/2", lang);
7018  if (!res) {
7019  res = wait_file(chan, ints, "digits/thousand", lang);
7020  }
7021  if (tm.tm_year > 100) {
7022  if (!res) {
7023  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
7024  res = wait_file(chan, ints, nextmsg, lang);
7025  if (!res) {
7026  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
7027  res = wait_file(chan, ints, nextmsg, lang);
7028  }
7029  }
7030  }
7031  if (!res) {
7032  res = wait_file(chan, ints, "digits/year", lang);
7033  }
7034  } else {
7035  if (tm.tm_year < 1) {
7036  /* I'm not going to handle 1900 and prior */
7037  /* We'll just be silent on the year, instead of bombing out. */
7038  } else {
7039  res = wait_file(chan, ints, "digits/1", lang);
7040  if (!res) {
7041  res = wait_file(chan, ints, "digits/9", lang);
7042  }
7043  if (!res) {
7044  if (tm.tm_year <= 9) {
7045  /* 1901 - 1909 */
7046  res = wait_file(chan, ints, "digits/0", lang);
7047  if (!res) {
7048  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
7049  res = wait_file(chan, ints, nextmsg, lang);
7050  }
7051  } else {
7052  /* 1910 - 1999 */
7053  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
7054  res = wait_file(chan, ints, nextmsg, lang);
7055  if (!res) {
7056  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
7057  res = wait_file(chan, ints, nextmsg, lang);
7058  }
7059  }
7060  }
7061  }
7062  if (!res) {
7063  res = wait_file(chan, ints, "digits/year", lang);
7064  }
7065  }
7066  break;
7067  case 'I':
7068  case 'l':
7069  /* 12-Hour */
7070  if (tm.tm_hour == 0)
7071  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
7072  else if (tm.tm_hour > 12)
7073  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
7074  else
7075  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
7076  res = wait_file(chan, ints, nextmsg, lang);
7077  if (!res) {
7078  res = wait_file(chan, ints, "digits/oclock", lang);
7079  }
7080  break;
7081  case 'H':
7082  if (tm.tm_hour < 10) {
7083  res = wait_file(chan, ints, "digits/0", lang);
7084  }
7085  /* XXX Static analysis warns of no break here. No idea if this is
7086  * correct or not
7087  */
7088  case 'k':
7089  /* 24-Hour */
7090  if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
7091  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
7092  res = wait_file(chan, ints, nextmsg, lang);
7093  } else {
7094  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
7095  res = wait_file(chan, ints, nextmsg, lang);
7096  if (!res) {
7097  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
7098  res = wait_file(chan, ints, nextmsg, lang);
7099  }
7100  }
7101  if (!res) {
7102  res = wait_file(chan, ints, "digits/oclock", lang);
7103  }
7104  break;
7105  case 'M':
7106  /* Minute */
7107  if (!(tm.tm_min % 10) || tm.tm_min < 10) {
7108  if (tm.tm_min < 10) {
7109  res = wait_file(chan, ints, "digits/0", lang);
7110  }
7111  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
7112  res = wait_file(chan, ints, nextmsg, lang);
7113  } else {
7114  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
7115  res = wait_file(chan, ints, nextmsg, lang);
7116  if (!res) {
7117  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
7118  res = wait_file(chan, ints, nextmsg, lang);
7119  }
7120  }
7121  if (!res) {
7122  res = wait_file(chan, ints, "minute", lang);
7123  }
7124  break;
7125  case 'P':
7126  case 'p':
7127  /* AM/PM */
7128  if (tm.tm_hour > 11)
7129  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
7130  else
7131  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
7132  res = wait_file(chan, ints, nextmsg, lang);
7133  break;
7134  case 'Q':
7135  /* Shorthand for "Today", "Yesterday", or ABdY */
7136  /* XXX As emphasized elsewhere, this should the native way in your
7137  * language to say the date, with changes in what you say, depending
7138  * upon how recent the date is. XXX */
7139  {
7140  struct timeval now = ast_tvnow();
7141  struct ast_tm tmnow;
7142  time_t beg_today;
7143 
7144  ast_localtime(&now, &tmnow, tzone);
7145  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7146  /* In any case, it saves not having to do ast_mktime() */
7147  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7148  if (beg_today < t) {
7149  /* Today */
7150  res = wait_file(chan, ints, "digits/today", lang);
7151  } else if (beg_today - 86400 < t) {
7152  /* Yesterday */
7153  res = wait_file(chan, ints, "digits/yesterday", lang);
7154  } else {
7155  res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
7156  }
7157  }
7158  break;
7159  case 'q':
7160  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
7161  /* XXX As emphasized elsewhere, this should the native way in your
7162  * language to say the date, with changes in what you say, depending
7163  * upon how recent the date is. XXX */
7164  {
7165  struct timeval now = ast_tvnow();
7166  struct ast_tm tmnow;
7167  time_t beg_today;
7168 
7169  ast_localtime(&now, &tmnow, tzone);
7170  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
7171  /* In any case, it saves not having to do ast_mktime() */
7172  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
7173  if (beg_today < t) {
7174  /* Today */
7175  } else if ((beg_today - 86400) < t) {
7176  /* Yesterday */
7177  res = wait_file(chan, ints, "digits/yesterday", lang);
7178  } else if (beg_today - 86400 * 6 < t) {
7179  /* Within the last week */
7180  res = ast_say_date_with_format_zh(chan, t, ints, lang, "A", tzone);
7181  } else {
7182  res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
7183  }
7184  }
7185  break;
7186  case 'R':
7187  res = ast_say_date_with_format_zh(chan, t, ints, lang, "kM", tzone);
7188  break;
7189  case 'S':
7190  /* Seconds */
7191  if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
7192  if (tm.tm_sec < 10) {
7193  res = wait_file(chan, ints, "digits/0", lang);
7194  }
7195  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
7196  res = wait_file(chan, ints, nextmsg, lang);
7197  } else {
7198  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
7199  res = wait_file(chan, ints, nextmsg, lang);
7200  if (!res) {
7201  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
7202  res = wait_file(chan, ints, nextmsg, lang);
7203  }
7204  }
7205  if (!res) {
7206  res = wait_file(chan, ints, "second", lang);
7207  }
7208  break;
7209  case 'T':
7210  res = ast_say_date_with_format_zh(chan, t, ints, lang, "HMS", tzone);
7211  break;
7212  case ' ':
7213  case ' ':
7214  /* Just ignore spaces and tabs */
7215  break;
7216  default:
7217  /* Unknown character */
7218  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
7219  }
7220  /* Jump out on DTMF */
7221  if (res) {
7222  break;
7223  }
7224  }
7225  return res;
7226 }
7227 
7228 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7229 {
7230  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7231  return ast_say_time_en(chan, t, ints, lang);
7232  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
7233  return ast_say_time_de(chan, t, ints, lang);
7234  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7235  return ast_say_time_fr(chan, t, ints, lang);
7236  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
7237  return ast_say_time_gr(chan, t, ints, lang);
7238  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
7239  return ast_say_time_ja(chan, t, ints, lang);
7240  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7241  return ast_say_time_he(chan, t, ints, lang);
7242  } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
7243  return(ast_say_time_hu(chan, t, ints, lang));
7244  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7245  return ast_say_time_ka(chan, t, ints, lang);
7246  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
7247  return ast_say_time_nl(chan, t, ints, lang);
7248  } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
7249  return ast_say_time_pt_BR(chan, t, ints, lang);
7250  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7251  return ast_say_time_pt(chan, t, ints, lang);
7252  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
7253  return(ast_say_time_th(chan, t, ints, lang));
7254  } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
7255  return ast_say_time_zh(chan, t, ints, lang);
7256  }
7257 
7258  /* Default to English */
7259  return ast_say_time_en(chan, t, ints, lang);
7260 }
7261 
7262 /*! \brief English syntax */
7263 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7264 {
7265  struct timeval when = { t, 0 };
7266  struct ast_tm tm;
7267  int res = 0;
7268  int hour, pm=0;
7269 
7270  ast_localtime(&when, &tm, NULL);
7271  hour = tm.tm_hour;
7272  if (!hour)
7273  hour = 12;
7274  else if (hour == 12)
7275  pm = 1;
7276  else if (hour > 12) {
7277  hour -= 12;
7278  pm = 1;
7279  }
7280  if (!res)
7281  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7282 
7283  if (tm.tm_min > 9) {
7284  if (!res)
7285  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7286  } else if (tm.tm_min) {
7287  if (!res)
7288  res = ast_streamfile(chan, "digits/oh", lang);
7289  if (!res)
7290  res = ast_waitstream(chan, ints);
7291  if (!res)
7292  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7293  } else {
7294  if (!res)
7295  res = ast_streamfile(chan, "digits/oclock", lang);
7296  if (!res)
7297  res = ast_waitstream(chan, ints);
7298  }
7299  if (pm) {
7300  if (!res)
7301  res = ast_streamfile(chan, "digits/p-m", lang);
7302  } else {
7303  if (!res)
7304  res = ast_streamfile(chan, "digits/a-m", lang);
7305  }
7306  if (!res)
7307  res = ast_waitstream(chan, ints);
7308  return res;
7309 }
7310 
7311 /*! \brief German syntax */
7312 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7313 {
7314  struct timeval when = { t, 0 };
7315  struct ast_tm tm;
7316  int res = 0;
7317 
7318  ast_localtime(&when, &tm, NULL);
7319  if (!res)
7320  res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7321  if (!res)
7322  res = ast_streamfile(chan, "digits/oclock", lang);
7323  if (!res)
7324  res = ast_waitstream(chan, ints);
7325  if (!res)
7326  if (tm.tm_min > 0)
7327  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7328  return res;
7329 }
7330 
7331 /*! \brief Hungarian syntax */
7332 int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7333 {
7334  struct timeval when = { t, 0 };
7335  struct ast_tm tm;
7336  int res = 0;
7337 
7338  ast_localtime(&when, &tm, NULL);
7339  if (!res)
7340  res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
7341  if (!res)
7342  res = ast_streamfile(chan, "digits/oclock", lang);
7343  if (!res)
7344  res = ast_waitstream(chan, ints);
7345  if (!res)
7346  if (tm.tm_min > 0) {
7347  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7348  if (!res)
7349  res = ast_streamfile(chan, "minute", lang);
7350  }
7351  return res;
7352 }
7353 
7354 /*! \brief French syntax */
7355 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7356 {
7357  struct timeval when = { t, 0 };
7358  struct ast_tm tm;
7359  int res = 0;
7360 
7361  ast_localtime(&when, &tm, NULL);
7362 
7363  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7364  if (!res) {
7365  res = ast_streamfile(chan, "digits/oclock", lang);
7366  }
7367  if (!res) {
7368  res = ast_waitstream(chan, ints);
7369  }
7370  if (tm.tm_min) {
7371  if (!res) {
7372  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7373  }
7374  }
7375  return res;
7376 }
7377 
7378 /*! \brief Dutch syntax */
7379 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7380 {
7381  struct timeval when = { t, 0 };
7382  struct ast_tm tm;
7383  int res = 0;
7384 
7385  ast_localtime(&when, &tm, NULL);
7386  if (!res)
7387  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
7388  if (!res)
7389  res = ast_streamfile(chan, "digits/nl-uur", lang);
7390  if (!res)
7391  res = ast_waitstream(chan, ints);
7392  if (!res)
7393  if (tm.tm_min > 0)
7394  res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
7395  return res;
7396 }
7397 
7398 /*! \brief Portuguese syntax */
7399 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7400 {
7401  struct timeval when = { t, 0 };
7402  struct ast_tm tm;
7403  int res = 0;
7404  int hour;
7405 
7406  ast_localtime(&when, &tm, NULL);
7407  hour = tm.tm_hour;
7408  if (!res)
7409  res = ast_say_number(chan, hour, ints, lang, "f");
7410  if (tm.tm_min) {
7411  if (!res)
7412  res = wait_file(chan, ints, "digits/pt-e", lang);
7413  if (!res)
7414  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7415  } else {
7416  if (!res)
7417  res = wait_file(chan, ints, "digits/pt-hora", lang);
7418  if (tm.tm_hour != 1)
7419  if (!res)
7420  res = wait_file(chan, ints, "digits/pt-sss", lang);
7421  }
7422  if (!res)
7423  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7424  return res;
7425 }
7426 
7427 /*! \brief Brazilian Portuguese syntax */
7428 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7429 {
7430  struct timeval when = { t, 0 };
7431  struct ast_tm tm;
7432  int res = 0;
7433 
7434  ast_localtime(&when, &tm, NULL);
7435 
7436  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7437  if (!res) {
7438  if (tm.tm_hour > 1)
7439  res = wait_file(chan, ints, "digits/hours", lang);
7440  else
7441  res = wait_file(chan, ints, "digits/hour", lang);
7442  }
7443  if ((!res) && (tm.tm_min)) {
7444  res = wait_file(chan, ints, "digits/pt-e", lang);
7445  if (!res)
7446  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7447  if (!res) {
7448  if (tm.tm_min > 1)
7449  res = wait_file(chan, ints, "minutes", lang);
7450  else
7451  res = wait_file(chan, ints, "minute", lang);
7452  }
7453  }
7454  return res;
7455 }
7456 
7457 /*! \brief Thai syntax */
7458 int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7459 {
7460  struct timeval when = { t, 0 };
7461  struct ast_tm tm;
7462  int res = 0;
7463  int hour;
7464  ast_localtime(&when, &tm, NULL);
7465  hour = tm.tm_hour;
7466  if (!hour)
7467  hour = 24;
7468  if (!res)
7469  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7470  if (!res)
7471  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7472  return res;
7473 }
7474 
7475 /*! \brief Taiwanese / Chinese syntax */
7476 int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7477 {
7478  struct timeval when = { t, 0 };
7479  struct ast_tm tm;
7480  int res = 0;
7481  int hour, pm=0;
7482 
7483  ast_localtime(&when, &tm, NULL);
7484  hour = tm.tm_hour;
7485  if (!hour)
7486  hour = 12;
7487  else if (hour == 12)
7488  pm = 1;
7489  else if (hour > 12) {
7490  hour -= 12;
7491  pm = 1;
7492  }
7493  if (pm) {
7494  if (!res)
7495  res = ast_streamfile(chan, "digits/p-m", lang);
7496  } else {
7497  if (!res)
7498  res = ast_streamfile(chan, "digits/a-m", lang);
7499  }
7500  if (!res)
7501  res = ast_waitstream(chan, ints);
7502  if (!res)
7503  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7504  if (!res)
7505  res = ast_streamfile(chan, "digits/oclock", lang);
7506  if (!res)
7507  res = ast_waitstream(chan, ints);
7508  if (!res)
7509  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7510  if (!res)
7511  res = ast_streamfile(chan, "minute", lang);
7512  if (!res)
7513  res = ast_waitstream(chan, ints);
7514  return res;
7515 }
7516 
7517 /*! \brief Hebrew syntax */
7518 int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7519 {
7520  struct timeval when = { t, 0 };
7521  struct ast_tm tm;
7522  int res = 0;
7523  int hour;
7524 
7525  ast_localtime(&when, &tm, NULL);
7526  hour = tm.tm_hour;
7527  if (!hour)
7528  hour = 12;
7529 
7530  if (!res)
7531  res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
7532 
7533  if (tm.tm_min > 9) {
7534  if (!res)
7535  res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7536  } else if (tm.tm_min) {
7537  if (!res) { /* say a leading zero if needed */
7538  res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
7539  }
7540  if (!res)
7541  res = ast_waitstream(chan, ints);
7542  if (!res)
7543  res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
7544  } else {
7545  if (!res)
7546  res = ast_waitstream(chan, ints);
7547  }
7548  if (!res)
7549  res = ast_waitstream(chan, ints);
7550  return res;
7551 }
7552 
7553 
7554 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7555 {
7556  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7557  return ast_say_datetime_en(chan, t, ints, lang);
7558  } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
7559  return ast_say_datetime_de(chan, t, ints, lang);
7560  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7561  return ast_say_datetime_fr(chan, t, ints, lang);
7562  } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
7563  return ast_say_datetime_gr(chan, t, ints, lang);
7564  } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */
7565  return ast_say_datetime_ja(chan, t, ints, lang);
7566  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
7567  return ast_say_datetime_he(chan, t, ints, lang);
7568  } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
7569  return ast_say_datetime_hu(chan, t, ints, lang);
7570  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
7571  return ast_say_datetime_ka(chan, t, ints, lang);
7572  } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
7573  return ast_say_datetime_nl(chan, t, ints, lang);
7574  } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
7575  return ast_say_datetime_pt_BR(chan, t, ints, lang);
7576  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
7577  return ast_say_datetime_pt(chan, t, ints, lang);
7578  } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
7579  return ast_say_datetime_th(chan, t, ints, lang);
7580  } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
7581  return ast_say_datetime_zh(chan, t, ints, lang);
7582  }
7583 
7584  /* Default to English */
7585  return ast_say_datetime_en(chan, t, ints, lang);
7586 }
7587 
7588 /*! \brief English syntax */
7589 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7590 {
7591  struct timeval when = { t, 0 };
7592  struct ast_tm tm;
7593  char fn[256];
7594  int res = 0;
7595  int hour, pm=0;
7596 
7597  ast_localtime(&when, &tm, NULL);
7598  if (!res) {
7599  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7600  res = ast_streamfile(chan, fn, lang);
7601  if (!res)
7602  res = ast_waitstream(chan, ints);
7603  }
7604  if (!res) {
7605  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7606  res = ast_streamfile(chan, fn, lang);
7607  if (!res)
7608  res = ast_waitstream(chan, ints);
7609  }
7610  if (!res)
7611  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7612 
7613  hour = tm.tm_hour;
7614  if (!hour)
7615  hour = 12;
7616  else if (hour == 12)
7617  pm = 1;
7618  else if (hour > 12) {
7619  hour -= 12;
7620  pm = 1;
7621  }
7622  if (!res)
7623  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7624 
7625  if (tm.tm_min > 9) {
7626  if (!res)
7627  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7628  } else if (tm.tm_min) {
7629  if (!res)
7630  res = ast_streamfile(chan, "digits/oh", lang);
7631  if (!res)
7632  res = ast_waitstream(chan, ints);
7633  if (!res)
7634  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7635  } else {
7636  if (!res)
7637  res = ast_streamfile(chan, "digits/oclock", lang);
7638  if (!res)
7639  res = ast_waitstream(chan, ints);
7640  }
7641  if (pm) {
7642  if (!res)
7643  res = ast_streamfile(chan, "digits/p-m", lang);
7644  } else {
7645  if (!res)
7646  res = ast_streamfile(chan, "digits/a-m", lang);
7647  }
7648  if (!res)
7649  res = ast_waitstream(chan, ints);
7650  if (!res)
7651  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7652  return res;
7653 }
7654 
7655 /*! \brief German syntax */
7656 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7657 {
7658  struct timeval when = { t, 0 };
7659  struct ast_tm tm;
7660  int res = 0;
7661 
7662  ast_localtime(&when, &tm, NULL);
7663  res = ast_say_date(chan, t, ints, lang);
7664  if (!res)
7665  ast_say_time(chan, t, ints, lang);
7666  return res;
7667 
7668 }
7669 
7670 /*! \brief Hungarian syntax */
7671 int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7672 {
7673  struct timeval when = { t, 0 };
7674  struct ast_tm tm;
7675  int res = 0;
7676 
7677  ast_localtime(&when, &tm, NULL);
7678  res = ast_say_date(chan, t, ints, lang);
7679  if (!res)
7680  ast_say_time(chan, t, ints, lang);
7681  return res;
7682 }
7683 
7684 /*! \brief French syntax */
7685 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7686 {
7687  struct timeval when = { t, 0 };
7688  struct ast_tm tm;
7689  char fn[256];
7690  int res = 0;
7691 
7692  ast_localtime(&when, &tm, NULL);
7693 
7694  if (!res)
7695  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7696 
7697  if (!res) {
7698  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7699  res = ast_streamfile(chan, fn, lang);
7700  if (!res)
7701  res = ast_waitstream(chan, ints);
7702  }
7703  if (!res) {
7704  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7705  res = ast_streamfile(chan, fn, lang);
7706  if (!res)
7707  res = ast_waitstream(chan, ints);
7708  }
7709 
7710  if (!res)
7711  res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
7712  if (!res)
7713  res = ast_streamfile(chan, "digits/oclock", lang);
7714  if (tm.tm_min > 0) {
7715  if (!res)
7716  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7717  }
7718  if (!res)
7719  res = ast_waitstream(chan, ints);
7720  if (!res)
7721  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7722  return res;
7723 }
7724 
7725 /*! \brief Dutch syntax */
7726 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7727 {
7728  struct timeval when = { t, 0 };
7729  struct ast_tm tm;
7730  int res = 0;
7731 
7732  ast_localtime(&when, &tm, NULL);
7733  res = ast_say_date(chan, t, ints, lang);
7734  if (!res) {
7735  res = ast_streamfile(chan, "digits/nl-om", lang);
7736  if (!res)
7737  res = ast_waitstream(chan, ints);
7738  }
7739  if (!res)
7740  ast_say_time(chan, t, ints, lang);
7741  return res;
7742 }
7743 
7744 /*! \brief Portuguese syntax */
7745 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7746 {
7747  struct timeval when = { t, 0 };
7748  struct ast_tm tm;
7749  char fn[256];
7750  int res = 0;
7751  int hour, pm=0;
7752 
7753  ast_localtime(&when, &tm, NULL);
7754  if (!res) {
7755  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7756  res = ast_streamfile(chan, fn, lang);
7757  if (!res)
7758  res = ast_waitstream(chan, ints);
7759  }
7760  if (!res) {
7761  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7762  res = ast_streamfile(chan, fn, lang);
7763  if (!res)
7764  res = ast_waitstream(chan, ints);
7765  }
7766  if (!res)
7767  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7768 
7769  hour = tm.tm_hour;
7770  if (!hour)
7771  hour = 12;
7772  else if (hour == 12)
7773  pm = 1;
7774  else if (hour > 12) {
7775  hour -= 12;
7776  pm = 1;
7777  }
7778  if (!res)
7779  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7780 
7781  if (tm.tm_min > 9) {
7782  if (!res)
7783  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7784  } else if (tm.tm_min) {
7785  if (!res)
7786  res = ast_streamfile(chan, "digits/oh", lang);
7787  if (!res)
7788  res = ast_waitstream(chan, ints);
7789  if (!res)
7790  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7791  } else {
7792  if (!res)
7793  res = ast_streamfile(chan, "digits/oclock", lang);
7794  if (!res)
7795  res = ast_waitstream(chan, ints);
7796  }
7797  if (pm) {
7798  if (!res)
7799  res = ast_streamfile(chan, "digits/p-m", lang);
7800  } else {
7801  if (!res)
7802  res = ast_streamfile(chan, "digits/a-m", lang);
7803  }
7804  if (!res)
7805  res = ast_waitstream(chan, ints);
7806  if (!res)
7807  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7808  return res;
7809 }
7810 
7811 /*! \brief Brazilian Portuguese syntax */
7812 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7813 {
7814  struct timeval when = { t, 0 };
7815  struct ast_tm tm;
7816  int res = 0;
7817 
7818  ast_localtime(&when, &tm, NULL);
7819  res = ast_say_date(chan, t, ints, lang);
7820  if (!res)
7821  res = ast_say_time(chan, t, ints, lang);
7822  return res;
7823 }
7824 
7825 /*! \brief Thai syntax */
7826 int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7827 {
7828  struct timeval when = { t, 0 };
7829  struct ast_tm tm;
7830  char fn[256];
7831  int res = 0;
7832  int hour;
7833  ast_localtime(&when, &tm, NULL);
7834  if (!res) {
7835  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7836  res = ast_streamfile(chan, fn, lang);
7837  if (!res)
7838  res = ast_waitstream(chan, ints);
7839  }
7840  if (!res) {
7841  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7842  res = ast_streamfile(chan, fn, lang);
7843  if (!res)
7844  res = ast_waitstream(chan, ints);
7845  }
7846  if (!res){
7847  ast_copy_string(fn, "digits/posor", sizeof(fn));
7848  res = ast_streamfile(chan, fn, lang);
7849  res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
7850  }
7851  if (!res)
7852  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7853 
7854  hour = tm.tm_hour;
7855  if (!hour)
7856  hour = 24;
7857  if (!res){
7858  ast_copy_string(fn, "digits/wela", sizeof(fn));
7859  res = ast_streamfile(chan, fn, lang);
7860  }
7861  if (!res)
7862  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7863  if (!res)
7864  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7865  return res;
7866 }
7867 
7868 /*! \brief Taiwanese / Chinese syntax */
7869 int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7870 {
7871  struct timeval when = { t, 0 };
7872  struct ast_tm tm;
7873  char fn[256];
7874  int res = 0;
7875  int hour, pm=0;
7876 
7877  ast_localtime(&when, &tm, NULL);
7878  if (!res)
7879  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
7880  if (!res) {
7881  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7882  res = ast_streamfile(chan, fn, lang);
7883  if (!res)
7884  res = ast_waitstream(chan, ints);
7885  }
7886  if (!res)
7887  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
7888  if (!res) {
7889  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7890  res = ast_streamfile(chan, fn, lang);
7891  if (!res)
7892  res = ast_waitstream(chan, ints);
7893  }
7894 
7895  hour = tm.tm_hour;
7896  if (!hour)
7897  hour = 12;
7898  else if (hour == 12)
7899  pm = 1;
7900  else if (hour > 12) {
7901  hour -= 12;
7902  pm = 1;
7903  }
7904  if (pm) {
7905  if (!res)
7906  res = ast_streamfile(chan, "digits/p-m", lang);
7907  } else {
7908  if (!res)
7909  res = ast_streamfile(chan, "digits/a-m", lang);
7910  }
7911  if (!res)
7912  res = ast_waitstream(chan, ints);
7913  if (!res)
7914  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
7915  if (!res)
7916  res = ast_streamfile(chan, "digits/oclock", lang);
7917  if (!res)
7918  res = ast_waitstream(chan, ints);
7919  if (!res)
7920  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
7921  if (!res)
7922  res = ast_streamfile(chan, "minute", lang);
7923  if (!res)
7924  res = ast_waitstream(chan, ints);
7925  return res;
7926 }
7927 
7928 /*! \brief Hebrew syntax */
7929 int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7930 {
7931  struct timeval when = { t, 0 };
7932  struct ast_tm tm;
7933  char fn[256];
7934  int res = 0;
7935  int hour;
7936 
7937  ast_localtime(&when, &tm, NULL);
7938  if (!res) {
7939  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
7940  res = ast_streamfile(chan, fn, lang);
7941  if (!res) {
7942  res = ast_waitstream(chan, ints);
7943  }
7944  }
7945  if (!res) {
7946  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
7947  res = ast_streamfile(chan, fn, lang);
7948  if (!res) {
7949  res = ast_waitstream(chan, ints);
7950  }
7951  }
7952  if (!res) {
7953  res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
7954  }
7955 
7956  hour = tm.tm_hour;
7957  if (!hour) {
7958  hour = 12;
7959  }
7960 
7961  if (!res) {
7962  res = ast_say_number(chan, hour, ints, lang, "f");
7963  }
7964 
7965  if (tm.tm_min > 9) {
7966  if (!res) {
7967  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7968  }
7969  } else if (tm.tm_min) {
7970  if (!res) {
7971  /* say a leading zero if needed */
7972  res = ast_say_number(chan, 0, ints, lang, "f");
7973  }
7974  if (!res) {
7975  res = ast_waitstream(chan, ints);
7976  }
7977  if (!res) {
7978  res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
7979  }
7980  } else {
7981  if (!res) {
7982  res = ast_waitstream(chan, ints);
7983  }
7984  }
7985  if (!res) {
7986  res = ast_waitstream(chan, ints);
7987  }
7988  if (!res) {
7989  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
7990  }
7991  return res;
7992 }
7993 
7994 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
7995 {
7996  if (!strncasecmp(lang, "en", 2)) { /* English syntax */
7997  return ast_say_datetime_from_now_en(chan, t, ints, lang);
7998  } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
7999  return ast_say_datetime_from_now_fr(chan, t, ints, lang);
8000  } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
8001  return ast_say_datetime_from_now_he(chan, t, ints, lang);
8002  } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
8003  return ast_say_datetime_from_now_ka(chan, t, ints, lang);
8004  } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
8005  return ast_say_datetime_from_now_pt(chan, t, ints, lang);
8006  }
8007 
8008  /* Default to English */
8009  return ast_say_datetime_from_now_en(chan, t, ints, lang);
8010 }
8011 
8012 /*! \brief English syntax */
8013 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8014 {
8015  int res=0;
8016  struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8017  int daydiff;
8018  struct ast_tm tm;
8019  struct ast_tm now;
8020  char fn[256];
8021 
8022  ast_localtime(&when, &tm, NULL);
8023  ast_localtime(&nowtv, &now, NULL);
8024  daydiff = now.tm_yday - tm.tm_yday;
8025  if ((daydiff < 0) || (daydiff > 6)) {
8026  /* Day of month and month */
8027  if (!res) {
8028  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8029  res = ast_streamfile(chan, fn, lang);
8030  if (!res)
8031  res = ast_waitstream(chan, ints);
8032  }
8033  if (!res)
8034  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8035 
8036  } else if (daydiff) {
8037  /* Just what day of the week */
8038  if (!res) {
8039  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8040  res = ast_streamfile(chan, fn, lang);
8041  if (!res)
8042  res = ast_waitstream(chan, ints);
8043  }
8044  } /* Otherwise, it was today */
8045  if (!res)
8046  res = ast_say_time(chan, t, ints, lang);
8047  return res;
8048 }
8049 
8050 /*! \brief French syntax */
8051 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8052 {
8053  int res=0;
8054  struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8055  int daydiff;
8056  struct ast_tm tm;
8057  struct ast_tm now;
8058  char fn[256];
8059 
8060  ast_localtime(&when, &tm, NULL);
8061  ast_localtime(&nowtv, &now, NULL);
8062  daydiff = now.tm_yday - tm.tm_yday;
8063  if ((daydiff < 0) || (daydiff > 6)) {
8064  /* Day of month and month */
8065  if (!res) {
8066  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8067  res = ast_streamfile(chan, fn, lang);
8068  if (!res)
8069  res = ast_waitstream(chan, ints);
8070  }
8071  if (!res)
8072  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8073 
8074  } else if (daydiff) {
8075  /* Just what day of the week */
8076  if (!res) {
8077  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8078  res = ast_streamfile(chan, fn, lang);
8079  if (!res)
8080  res = ast_waitstream(chan, ints);
8081  }
8082  } /* Otherwise, it was today */
8083  if (!res)
8084  res = ast_say_time(chan, t, ints, lang);
8085  return res;
8086 }
8087 
8088 /*! \brief Portuguese syntax */
8089 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8090 {
8091  int res=0;
8092  int daydiff;
8093  struct ast_tm tm;
8094  struct ast_tm now;
8095  struct timeval nowtv = ast_tvnow(), when = { t, 0 };
8096  char fn[256];
8097 
8098  ast_localtime(&when, &tm, NULL);
8099  ast_localtime(&nowtv, &now, NULL);
8100  daydiff = now.tm_yday - tm.tm_yday;
8101  if ((daydiff < 0) || (daydiff > 6)) {
8102  /* Day of month and month */
8103  if (!res)
8104  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8105  if (!res)
8106  res = wait_file(chan, ints, "digits/pt-de", lang);
8107  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8108  if (!res)
8109  res = wait_file(chan, ints, fn, lang);
8110 
8111  } else if (daydiff) {
8112  /* Just what day of the week */
8113  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8114  if (!res)
8115  res = wait_file(chan, ints, fn, lang);
8116  } /* Otherwise, it was today */
8117  if (!strcasecmp(lang, "pt_BR")) {
8118  if (tm.tm_hour > 1) {
8119  ast_copy_string(fn, "digits/pt-as", sizeof(fn));
8120  } else {
8121  ast_copy_string(fn, "digits/pt-a", sizeof(fn));
8122  }
8123  if (!res)
8124  res = wait_file(chan, ints, fn, lang);
8125  } else {
8126  ast_copy_string(fn, "digits/pt-ah", sizeof(fn));
8127  if (!res)
8128  res = wait_file(chan, ints, fn, lang);
8129  if (tm.tm_hour != 1)
8130  if (!res)
8131  res = wait_file(chan, ints, "digits/pt-sss", lang);
8132  if (!res)
8133  res = ast_say_time(chan, t, ints, lang);
8134  }
8135  return res;
8136 }
8137 
8138 /*! \brief Hebrew syntax */
8139 int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8140 {
8141  int res = 0;
8142  struct timeval nowt = ast_tvnow(), when = { t, 0 };
8143  int daydiff;
8144  struct ast_tm tm;
8145  struct ast_tm now;
8146  char fn[256];
8147 
8148  ast_localtime(&when, &tm, NULL);
8149  ast_localtime(&nowt, &now, NULL);
8150  daydiff = now.tm_yday - tm.tm_yday;
8151  if ((daydiff < 0) || (daydiff > 6)) {
8152  /* Day of month and month */
8153  if (!res) {
8154  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8155  res = ast_streamfile(chan, fn, lang);
8156  if (!res)
8157  res = ast_waitstream(chan, ints);
8158  }
8159  if (!res) {
8160  res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
8161  }
8162  } else if (daydiff) {
8163  /* Just what day of the week */
8164  if (!res) {
8165  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8166  res = ast_streamfile(chan, fn, lang);
8167  if (!res) {
8168  res = ast_waitstream(chan, ints);
8169  }
8170  }
8171  } /* Otherwise, it was today */
8172  if (!res) {
8173  res = ast_say_time(chan, t, ints, lang);
8174  }
8175  return res;
8176 }
8177 
8178 
8179 
8180 /*! \brief Greek
8181  * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
8182  */
8183 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
8184  int tmp;
8185  int left;
8186  int res;
8187  char fn[256] = "";
8188 
8189  /* ast_debug(1, "\n\n Saying number female %s %d \n\n", lang, num); */
8190  if (num < 5) {
8191  snprintf(fn, sizeof(fn), "digits/female-%d", num);
8192  res = wait_file(chan, ints, fn, lang);
8193  } else if (num < 13) {
8194  res = ast_say_number(chan, num, ints, lang, (char *) NULL);
8195  } else if (num <100 ) {
8196  tmp = (num/10) * 10;
8197  left = num - tmp;
8198  snprintf(fn, sizeof(fn), "digits/%d", tmp);
8199  res = ast_streamfile(chan, fn, lang);
8200  if (!res)
8201  res = ast_waitstream(chan, ints);
8202  if (left)
8203  gr_say_number_female(left, chan, ints, lang);
8204 
8205  } else {
8206  return -1;
8207  }
8208  return res;
8209 }
8210 
8211 
8212 
8213 /*! \brief Greek support
8214  * A list of the files that you need to create
8215  -> digits/xilia = "xilia"
8216  -> digits/myrio = "ekatomyrio"
8217  -> digits/thousands = "xiliades"
8218  -> digits/millions = "ektatomyria"
8219  -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
8220  -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
8221  e.g. 80 = "ogdonta"
8222  Here we must note that we use digits/tens/100 to utter "ekato"
8223  and digits/hundred-100 to utter "ekaton"
8224  -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
8225  "terakosia". Here again we use hundreds/1000 for "xilia"
8226  and digits/thousands for "xiliades"
8227 */
8228 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8229 {
8230  int res = 0;
8231  char fn[256] = "";
8232  int i=0;
8233 
8234 
8235  if (!num) {
8236  ast_copy_string(fn, "digits/0", sizeof(fn));
8237  res = ast_streamfile(chan, fn, ast_channel_language(chan));
8238  if (!res)
8239  return ast_waitstream(chan, ints);
8240  }
8241 
8242  while (!res && num ) {
8243  i++;
8244  if (num < 13) {
8245  snprintf(fn, sizeof(fn), "digits/%d", num);
8246  num = 0;
8247  } else if (num <= 100) {
8248  /* 13 < num <= 100 */
8249  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8250  num %= 10;
8251  } else if (num < 200) {
8252  /* 100 < num < 200 */
8253  snprintf(fn, sizeof(fn), "digits/hundred-100");
8254  num %= 100;
8255  } else if (num < 1000) {
8256  /* 200 < num < 1000 */
8257  snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
8258  num %= 100;
8259  } else if (num < 2000){
8260  snprintf(fn, sizeof(fn), "digits/xilia");
8261  num %= 1000;
8262  } else {
8263  /* num > 1000 */
8264  if (num < 1000000) {
8265  res = ast_say_number_full_gr(chan, (num / 1000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8266  if (res)
8267  return res;
8268  num %= 1000;
8269  snprintf(fn, sizeof(fn), "digits/thousands");
8270  } else {
8271  if (num < 1000000000) { /* 1,000,000,000 */
8272  res = ast_say_number_full_gr(chan, (num / 1000000), ints, ast_channel_language(chan), audiofd, ctrlfd);
8273  if (res)
8274  return res;
8275  num %= 1000000;
8276  snprintf(fn, sizeof(fn), "digits/millions");
8277  } else {
8278  ast_debug(1, "Number '%d' is too big for me\n", num);
8279  res = -1;
8280  }
8281  }
8282  }
8283  if (!res) {
8284  if (!ast_streamfile(chan, fn, language)) {
8285  if ((audiofd > -1) && (ctrlfd > -1))
8286  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8287  else
8288  res = ast_waitstream(chan, ints);
8289  }
8290  ast_stopstream(chan);
8291  }
8292  }
8293  return res;
8294 }
8295 
8296 /* Japanese syntax */
8297 static int ast_say_number_full_ja(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
8298 {
8299  int res = 0;
8300  int playh = 0;
8301  char fn[256] = "";
8302 
8303  if (!num)
8304  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
8305 
8306  while (!res && (num || playh)) {
8307  if (num < 0) {
8308  ast_copy_string(fn, "digits/minus", sizeof(fn));
8309  if ( num > INT_MIN ) {
8310  num = -num;
8311  } else {
8312  num = 0;
8313  }
8314  } else if (playh) {
8315  ast_copy_string(fn, "digits/hundred", sizeof(fn));
8316  playh = 0;
8317  } else if (num < 20) {
8318  snprintf(fn, sizeof(fn), "digits/%d", num);
8319  num = 0;
8320  } else if (num < 100) {
8321  snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
8322  num %= 10;
8323  } else {
8324  if (num < 1000){
8325  snprintf(fn, sizeof(fn), "digits/%d", (num/100));
8326  playh++;
8327  num %= 100;
8328  } else {
8329  if (num < 1000000) { /* 1,000,000 */
8330  res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
8331  if (res)
8332  return res;
8333  num %= 1000;
8334  snprintf(fn, sizeof(fn), "digits/thousand");
8335  } else {
8336  if (num < 1000000000) { /* 1,000,000,000 */
8337  res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
8338  if (res)
8339  return res;
8340  num %= 1000000;
8341  ast_copy_string(fn, "digits/million", sizeof(fn));
8342  } else {
8343  ast_debug(1, "Number '%d' is too big for me\n", num);
8344  res = -1;
8345  }
8346  }
8347  }
8348  }
8349  if (!res) {
8350  if (!ast_streamfile(chan, fn, language)) {
8351  if ((audiofd > -1) && (ctrlfd > -1))
8352  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
8353  else
8354  res = ast_waitstream(chan, ints);
8355  }
8356  ast_stopstream(chan);
8357  }
8358  }
8359  return res;
8360 }
8361 
8362 
8363 /*! \brief Greek support
8364  *
8365  * The format is weekday - day - month -year
8366  *
8367  * A list of the files that you need to create
8368  * digits/day-[1..7] : "Deytera .. Paraskeyh"
8369  * digits/months/1..12 : "Ianouariou .. Dekembriou"
8370  Attention the months are in "gekinh klhsh"
8371  */
8372 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8373 {
8374  struct ast_tm tm;
8375  struct timeval when = { t, 0 };
8376 
8377  char fn[256];
8378  int res = 0;
8379 
8380 
8381  ast_localtime(&when, &tm, NULL);
8382  /* W E E K - D A Y */
8383  if (!res) {
8384  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8385  res = ast_streamfile(chan, fn, lang);
8386  if (!res)
8387  res = ast_waitstream(chan, ints);
8388  }
8389  /* D A Y */
8390  if (!res) {
8391  gr_say_number_female(tm.tm_mday, chan, ints, lang);
8392  }
8393  /* M O N T H */
8394  if (!res) {
8395  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8396  res = ast_streamfile(chan, fn, lang);
8397  if (!res)
8398  res = ast_waitstream(chan, ints);
8399  }
8400  /* Y E A R */
8401  if (!res)
8402  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8403  return res;
8404 }
8405 
8406 
8407 /* Japanese syntax */
8408 int ast_say_date_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8409 {
8410  struct ast_tm tm;
8411  struct timeval tv = { t, 0 };
8412  char fn[256];
8413  int res = 0;
8414 
8415  ast_localtime(&tv, &tm, NULL);
8416 
8417  if (!res)
8418  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8419  if (!res)
8420  res = ast_waitstream(chan, ints);
8421  if (!res)
8422  res = ast_streamfile(chan, "digits/nen", lang);
8423  if (!res) {
8424  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8425  res = ast_streamfile(chan, fn, lang);
8426  if (!res)
8427  res = ast_waitstream(chan, ints);
8428  }
8429  if (!res)
8430  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
8431  if (!res)
8432  res = ast_streamfile(chan, "digits/nichi", lang);
8433  if (!res) {
8434  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8435  res = ast_streamfile(chan, fn, lang);
8436  if (!res)
8437  res = ast_waitstream(chan, ints);
8438  }
8439  return res;
8440 }
8441 
8442 
8443 /*! \brief Greek support
8444  *
8445  * A list of the files that you need to create
8446  * - digits/female/1..4 : "Mia, dyo , treis, tesseris "
8447  * - digits/kai : "KAI"
8448  * - digits : "h wra"
8449  * - digits/p-m : "meta meshmbrias"
8450  * - digits/a-m : "pro meshmbrias"
8451  */
8452 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8453 {
8454 
8455  struct timeval when = { t, 0 };
8456  struct ast_tm tm;
8457  int res = 0;
8458  int hour, pm=0;
8459 
8460  ast_localtime(&when, &tm, NULL);
8461  hour = tm.tm_hour;
8462 
8463  if (!hour)
8464  hour = 12;
8465  else if (hour == 12)
8466  pm = 1;
8467  else if (hour > 12) {
8468  hour -= 12;
8469  pm = 1;
8470  }
8471 
8472  res = gr_say_number_female(hour, chan, ints, lang);
8473  if (tm.tm_min) {
8474  if (!res)
8475  res = ast_streamfile(chan, "digits/kai", lang);
8476  if (!res)
8477  res = ast_waitstream(chan, ints);
8478  if (!res)
8479  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8480  } else {
8481  if (!res)
8482  res = ast_streamfile(chan, "digits/hwra", lang);
8483  if (!res)
8484  res = ast_waitstream(chan, ints);
8485  }
8486  if (pm) {
8487  if (!res)
8488  res = ast_streamfile(chan, "digits/p-m", lang);
8489  } else {
8490  if (!res)
8491  res = ast_streamfile(chan, "digits/a-m", lang);
8492  }
8493  if (!res)
8494  res = ast_waitstream(chan, ints);
8495  return res;
8496 }
8497 
8498 
8499 /* Japanese */
8500 static int ast_say_time_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8501 {
8502  struct timeval tv = { t, 0 };
8503  struct ast_tm tm;
8504  int res = 0;
8505  int hour, pm=0;
8506 
8507  ast_localtime(&tv, &tm, NULL);
8508  hour = tm.tm_hour;
8509 
8510  if (!hour)
8511  hour = 12;
8512  else if (hour == 12)
8513  pm = 1;
8514  else if (hour > 12) {
8515  hour -= 12;
8516  pm = 1;
8517  }
8518 
8519  if (pm) {
8520  if (!res)
8521  res = ast_streamfile(chan, "digits/p-m", lang);
8522  } else {
8523  if (!res)
8524  res = ast_streamfile(chan, "digits/a-m", lang);
8525  }
8526  if (hour == 9 || hour == 21) {
8527  if (!res)
8528  res = ast_streamfile(chan, "digits/9_2", lang);
8529  } else {
8530  if (!res)
8531  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8532  }
8533  if (!res)
8534  res = ast_streamfile(chan, "digits/ji", lang);
8535  if (!res)
8536  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8537  if (!res)
8538  res = ast_streamfile(chan, "digits/fun", lang);
8539  if (!res)
8540  res = ast_waitstream(chan, ints);
8541  return res;
8542 }
8543 
8544 
8545 /*! \brief Greek support
8546  */
8547 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8548 {
8549  struct timeval when = { t, 0 };
8550  struct ast_tm tm;
8551  char fn[256];
8552  int res = 0;
8553 
8554  ast_localtime(&when, &tm, NULL);
8555 
8556  /* W E E K - D A Y */
8557  if (!res) {
8558  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8559  res = ast_streamfile(chan, fn, lang);
8560  if (!res)
8561  res = ast_waitstream(chan, ints);
8562  }
8563  /* D A Y */
8564  if (!res) {
8565  gr_say_number_female(tm.tm_mday, chan, ints, lang);
8566  }
8567  /* M O N T H */
8568  if (!res) {
8569  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8570  res = ast_streamfile(chan, fn, lang);
8571  if (!res)
8572  res = ast_waitstream(chan, ints);
8573  }
8574 
8575  res = ast_say_time_gr(chan, t, ints, lang);
8576  return res;
8577 }
8578 
8579 /* Japanese syntax */
8580 int ast_say_datetime_ja(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
8581 {
8582  struct timeval tv = { t, 0 };
8583  struct ast_tm tm;
8584  char fn[256];
8585  int res = 0;
8586  int hour, pm = 0;
8587 
8588  ast_localtime(&tv, &tm, NULL);
8589 
8590  if (!res)
8591  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
8592  if (!res)
8593  res = ast_streamfile(chan, "digits/nen", lang);
8594  if (!res) {
8595  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
8596  res = ast_streamfile(chan, fn, lang);
8597  if (!res)
8598  res = ast_waitstream(chan, ints);
8599  }
8600  if (!res)
8601  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
8602  if (!res)
8603  res = ast_streamfile(chan, "digits/nichi", lang);
8604  if (!res) {
8605  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
8606  res = ast_streamfile(chan, fn, lang);
8607  if (!res)
8608  res = ast_waitstream(chan, ints);
8609  }
8610 
8611  hour = tm.tm_hour;
8612  if (!hour)
8613  hour = 12;
8614  else if (hour == 12)
8615  pm = 1;
8616  else if (hour > 12) {
8617  hour -= 12;
8618  pm = 1;
8619  }
8620  if (pm) {
8621  if (!res)
8622  res = ast_streamfile(chan, "digits/p-m", lang);
8623  } else {
8624  if (!res)
8625  res = ast_streamfile(chan, "digits/a-m", lang);
8626  }
8627  if (hour == 9 || hour == 21) {
8628  if (!res)
8629  res = ast_streamfile(chan, "digits/9_2", lang);
8630  } else {
8631  if (!res)
8632  res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
8633  }
8634  if (!res)
8635  res = ast_streamfile(chan, "digits/ji", lang);
8636  if (!res)
8637  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
8638  if (!res)
8639  res = ast_streamfile(chan, "digits/fun", lang);
8640  if (!res)
8641  res = ast_waitstream(chan, ints);
8642  return res;
8643 }
8644 
8645 /*! \brief Greek support
8646  */
8647 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)
8648 {
8649  struct timeval when = { t, 0 };
8650  struct ast_tm tm;
8651  int res=0, offset, sndoffset;
8652  char sndfile[256], nextmsg[256];
8653 
8654  if (!format)
8655  format = "AdBY 'digits/at' IMp";
8656 
8657  ast_localtime(&when, &tm, tzone);
8658 
8659  for (offset=0 ; format[offset] != '\0' ; offset++) {
8660  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
8661  switch (format[offset]) {
8662  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
8663  case '\'':
8664  /* Literal name of a sound file */
8665  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
8666  sndfile[sndoffset] = format[offset];
8667  }
8668  sndfile[sndoffset] = '\0';
8669  res = wait_file(chan, ints, sndfile, lang);
8670  break;
8671  case 'A':
8672  case 'a':
8673  /* Sunday - Saturday */
8674  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
8675  res = wait_file(chan, ints, nextmsg, lang);
8676  break;
8677  case 'B':
8678  case 'b':
8679  case 'h':
8680  /* January - December */
8681  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
8682  res = wait_file(chan, ints, nextmsg, lang);
8683  break;
8684  case 'd':
8685  case 'e':
8686  /* first - thirtyfirst */
8687  gr_say_number_female(tm.tm_mday, chan, ints, lang);
8688  break;
8689  case 'Y':
8690  /* Year */
8691 
8692  ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, ast_channel_language(chan), -1, -1);
8693  break;
8694  case 'I':
8695  case 'l':
8696  /* 12-Hour */
8697  if (tm.tm_hour == 0)
8698  gr_say_number_female(12, chan, ints, lang);
8699  else if (tm.tm_hour > 12)
8700  gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
8701  else
8702  gr_say_number_female(tm.tm_hour, chan, ints, lang);
8703  break;
8704  case 'H':
8705  case 'k':
8706  /* 24-Hour */
8707  gr_say_number_female(tm.tm_hour, chan, ints, lang);
8708  break;
8709  case 'M':
8710  /* Minute */
8711  if (tm.tm_min) {
8712  if (!res)
8713  res = ast_streamfile(chan, "digits/kai", lang);
8714  if (!res)
8715  res = ast_waitstream(chan, ints);
8716  if (!res)
8717  res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
8718  } else {
8719  if (!res)
8720  res = ast_streamfile(chan, "digits/oclock", lang);
8721  if (!res)
8722  res = ast_waitstream(chan, ints);
8723  }
8724  break;
8725  case 'P':
8726  case 'p':
8727  /* AM/PM */
8728  if (tm.tm_hour > 11)
8729  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
8730  else
8731  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
8732  res = wait_file(chan, ints, nextmsg, lang);
8733  break;
8734  case 'Q':
8735  /* Shorthand for "Today", "Yesterday", or ABdY */
8736  /* XXX As emphasized elsewhere, this should the native way in your
8737  * language to say the date, with changes in what you say, depending
8738  * upon how recent the date is. XXX */
8739  {
8740  struct timeval now = ast_tvnow();
8741  struct ast_tm tmnow;
8742  time_t beg_today;
8743 
8744  ast_localtime(&now, &tmnow, tzone);
8745  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8746  /* In any case, it saves not having to do ast_mktime() */
8747  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8748  if (beg_today < t) {
8749  /* Today */
8750  res = wait_file(chan, ints, "digits/today", lang);
8751  } else if (beg_today - 86400 < t) {
8752  /* Yesterday */
8753  res = wait_file(chan, ints, "digits/yesterday", lang);
8754  } else {
8755  res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
8756  }
8757  }
8758  break;
8759  case 'q':
8760  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
8761  /* XXX As emphasized elsewhere, this should the native way in your
8762  * language to say the date, with changes in what you say, depending
8763  * upon how recent the date is. XXX */
8764  {
8765  struct timeval now = ast_tvnow();
8766  struct ast_tm tmnow;
8767  time_t beg_today;
8768 
8769  ast_localtime(&now, &tmnow, tzone);
8770  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
8771  /* In any case, it saves not having to do ast_mktime() */
8772  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
8773  if (beg_today < t) {
8774  /* Today */
8775  } else if ((beg_today - 86400) < t) {
8776  /* Yesterday */
8777  res = wait_file(chan, ints, "digits/yesterday", lang);
8778  } else if (beg_today - 86400 * 6 < t) {
8779  /* Within the last week */
8780  res = ast_say_date_with_format_gr(chan, t, ints, lang, "A", tzone);
8781  } else {
8782  res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
8783  }
8784  }
8785  break;
8786  case 'R':
8787  res = ast_say_date_with_format_gr(chan, t, ints, lang, "HM", tzone);
8788  break;
8789  case 'S':
8790  /* Seconds */
8791  ast_copy_string(nextmsg, "digits/kai", sizeof(nextmsg));
8792  res = wait_file(chan, ints, nextmsg, lang);
8793  if (!res)
8794  res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
8795  if (!res)
8796  ast_copy_string(nextmsg, "seconds", sizeof(nextmsg));
8797  res = wait_file(chan, ints, nextmsg, lang);
8798  break;
8799  case 'T':
8800  res = ast_say_date_with_format_gr(chan, t, ints, lang, "HMS", tzone);
8801  break;
8802  case ' ':
8803  case ' ':
8804  /* Just ignore spaces and tabs */
8805  break;
8806  default:
8807  /* Unknown character */
8808  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
8809  }
8810  /* Jump out on DTMF */
8811  if (res) {
8812  break;
8813  }
8814  }
8815  return res;
8816 }
8817 
8818 /* Japanese syntax */
8819 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)
8820 {
8821  struct timeval tv = { time, 0 };
8822  struct ast_tm tm;
8823  int res = 0, offset, sndoffset;
8824  char sndfile[256], nextmsg[256];
8825 
8826  if (!format)
8827  format = "YbdAPIMS";
8828 
8829  ast_localtime(&tv, &tm, timezone);
8830 
8831  for (offset = 0; format[offset] != '\0'; offset++) {
8832  ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
8833  switch (format[offset]) {
8834  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
8835  case '\'':
8836  /* Literal name of a sound file */
8837  for (sndoffset = 0; (format[++offset] != '\'') && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
8838  sndfile[sndoffset] = format[offset];
8839  }
8840  sndfile[sndoffset] = '\0';
8841  res = wait_file(chan,ints,sndfile,lang);
8842  break;
8843  case 'A':
8844  case 'a':
8845  /* Sunday - Saturday */
8846  snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
8847  res = wait_file(chan,ints,nextmsg,lang);
8848  break;
8849  case 'B':
8850  case 'b':
8851  case 'h':
8852  /* January - December */
8853  snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
8854  res = wait_file(chan,ints,nextmsg,lang);
8855  break;
8856  case 'd':
8857  case 'e':
8858  /* First - Thirtyfirst */
8859  if (tm.tm_mday < 21) {
8860  snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d_2", tm.tm_mday);
8861  res = wait_file(chan,ints,nextmsg,lang);
8862  } else if (tm.tm_mday < 30) {
8863  /* Between 21 and 29 - two sounds */
8864  res = wait_file(chan,ints, "digits/20",lang);
8865  if (!res) {
8866  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
8867  res = wait_file(chan,ints,nextmsg,lang);
8868  }
8869  res = wait_file(chan,ints, "digits/nichi",lang);
8870  } else if (tm.tm_mday == 30) {
8871  /* 30 */
8872  res = wait_file(chan,ints, "digits/h-30_2",lang);
8873  } else {
8874  /* 31 */
8875  res = wait_file(chan,ints, "digits/30",lang);
8876  res = wait_file(chan,ints, "digits/1",lang);
8877  res = wait_file(chan,ints, "digits/nichi",lang);
8878  }
8879  break;
8880  case 'Y':
8881  /* Year */
8882  if (tm.tm_year > 99) {
8883  res = wait_file(chan,ints, "digits/2",lang);
8884  if (!res) {
8885  res = wait_file(chan,ints, "digits/thousand",lang);
8886  }
8887  if (tm.tm_year > 100) {
8888  if (!res) {
8889  /* This works until the end of 2020 */
8890  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
8891  res = wait_file(chan,ints,nextmsg,lang);
8892  }
8893  }
8894  } else {
8895  if (tm.tm_year < 1) {
8896  /* I'm not going to handle 1900 and prior */
8897  /* We'll just be silent on the year, instead of bombing out. */
8898  } else {
8899  res = wait_file(chan,ints, "digits/19",lang);
8900  if (!res) {
8901  if (tm.tm_year <= 9) {
8902  /* 1901 - 1909 */
8903  res = wait_file(chan,ints, "digits/oh",lang);
8904  if (!res) {
8905  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
8906  res = wait_file(chan,ints,nextmsg,lang);
8907  }
8908  } else if (tm.tm_year <= 20) {
8909  /* 1910 - 1920 */
8910  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
8911  res = wait_file(chan,ints,nextmsg,lang);
8912  } else {
8913  /* 1921 - 1999 */
8914  int ten, one;
8915  ten = tm.tm_year / 10;
8916  one = tm.tm_year % 10;
8917  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
8918  res = wait_file(chan,ints,nextmsg,lang);
8919  if (!res) {
8920  if (one != 0) {
8921  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
8922  res = wait_file(chan,ints,nextmsg,lang);
8923  }
8924  }
8925  }
8926  }
8927  }
8928  }
8929  res = wait_file(chan,ints, "digits/nen",lang);
8930  break;
8931  case 'P':
8932  case 'p':
8933  /* AM/PM */
8934  if (tm.tm_hour > 11)
8935  snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
8936  else
8937  snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
8938  res = wait_file(chan,ints,nextmsg,lang);
8939  break;
8940  case 'I':
8941  case 'l':
8942  /* 12-Hour */
8943  if (tm.tm_hour == 0)
8944  snprintf(nextmsg,sizeof(nextmsg), "digits/12");
8945  else if (tm.tm_hour == 9 || tm.tm_hour == 21)
8946  snprintf(nextmsg,sizeof(nextmsg), "digits/9_2");
8947  else if (tm.tm_hour > 12)
8948  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
8949  else
8950  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
8951  res = wait_file(chan,ints,nextmsg,lang);
8952  if(!res) res = wait_file(chan,ints, "digits/ji",lang);
8953  break;
8954  case 'H':
8955  case 'k':
8956  if (!res) {
8957  if (tm.tm_hour != 0) {
8958  int remainder = tm.tm_hour;
8959  if (tm.tm_hour > 20) {
8960  res = wait_file(chan,ints, "digits/20",lang);
8961  remainder -= 20;
8962  }
8963  if (!res) {
8964  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
8965  res = wait_file(chan,ints,nextmsg,lang);
8966  }
8967  }
8968  }
8969  res = wait_file(chan,ints, "digits/ji",lang);
8970  break;
8971  case 'M':
8972  /* Minute */
8973  if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
8974  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
8975  res = wait_file(chan,ints,nextmsg,lang);
8976  } else {
8977  int ten, one;
8978  ten = (tm.tm_min / 10) * 10;
8979  one = (tm.tm_min % 10);
8980  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
8981  res = wait_file(chan,ints,nextmsg,lang);
8982  if (!res) {
8983  /* Fifty, not fifty-zero */
8984  if (one != 0) {
8985  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
8986  res = wait_file(chan,ints,nextmsg,lang);
8987  }
8988  }
8989  }
8990  res = wait_file(chan,ints, "digits/fun",lang);
8991  break;
8992  case 'Q':
8993  /* Shorthand for "Today", "Yesterday", or ABdY */
8994  {
8995  struct timeval now;
8996  struct ast_tm tmnow;
8997  time_t beg_today;
8998 
8999  gettimeofday(&now,NULL);
9000  ast_localtime(&now,&tmnow,timezone);
9001  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9002  /* In any case, it saves not having to do ast_mktime() */
9003  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9004  if (beg_today < time) {
9005  /* Today */
9006  res = wait_file(chan,ints, "digits/today",lang);
9007  } else if (beg_today - 86400 < time) {
9008  /* Yesterday */
9009  res = wait_file(chan,ints, "digits/yesterday",lang);
9010  } else {
9011  res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
9012  }
9013  }
9014  break;
9015  case 'q':
9016  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
9017  {
9018  struct timeval now;
9019  struct ast_tm tmnow;
9020  time_t beg_today;
9021 
9022  gettimeofday(&now,NULL);
9023  ast_localtime(&now,&tmnow,timezone);
9024  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9025  /* In any case, it saves not having to do ast_mktime() */
9026  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9027  if (beg_today < time) {
9028  /* Today */
9029  } else if ((beg_today - 86400) < time) {
9030  /* Yesterday */
9031  res = wait_file(chan,ints, "digits/yesterday",lang);
9032  } else if (beg_today - 86400 * 6 < time) {
9033  /* Within the last week */
9034  res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
9035  } else {
9036  res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
9037  }
9038  }
9039  break;
9040  case 'R':
9041  res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
9042  break;
9043  case 'S':
9044  /* Seconds */
9045  if (tm.tm_sec == 0) {
9046  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
9047  res = wait_file(chan,ints,nextmsg,lang);
9048  } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
9049  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
9050  res = wait_file(chan,ints,nextmsg,lang);
9051  } else {
9052  int ten, one;
9053  ten = (tm.tm_sec / 10) * 10;
9054  one = (tm.tm_sec % 10);
9055  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
9056  res = wait_file(chan,ints,nextmsg,lang);
9057  if (!res) {
9058  /* Fifty, not fifty-zero */
9059  if (one != 0) {
9060  snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
9061  res = wait_file(chan,ints,nextmsg,lang);
9062  }
9063  }
9064  }
9065  res = wait_file(chan,ints, "digits/byou",lang);
9066  break;
9067  case 'T':
9068  res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
9069  break;
9070  case ' ':
9071  case ' ':
9072  /* Just ignore spaces and tabs */
9073  break;
9074  default:
9075  /* Unknown character */
9076  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9077  }
9078  /* Jump out on DTMF */
9079  if (res) {
9080  break;
9081  }
9082  }
9083  return res;
9084 }
9085 
9086 /*! \brief Vietnamese syntax */
9087 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)
9088 {
9089  struct timeval when = { t, 0 };
9090  struct ast_tm tm;
9091  int res = 0, offset, sndoffset;
9092  char sndfile[256], nextmsg[256];
9093 
9094  if (format == NULL)
9095  format = "A 'digits/day' eB 'digits/year' Y 'digits/at' k 'hours' M 'minutes' p";
9096 
9097  ast_localtime(&when, &tm, tzone);
9098 
9099  for (offset=0 ; format[offset] != '\0' ; offset++) {
9100  ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
9101  switch (format[offset]) {
9102  /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
9103  case '\'':
9104  /* Literal name of a sound file */
9105  for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
9106  sndfile[sndoffset] = format[offset];
9107  }
9108  sndfile[sndoffset] = '\0';
9109  res = wait_file(chan, ints, sndfile, lang);
9110  break;
9111  case 'A':
9112  case 'a':
9113  /* Sunday - Saturday */
9114  snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
9115  res = wait_file(chan, ints, nextmsg, lang);
9116  break;
9117  case 'B':
9118  case 'b':
9119  case 'h':
9120  /* January - December */
9121  snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
9122  res = wait_file(chan, ints, nextmsg, lang);
9123  break;
9124  case 'm':
9125  /* Month enumerated */
9126  res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
9127  break;
9128  case 'd':
9129  case 'e':
9130  /* 1 - 31 */
9131  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
9132  break;
9133  case 'Y':
9134  /* Year */
9135  if (tm.tm_year > 99) {
9136  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
9137  } else if (tm.tm_year < 1) {
9138  /* I'm not going to handle 1900 and prior */
9139  /* We'll just be silent on the year, instead of bombing out. */
9140  } else {
9141  res = wait_file(chan, ints, "digits/19", lang);
9142  if (!res) {
9143  if (tm.tm_year <= 9) {
9144  /* 1901 - 1909 */
9145  res = wait_file(chan, ints, "digits/odd", lang);
9146  }
9147 
9148  res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
9149  }
9150  }
9151  break;
9152  case 'I':
9153  case 'l':
9154  /* 12-Hour */
9155  if (tm.tm_hour == 0)
9156  ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
9157  else if (tm.tm_hour > 12)
9158  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
9159  else
9160  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
9161  res = wait_file(chan, ints, nextmsg, lang);
9162  break;
9163  case 'H':
9164  case 'k':
9165  /* 24-Hour */
9166  if (format[offset] == 'H') {
9167  /* e.g. oh-eight */
9168  if (tm.tm_hour < 10) {
9169  res = wait_file(chan, ints, "digits/0", lang);
9170  }
9171  } else {
9172  /* e.g. eight */
9173  if (tm.tm_hour == 0) {
9174  res = wait_file(chan, ints, "digits/0", lang);
9175  }
9176  }
9177  if (!res) {
9178  if (tm.tm_hour != 0) {
9179  int remaining = tm.tm_hour;
9180  if (tm.tm_hour > 20) {
9181  res = wait_file(chan, ints, "digits/20", lang);
9182  remaining -= 20;
9183  }
9184  if (!res) {
9185  snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
9186  res = wait_file(chan, ints, nextmsg, lang);
9187  }
9188  }
9189  }
9190  break;
9191  case 'M':
9192  case 'N':
9193  /* Minute */
9194  res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
9195  break;
9196  case 'P':
9197  case 'p':
9198  /* AM/PM */
9199  if (tm.tm_hour > 11)
9200  ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
9201  else
9202  ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
9203  res = wait_file(chan, ints, nextmsg, lang);
9204  break;
9205  case 'Q':
9206  /* Shorthand for "Today", "Yesterday", or ABdY */
9207  /* XXX As emphasized elsewhere, this should the native way in your
9208  * language to say the date, with changes in what you say, depending
9209  * upon how recent the date is. XXX */
9210  {
9211  struct timeval now = ast_tvnow();
9212  struct ast_tm tmnow;
9213  time_t beg_today;
9214 
9215  gettimeofday(&now, NULL);
9216  ast_localtime(&now, &tmnow, tzone);
9217  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9218  /* In any case, it saves not having to do ast_mktime() */
9219  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9220  if (beg_today < t) {
9221  /* Today */
9222  res = wait_file(chan, ints, "digits/today", lang);
9223  } else if (beg_today - 86400 < t) {
9224  /* Yesterday */
9225  res = wait_file(chan, ints, "digits/yesterday", lang);
9226  } else if (beg_today - 86400 * 6 < t) {
9227  /* Within the last week */
9228  res = ast_say_date_with_format_vi(chan, t, ints, lang, "A", tzone);
9229  } else if (beg_today - 2628000 < t) {
9230  /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9231  res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9232  } else if (beg_today - 15768000 < t) {
9233  /* Less than 6 months ago - "August seventh" */
9234  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9235  } else {
9236  /* More than 6 months ago - "April nineteenth two thousand three" */
9237  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9238  }
9239  }
9240  break;
9241  case 'q':
9242  /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
9243  /* XXX As emphasized elsewhere, this should the native way in your
9244  * language to say the date, with changes in what you say, depending
9245  * upon how recent the date is. XXX */
9246  {
9247  struct timeval now;
9248  struct ast_tm tmnow;
9249  time_t beg_today;
9250 
9251  now = ast_tvnow();
9252  ast_localtime(&now, &tmnow, tzone);
9253  /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
9254  /* In any case, it saves not having to do ast_mktime() */
9255  beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
9256  if (beg_today < t) {
9257  /* Today */
9258  } else if ((beg_today - 86400) < t) {
9259  /* Yesterday */
9260  res = wait_file(chan, ints, "digits/yesterday", lang);
9261  } else if (beg_today - 86400 * 6 < t) {
9262  /* Within the last week */
9263  res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
9264  } else if (beg_today - 2628000 < t) {
9265  /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
9266  res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
9267  } else if (beg_today - 15768000 < t) {
9268  /* Less than 6 months ago - "August seventh" */
9269  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
9270  } else {
9271  /* More than 6 months ago - "April nineteenth two thousand three" */
9272  res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
9273  }
9274  }
9275  break;
9276  case 'R':
9277  res = ast_say_date_with_format_vi(chan, t, ints, lang, "HM", tzone);
9278  break;
9279  case 'S':
9280  /* Seconds */
9281  res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
9282  break;
9283  case 'T':
9284  res = ast_say_date_with_format_vi(chan, t, ints, lang, "H 'hours' M 'minutes' S 'seconds'", tzone);
9285  break;
9286  case ' ':
9287  case ' ':
9288  /* Just ignore spaces and tabs */
9289  break;
9290  default:
9291  /* Unknown character */
9292  ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
9293  }
9294  /* Jump out on DTMF */
9295  if (res) {
9296  break;
9297  }
9298  }
9299  return res;
9300 }
9301 
9302 /*! \brief Georgian support
9303 
9304  Convert a number into a semi-localized string. Only for Georgian.
9305  res must be of at least 256 bytes, preallocated.
9306  The output corresponds to Georgian spoken numbers, so
9307  it may be either converted to real words by applying a direct conversion
9308  table, or played just by substituting the entities with played files.
9309 
9310  Output may consist of the following tokens (separated by spaces):
9311  0, minus.
9312  1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
9313  10-19.
9314  20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
9315  100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
9316  1000, 1000_. (atasi, atas).
9317  1000000, 1000000_. (milioni, milion).
9318  1000000000, 1000000000_. (miliardi, miliard).
9319 
9320  To be able to play the sounds, each of the above tokens needs
9321  a corresponding sound file. (e.g. 200_.gsm).
9322 */
9323 static char* ast_translate_number_ka(int num, char* res, int res_len)
9324 {
9325  char buf[256];
9326  int digit = 0;
9327  int remaining = 0;
9328 
9329 
9330  if (num < 0) {
9331  strncat(res, "minus ", res_len - strlen(res) - 1);
9332  if ( num > INT_MIN ) {
9333  num = -num;
9334  } else {
9335  num = 0;
9336  }
9337  }
9338 
9339 
9340  /* directly read the numbers */
9341  if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
9342  snprintf(buf, sizeof(buf), "%d", num);
9343  strncat(res, buf, res_len - strlen(res) - 1);
9344  return res;
9345  }
9346 
9347 
9348  if (num < 40) { /* ocda... */
9349  strncat(res, "20_ ", res_len - strlen(res) - 1);
9350  return ast_translate_number_ka(num - 20, res, res_len);
9351  }
9352 
9353  if (num < 60) { /* ormocda... */
9354  strncat(res, "40_ ", res_len - strlen(res) - 1);
9355  return ast_translate_number_ka(num - 40, res, res_len);
9356  }
9357 
9358  if (num < 80) { /* samocda... */
9359  strncat(res, "60_ ", res_len - strlen(res) - 1);
9360  return ast_translate_number_ka(num - 60, res, res_len);
9361  }
9362 
9363  if (num < 100) { /* otxmocda... */
9364  strncat(res, "80_ ", res_len - strlen(res) - 1);
9365  return ast_translate_number_ka(num - 80, res, res_len);
9366  }
9367 
9368 
9369  if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
9370  remaining = num % 100;
9371  digit = (num - remaining) / 100;
9372 
9373  if (remaining == 0) {
9374  snprintf(buf, sizeof(buf), "%d", num);
9375  strncat(res, buf, res_len - strlen(res) - 1);
9376  return res;
9377  } else {
9378  snprintf(buf, sizeof(buf), "%d_ ", digit*100);
9379  strncat(res, buf, res_len - strlen(res) - 1);
9380  return ast_translate_number_ka(remaining, res, res_len);
9381  }
9382  }
9383 
9384 
9385  if (num == 1000) {
9386  strncat(res, "1000", res_len - strlen(res) - 1);
9387  return res;
9388  }
9389 
9390 
9391  if (num < 1000000) {
9392  remaining = num % 1000;
9393  digit = (num - remaining) / 1000;
9394 
9395  if (remaining == 0) {
9396  ast_translate_number_ka(digit, res, res_len);
9397  strncat(res, " 1000", res_len - strlen(res) - 1);
9398  return res;
9399  }
9400 
9401  if (digit == 1) {
9402  strncat(res, "1000_ ", res_len - strlen(res) - 1);
9403  return ast_translate_number_ka(remaining, res, res_len);
9404  }
9405 
9406  ast_translate_number_ka(digit, res, res_len);
9407  strncat(res, " 1000_ ", res_len - strlen(res) - 1);
9408  return ast_translate_number_ka(remaining, res, res_len);
9409  }
9410 
9411 
9412  if (num == 1000000) {
9413  strncat(res, "1 1000000", res_len - strlen(res) - 1);
9414  return res;
9415  }
9416 
9417 
9418  if (num < 1000000000) {
9419  remaining = num % 1000000;
9420  digit = (num - remaining) / 1000000;
9421 
9422  if (remaining == 0) {
9423  ast_translate_number_ka(digit, res, res_len);
9424  strncat(res, " 1000000", res_len - strlen(res) - 1);
9425  return res;
9426  }
9427 
9428  ast_translate_number_ka(digit, res, res_len);
9429  strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
9430  return ast_translate_number_ka(remaining, res, res_len);
9431  }
9432 
9433 
9434  if (num == 1000000000) {
9435  strncat(res, "1 1000000000", res_len - strlen(res) - 1);
9436  return res;
9437  }
9438 
9439 
9440  if (num > 1000000000) {
9441  remaining = num % 1000000000;
9442  digit = (num - remaining) / 1000000000;
9443 
9444  if (remaining == 0) {
9445  ast_translate_number_ka(digit, res, res_len);
9446  strncat(res, " 1000000000", res_len - strlen(res) - 1);
9447  return res;
9448  }
9449 
9450  ast_translate_number_ka(digit, res, res_len);
9451  strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
9452  return ast_translate_number_ka(remaining, res, res_len);
9453  }
9454 
9455  return res;
9456 
9457 }
9458 
9459 
9460 
9461 /*! \brief ast_say_number_full_ka: Georgian syntax */
9462 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)
9463 {
9464  int res = 0;
9465  char fn[512] = "";
9466  char* s = 0;
9467  const char* remaining = fn;
9468 
9469  if (!num) {
9470  return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
9471  }
9472 
9473 
9474  ast_translate_number_ka(num, fn, 512);
9475 
9476 
9477 
9478  while (res == 0 && (s = strstr(remaining, " "))) {
9479  size_t len = s - remaining;
9480  char* new_string = ast_malloc(len + 1 + strlen("digits/"));
9481 
9482  sprintf(new_string, "digits/");
9483  strncat(new_string, remaining, len); /* we can't sprintf() it, it's not null-terminated. */
9484 /* new_string[len + strlen("digits/")] = '\0'; */
9485 
9486  if (!ast_streamfile(chan, new_string, language)) {
9487  if ((audiofd > -1) && (ctrlfd > -1)) {
9488  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9489  } else {
9490  res = ast_waitstream(chan, ints);
9491  }
9492  }
9493  ast_stopstream(chan);
9494 
9495  ast_free(new_string);
9496 
9497  remaining = s + 1; /* position just after the found space char. */
9498  while (*remaining == ' ') { /* skip multiple spaces */
9499  remaining++;
9500  }
9501  }
9502 
9503 
9504  /* the last chunk. */
9505  if (res == 0 && *remaining) {
9506 
9507  char* new_string = ast_malloc(strlen(remaining) + 1 + strlen("digits/"));
9508  sprintf(new_string, "digits/%s", remaining);
9509 
9510  if (!ast_streamfile(chan, new_string, language)) {
9511  if ((audiofd > -1) && (ctrlfd > -1)) {
9512  res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
9513  } else {
9514  res = ast_waitstream(chan, ints);
9515  }
9516  }
9517  ast_stopstream(chan);
9518 
9519  ast_free(new_string);
9520 
9521  }
9522 
9523 
9524  return res;
9525 
9526 }
9527 
9528 
9529 
9530 /*! \brief Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi".
9531 
9532 Georgian support for date/time requires the following files (*.gsm):
9533 
9534  - mon-1, mon-2, ... (ianvari, tebervali, ...)
9535  - day-1, day-2, ... (orshabati, samshabati, ...)
9536  - saati_da
9537  - tsuti
9538  - tslis
9539 */
9540 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9541 {
9542  struct timeval when = { t, 0 };
9543  struct ast_tm tm;
9544  char fn[256];
9545  int res = 0;
9546  ast_localtime(&when, &tm, NULL);
9547 
9548  if (!res) {
9549  res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
9550  }
9551 
9552  if (!res) {
9553  snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
9554  res = ast_streamfile(chan, fn, lang);
9555  if (!res) {
9556  res = ast_waitstream(chan, ints);
9557  }
9558  }
9559 
9560  if (!res) {
9561  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
9562 /* if (!res)
9563  res = ast_waitstream(chan, ints);
9564 */
9565  }
9566 
9567  if (!res) {
9568  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9569  res = ast_streamfile(chan, fn, lang);
9570  if (!res) {
9571  res = ast_waitstream(chan, ints);
9572  }
9573  }
9574  return res;
9575 
9576 }
9577 
9578 
9579 
9580 
9581 
9582 /*! \brief Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
9583 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9584 {
9585  struct timeval when = { t, 0 };
9586  struct ast_tm tm;
9587  int res = 0;
9588 
9589  ast_localtime(&when, &tm, NULL);
9590 
9591  res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
9592  if (!res) {
9593  res = ast_streamfile(chan, "digits/saati_da", lang);
9594  if (!res) {
9595  res = ast_waitstream(chan, ints);
9596  }
9597  }
9598 
9599  if (tm.tm_min) {
9600  if (!res) {
9601  res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
9602 
9603  if (!res) {
9604  res = ast_streamfile(chan, "digits/tsuti", lang);
9605  if (!res) {
9606  res = ast_waitstream(chan, ints);
9607  }
9608  }
9609  }
9610  }
9611  return res;
9612 }
9613 
9614 
9615 
9616 /*! \brief Georgian syntax. Say date, then say time. */
9617 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9618 {
9619  struct timeval when = { t, 0 };
9620  struct ast_tm tm;
9621  int res = 0;
9622 
9623  ast_localtime(&when, &tm, NULL);
9624  res = ast_say_date(chan, t, ints, lang);
9625  if (!res) {
9626  ast_say_time(chan, t, ints, lang);
9627  }
9628  return res;
9629 
9630 }
9631 
9632 
9633 
9634 
9635 /*! \brief Georgian syntax */
9636 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
9637 {
9638  int res=0;
9639  int daydiff;
9640  struct ast_tm tm;
9641  struct ast_tm now;
9642  struct timeval when = { t, 0 }, nowt = ast_tvnow();
9643  char fn[256];
9644 
9645  ast_localtime(&when, &tm, NULL);
9646  ast_localtime(&nowt, &now, NULL);
9647  daydiff = now.tm_yday - tm.tm_yday;
9648  if ((daydiff < 0) || (daydiff > 6)) {
9649  /* Day of month and month */
9650  if (!res) {
9651  res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
9652  }
9653  if (!res) {
9654  snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
9655  res = ast_streamfile(chan, fn, lang);
9656  if (!res) {
9657  res = ast_waitstream(chan, ints);
9658  }
9659  }
9660 
9661  } else if (daydiff) {
9662  /* Just what day of the week */
9663  if (!res) {
9664  snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
9665  res = ast_streamfile(chan, fn, lang);
9666  if (!res) {
9667  res = ast_waitstream(chan, ints);
9668  }
9669  }
9670  } /* Otherwise, it was today */
9671  if (!res) {
9672  res = ast_say_time(chan, t, ints, lang);
9673  }
9674 
9675  return res;
9676 }
9677 
9678 /*! \brief
9679  * In English, we use the plural for everything but one. For example:
9680  * - 1 degree
9681  * - 2 degrees
9682  * - 5 degrees
9683  * The filename for the plural form is generated by appending "s". Note that
9684  * purpose is to generate a unique filename, not to implement irregular
9685  * declensions. Thus:
9686  * - 1 man
9687  * - 2 mans (the "mans" soundfile will of course say "men")
9688  */
9689 static const char *counted_noun_ending_en(int num)
9690 {
9691  if (num == 1 || num == -1) {
9692  return "";
9693  } else {
9694  return "s";
9695  }
9696 }
9697 
9698 /*! \brief
9699  * Counting of objects in slavic languages such as Russian and Ukrainian the
9700  * rules are more complicated. There are two plural forms used in counting.
9701  * They are the genative singular which we represent with the suffix "x1" and
9702  * the genative plural which we represent with the suffix "x2". The base names
9703  * of the soundfiles remain in English. For example:
9704  * - 1 degree (soundfile says "gradus")
9705  * - 2 degreex1 (soundfile says "gradusa")
9706  * - 5 degreex2 (soundfile says "gradusov")
9707  */
9708 static const char *counted_noun_ending_slavic(int num)
9709 {
9710  if (num < 0) {
9711  num *= -1;
9712  }
9713  num %= 100; /* never pay attention to more than two digits */
9714  if (num >= 20) { /* for numbers 20 and above, pay attention to only last digit */
9715  num %= 10;
9716  }
9717  if (num == 1) { /* singular */
9718  return "";
9719  }
9720  if (num > 0 && num < 5) { /* 2--4 get genative singular */
9721  return "x1";
9722  } else { /* 5--19 get genative plural */
9723  return "x2";
9724  }
9725 }
9726 
9727 int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
9728 {
9729  char *temp;
9730  int temp_len;
9731  const char *ending;
9732  if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
9733  ending = counted_noun_ending_slavic(num);
9734  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
9735  ending = counted_noun_ending_slavic(num);
9736  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
9737  ending = counted_noun_ending_slavic(num);
9738  } else { /* English and default */
9739  ending = counted_noun_ending_en(num);
9740  }
9741  temp = ast_alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
9742  snprintf(temp, temp_len, "%s%s", noun, ending);
9743  return ast_play_and_wait(chan, temp);
9744 }
9745 
9746 /*! \brief
9747  * In slavic languages such as Russian and Ukrainian the rules for declining
9748  * adjectives are simpler than those for nouns. When counting we use only
9749  * the singular (to which we give no suffix) and the genative plural (which
9750  * we represent by adding an "x"). Oh, an in the singular gender matters
9751  * so we append the supplied gender suffix ("m", "f", "n").
9752  */
9753 static const char *counted_adjective_ending_ru(int num, const char gender[])
9754 {
9755  if (num < 0) {
9756  num *= -1;
9757  }
9758  num %= 100; /* never pay attention to more than two digits */
9759  if (num >= 20) { /* at 20 and beyond only the last digit matters */
9760  num %= 10;
9761  }
9762  if (num == 1) {
9763  return gender ? gender : "";
9764  } else { /* all other numbers get the genative plural */
9765  return "x";
9766  }
9767 }
9768 
9769 int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
9770 {
9771  char *temp;
9772  int temp_len;
9773  const char *ending;
9774  if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* Russian */
9775  ending = counted_adjective_ending_ru(num, gender);
9776  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
9777  ending = counted_adjective_ending_ru(num, gender);
9778  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
9779  ending = counted_adjective_ending_ru(num, gender);
9780  } else { /* English and default */
9781  ending = "";
9782  }
9783  temp = ast_alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
9784  snprintf(temp, temp_len, "%s%s", adjective, ending);
9785  return ast_play_and_wait(chan, temp);
9786 }
9787 
9788 /*! \brief
9789  * remap the 'say' functions to use those in this file
9790  */
9791 static void __attribute__((constructor)) __say_init(void)
9792 {
9796  ast_say_digit_str_full = say_digit_str_full;
9797  ast_say_money_str_full = say_money_str_full;
9798  ast_say_character_str_full = say_character_str_full;
9799  ast_say_phonetic_str_full = say_phonetic_str_full;
9800  ast_say_datetime = say_datetime;
9801  ast_say_time = say_time;
9802  ast_say_date = say_date;
9803  ast_say_datetime_from_now = say_datetime_from_now;
9804  ast_say_date_with_format = say_date_with_format;
9805 }
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:8139
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:64
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
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:874
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
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:7812
Asterisk main include file. File version handling, generic pbx functions.
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:2705
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:125
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:1769
static char * ast_translate_number_ka(int num, char *res, int res_len)
Georgian support.
Definition: say.c:9323
static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:7826
static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:4152
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:9583
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:2984
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:7518
static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:7726
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:6248
struct ast_str * ast_get_number_str(int num, const char *lang)
ast_get_number_str: call language-specific functions
Definition: say.c:566
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:6960
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:4210
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:4396
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:3782
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:9617
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:3251
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:8283
static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:7263
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:3368
static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8372
static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:7685
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:5629
Test Framework API.
static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:3992
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:8051
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:1139
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:8228
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
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:2124
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
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:1969
struct ast_str * ast_get_money_str(const char *str, const char *lang)
ast_get_money_str: call language-specific functions
Definition: say.c:449
static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
English syntax.
Definition: say.c:7589
int tm_year
Definition: localtime.h:41
struct ast_str * ast_get_ordinal_str(int num, const char *lang)
ast_get_ordinal_str: call language-specific functions
Definition: say.c:686
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:1849
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:8647
static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:4181
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:8089
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:9540
Custom localtime functions for multiple timezones.
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 'une' et: 'and' ...
Definition: say.c:1505
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:9087
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:2222
static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:7745
static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:4120
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:300
static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Thai syntax.
Definition: say.c:7458
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:162
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:976
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:1188
int tm_mon
Definition: localtime.h:40
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:1616
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:5482
static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:7312
static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
say_ordinal_full
Definition: say.c:940
SAY_EXTERN int(* ast_say_ordinal_full)(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options, int audiofd, int ctrlfd) SAY_INIT(ast_say_ordinal_full)
Same as ast_say_number() with audiofd for received audio and returns 1 on ctrlfd being readable...
Definition: say.h:105
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:9753
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:3229
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_debug(level,...)
Log a DEBUG message.
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:4876
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:5297
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:1322
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:7869
ast_say_case_sensitivity
Controls how ast_say_character_str denotes the case of characters in a string.
Definition: say.h:181
static int get_lastdigits_ru(int num)
determine last digits for thousands/millions (ru)
Definition: say.c:2957
static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hebrew syntax.
Definition: say.c:4273
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Dutch syntax.
Definition: say.c:7379
static void __say_init(void)
remap the 'say' functions to use those in this file
Definition: say.c:9791
Support for dynamic strings.
Definition: strings.h:623
static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:7656
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:9708
Definition: say.c:2304
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
#define ESS(x)
Definition: cli.h:59
int tm_wday
Definition: localtime.h:42
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:8013
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:1401
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:1074
static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
German syntax.
Definition: say.c:4070
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:3531
static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:4246
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:3133
static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Danish syntax.
Definition: say.c:4021
static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Portuguese syntax.
Definition: say.c:7399
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:693
static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
French syntax.
Definition: say.c:7355
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:7428
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
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:6016
int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int monfd)
Definition: file.c:1849
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:6451
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:3063
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:6668
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:8235
Standard Command Line Interface.
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:216
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:7332
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Hungarian syntax.
Definition: say.c:7671
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:2785
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:7476
static const char * counted_noun_ending_en(int num)
In English, we use the plural for everything but one. For example:
Definition: say.c:9689
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:9636
static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8547
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:8247
static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
Greek support.
Definition: say.c:8452
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:933
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:9462
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:7929
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:8183
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
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:4674
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
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:5820