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