Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


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