Sat Apr 26 2014 22:01:41

Asterisk developer's documentation


say.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  * George Konstantoulakis <gkon@inaccessnetworks.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Say numbers and dates (maybe words one day too)
00023  *
00024  * \author Mark Spencer <markster@digium.com>
00025  *
00026  * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
00027  *
00028  * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
00029  *                Next Generation Networks (NGN).
00030  * \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,
00031  *                IP Crossing Co., Ltd.
00032  */
00033 
00034 /*** MODULEINFO
00035    <support_level>core</support_level>
00036  ***/
00037 
00038 #include "asterisk.h"
00039 
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411244 $")
00041 
00042 #include <netinet/in.h>
00043 #include <time.h>
00044 #include <ctype.h>
00045 #include <math.h>
00046 
00047 #ifdef SOLARIS
00048 #include <iso/limits_iso.h>
00049 #endif
00050 
00051 #include "asterisk/file.h"
00052 #include "asterisk/channel.h"
00053 #include "asterisk/say.h"
00054 #include "asterisk/lock.h"
00055 #include "asterisk/localtime.h"
00056 #include "asterisk/utils.h"
00057 #include "asterisk/app.h"
00058 #include "asterisk/test.h"
00059 
00060 /* Forward declaration */
00061 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
00062 
00063 
00064 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
00065 {
00066    const char *fn;
00067    char fnbuf[10], asciibuf[20] = "letters/ascii";
00068    char ltr;
00069    int num = 0;
00070    int res = 0;
00071 
00072    while (str[num] && !res) {
00073       fn = NULL;
00074       switch (str[num]) {
00075       case ('*'):
00076          fn = "digits/star";
00077          break;
00078       case ('#'):
00079          fn = "digits/pound";
00080          break;
00081       case ('!'):
00082          fn = "letters/exclaimation-point";
00083          break;
00084       case ('@'):
00085          fn = "letters/at";
00086          break;
00087       case ('$'):
00088          fn = "letters/dollar";
00089          break;
00090       case ('-'):
00091          fn = "letters/dash";
00092          break;
00093       case ('.'):
00094          fn = "letters/dot";
00095          break;
00096       case ('='):
00097          fn = "letters/equals";
00098          break;
00099       case ('+'):
00100          fn = "letters/plus";
00101          break;
00102       case ('/'):
00103          fn = "letters/slash";
00104          break;
00105       case (' '):
00106          fn = "letters/space";
00107          break;
00108       case ('0'):
00109       case ('1'):
00110       case ('2'):
00111       case ('3'):
00112       case ('4'):
00113       case ('5'):
00114       case ('6'):
00115       case ('7'):
00116       case ('8'):
00117       case ('9'):
00118          strcpy(fnbuf, "digits/X");
00119          fnbuf[7] = str[num];
00120          fn = fnbuf;
00121          break;
00122       default:
00123          ltr = str[num];
00124          if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';    /* file names are all lower-case */
00125          strcpy(fnbuf, "letters/X");
00126          fnbuf[8] = ltr;
00127          fn = fnbuf;
00128       }
00129       if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
00130          (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
00131          res = ast_streamfile(chan, fn, lang);
00132          if (!res) {
00133             if ((audiofd  > -1) && (ctrlfd > -1))
00134                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00135             else
00136                res = ast_waitstream(chan, ints);
00137          }
00138          ast_stopstream(chan);
00139       }
00140       num++;
00141    }
00142 
00143    return res;
00144 }
00145 
00146 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
00147 {
00148    const char *fn;
00149    char fnbuf[256];
00150    char ltr;
00151    int num = 0;
00152    int res = 0;
00153 
00154    while (str[num] && !res) {
00155       fn = NULL;
00156       switch (str[num]) {
00157       case ('*'):
00158          fn = "digits/star";
00159          break;
00160       case ('#'):
00161          fn = "digits/pound";
00162          break;
00163       case ('!'):
00164          fn = "letters/exclaimation-point";
00165          break;
00166       case ('@'):
00167          fn = "letters/at";
00168          break;
00169       case ('$'):
00170          fn = "letters/dollar";
00171          break;
00172       case ('-'):
00173          fn = "letters/dash";
00174          break;
00175       case ('.'):
00176          fn = "letters/dot";
00177          break;
00178       case ('='):
00179          fn = "letters/equals";
00180          break;
00181       case ('+'):
00182          fn = "letters/plus";
00183          break;
00184       case ('/'):
00185          fn = "letters/slash";
00186          break;
00187       case (' '):
00188          fn = "letters/space";
00189          break;
00190       case ('0'):
00191       case ('1'):
00192       case ('2'):
00193       case ('3'):
00194       case ('4'):
00195       case ('5'):
00196       case ('6'):
00197       case ('7'):
00198       case ('8'):
00199          strcpy(fnbuf, "digits/X");
00200          fnbuf[7] = str[num];
00201          fn = fnbuf;
00202          break;
00203       default: /* '9' falls here... */
00204          ltr = str[num];
00205          if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';    /* file names are all lower-case */
00206          strcpy(fnbuf, "phonetic/X_p");
00207          fnbuf[9] = ltr;
00208          fn = fnbuf;
00209       }
00210       if (fn && ast_fileexists(fn, NULL, lang) > 0) {
00211          res = ast_streamfile(chan, fn, lang);
00212          if (!res) {
00213             if ((audiofd  > -1) && (ctrlfd > -1))
00214                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00215             else
00216                res = ast_waitstream(chan, ints);
00217          }
00218          ast_stopstream(chan);
00219       }
00220       num++;
00221    }
00222 
00223    return res;
00224 }
00225 
00226 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
00227 {
00228    const char *fn;
00229    char fnbuf[256];
00230    int num = 0;
00231    int res = 0;
00232 
00233    while (str[num] && !res) {
00234       fn = NULL;
00235       switch (str[num]) {
00236       case ('*'):
00237          fn = "digits/star";
00238          break;
00239       case ('#'):
00240          fn = "digits/pound";
00241          break;
00242       case ('-'):
00243          fn = "digits/minus";
00244          break;
00245       case '0':
00246       case '1':
00247       case '2':
00248       case '3':
00249       case '4':
00250       case '5':
00251       case '6':
00252       case '7':
00253       case '8':
00254       case '9':
00255          strcpy(fnbuf, "digits/X");
00256          fnbuf[7] = str[num];
00257          fn = fnbuf;
00258          break;
00259       }
00260       if (fn && ast_fileexists(fn, NULL, lang) > 0) {
00261          res = ast_streamfile(chan, fn, lang);
00262          if (!res) {
00263             if ((audiofd  > -1) && (ctrlfd > -1))
00264                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00265             else
00266                res = ast_waitstream(chan, ints);
00267          }
00268          ast_stopstream(chan);
00269       }
00270       num++;
00271    }
00272 
00273    return res;
00274 }
00275 
00276 /* Forward declarations */
00277 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
00278     \note Not really language codes.
00279    For these language codes, Asterisk will change the syntax when
00280    saying numbers (and in some cases dates and voicemail messages
00281    as well)
00282       \arg \b da    - Danish
00283       \arg \b de    - German
00284       \arg \b en    - English (US)
00285       \arg \b en_GB - English (British)
00286       \arg \b es    - Spanish, Mexican
00287       \arg \b fr    - French
00288       \arg \b he    - Hebrew
00289       \arg \b it    - Italian
00290       \arg \b nl    - Dutch
00291       \arg \b no    - Norwegian
00292       \arg \b pl    - Polish
00293       \arg \b pt    - Portuguese
00294       \arg \b pt_BR - Portuguese (Brazil)
00295       \arg \b se    - Swedish
00296       \arg \b zh    - Taiwanese / Chinese
00297       \arg \b ru    - Russian
00298       \arg \b ka    - Georgian
00299       \arg \b hu    - Hungarian
00300 
00301  \par Gender:
00302  For Some languages the numbers differ for gender and plural.
00303  \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
00304  \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
00305  use the option argument 'p' for plural enumerations like in German
00306 
00307  Date/Time functions currently have less languages supported than saynumber().
00308 
00309  \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
00310 
00311  See contrib/i18n.testsuite.conf for some examples of the different syntaxes
00312 
00313  \par Portuguese
00314  Portuguese sound files needed for Time/Date functions:
00315  pt-ah
00316  pt-ao
00317  pt-de
00318  pt-e
00319  pt-ora
00320  pt-meianoite
00321  pt-meiodia
00322  pt-sss
00323 
00324  \par Spanish
00325  Spanish sound files needed for Time/Date functions:
00326  es-de
00327  es-el
00328 
00329  \par Italian
00330  Italian sound files needed for Time/Date functions:
00331  ore-una
00332  ore-mezzanotte
00333 
00334 */
00335 
00336 /* Forward declarations of language specific variants of ast_say_number_full */
00337 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00338 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);
00339 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);
00340 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);
00341 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00342 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);
00343 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);
00344 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);
00345 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00346 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00347 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);
00348 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);
00349 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);
00350 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);
00351 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00352 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00353 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);
00354 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);
00355 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00356 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00357 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);
00358 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00359 
00360 /* Forward declarations of language specific variants of ast_say_enumeration_full */
00361 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00362 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);
00363 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);
00364 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);
00365 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
00366 
00367 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
00368 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00369 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00370 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00371 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00372 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00373 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00374 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00375 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00376 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00377 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00378 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00379 
00380 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);
00381 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);
00382 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);
00383 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);
00384 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);
00385 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);
00386 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);
00387 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);
00388 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);
00389 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);
00390 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);
00391 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);
00392 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);
00393 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);
00394 
00395 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00396 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00397 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00398 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00399 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00400 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00401 static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00402 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00403 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00404 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00405 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00406 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00407 
00408 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00409 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00410 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00411 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00412 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00413 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00414 static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00415 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00416 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00417 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00418 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00419 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00420 
00421 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00422 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00423 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00424 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00425 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
00426 
00427 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
00428 {
00429    int res;
00430    if ((res = ast_streamfile(chan, file, lang))) {
00431       ast_log(LOG_WARNING, "Unable to play message %s\n", file);
00432    }
00433    if (!res) {
00434       res = ast_waitstream(chan, ints);
00435    }
00436    return res;
00437 }
00438 
00439 /*! \brief  ast_say_number_full: call language-specific functions
00440      \note Called from AGI */
00441 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
00442 {
00443    ast_test_suite_event_notify("SAYNUM", "Message: saying number %d\r\nNumber: %d\r\nChannel: %s", num, num, ast_channel_name(chan));
00444    if (!strncasecmp(language, "en_GB", 5)) {     /* British syntax */
00445       return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
00446    } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
00447       return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
00448    } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
00449       return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
00450    } else if (!strncasecmp(language, "cz", 2)) { /* deprecated Czech syntax */
00451       static int deprecation_warning = 0;
00452       if (deprecation_warning++ % 10 == 0) {
00453          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
00454       }
00455       return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
00456    } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
00457       return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
00458    } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
00459       return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
00460    } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
00461       return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
00462    } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
00463       return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
00464    } else if (!strncasecmp(language, "ge", 2)) { /* deprecated Georgian syntax */
00465       static int deprecation_warning = 0;
00466       if (deprecation_warning++ % 10 == 0) {
00467          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
00468       }
00469       return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
00470    } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
00471       return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
00472    } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
00473       return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
00474    } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
00475       return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
00476    } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
00477       return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
00478    } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
00479       return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
00480    } else if (!strncasecmp(language, "mx", 2)) { /* deprecated Mexican syntax */
00481       static int deprecation_warning = 0;
00482       if (deprecation_warning++ % 10 == 0) {
00483          ast_log(LOG_WARNING, "mx is not a standard language code.  Please switch to using es_MX instead.\n");
00484       }
00485       return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
00486    } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
00487       return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
00488    } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
00489       return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
00490    } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
00491       return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
00492    } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
00493       return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
00494    } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
00495       return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
00496    } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
00497       return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
00498    } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
00499       return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
00500    } else if (!strncasecmp(language, "tw", 2)) { /* deprecated Taiwanese syntax */
00501       static int deprecation_warning = 0;
00502       if (deprecation_warning++ % 10 == 0) {
00503          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
00504       }
00505       return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
00506    } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
00507       return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
00508    } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
00509       return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
00510    } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
00511       return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
00512    }
00513 
00514    /* Default to english */
00515    return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
00516 }
00517 
00518 /*! \brief  ast_say_number_full_en: English syntax
00519    \note This is the default syntax, if no other syntax defined in this file is used */
00520 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
00521 {
00522    int res = 0;
00523    int playh = 0;
00524    char fn[256] = "";
00525    if (!num)
00526       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
00527 
00528    while (!res && (num || playh)) {
00529       if (num < 0) {
00530          ast_copy_string(fn, "digits/minus", sizeof(fn));
00531          if ( num > INT_MIN ) {
00532             num = -num;
00533          } else {
00534             num = 0;
00535          }
00536       } else if (playh) {
00537          ast_copy_string(fn, "digits/hundred", sizeof(fn));
00538          playh = 0;
00539       } else   if (num < 20) {
00540          snprintf(fn, sizeof(fn), "digits/%d", num);
00541          num = 0;
00542       } else   if (num < 100) {
00543          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
00544          num %= 10;
00545       } else {
00546          if (num < 1000){
00547             snprintf(fn, sizeof(fn), "digits/%d", (num/100));
00548             playh++;
00549             num %= 100;
00550          } else {
00551             if (num < 1000000) { /* 1,000,000 */
00552                res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
00553                if (res)
00554                   return res;
00555                num %= 1000;
00556                snprintf(fn, sizeof(fn), "digits/thousand");
00557             } else {
00558                if (num < 1000000000) { /* 1,000,000,000 */
00559                   res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
00560                   if (res)
00561                      return res;
00562                   num %= 1000000;
00563                   ast_copy_string(fn, "digits/million", sizeof(fn));
00564                } else {
00565                   ast_debug(1, "Number '%d' is too big for me\n", num);
00566                   res = -1;
00567                }
00568             }
00569          }
00570       }
00571       if (!res) {
00572          if (!ast_streamfile(chan, fn, language)) {
00573             if ((audiofd  > -1) && (ctrlfd > -1))
00574                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00575             else
00576                res = ast_waitstream(chan, ints);
00577          }
00578          ast_stopstream(chan);
00579       }
00580    }
00581    return res;
00582 }
00583 
00584 static int exp10_int(int power)
00585 {
00586    int x, res= 1;
00587    for (x=0;x<power;x++)
00588       res *= 10;
00589    return res;
00590 }
00591 
00592 /*! \brief  ast_say_number_full_cs: Czech syntax
00593  *
00594  * files needed:
00595  * - 1m,2m - gender male
00596  * - 1w,2w - gender female
00597  * - 3,4,...,20
00598  * - 30,40,...,90
00599  *
00600  * - hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
00601  *
00602  * for each number 10^(3n + 3) exist 3 files represented as:
00603  *    1 tousand = jeden tisic = 1_E3
00604  *    2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
00605  *    5,6,... tousands = pet,sest,... tisic = 5_E3
00606  *
00607  *    million = _E6
00608  *    miliard = _E9
00609  *    etc...
00610  *
00611  * tousand, milion are  gender male, so 1 and 2 is 1m 2m
00612  * miliard is gender female, so 1 and 2 is 1w 2w
00613  */
00614 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)
00615 {
00616    int res = 0;
00617    int playh = 0;
00618    char fn[256] = "";
00619 
00620    int hundered = 0;
00621    int left = 0;
00622    int length = 0;
00623 
00624    /* options - w = woman, m = man, n = neutral. Defaultl is woman */
00625    if (!options)
00626       options = "w";
00627 
00628    if (!num)
00629       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
00630 
00631    while (!res && (num || playh)) {
00632       if (num < 0) {
00633          ast_copy_string(fn, "digits/minus", sizeof(fn));
00634          if ( num > INT_MIN ) {
00635             num = -num;
00636          } else {
00637             num = 0;
00638          }
00639       } else if (num < 3 ) {
00640          snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
00641          playh = 0;
00642          num = 0;
00643       } else if (num < 20) {
00644          snprintf(fn, sizeof(fn), "digits/%d", num);
00645          playh = 0;
00646          num = 0;
00647       } else if (num < 100) {
00648          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
00649          num %= 10;
00650       } else if (num < 1000) {
00651          hundered = num / 100;
00652          if ( hundered == 1 ) {
00653             ast_copy_string(fn, "digits/1sto", sizeof(fn));
00654          } else if ( hundered == 2 ) {
00655             ast_copy_string(fn, "digits/2ste", sizeof(fn));
00656          } else {
00657             res = ast_say_number_full_cs(chan, hundered, ints, language, options, audiofd, ctrlfd);
00658             if (res)
00659                return res;
00660             if (hundered == 3 || hundered == 4) {
00661                ast_copy_string(fn, "digits/sta", sizeof(fn));
00662             } else if ( hundered > 4 ) {
00663                ast_copy_string(fn, "digits/set", sizeof(fn));
00664             }
00665          }
00666          num -= (hundered * 100);
00667       } else { /* num > 1000 */
00668          length = (int)log10(num)+1;
00669          while ( (length % 3 ) != 1 ) {
00670             length--;
00671          }
00672          left = num / (exp10_int(length-1));
00673          if ( left == 2 ) {
00674             switch (length-1) {
00675                case 9: options = "w";  /* 1,000,000,000 gender female */
00676                   break;
00677                default : options = "m"; /* others are male */
00678             }
00679          }
00680          if ( left > 1 )   { /* we don't say "one thousand" but only thousand */
00681             res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
00682             if (res)
00683                return res;
00684          }
00685          if ( left >= 5 ) { /* >= 5 have the same declesion */
00686             snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
00687          } else if ( left >= 2 && left <= 4 ) {
00688             snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
00689          } else { /* left == 1 */
00690             snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
00691          }
00692          num -= left * (exp10_int(length-1));
00693       }
00694       if (!res) {
00695          if (!ast_streamfile(chan, fn, language)) {
00696             if ((audiofd > -1) && (ctrlfd > -1)) {
00697                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00698             } else {
00699                res = ast_waitstream(chan, ints);
00700             }
00701          }
00702          ast_stopstream(chan);
00703       }
00704    }
00705    return res;
00706 }
00707 
00708 /*! \brief  ast_say_number_full_da: Danish syntax
00709  New files:
00710  - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
00711  */
00712 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)
00713 {
00714    int res = 0;
00715    int playh = 0;
00716    int playa = 0;
00717    int cn = 1;    /* +1 = commune; -1 = neuter */
00718    char fn[256] = "";
00719    if (!num)
00720       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
00721 
00722    if (options && !strncasecmp(options, "n", 1)) cn = -1;
00723 
00724    while (!res && (num || playh || playa )) {
00725       /* The grammar for Danish numbers is the same as for English except
00726       * for the following:
00727       * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
00728       * - numbers 20 through 99 are said in reverse order, i.e. 21 is
00729       *   "one-and twenty" and 68 is "eight-and sixty".
00730       * - "million" is different in singular and plural form
00731       * - numbers > 1000 with zero as the third digit from last have an
00732       *   "and" before the last two digits, i.e. 2034 is "two thousand and
00733       *   four-and thirty" and 1000012 is "one million and twelve".
00734       */
00735       if (num < 0) {
00736          ast_copy_string(fn, "digits/minus", sizeof(fn));
00737          if ( num > INT_MIN ) {
00738             num = -num;
00739          } else {
00740             num = 0;
00741          }
00742       } else if (playh) {
00743          ast_copy_string(fn, "digits/hundred", sizeof(fn));
00744          playh = 0;
00745       } else if (playa) {
00746          ast_copy_string(fn, "digits/and", sizeof(fn));
00747          playa = 0;
00748       } else if (num == 1 && cn == -1) {
00749          ast_copy_string(fn, "digits/1N", sizeof(fn));
00750          num = 0;
00751       } else if (num < 20) {
00752          snprintf(fn, sizeof(fn), "digits/%d", num);
00753          num = 0;
00754       } else if (num < 100) {
00755          int ones = num % 10;
00756          if (ones) {
00757             snprintf(fn, sizeof(fn), "digits/%d-and", ones);
00758             num -= ones;
00759          } else {
00760             snprintf(fn, sizeof(fn), "digits/%d", num);
00761             num = 0;
00762          }
00763       } else {
00764          if (num < 1000) {
00765             int hundreds = num / 100;
00766             if (hundreds == 1)
00767                ast_copy_string(fn, "digits/1N", sizeof(fn));
00768             else
00769                snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
00770 
00771             playh++;
00772             num -= 100 * hundreds;
00773             if (num)
00774                playa++;
00775 
00776          } else {
00777             if (num < 1000000) {
00778                res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
00779                if (res)
00780                   return res;
00781                num = num % 1000;
00782                ast_copy_string(fn, "digits/thousand", sizeof(fn));
00783             } else {
00784                if (num < 1000000000) {
00785                   int millions = num / 1000000;
00786                   res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
00787                   if (res)
00788                      return res;
00789                   if (millions == 1)
00790                      ast_copy_string(fn, "digits/million", sizeof(fn));
00791                   else
00792                      ast_copy_string(fn, "digits/millions", sizeof(fn));
00793                   num = num % 1000000;
00794                } else {
00795                   ast_debug(1, "Number '%d' is too big for me\n", num);
00796                   res = -1;
00797                }
00798             }
00799             if (num && num < 100)
00800                playa++;
00801          }
00802       }
00803       if (!res) {
00804          if (!ast_streamfile(chan, fn, language)) {
00805             if ((audiofd > -1) && (ctrlfd > -1))
00806                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00807             else
00808                res = ast_waitstream(chan, ints);
00809          }
00810          ast_stopstream(chan);
00811       }
00812    }
00813    return res;
00814 }
00815 
00816 /*! \brief  ast_say_number_full_de: German syntax
00817 
00818  New files:
00819  In addition to English, the following sounds are required:
00820  - "millions"
00821  - "1-and" through "9-and"
00822  - "1F" (eine)
00823  - "1N" (ein)
00824  - NB "1" is recorded as 'eins'
00825  */
00826 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)
00827 {
00828    int res = 0, t = 0;
00829    int mf = 1;                            /* +1 = male and neuter; -1 = female */
00830    char fn[256] = "";
00831    char fna[256] = "";
00832    if (!num)
00833       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
00834 
00835    if (options && (!strncasecmp(options, "f", 1)))
00836       mf = -1;
00837 
00838    while (!res && num) {
00839       /* The grammar for German numbers is the same as for English except
00840       * for the following:
00841       * - numbers 20 through 99 are said in reverse order, i.e. 21 is
00842       *   "one-and twenty" and 68 is "eight-and sixty".
00843       * - "one" varies according to gender
00844       * - 100 is 'hundert', however all other instances are 'ein hundert'
00845       * - 1000 is 'tausend', however all other instances are 'ein tausend'
00846       * - 1000000 is always 'eine million'
00847       * - "million" is different in singular and plural form
00848       * - 'and' should not go between a hundreds place value and any
00849       *   tens/ones place values that follows it. i.e 136 is ein hundert
00850       *   sechs und dreizig, not ein hundert und sechs und dreizig.
00851       */
00852       if (num < 0) {
00853          ast_copy_string(fn, "digits/minus", sizeof(fn));
00854          if ( num > INT_MIN ) {
00855             num = -num;
00856          } else {
00857             num = 0;
00858          }
00859       } else if (num == 1 && mf == -1) {
00860          snprintf(fn, sizeof(fn), "digits/%dF", num);
00861          num = 0;
00862       } else if (num < 20) {
00863          snprintf(fn, sizeof(fn), "digits/%d", num);
00864          num = 0;
00865       } else if (num < 100) {
00866          int ones = num % 10;
00867          if (ones) {
00868             snprintf(fn, sizeof(fn), "digits/%d-and", ones);
00869             num -= ones;
00870          } else {
00871             snprintf(fn, sizeof(fn), "digits/%d", num);
00872             num = 0;
00873          }
00874       } else if (num == 100 && t == 0) {
00875          ast_copy_string(fn, "digits/hundred", sizeof(fn));
00876          num = 0;
00877       } else if (num < 1000) {
00878          int hundreds = num / 100;
00879          num = num % 100;
00880          if (hundreds == 1) {
00881             ast_copy_string(fn, "digits/1N", sizeof(fn));
00882          } else {
00883             snprintf(fn, sizeof(fn), "digits/%d", hundreds);
00884          }
00885          ast_copy_string(fna, "digits/hundred", sizeof(fna));
00886       } else if (num == 1000 && t == 0) {
00887          ast_copy_string(fn, "digits/thousand", sizeof(fn));
00888          num = 0;
00889       } else if (num < 1000000) {
00890          int thousands = num / 1000;
00891          num = num % 1000;
00892          t = 1;
00893          if (thousands == 1) {
00894             ast_copy_string(fn, "digits/1N", sizeof(fn));
00895             ast_copy_string(fna, "digits/thousand", sizeof(fna));
00896          } else {
00897             res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
00898             if (res)
00899                return res;
00900             ast_copy_string(fn, "digits/thousand", sizeof(fn));
00901          }
00902       } else if (num < 1000000000) {
00903          int millions = num / 1000000;
00904          num = num % 1000000;
00905          t = 1;
00906          if (millions == 1) {
00907             ast_copy_string(fn, "digits/1F", sizeof(fn));
00908             ast_copy_string(fna, "digits/million", sizeof(fna));
00909          } else {
00910             res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
00911             if (res)
00912                return res;
00913             ast_copy_string(fn, "digits/millions", sizeof(fn));
00914          }
00915       } else if (num <= INT_MAX) {
00916          int billions = num / 1000000000;
00917          num = num % 1000000000;
00918          t = 1;
00919          if (billions == 1) {
00920             ast_copy_string(fn, "digits/1F", sizeof(fn));
00921             ast_copy_string(fna, "digits/milliard", sizeof(fna));
00922          } else {
00923             res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
00924             if (res) {
00925                return res;
00926             }
00927             ast_copy_string(fn, "digits/milliards", sizeof(fn));
00928          }
00929       } else {
00930          ast_debug(1, "Number '%d' is too big for me\n", num);
00931          res = -1;
00932       }
00933       if (!res) {
00934          if (!ast_streamfile(chan, fn, language)) {
00935             if ((audiofd > -1) && (ctrlfd > -1))
00936                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00937             else
00938                res = ast_waitstream(chan, ints);
00939          }
00940          ast_stopstream(chan);
00941          if (!res) {
00942             if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
00943                if ((audiofd > -1) && (ctrlfd > -1))
00944                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
00945                else
00946                   res = ast_waitstream(chan, ints);
00947             }
00948             ast_stopstream(chan);
00949             strcpy(fna, "");
00950          }
00951       }
00952    }
00953    return res;
00954 }
00955 
00956 /*! \brief  ast_say_number_full_en_GB: British syntax
00957  New files:
00958   - In addition to American English, the following sounds are required:  "and"
00959  */
00960 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
00961 {
00962    int res = 0;
00963    int playh = 0;
00964    int playa = 0;
00965    char fn[256] = "";
00966    if (!num)
00967       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
00968 
00969    while (!res && (num || playh || playa )) {
00970       if (num < 0) {
00971          ast_copy_string(fn, "digits/minus", sizeof(fn));
00972          if ( num > INT_MIN ) {
00973             num = -num;
00974          } else {
00975             num = 0;
00976          }
00977       } else if (playh) {
00978          ast_copy_string(fn, "digits/hundred", sizeof(fn));
00979          playh = 0;
00980       } else if (playa) {
00981          ast_copy_string(fn, "digits/and", sizeof(fn));
00982          playa = 0;
00983       } else if (num < 20) {
00984          snprintf(fn, sizeof(fn), "digits/%d", num);
00985          num = 0;
00986       } else if (num < 100) {
00987          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
00988          num %= 10;
00989       } else if (num < 1000) {
00990          int hundreds = num / 100;
00991          snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
00992 
00993          playh++;
00994          num -= 100 * hundreds;
00995          if (num)
00996             playa++;
00997       } else if (num < 1000000) {
00998          res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
00999          if (res)
01000             return res;
01001          ast_copy_string(fn, "digits/thousand", sizeof(fn));
01002          num %= 1000;
01003          if (num && num < 100)
01004             playa++;
01005       } else if (num < 1000000000) {
01006             int millions = num / 1000000;
01007             res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
01008             if (res)
01009                return res;
01010             ast_copy_string(fn, "digits/million", sizeof(fn));
01011             num %= 1000000;
01012             if (num && num < 100)
01013                playa++;
01014       } else {
01015             ast_debug(1, "Number '%d' is too big for me\n", num);
01016             res = -1;
01017       }
01018 
01019       if (!res) {
01020          if (!ast_streamfile(chan, fn, language)) {
01021             if ((audiofd > -1) && (ctrlfd > -1))
01022                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01023             else
01024                res = ast_waitstream(chan, ints);
01025          }
01026          ast_stopstream(chan);
01027       }
01028    }
01029    return res;
01030 }
01031 
01032 /*! \brief  ast_say_number_full_es: Spanish syntax
01033 
01034  New files:
01035  Requires a few new audios:
01036    1F.gsm: feminine 'una'
01037    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
01038  */
01039 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)
01040 {
01041    int res = 0;
01042    int playa = 0;
01043    int mf = 0;                            /* +1 = male; -1 = female */
01044    char fn[256] = "";
01045    if (!num)
01046       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
01047 
01048    if (options) {
01049       if (!strncasecmp(options, "f", 1))
01050          mf = -1;
01051       else if (!strncasecmp(options, "m", 1))
01052          mf = 1;
01053    }
01054 
01055    while (!res && num) {
01056       if (num < 0) {
01057          ast_copy_string(fn, "digits/minus", sizeof(fn));
01058          if ( num > INT_MIN ) {
01059             num = -num;
01060          } else {
01061             num = 0;
01062          }
01063       } else if (playa) {
01064          ast_copy_string(fn, "digits/and", sizeof(fn));
01065          playa = 0;
01066       } else if (num == 1) {
01067          if (mf < 0)
01068             snprintf(fn, sizeof(fn), "digits/%dF", num);
01069          else if (mf > 0)
01070             snprintf(fn, sizeof(fn), "digits/%dM", num);
01071          else
01072             snprintf(fn, sizeof(fn), "digits/%d", num);
01073          num = 0;
01074       } else if (num < 31) {
01075          snprintf(fn, sizeof(fn), "digits/%d", num);
01076          num = 0;
01077       } else if (num < 100) {
01078          snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
01079          num %= 10;
01080          if (num)
01081             playa++;
01082       } else if (num == 100) {
01083          ast_copy_string(fn, "digits/100", sizeof(fn));
01084          num = 0;
01085       } else if (num < 200) {
01086          ast_copy_string(fn, "digits/100-and", sizeof(fn));
01087          num -= 100;
01088       } else {
01089          if (num < 1000) {
01090             snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
01091             num %= 100;
01092          } else if (num < 2000) {
01093             num %= 1000;
01094             ast_copy_string(fn, "digits/thousand", sizeof(fn));
01095          } else {
01096             if (num < 1000000) {
01097                res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
01098                if (res)
01099                   return res;
01100                num %= 1000;
01101                ast_copy_string(fn, "digits/thousand", sizeof(fn));
01102             } else {
01103                if (num < 2147483640) {
01104                   if ((num/1000000) == 1) {
01105                      res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
01106                      if (res)
01107                         return res;
01108                      ast_copy_string(fn, "digits/million", sizeof(fn));
01109                   } else {
01110                      res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
01111                      if (res)
01112                         return res;
01113                      ast_copy_string(fn, "digits/millions", sizeof(fn));
01114                   }
01115                   num %= 1000000;
01116                } else {
01117                   ast_debug(1, "Number '%d' is too big for me\n", num);
01118                   res = -1;
01119                }
01120             }
01121          }
01122       }
01123 
01124       if (!res) {
01125          if (!ast_streamfile(chan, fn, language)) {
01126             if ((audiofd > -1) && (ctrlfd > -1))
01127                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01128             else
01129                res = ast_waitstream(chan, ints);
01130          }
01131          ast_stopstream(chan);
01132 
01133       }
01134 
01135    }
01136    return res;
01137 }
01138 
01139 /*! \brief  ast_say_number_full_fr: French syntax
01140    Extra sounds needed:
01141    1F: feminin 'une'
01142    et: 'and' */
01143 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)
01144 {
01145    int res = 0;
01146    int playh = 0;
01147    int playa = 0;
01148    int mf = 1;                            /* +1 = male; -1 = female */
01149    char fn[256] = "";
01150    if (!num)
01151       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
01152 
01153    if (options && !strncasecmp(options, "f", 1))
01154       mf = -1;
01155 
01156    while (!res && (num || playh || playa)) {
01157       if (num < 0) {
01158          ast_copy_string(fn, "digits/minus", sizeof(fn));
01159          if ( num > INT_MIN ) {
01160             num = -num;
01161          } else {
01162             num = 0;
01163          }
01164       } else if (playh) {
01165          ast_copy_string(fn, "digits/hundred", sizeof(fn));
01166          playh = 0;
01167       } else if (playa) {
01168          ast_copy_string(fn, "digits/et", sizeof(fn));
01169          playa = 0;
01170       } else if (num == 1) {
01171          if (mf < 0)
01172             snprintf(fn, sizeof(fn), "digits/%dF", num);
01173          else
01174             snprintf(fn, sizeof(fn), "digits/%d", num);
01175          num = 0;
01176       } else if (num < 21) {
01177          snprintf(fn, sizeof(fn), "digits/%d", num);
01178          num = 0;
01179       } else if (num < 70) {
01180          snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
01181          if ((num % 10) == 1) playa++;
01182          num = num % 10;
01183       } else if (num < 80) {
01184          ast_copy_string(fn, "digits/60", sizeof(fn));
01185          if ((num % 10) == 1) playa++;
01186          num -= 60;
01187       } else if (num < 100) {
01188          ast_copy_string(fn, "digits/80", sizeof(fn));
01189          num = num - 80;
01190       } else if (num < 200) {
01191          ast_copy_string(fn, "digits/hundred", sizeof(fn));
01192          num = num - 100;
01193       } else if (num < 1000) {
01194          snprintf(fn, sizeof(fn), "digits/%d", (num/100));
01195          playh++;
01196          num = num % 100;
01197       } else if (num < 2000) {
01198          ast_copy_string(fn, "digits/thousand", sizeof(fn));
01199          num = num - 1000;
01200       } else if (num < 1000000) {
01201          res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
01202          if (res)
01203             return res;
01204          ast_copy_string(fn, "digits/thousand", sizeof(fn));
01205          num = num % 1000;
01206       } else   if (num < 1000000000) {
01207          res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
01208          if (res)
01209             return res;
01210          ast_copy_string(fn, "digits/million", sizeof(fn));
01211          num = num % 1000000;
01212       } else {
01213          ast_debug(1, "Number '%d' is too big for me\n", num);
01214          res = -1;
01215       }
01216       if (!res) {
01217          if (!ast_streamfile(chan, fn, language)) {
01218             if ((audiofd > -1) && (ctrlfd > -1))
01219                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01220             else
01221                res = ast_waitstream(chan, ints);
01222          }
01223          ast_stopstream(chan);
01224       }
01225    }
01226    return res;
01227 }
01228 
01229 
01230 
01231 /* Hebrew syntax
01232  * Check doc/lang/hebrew-digits.txt for information about the various
01233  * recordings required to make this translation work properly */
01234 #define SAY_NUM_BUF_SIZE 256
01235 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)
01236 {
01237    int res = 0;
01238    int state = 0;          /* no need to save anything */
01239    int mf = -1;            /* +1 = Masculin; -1 = Feminin */
01240    int tmpnum = 0;
01241 
01242    char fn[SAY_NUM_BUF_SIZE] = "";
01243 
01244    ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
01245 
01246    if (!num) {
01247       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
01248    }
01249    if (options && !strncasecmp(options, "m", 1)) {
01250       mf = 1;
01251    }
01252    ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
01253 
01254    /* Do we have work to do? */
01255    while (!res && (num || (state > 0))) {
01256       /* first type of work: play a second sound. In this loop
01257        * we can only play one sound file at a time. Thus playing
01258        * a second one requires repeating the loop just for the
01259        * second file. The variable 'state' remembers where we were.
01260        * state==0 is the normal mode and it means that we continue
01261        * to check if the number num has yet anything left.
01262        */
01263       ast_verb(3, "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num, state, options, mf, tmpnum);
01264 
01265       if (state == 1) {
01266          state = 0;
01267       } else if (state == 2) {
01268          if ((num >= 11) && (num < 21)) {
01269             if (mf < 0) {
01270                snprintf(fn, sizeof(fn), "digits/ve");
01271             } else {
01272                snprintf(fn, sizeof(fn), "digits/uu");
01273             }
01274          } else {
01275             switch (num) {
01276             case 1:
01277                snprintf(fn, sizeof(fn), "digits/ve");
01278                break;
01279             case 2:
01280                snprintf(fn, sizeof(fn), "digits/uu");
01281                break;
01282             case 3:
01283                if (mf < 0) {
01284                   snprintf(fn, sizeof(fn), "digits/ve");
01285                } else {
01286                   snprintf(fn, sizeof(fn), "digits/uu");
01287                }
01288                break;
01289             case 4:
01290                snprintf(fn, sizeof(fn), "digits/ve");
01291                break;
01292             case 5:
01293                snprintf(fn, sizeof(fn), "digits/ve");
01294                break;
01295             case 6:
01296                snprintf(fn, sizeof(fn), "digits/ve");
01297                break;
01298             case 7:
01299                snprintf(fn, sizeof(fn), "digits/ve");
01300                break;
01301             case 8:
01302                snprintf(fn, sizeof(fn), "digits/uu");
01303                break;
01304             case 9:
01305                snprintf(fn, sizeof(fn), "digits/ve");
01306                break;
01307             case 10:
01308                snprintf(fn, sizeof(fn), "digits/ve");
01309                break;
01310             }
01311          }
01312          state = 0;
01313       } else if (state == 3) {
01314          snprintf(fn, sizeof(fn), "digits/1k");
01315          state = 0;
01316       } else if (num < 0) {
01317          snprintf(fn, sizeof(fn), "digits/minus");
01318          num = (-1) * num;
01319       } else if (num < 20) {
01320          if (mf < 0) {
01321             snprintf(fn, sizeof(fn), "digits/%d", num);
01322          } else {
01323             snprintf(fn, sizeof(fn), "digits/%dm", num);
01324          }
01325          num = 0;
01326       } else if ((num < 100) && (num >= 20)) {
01327          snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
01328          num = num % 10;
01329          if (num > 0) {
01330             state = 2;
01331          }
01332       } else if ((num >= 100) && (num < 1000)) {
01333          tmpnum = num / 100;
01334          snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
01335          num = num - (tmpnum * 100);
01336          if ((num > 0) && (num < 11)) {
01337             state = 2;
01338          }
01339       } else if ((num >= 1000) && (num < 10000)) {
01340          tmpnum = num / 1000;
01341          snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
01342          num = num - (tmpnum * 1000);
01343          if ((num > 0) && (num < 11)) {
01344             state = 2;
01345          }
01346       } else if (num < 20000) {
01347          snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
01348          num = num % 1000;
01349          state = 3;
01350       } else if (num < 1000000) {
01351          res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
01352          if (res) {
01353             return res;
01354          }
01355          snprintf(fn, sizeof(fn), "digits/1k");
01356          num = num % 1000;
01357          if ((num > 0) && (num < 11)) {
01358             state = 2;
01359          }
01360       } else if (num < 2000000) {
01361          snprintf(fn, sizeof(fn), "digits/million");
01362          num = num % 1000000;
01363          if ((num > 0) && (num < 11)) {
01364             state = 2;
01365          }
01366       } else if (num < 3000000) {
01367          snprintf(fn, sizeof(fn), "digits/twomillion");
01368          num = num - 2000000;
01369          if ((num > 0) && (num < 11)) {
01370             state = 2;
01371          }
01372       } else if (num < 1000000000) {
01373          res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
01374          if (res) {
01375             return res;
01376          }
01377          snprintf(fn, sizeof(fn), "digits/million");
01378          num = num % 1000000;
01379          if ((num > 0) && (num < 11)) {
01380             state = 2;
01381          }
01382       } else {
01383          ast_debug(1, "Number '%d' is too big for me\n", num);
01384          res = -1;
01385       }
01386       tmpnum = 0;
01387       if (!res) {
01388          if (!ast_streamfile(chan, fn, language)) {
01389             if ((audiofd > -1) && (ctrlfd > -1)) {
01390                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01391             } else {
01392                res = ast_waitstream(chan, ints);
01393             }
01394          }
01395          ast_stopstream(chan);
01396       }
01397    }
01398    return res;
01399 }
01400 
01401 /*! \brief  ast_say_number_full_hu: Hungarian syntax
01402 
01403   Extra sounds needed:
01404    10en: "tizen"
01405    20on: "huszon"
01406 */
01407 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
01408 {
01409    int res = 0;
01410    int playh = 0;
01411    char fn[256] = "";
01412    if (!num)
01413       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
01414 
01415    /*
01416    Hungarian support
01417    like english, except numbers up to 29 are from 2 words.
01418    10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
01419    */
01420 
01421    while(!res && (num || playh)) {
01422       if (num < 0) {
01423          ast_copy_string(fn, "digits/minus", sizeof(fn));
01424          if ( num > INT_MIN ) {
01425             num = -num;
01426          } else {
01427             num = 0;
01428          }
01429       } else if (playh) {
01430          ast_copy_string(fn, "digits/hundred", sizeof(fn));
01431          playh = 0;
01432       } else if (num < 11 || num == 20) {
01433          snprintf(fn, sizeof(fn), "digits/%d", num);
01434          num = 0;
01435       } else if (num < 20) {
01436          ast_copy_string(fn, "digits/10en", sizeof(fn));
01437          num -= 10;
01438       } else if (num < 30) {
01439          ast_copy_string(fn, "digits/20on", sizeof(fn));
01440          num -= 20;
01441       } else   if (num < 100) {
01442          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
01443          num %= 10;
01444       } else {
01445          if (num < 1000){
01446             snprintf(fn, sizeof(fn), "digits/%d", (num/100));
01447             playh++;
01448             num %= 100;
01449          } else {
01450             if (num < 1000000) { /* 1,000,000 */
01451                res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
01452                if (res)
01453                   return res;
01454                num %= 1000;
01455                ast_copy_string(fn, "digits/thousand", sizeof(fn));
01456             } else {
01457                if (num < 1000000000) { /* 1,000,000,000 */
01458                   res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
01459                   if (res)
01460                      return res;
01461                   num %= 1000000;
01462                   ast_copy_string(fn, "digits/million", sizeof(fn));
01463                } else {
01464                   ast_debug(1, "Number '%d' is too big for me\n", num);
01465                   res = -1;
01466                }
01467             }
01468          }
01469       }
01470       if (!res) {
01471          if(!ast_streamfile(chan, fn, language)) {
01472             if ((audiofd  > -1) && (ctrlfd > -1))
01473                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01474             else
01475                res = ast_waitstream(chan, ints);
01476          }
01477          ast_stopstream(chan);
01478       }
01479    }
01480    return res;
01481 }
01482 
01483 /*! \brief  ast_say_number_full_it:  Italian */
01484 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
01485 {
01486    int res = 0;
01487    int playh = 0;
01488    int tempnum = 0;
01489    char fn[256] = "";
01490 
01491    if (!num)
01492       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
01493 
01494       /*
01495       Italian support
01496 
01497       Like english, numbers up to 20 are a single 'word', and others
01498       compound, but with exceptions.
01499       For example 21 is not twenty-one, but there is a single word in 'it'.
01500       Idem for 28 (ie when a the 2nd part of a compund number
01501       starts with a vowel)
01502 
01503       There are exceptions also for hundred, thousand and million.
01504       In english 100 = one hundred, 200 is two hundred.
01505       In italian 100 = cento , like to say hundred (without one),
01506       200 and more are like english.
01507 
01508       Same applies for thousand:
01509       1000 is one thousand in en, 2000 is two thousand.
01510       In it we have 1000 = mille , 2000 = 2 mila
01511 
01512       For million(s) we use the plural, if more than one
01513       Also, one million is abbreviated in it, like on-million,
01514       or 'un milione', not 'uno milione'.
01515       So the right file is provided.
01516       */
01517 
01518    while (!res && (num || playh)) {
01519          if (num < 0) {
01520             ast_copy_string(fn, "digits/minus", sizeof(fn));
01521             if ( num > INT_MIN ) {
01522                num = -num;
01523             } else {
01524                num = 0;
01525             }
01526          } else if (playh) {
01527             ast_copy_string(fn, "digits/hundred", sizeof(fn));
01528             playh = 0;
01529          } else if (num < 20) {
01530             snprintf(fn, sizeof(fn), "digits/%d", num);
01531             num = 0;
01532          } else if (num == 21) {
01533             snprintf(fn, sizeof(fn), "digits/%d", num);
01534             num = 0;
01535          } else if (num == 28) {
01536             snprintf(fn, sizeof(fn), "digits/%d", num);
01537             num = 0;
01538          } else if (num == 31) {
01539             snprintf(fn, sizeof(fn), "digits/%d", num);
01540             num = 0;
01541          } else if (num == 38) {
01542             snprintf(fn, sizeof(fn), "digits/%d", num);
01543             num = 0;
01544          } else if (num == 41) {
01545             snprintf(fn, sizeof(fn), "digits/%d", num);
01546             num = 0;
01547          } else if (num == 48) {
01548             snprintf(fn, sizeof(fn), "digits/%d", num);
01549             num = 0;
01550          } else if (num == 51) {
01551             snprintf(fn, sizeof(fn), "digits/%d", num);
01552             num = 0;
01553          } else if (num == 58) {
01554             snprintf(fn, sizeof(fn), "digits/%d", num);
01555             num = 0;
01556          } else if (num == 61) {
01557             snprintf(fn, sizeof(fn), "digits/%d", num);
01558             num = 0;
01559          } else if (num == 68) {
01560             snprintf(fn, sizeof(fn), "digits/%d", num);
01561             num = 0;
01562          } else if (num == 71) {
01563             snprintf(fn, sizeof(fn), "digits/%d", num);
01564             num = 0;
01565          } else if (num == 78) {
01566             snprintf(fn, sizeof(fn), "digits/%d", num);
01567             num = 0;
01568          } else if (num == 81) {
01569             snprintf(fn, sizeof(fn), "digits/%d", num);
01570             num = 0;
01571          } else if (num == 88) {
01572             snprintf(fn, sizeof(fn), "digits/%d", num);
01573             num = 0;
01574          } else if (num == 91) {
01575             snprintf(fn, sizeof(fn), "digits/%d", num);
01576             num = 0;
01577          } else if (num == 98) {
01578             snprintf(fn, sizeof(fn), "digits/%d", num);
01579             num = 0;
01580          } else if (num < 100) {
01581             snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
01582             num %= 10;
01583          } else {
01584             if (num < 1000) {
01585                if ((num / 100) > 1) {
01586                   snprintf(fn, sizeof(fn), "digits/%d", (num/100));
01587                   playh++;
01588                } else {
01589                   ast_copy_string(fn, "digits/hundred", sizeof(fn));
01590                }
01591                num %= 100;
01592             } else {
01593                if (num < 1000000) { /* 1,000,000 */
01594                   if ((num/1000) > 1)
01595                      res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
01596                   if (res)
01597                      return res;
01598                   tempnum = num;
01599                   num %= 1000;
01600                   if ((tempnum / 1000) < 2)
01601                      ast_copy_string(fn, "digits/thousand", sizeof(fn));
01602                   else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
01603                      ast_copy_string(fn, "digits/thousands", sizeof(fn));
01604                } else {
01605                   if (num < 1000000000) { /* 1,000,000,000 */
01606                      if ((num / 1000000) > 1)
01607                         res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
01608                      if (res)
01609                         return res;
01610                      tempnum = num;
01611                      num %= 1000000;
01612                      if ((tempnum / 1000000) < 2)
01613                         ast_copy_string(fn, "digits/million", sizeof(fn));
01614                      else
01615                         ast_copy_string(fn, "digits/millions", sizeof(fn));
01616                   } else {
01617                      ast_debug(1, "Number '%d' is too big for me\n", num);
01618                      res = -1;
01619                   }
01620                }
01621             }
01622          }
01623          if (!res) {
01624             if (!ast_streamfile(chan, fn, language)) {
01625                if ((audiofd > -1) && (ctrlfd > -1))
01626                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01627                else
01628                   res = ast_waitstream(chan, ints);
01629             }
01630             ast_stopstream(chan);
01631          }
01632       }
01633    return res;
01634 }
01635 
01636 /*! \brief  ast_say_number_full_nl: dutch syntax
01637  * New files: digits/nl-en
01638  */
01639 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
01640 {
01641    int res = 0;
01642    int playh = 0;
01643    int units = 0;
01644    char fn[256] = "";
01645    if (!num)
01646       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
01647    while (!res && (num || playh )) {
01648       if (num < 0) {
01649          ast_copy_string(fn, "digits/minus", sizeof(fn));
01650          if ( num > INT_MIN ) {
01651             num = -num;
01652          } else {
01653             num = 0;
01654          }
01655       } else if (playh) {
01656          ast_copy_string(fn, "digits/hundred", sizeof(fn));
01657          playh = 0;
01658       } else if (num < 20) {
01659          snprintf(fn, sizeof(fn), "digits/%d", num);
01660          num = 0;
01661       } else if (num < 100) {
01662          units = num % 10;
01663          if (units > 0) {
01664             res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
01665             if (res)
01666                return res;
01667             num = num - units;
01668             ast_copy_string(fn, "digits/nl-en", sizeof(fn));
01669          } else {
01670             snprintf(fn, sizeof(fn), "digits/%d", num - units);
01671             num = 0;
01672          }
01673       } else if (num < 200) {
01674          /* hundred, not one-hundred */
01675          ast_copy_string(fn, "digits/hundred", sizeof(fn));
01676          num %= 100;
01677       } else if (num < 1000) {
01678          snprintf(fn, sizeof(fn), "digits/%d", num / 100);
01679          playh++;
01680          num %= 100;
01681       } else {
01682          if (num < 1100) {
01683             /* thousand, not one-thousand */
01684             num %= 1000;
01685             ast_copy_string(fn, "digits/thousand", sizeof(fn));
01686          } else if (num < 10000) { /* 1,100 to 9,9999 */
01687             res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
01688             if (res)
01689                return res;
01690             num %= 100;
01691             ast_copy_string(fn, "digits/hundred", sizeof(fn));
01692          } else {
01693             if (num < 1000000) { /* 1,000,000 */
01694                res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
01695                if (res)
01696                   return res;
01697                num %= 1000;
01698                ast_copy_string(fn, "digits/thousand", sizeof(fn));
01699             } else {
01700                if (num < 1000000000) { /* 1,000,000,000 */
01701                   res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
01702                   if (res)
01703                      return res;
01704                   num %= 1000000;
01705                   ast_copy_string(fn, "digits/million", sizeof(fn));
01706                } else {
01707                   ast_debug(1, "Number '%d' is too big for me\n", num);
01708                   res = -1;
01709                }
01710             }
01711          }
01712       }
01713 
01714       if (!res) {
01715          if (!ast_streamfile(chan, fn, language)) {
01716             if ((audiofd > -1) && (ctrlfd > -1))
01717                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01718             else
01719                res = ast_waitstream(chan, ints);
01720          }
01721          ast_stopstream(chan);
01722       }
01723    }
01724    return res;
01725 }
01726 
01727 /*! \brief  ast_say_number_full_no: Norwegian syntax
01728  * New files:
01729  * In addition to American English, the following sounds are required:  "and", "1N"
01730  *
01731  * The grammar for Norwegian numbers is the same as for English except
01732  * for the following:
01733  * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
01734  *   "and" before the last two digits, i.e. 2034 is "two thousand and
01735  *   thirty-four" and 1000012 is "one million and twelve".
01736  */
01737 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)
01738 {
01739    int res = 0;
01740    int playh = 0;
01741    int playa = 0;
01742    int cn = 1;    /* +1 = commune; -1 = neuter */
01743    char fn[256] = "";
01744 
01745    if (!num)
01746       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
01747 
01748    if (options && !strncasecmp(options, "n", 1)) cn = -1;
01749 
01750    while (!res && (num || playh || playa )) {
01751       if (num < 0) {
01752          ast_copy_string(fn, "digits/minus", sizeof(fn));
01753          if ( num > INT_MIN ) {
01754             num = -num;
01755          } else {
01756             num = 0;
01757          }
01758       } else if (playh) {
01759          ast_copy_string(fn, "digits/hundred", sizeof(fn));
01760          playh = 0;
01761       } else if (playa) {
01762          ast_copy_string(fn, "digits/and", sizeof(fn));
01763          playa = 0;
01764       } else if (num == 1 && cn == -1) {
01765          ast_copy_string(fn, "digits/1N", sizeof(fn));
01766          num = 0;
01767       } else if (num < 20) {
01768          snprintf(fn, sizeof(fn), "digits/%d", num);
01769          num = 0;
01770       } else if (num < 100) {
01771          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
01772          num %= 10;
01773       } else if (num < 1000) {
01774          int hundreds = num / 100;
01775          if (hundreds == 1)
01776             ast_copy_string(fn, "digits/1N", sizeof(fn));
01777          else
01778             snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
01779 
01780          playh++;
01781          num -= 100 * hundreds;
01782          if (num)
01783             playa++;
01784       } else if (num < 1000000) {
01785          res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
01786          if (res)
01787             return res;
01788          ast_copy_string(fn, "digits/thousand", sizeof(fn));
01789          num %= 1000;
01790          if (num && num < 100)
01791             playa++;
01792       } else if (num < 1000000000) {
01793             int millions = num / 1000000;
01794             res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
01795             if (res)
01796                return res;
01797             ast_copy_string(fn, "digits/million", sizeof(fn));
01798             num %= 1000000;
01799             if (num && num < 100)
01800                playa++;
01801       } else {
01802             ast_debug(1, "Number '%d' is too big for me\n", num);
01803             res = -1;
01804       }
01805 
01806       if (!res) {
01807          if (!ast_streamfile(chan, fn, language)) {
01808             if ((audiofd > -1) && (ctrlfd > -1))
01809                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01810             else
01811                res = ast_waitstream(chan, ints);
01812          }
01813          ast_stopstream(chan);
01814       }
01815    }
01816    return res;
01817 }
01818 
01819 typedef struct {
01820    char *separator_dziesiatek;
01821    char *cyfry[10];
01822    char *cyfry2[10];
01823    char *setki[10];
01824    char *dziesiatki[10];
01825    char *nastki[10];
01826    char *rzedy[3][3];
01827 } odmiana;
01828 
01829 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
01830 {
01831    if (rzad==0)
01832       return "";
01833 
01834    if (i==1)
01835       return odm->rzedy[rzad - 1][0];
01836    if ((i > 21 || i < 11) &&  i%10 > 1 && i%10 < 5)
01837       return odm->rzedy[rzad - 1][1];
01838    else
01839       return odm->rzedy[rzad - 1][2];
01840 }
01841 
01842 static char* pl_append(char* buffer, char* str)
01843 {
01844    strcpy(buffer, str);
01845    buffer += strlen(str);
01846    return buffer;
01847 }
01848 
01849 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
01850 {
01851    char file_name[255] = "digits/";
01852    strcat(file_name, fn);
01853    ast_debug(1, "Trying to play: %s\n", file_name);
01854    if (!ast_streamfile(chan, file_name, language)) {
01855       if ((audiofd > -1) && (ctrlfd > -1))
01856          ast_waitstream_full(chan, ints, audiofd, ctrlfd);
01857       else
01858          ast_waitstream(chan, ints);
01859    }
01860    ast_stopstream(chan);
01861 }
01862 
01863 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
01864 {
01865    /* Initialise variables to allow compilation on Debian-stable, etc */
01866    int m1000E6 = 0;
01867    int i1000E6 = 0;
01868    int m1000E3 = 0;
01869    int i1000E3 = 0;
01870    int m1000 = 0;
01871    int i1000 = 0;
01872    int m100 = 0;
01873    int i100 = 0;
01874 
01875    if (i == 0 && rzad > 0) {
01876       return;
01877    }
01878    if (i == 0) {
01879       pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
01880       return;
01881    }
01882 
01883    m1000E6 = i % 1000000000;
01884    i1000E6 = i / 1000000000;
01885 
01886    powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
01887 
01888    m1000E3 = m1000E6 % 1000000;
01889    i1000E3 = m1000E6 / 1000000;
01890 
01891    powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
01892 
01893    m1000 = m1000E3 % 1000;
01894    i1000 = m1000E3 / 1000;
01895 
01896    powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
01897 
01898    m100 = m1000 % 100;
01899    i100 = m1000 / 100;
01900 
01901    if (i100>0)
01902       pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
01903 
01904    if (m100 > 0 && m100 <= 9) {
01905       if (m1000 > 0)
01906          pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
01907       else
01908          pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
01909    } else if (m100 % 10 == 0 && m100 != 0) {
01910       pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
01911    } else if (m100 > 10 && m100 <= 19) {
01912       pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
01913    } else if (m100 > 20) {
01914       if (odm->separator_dziesiatek[0] == ' ') {
01915          pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
01916          pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
01917       } else {
01918          char buf[10];
01919          char *b = buf;
01920          b = pl_append(b, odm->dziesiatki[m100 / 10]);
01921          b = pl_append(b, odm->separator_dziesiatek);
01922          pl_append(b, odm->cyfry2[m100 % 10]);
01923          pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
01924       }
01925    }
01926 
01927    if (rzad > 0) {
01928       pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
01929    }
01930 }
01931 
01932 /* ast_say_number_full_pl: Polish syntax
01933 
01934 Sounds needed:
01935 0     zero
01936 1     jeden
01937 10    dziesiec
01938 100      sto
01939 1000     tysiac
01940 1000000     milion
01941 1000000000  miliard
01942 1000000000.2   miliardy
01943 1000000000.5   miliardow
01944 1000000.2   miliony
01945 1000000.5   milionow
01946 1000.2      tysiace
01947 1000.5      tysiecy
01948 100m     stu
01949 10m      dziesieciu
01950 11    jedenascie
01951 11m      jedenastu
01952 12    dwanascie
01953 12m      dwunastu
01954 13    trzynascie
01955 13m      trzynastu
01956 14    czternascie
01957 14m      czternastu
01958 15    pietnascie
01959 15m      pietnastu
01960 16    szesnascie
01961 16m      szesnastu
01962 17    siedemnascie
01963 17m      siedemnastu
01964 18    osiemnascie
01965 18m      osiemnastu
01966 19    dziewietnascie
01967 19m      dziewietnastu
01968 1z    jedna
01969 2     dwa
01970 20    dwadziescia
01971 200      dwiescie
01972 200m     dwustu
01973 20m      dwudziestu
01974 2-1m     dwaj
01975 2-2m     dwoch
01976 2z    dwie
01977 3     trzy
01978 30    trzydziesci
01979 300      trzysta
01980 300m     trzystu
01981 30m      trzydziestu
01982 3-1m     trzej
01983 3-2m     trzech
01984 4     cztery
01985 40    czterdziesci
01986 400      czterysta
01987 400m     czterystu
01988 40m      czterdziestu
01989 4-1m     czterej
01990 4-2m     czterech
01991 5     piec
01992 50    piecdziesiat
01993 500      piecset
01994 500m     pieciuset
01995 50m      piedziesieciu
01996 5m    pieciu
01997 6     szesc
01998 60    szescdziesiat
01999 600      szescset
02000 600m     szesciuset
02001 60m      szescdziesieciu
02002 6m    szesciu
02003 7     siedem
02004 70    siedemdziesiat
02005 700      siedemset
02006 700m     siedmiuset
02007 70m      siedemdziesieciu
02008 7m    siedmiu
02009 8     osiem
02010 80    osiemdziesiat
02011 800      osiemset
02012 800m     osmiuset
02013 80m      osiemdziesieciu
02014 8m    osmiu
02015 9     dziewiec
02016 90    dziewiecdziesiat
02017 900      dziewiecset
02018 900m     dziewieciuset
02019 90m      dziewiedziesieciu
02020 9m    dziewieciu
02021 and combinations of eg.: 20_1, 30m_3m, etc...
02022 
02023 */
02024 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)
02025 {
02026    char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
02027 
02028    char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
02029 
02030    char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m",  /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
02031 
02032    char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
02033 
02034    char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
02035 
02036    char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
02037 
02038    char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
02039 
02040    char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
02041 
02042    char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
02043 
02044    char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
02045 
02046    char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
02047 
02048    char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
02049 
02050    char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
02051 
02052    /* Initialise variables to allow compilation on Debian-stable, etc */
02053    odmiana *o;
02054 
02055    static odmiana *odmiana_nieosobowa = NULL;
02056    static odmiana *odmiana_meska = NULL;
02057    static odmiana *odmiana_zenska = NULL;
02058 
02059    if (odmiana_nieosobowa == NULL) {
02060       odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
02061 
02062       odmiana_nieosobowa->separator_dziesiatek = " ";
02063 
02064       memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
02065       memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
02066       memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
02067       memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
02068       memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
02069       memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
02070    }
02071 
02072    if (odmiana_zenska == NULL) {
02073       odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
02074 
02075       odmiana_zenska->separator_dziesiatek = " ";
02076 
02077       memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
02078       memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
02079       memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
02080       memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
02081       memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
02082       memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
02083    }
02084 
02085    if (odmiana_meska == NULL) {
02086       odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
02087 
02088       odmiana_meska->separator_dziesiatek = " ";
02089 
02090       memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
02091       memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
02092       memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
02093       memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
02094       memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
02095       memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
02096    }
02097 
02098    if (options) {
02099       if (strncasecmp(options, "f", 1) == 0)
02100          o = odmiana_zenska;
02101       else if (strncasecmp(options, "m", 1) == 0)
02102          o = odmiana_meska;
02103       else
02104          o = odmiana_nieosobowa;
02105    } else
02106       o = odmiana_nieosobowa;
02107 
02108    powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
02109    return 0;
02110 }
02111 
02112 /* ast_say_number_full_pt: Portuguese syntax
02113 
02114  * Extra sounds needed:
02115  * For feminin all sound files ends with F
02116  * 100E for 100+ something
02117  * 1000000S for plural
02118  * pt-e for 'and'
02119  */
02120 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)
02121 {
02122    int res = 0;
02123    int playh = 0;
02124    int mf = 1;                            /* +1 = male; -1 = female */
02125    char fn[256] = "";
02126 
02127    if (!num)
02128       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02129 
02130    if (options && !strncasecmp(options, "f", 1))
02131       mf = -1;
02132 
02133    while (!res && num ) {
02134       if (num < 0) {
02135          ast_copy_string(fn, "digits/minus", sizeof(fn));
02136          if ( num > INT_MIN ) {
02137             num = -num;
02138          } else {
02139             num = 0;
02140          }
02141       } else if (num < 20) {
02142          if ((num == 1 || num == 2) && (mf < 0))
02143             snprintf(fn, sizeof(fn), "digits/%dF", num);
02144          else
02145             snprintf(fn, sizeof(fn), "digits/%d", num);
02146          num = 0;
02147       } else if (num < 100) {
02148          snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
02149          if (num % 10)
02150             playh = 1;
02151          num = num % 10;
02152       } else if (num < 1000) {
02153          if (num == 100)
02154             ast_copy_string(fn, "digits/100", sizeof(fn));
02155          else if (num < 200)
02156             ast_copy_string(fn, "digits/100E", sizeof(fn));
02157          else {
02158             if (mf < 0 && num > 199)
02159                snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
02160             else
02161                snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
02162             if (num % 100)
02163                playh = 1;
02164          }
02165          num = num % 100;
02166       } else if (num < 1000000) {
02167          if (num > 1999) {
02168             res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
02169             if (res)
02170                return res;
02171          }
02172          ast_copy_string(fn, "digits/1000", sizeof(fn));
02173          if ((num % 1000) && ((num % 1000) < 100  || !(num % 100)))
02174             playh = 1;
02175          num = num % 1000;
02176       } else if (num < 1000000000) {
02177          res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
02178          if (res)
02179             return res;
02180          if (num < 2000000)
02181             ast_copy_string(fn, "digits/1000000", sizeof(fn));
02182          else
02183             ast_copy_string(fn, "digits/1000000S", sizeof(fn));
02184 
02185          if ((num % 1000000) &&
02186             /* no thousands */
02187             ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
02188             /* no hundreds and below */
02189             (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
02190             playh = 1;
02191          num = num % 1000000;
02192       } else {
02193          /* number is too big */
02194          ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
02195          res = -1;
02196       }
02197       if (!res) {
02198          if (!ast_streamfile(chan, fn, language)) {
02199             if ((audiofd > -1) && (ctrlfd > -1))
02200                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02201             else
02202                res = ast_waitstream(chan, ints);
02203          }
02204          ast_stopstream(chan);
02205       }
02206       if (!res && playh) {
02207          res = wait_file(chan, ints, "digits/pt-e", language);
02208          ast_stopstream(chan);
02209          playh = 0;
02210       }
02211    }
02212    return res;
02213 }
02214 
02215 /*! \brief  ast_say_number_full_se: Swedish syntax
02216 
02217  Sound files needed
02218  - 1N
02219 */
02220 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)
02221 {
02222    int playh = 0;
02223    int start = 1;
02224    char fn[256] = "";
02225    int cn = 1;    /* +1 = commune; -1 = neuter */
02226    int res = 0;
02227 
02228    if (!num) {
02229       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02230    }
02231    if (options && !strncasecmp(options, "n", 1)) cn = -1;
02232 
02233    while (num || playh) {
02234       if (num < 0) {
02235          ast_copy_string(fn, "digits/minus", sizeof(fn));
02236          if ( num > INT_MIN ) {
02237             num = -num;
02238          } else {
02239             num = 0;
02240          }
02241       } else if (playh) {
02242          ast_copy_string(fn, "digits/hundred", sizeof(fn));
02243          playh = 0;
02244       } else if (start  && num < 200 && num > 99 && cn == -1) {
02245          /* Don't say "en hundra" just say "hundra". */
02246          snprintf(fn, sizeof(fn), "digits/hundred");
02247          num -= 100;
02248       } else if (num == 1 && cn == -1) {  /* En eller ett? */
02249          ast_copy_string(fn, "digits/1N", sizeof(fn));
02250          num = 0;
02251       } else if (num < 20) {
02252          snprintf(fn, sizeof(fn), "digits/%d", num);
02253          num = 0;
02254       } else if (num < 100) { /* Below hundreds - teens and tens */
02255          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
02256          num %= 10;
02257       } else if (num < 1000) {
02258          /* Hundreds */
02259          snprintf(fn, sizeof(fn), "digits/%d", (num/100));
02260          playh++;
02261          num %= 100;
02262       } else if (num < 1000000) { /* 1,000,000 */
02263          /* Always say "ett hundra tusen", not "en hundra tusen" */
02264          res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
02265          if (res) {
02266             return res;
02267          }
02268          num %= 1000;
02269          ast_copy_string(fn, "digits/thousand", sizeof(fn));
02270       } else if (num < 1000000000) {   /* 1,000,000,000 */
02271          /* Always say "en miljon", not "ett miljon" */
02272          res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
02273          if (res) {
02274             return res;
02275          }
02276          num %= 1000000;
02277          ast_copy_string(fn, "digits/million", sizeof(fn));
02278       } else { /* Miljarder - Billions */
02279          ast_debug(1, "Number '%d' is too big for me\n", num);
02280          return -1;
02281       }
02282 
02283       if (!ast_streamfile(chan, fn, language)) {
02284          if ((audiofd > -1) && (ctrlfd > -1)) {
02285             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02286          } else {
02287             res = ast_waitstream(chan, ints);
02288          }
02289          ast_stopstream(chan);
02290          if (res) {
02291             return res;
02292          }
02293       }
02294       start = 0;
02295    }
02296    return 0;
02297 }
02298 
02299 /*! \brief  ast_say_number_full_zh: Taiwanese / Chinese syntax */
02300 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
02301 {
02302    int res = 0;
02303    int playh = 0;
02304    int playt = 0;
02305    int playz = 0;
02306    int last_length = 0;
02307    char buf[20] = "";
02308    char fn[256] = "";
02309    if (!num)
02310       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02311 
02312    while (!res && (num || playh || playt || playz)) {
02313          if (num < 0) {
02314             ast_copy_string(fn, "digits/minus", sizeof(fn));
02315             if ( num > INT_MIN ) {
02316                num = -num;
02317             } else {
02318                num = 0;
02319             }
02320          } else if (playz) {
02321             snprintf(fn, sizeof(fn), "digits/0");
02322             last_length = 0;
02323             playz = 0;
02324          } else if (playh) {
02325             ast_copy_string(fn, "digits/hundred", sizeof(fn));
02326             playh = 0;
02327          } else if (playt) {
02328             snprintf(fn, sizeof(fn), "digits/thousand");
02329             playt = 0;
02330          } else   if (num < 10) {
02331             snprintf(buf, 10, "%d", num);
02332             if (last_length - strlen(buf) > 1 && last_length != 0) {
02333                last_length = strlen(buf);
02334                playz++;
02335                continue;
02336             }
02337             snprintf(fn, sizeof(fn), "digits/%d", num);
02338             num = 0;
02339          } else   if (num < 100) {
02340             snprintf(buf, 10, "%d", num);
02341             if (last_length - strlen(buf) > 1 && last_length != 0) {
02342                last_length = strlen(buf);
02343                playz++;
02344                continue;
02345             }
02346             last_length = strlen(buf);
02347             snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
02348             num %= 10;
02349          } else {
02350             if (num < 1000){
02351                snprintf(buf, 10, "%d", num);
02352                if (last_length - strlen(buf) > 1 && last_length != 0) {
02353                   last_length = strlen(buf);
02354                   playz++;
02355                   continue;
02356                }
02357                snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
02358                playh++;
02359                snprintf(buf, 10, "%d", num);
02360                ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02361                last_length = strlen(buf);
02362                num -= ((num / 100) * 100);
02363             } else if (num < 10000){
02364                snprintf(buf, 10, "%d", num);
02365                snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
02366                playt++;
02367                snprintf(buf, 10, "%d", num);
02368                ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02369                last_length = strlen(buf);
02370                num -= ((num / 1000) * 1000);
02371             } else if (num < 100000000) { /* 100,000,000 */
02372                   res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
02373                   if (res)
02374                      return res;
02375                   snprintf(buf, 10, "%d", num);
02376                   ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02377                   num -= ((num / 10000) * 10000);
02378                   last_length = strlen(buf);
02379                   snprintf(fn, sizeof(fn), "digits/wan");
02380             } else {
02381                if (num < 1000000000) { /* 1,000,000,000 */
02382                   res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
02383                   if (res)
02384                      return res;
02385                   snprintf(buf, 10, "%d", num);
02386                   ast_debug(1, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02387                   last_length = strlen(buf);
02388                   num -= ((num / 100000000) * 100000000);
02389                   snprintf(fn, sizeof(fn), "digits/yi");
02390                } else {
02391                   ast_debug(1, "Number '%d' is too big for me\n", num);
02392                   res = -1;
02393                }
02394             }
02395          }
02396          if (!res) {
02397             if (!ast_streamfile(chan, fn, language)) {
02398                if ((audiofd > -1) && (ctrlfd > -1))
02399                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02400                else
02401                   res = ast_waitstream(chan, ints);
02402             }
02403             ast_stopstream(chan);
02404          }
02405    }
02406    return res;
02407 }
02408 
02409 /*!\internal
02410  * \brief Counting in Urdu, the national language of Pakistan
02411  * \since 1.8
02412  */
02413 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)
02414 {
02415    int res = 0;
02416    int playh = 0;
02417    char fn[256] = "";
02418 
02419    if (!num) {
02420       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02421    }
02422 
02423    while (!res && (num || playh)) {
02424       if (playh) {
02425          snprintf(fn, sizeof(fn), "digits/hundred");
02426          playh = 0;
02427       } else if (num < 100) {
02428          snprintf(fn, sizeof(fn), "digits/%d", num);
02429          num = 0;
02430       } else if (num < 1000) {
02431          snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
02432          playh++;
02433          num -= ((num / 100) * 100);
02434       } else if (num < 100000) { /* 1,00,000 */
02435          if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
02436             return res;
02437          }
02438          num = num % 1000;
02439          snprintf(fn, sizeof(fn), "digits/thousand");
02440       } else if (num < 10000000) { /* 1,00,00,000 */
02441          if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
02442             return res;
02443          }
02444          num = num % 100000;
02445          snprintf(fn, sizeof(fn), "digits/lac");
02446       } else if (num < 1000000000) { /* 1,00,00,00,000 */
02447          if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
02448             return res;
02449          }
02450          num = num % 10000000;
02451          snprintf(fn, sizeof(fn), "digits/crore");
02452       } else {
02453          ast_debug(1, "Number '%d' is too big for me\n", num);
02454          res = -1;
02455       }
02456 
02457       if (!res) {
02458          if (!ast_streamfile(chan, fn, language)) {
02459             if ((audiofd > -1) && (ctrlfd > -1)) {
02460                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02461             } else {
02462                res = ast_waitstream(chan, ints);
02463             }
02464          }
02465          ast_stopstream(chan);
02466       }
02467    }
02468    return res;
02469 }
02470 
02471 /*! \brief  determine last digits for thousands/millions (ru) */
02472 static int get_lastdigits_ru(int num) {
02473    if (num < 20) {
02474       return num;
02475    } else if (num < 100) {
02476       return get_lastdigits_ru(num % 10);
02477    } else if (num < 1000) {
02478       return get_lastdigits_ru(num % 100);
02479    }
02480    return 0;   /* number too big */
02481 }
02482 
02483 
02484 /*! \brief  ast_say_number_full_ru: Russian syntax
02485 
02486  additional files:
02487    n00.gsm        (one hundred, two hundred, ...)
02488    thousand.gsm
02489    million.gsm
02490    thousands-i.gsm      (tisyachi)
02491    million-a.gsm     (milliona)
02492    thousands.gsm
02493    millions.gsm
02494    1f.gsm         (odna)
02495    2f.gsm         (dve)
02496 
02497    where 'n' from 1 to 9
02498 */
02499 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)
02500 {
02501    int res = 0;
02502    int lastdigits = 0;
02503    char fn[256] = "";
02504    if (!num)
02505       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02506 
02507    while (!res && (num)) {
02508       if (num < 0) {
02509          ast_copy_string(fn, "digits/minus", sizeof(fn));
02510          if ( num > INT_MIN ) {
02511             num = -num;
02512          } else {
02513             num = 0;
02514          }
02515       } else   if (num < 20) {
02516          if (options && strlen(options) == 1 && num < 3) {
02517              snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
02518          } else {
02519             snprintf(fn, sizeof(fn), "digits/%d", num);
02520          }
02521          num = 0;
02522       } else if (num < 100) {
02523          snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
02524          num %= 10;
02525       } else if (num < 1000){
02526          snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
02527          num %= 100;
02528       } else if (num < 1000000) { /* 1,000,000 */
02529          lastdigits = get_lastdigits_ru(num / 1000);
02530          /* say thousands */
02531          if (lastdigits < 3) {
02532             res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
02533          } else {
02534             res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
02535          }
02536          if (res)
02537             return res;
02538          if (lastdigits == 1) {
02539             ast_copy_string(fn, "digits/thousand", sizeof(fn));
02540          } else if (lastdigits > 1 && lastdigits < 5) {
02541             ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
02542          } else {
02543             ast_copy_string(fn, "digits/thousands", sizeof(fn));
02544          }
02545          num %= 1000;
02546       } else if (num < 1000000000) {   /* 1,000,000,000 */
02547          lastdigits = get_lastdigits_ru(num / 1000000);
02548          /* say millions */
02549          res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
02550          if (res)
02551             return res;
02552          if (lastdigits == 1) {
02553             ast_copy_string(fn, "digits/million", sizeof(fn));
02554          } else if (lastdigits > 1 && lastdigits < 5) {
02555             ast_copy_string(fn, "digits/million-a", sizeof(fn));
02556          } else {
02557             ast_copy_string(fn, "digits/millions", sizeof(fn));
02558          }
02559          num %= 1000000;
02560       } else {
02561          ast_debug(1, "Number '%d' is too big for me\n", num);
02562          res = -1;
02563       }
02564       if (!res) {
02565          if (!ast_streamfile(chan, fn, language)) {
02566             if ((audiofd  > -1) && (ctrlfd > -1))
02567                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02568             else
02569                res = ast_waitstream(chan, ints);
02570          }
02571          ast_stopstream(chan);
02572       }
02573    }
02574    return res;
02575 }
02576 
02577 /*! \brief Thai syntax */
02578 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
02579 {
02580    int res = 0;
02581    int playh = 0;
02582    char fn[256] = "";
02583    if (!num)
02584       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02585 
02586    while(!res && (num || playh)) {
02587       if (num < 0) {
02588          ast_copy_string(fn, "digits/lop", sizeof(fn));
02589          if ( num > INT_MIN ) {
02590             num = -num;
02591          } else {
02592             num = 0;
02593          }
02594       } else if (playh) {
02595          ast_copy_string(fn, "digits/roi", sizeof(fn));
02596          playh = 0;
02597       } else if (num < 100) {
02598          if ((num <= 20) || ((num % 10) == 1)) {
02599             snprintf(fn, sizeof(fn), "digits/%d", num);
02600             num = 0;
02601          } else {
02602             snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
02603             num %= 10;
02604          }
02605       } else if (num < 1000) {
02606          snprintf(fn, sizeof(fn), "digits/%d", (num/100));
02607          playh++;
02608          num %= 100;
02609       } else if (num < 10000) { /* 10,000 */
02610          res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
02611          if (res)
02612             return res;
02613          num %= 1000;
02614          ast_copy_string(fn, "digits/pan", sizeof(fn));
02615       } else if (num < 100000) { /* 100,000 */
02616          res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
02617          if (res)
02618             return res;
02619          num %= 10000;
02620          ast_copy_string(fn, "digits/muan", sizeof(fn));
02621       } else if (num < 1000000) { /* 1,000,000 */
02622          res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
02623          if (res)
02624             return res;
02625          num %= 100000;
02626          ast_copy_string(fn, "digits/san", sizeof(fn));
02627       } else {
02628          res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
02629          if (res)
02630             return res;
02631          num %= 1000000;
02632          ast_copy_string(fn, "digits/larn", sizeof(fn));
02633       }
02634       if (!res) {
02635          if(!ast_streamfile(chan, fn, language)) {
02636             if ((audiofd  > -1) && (ctrlfd > -1))
02637                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02638             else
02639                res = ast_waitstream(chan, ints);
02640          }
02641          ast_stopstream(chan);
02642       }
02643    }
02644    return res;
02645 }
02646 
02647 /*! \brief  ast_say_number_full_vi: Vietnamese syntax */
02648 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
02649 {
02650    int res = 0;
02651    int playh = 0;
02652    int playoh = 0;
02653    int playohz = 0;
02654    int playz = 0;
02655    int playl = 0;
02656    char fn[256] = "";
02657    if (!num)
02658       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02659    while (!res && (num || playh)) {
02660       if (num < 0) {
02661          ast_copy_string(fn, "digits/minus", sizeof(fn));
02662          if ( num > INT_MIN ) {
02663             num = -num;
02664          } else {
02665             num = 0;
02666          }
02667       } else if (playl) {
02668          snprintf(fn, sizeof(fn), "digits/%da", num);
02669          playl = 0;
02670          num = 0;
02671       } else if (playh) {
02672          ast_copy_string(fn, "digits/hundred", sizeof(fn));
02673          playh = 0;
02674       } else if (playz) {
02675          ast_copy_string(fn, "digits/odd", sizeof(fn));
02676          playz = 0;
02677       } else if (playoh) {
02678          ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
02679          playoh = 0;
02680       } else if (playohz) {
02681          ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
02682          playohz = 0;
02683       } else   if (num < 20) {
02684          snprintf(fn, sizeof(fn), "digits/%d", num);
02685          num = 0;
02686       } else   if (num < 100) {
02687          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
02688          num %= 10;
02689          if ((num == 5) || (num == 4) || (num == 1)) playl++;
02690       } else {
02691          if (num < 1000) {
02692             snprintf(fn, sizeof(fn), "digits/%d", (num/100));
02693             num %= 100;
02694             if (num && (num < 10)) {
02695                playz++;
02696                playh++;
02697             } else {
02698                playh++;
02699             }
02700          } else {
02701             if (num < 1000000) { /* 1,000,000 */
02702                res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
02703                if (res)
02704                   return res;
02705                num %= 1000;
02706                snprintf(fn, sizeof(fn), "digits/thousand");
02707                if (num && (num < 10)) {
02708                   playohz++;
02709                } else if (num && (num < 100)){
02710                   playoh++;
02711                } else {
02712                   playh = 0;
02713                   playohz = 0;
02714                   playoh = 0;
02715                }
02716             } else {
02717                if (num < 1000000000) { /* 1,000,000,000 */
02718                   res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
02719                   if (res)
02720                      return res;
02721                   num %= 1000000;
02722                   ast_copy_string(fn, "digits/million", sizeof(fn));
02723                } else {
02724                   res = -1;
02725                }
02726             }
02727          }
02728       }
02729       if (!res) {
02730          if (!ast_streamfile(chan, fn, language)) {
02731             if ((audiofd  > -1) && (ctrlfd > -1))
02732                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02733             else
02734                res = ast_waitstream(chan, ints);
02735          }
02736          ast_stopstream(chan);
02737       }
02738    }
02739    return res;
02740 }
02741 
02742 /*! \brief  ast_say_enumeration_full: call language-specific functions
02743  * \note Called from AGI */
02744 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
02745 {
02746    if (!strncasecmp(language, "en", 2)) {        /* English syntax */
02747       return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
02748    } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
02749       return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
02750    } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
02751       return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
02752    } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
02753       return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
02754    } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
02755       return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
02756    }
02757 
02758    /* Default to english */
02759    return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
02760 }
02761 
02762 /*! \brief  ast_say_enumeration_full_en: English syntax
02763  \note This is the default syntax, if no other syntax defined in this file is used */
02764 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
02765 {
02766    int res = 0, t = 0;
02767    char fn[256] = "";
02768 
02769    while (!res && num) {
02770       if (num < 0) {
02771          ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
02772          if ( num > INT_MIN ) {
02773             num = -num;
02774          } else {
02775             num = 0;
02776          }
02777       } else if (num < 20) {
02778          snprintf(fn, sizeof(fn), "digits/h-%d", num);
02779          num = 0;
02780       } else if (num < 100) {
02781          int tens = num / 10;
02782          num = num % 10;
02783          if (num == 0) {
02784             snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
02785          } else {
02786             snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
02787          }
02788       } else if (num < 1000) {
02789          int hundreds = num / 100;
02790          num = num % 100;
02791          if (hundreds > 1 || t == 1) {
02792             res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
02793          }
02794          if (res)
02795             return res;
02796          if (num) {
02797             ast_copy_string(fn, "digits/hundred", sizeof(fn));
02798          } else {
02799             ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
02800          }
02801       } else if (num < 1000000) {
02802          int thousands = num / 1000;
02803          num = num % 1000;
02804          if (thousands > 1 || t == 1) {
02805             res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
02806          }
02807          if (res)
02808             return res;
02809          if (num) {
02810             ast_copy_string(fn, "digits/thousand", sizeof(fn));
02811          } else {
02812             ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
02813          }
02814          t = 1;
02815       } else if (num < 1000000000) {
02816          int millions = num / 1000000;
02817          num = num % 1000000;
02818          t = 1;
02819          res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
02820          if (res)
02821             return res;
02822          if (num) {
02823             ast_copy_string(fn, "digits/million", sizeof(fn));
02824          } else {
02825             ast_copy_string(fn, "digits/h-million", sizeof(fn));
02826          }
02827       } else if (num < INT_MAX) {
02828          int billions = num / 1000000000;
02829          num = num % 1000000000;
02830          t = 1;
02831          res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
02832          if (res)
02833             return res;
02834          if (num) {
02835             ast_copy_string(fn, "digits/billion", sizeof(fn));
02836          } else {
02837             ast_copy_string(fn, "digits/h-billion", sizeof(fn));
02838          }
02839       } else if (num == INT_MAX) {
02840          ast_copy_string(fn, "digits/h-last", sizeof(fn));
02841          num = 0;
02842       } else {
02843          ast_debug(1, "Number '%d' is too big for me\n", num);
02844          res = -1;
02845       }
02846 
02847       if (!res) {
02848          if (!ast_streamfile(chan, fn, language)) {
02849             if ((audiofd > -1) && (ctrlfd > -1)) {
02850                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02851             } else {
02852                res = ast_waitstream(chan, ints);
02853             }
02854          }
02855          ast_stopstream(chan);
02856       }
02857    }
02858    return res;
02859 }
02860 
02861 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
02862 {
02863    int res = 0;
02864    char fn[256] = "";
02865    ast_copy_string(fn, "digits/h", sizeof(fn));
02866    if (!res) {
02867       if (!ast_streamfile(chan, fn, language)) {
02868          if ((audiofd > -1) && (ctrlfd > -1)) {
02869             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02870          } else {
02871             res = ast_waitstream(chan, ints);
02872          }
02873       }
02874       ast_stopstream(chan);
02875    }
02876 
02877    return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
02878 }
02879 
02880 /*! \brief  ast_say_enumeration_full_da: Danish syntax */
02881 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)
02882 {
02883    /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
02884    int res = 0, t = 0;
02885    char fn[256] = "", fna[256] = "";
02886    char *gender;
02887 
02888    if (options && !strncasecmp(options, "f", 1)) {
02889       gender = "F";
02890    } else if (options && !strncasecmp(options, "n", 1)) {
02891       gender = "N";
02892    } else {
02893       gender = "";
02894    }
02895 
02896    if (!num)
02897       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02898 
02899    while (!res && num) {
02900       if (num < 0) {
02901          ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
02902          if ( num > INT_MIN ) {
02903             num = -num;
02904          } else {
02905             num = 0;
02906          }
02907       } else if (num < 100 && t) {
02908          ast_copy_string(fn, "digits/and", sizeof(fn));
02909          t = 0;
02910       } else if (num < 20) {
02911          snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
02912          num = 0;
02913       } else if (num < 100) {
02914          int ones = num % 10;
02915          if (ones) {
02916             snprintf(fn, sizeof(fn), "digits/%d-and", ones);
02917             num -= ones;
02918          } else {
02919             snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
02920             num = 0;
02921          }
02922       } else if (num == 100 && t == 0) {
02923          snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
02924          num = 0;
02925       } else if (num < 1000) {
02926          int hundreds = num / 100;
02927          num = num % 100;
02928          if (hundreds == 1) {
02929             ast_copy_string(fn, "digits/1N", sizeof(fn));
02930          } else {
02931             snprintf(fn, sizeof(fn), "digits/%d", hundreds);
02932          }
02933          if (num) {
02934             ast_copy_string(fna, "digits/hundred", sizeof(fna));
02935          } else {
02936             snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
02937          }
02938          t = 1;
02939       } else if (num < 1000000) {
02940          int thousands = num / 1000;
02941          num = num % 1000;
02942          if (thousands == 1) {
02943             if (num) {
02944                ast_copy_string(fn, "digits/1N", sizeof(fn));
02945                ast_copy_string(fna, "digits/thousand", sizeof(fna));
02946             } else {
02947                if (t) {
02948                   ast_copy_string(fn, "digits/1N", sizeof(fn));
02949                   snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
02950                } else {
02951                   snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
02952                }
02953             }
02954          } else {
02955             res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
02956             if (res) {
02957                return res;
02958             }
02959             if (num) {
02960                ast_copy_string(fn, "digits/thousand", sizeof(fn));
02961             } else {
02962                snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
02963             }
02964          }
02965          t = 1;
02966       } else if (num < 1000000000) {
02967          int millions = num / 1000000;
02968          num = num % 1000000;
02969          if (millions == 1) {
02970             if (num) {
02971                ast_copy_string(fn, "digits/1F", sizeof(fn));
02972                ast_copy_string(fna, "digits/million", sizeof(fna));
02973             } else {
02974                ast_copy_string(fn, "digits/1N", sizeof(fn));
02975                snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
02976             }
02977          } else {
02978             res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
02979             if (res) {
02980                return res;
02981             }
02982             if (num) {
02983                ast_copy_string(fn, "digits/millions", sizeof(fn));
02984             } else {
02985                snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
02986             }
02987          }
02988          t = 1;
02989       } else if (num < INT_MAX) {
02990          int billions = num / 1000000000;
02991          num = num % 1000000000;
02992          if (billions == 1) {
02993             if (num) {
02994                ast_copy_string(fn, "digits/1F", sizeof(fn));
02995                ast_copy_string(fna, "digits/milliard", sizeof(fna));
02996             } else {
02997                ast_copy_string(fn, "digits/1N", sizeof(fn));
02998                snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
02999             }
03000          } else {
03001             res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
03002             if (res)
03003                return res;
03004             if (num) {
03005                ast_copy_string(fn, "digits/milliards", sizeof(fna));
03006             } else {
03007                snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
03008             }
03009          }
03010          t = 1;
03011       } else if (num == INT_MAX) {
03012          snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
03013          num = 0;
03014       } else {
03015          ast_debug(1, "Number '%d' is too big for me\n", num);
03016          res = -1;
03017       }
03018 
03019       if (!res) {
03020          if (!ast_streamfile(chan, fn, language)) {
03021             if ((audiofd > -1) && (ctrlfd > -1))
03022                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
03023             else
03024                res = ast_waitstream(chan, ints);
03025          }
03026          ast_stopstream(chan);
03027          if (!res) {
03028             if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
03029                if ((audiofd > -1) && (ctrlfd > -1)) {
03030                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
03031                } else {
03032                   res = ast_waitstream(chan, ints);
03033                }
03034             }
03035             ast_stopstream(chan);
03036             strcpy(fna, "");
03037          }
03038       }
03039    }
03040    return res;
03041 }
03042 
03043 /*! \brief  ast_say_enumeration_full_de: German syntax */
03044 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)
03045 {
03046    /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
03047    int res = 0, t = 0;
03048    char fn[256] = "", fna[256] = "";
03049    char *gender;
03050 
03051    if (options && !strncasecmp(options, "f", 1)) {
03052       gender = "F";
03053    } else if (options && !strncasecmp(options, "n", 1)) {
03054       gender = "N";
03055    } else {
03056       gender = "";
03057    }
03058 
03059    if (!num)
03060       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
03061 
03062    while (!res && num) {
03063       if (num < 0) {
03064          ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
03065          if ( num > INT_MIN ) {
03066             num = -num;
03067          } else {
03068             num = 0;
03069          }
03070       } else if (num < 100 && t) {
03071          ast_copy_string(fn, "digits/and", sizeof(fn));
03072          t = 0;
03073       } else if (num < 20) {
03074          snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
03075          num = 0;
03076       } else if (num < 100) {
03077          int ones = num % 10;
03078          if (ones) {
03079             snprintf(fn, sizeof(fn), "digits/%d-and", ones);
03080             num -= ones;
03081          } else {
03082             snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
03083             num = 0;
03084          }
03085       } else if (num == 100 && t == 0) {
03086          snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
03087          num = 0;
03088       } else if (num < 1000) {
03089          int hundreds = num / 100;
03090          num = num % 100;
03091          if (hundreds == 1) {
03092             ast_copy_string(fn, "digits/1N", sizeof(fn));
03093          } else {
03094             snprintf(fn, sizeof(fn), "digits/%d", hundreds);
03095          }
03096          if (num) {
03097             ast_copy_string(fna, "digits/hundred", sizeof(fna));
03098          } else {
03099             snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
03100          }
03101          t = 1;
03102       } else if (num < 1000000) {
03103          int thousands = num / 1000;
03104          num = num % 1000;
03105          if (thousands == 1) {
03106             if (num) {
03107                ast_copy_string(fn, "digits/1N", sizeof(fn));
03108                ast_copy_string(fna, "digits/thousand", sizeof(fna));
03109             } else {
03110                if (t) {
03111                   ast_copy_string(fn, "digits/1N", sizeof(fn));
03112                   snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
03113                } else {
03114                   snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
03115                }
03116             }
03117          } else {
03118             res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
03119             if (res) {
03120                return res;
03121             }
03122             if (num) {
03123                ast_copy_string(fn, "digits/thousand", sizeof(fn));
03124             } else {
03125                snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
03126             }
03127          }
03128          t = 1;
03129       } else if (num < 1000000000) {
03130          int millions = num / 1000000;
03131          num = num % 1000000;
03132          if (millions == 1) {
03133             if (num) {
03134                ast_copy_string(fn, "digits/1F", sizeof(fn));
03135                ast_copy_string(fna, "digits/million", sizeof(fna));
03136             } else {
03137                ast_copy_string(fn, "digits/1N", sizeof(fn));
03138                snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
03139             }
03140          } else {
03141             res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
03142             if (res) {
03143                return res;
03144             }
03145             if (num) {
03146                ast_copy_string(fn, "digits/millions", sizeof(fn));
03147             } else {
03148                snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
03149             }
03150          }
03151          t = 1;
03152       } else if (num < INT_MAX) {
03153          int billions = num / 1000000000;
03154          num = num % 1000000000;
03155          if (billions == 1) {
03156             if (num) {
03157                ast_copy_string(fn, "digits/1F", sizeof(fn));
03158                ast_copy_string(fna, "digits/milliard", sizeof(fna));
03159             } else {
03160                ast_copy_string(fn, "digits/1N", sizeof(fn));
03161                snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
03162             }
03163          } else {
03164             res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
03165             if (res)
03166                return res;
03167             if (num) {
03168                ast_copy_string(fn, "digits/milliards", sizeof(fna));
03169             } else {
03170                snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
03171             }
03172          }
03173          t = 1;
03174       } else if (num == INT_MAX) {
03175          snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
03176          num = 0;
03177       } else {
03178          ast_debug(1, "Number '%d' is too big for me\n", num);
03179          res = -1;
03180       }
03181 
03182       if (!res) {
03183          if (!ast_streamfile(chan, fn, language)) {
03184             if ((audiofd > -1) && (ctrlfd > -1))
03185                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
03186             else
03187                res = ast_waitstream(chan, ints);
03188          }
03189          ast_stopstream(chan);
03190          if (!res) {
03191             if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
03192                if ((audiofd > -1) && (ctrlfd > -1)) {
03193                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
03194                } else {
03195                   res = ast_waitstream(chan, ints);
03196                }
03197             }
03198             ast_stopstream(chan);
03199             strcpy(fna, "");
03200          }
03201       }
03202    }
03203    return res;
03204 }
03205 
03206 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)
03207 {
03208    int res = 0;
03209    char fn[256] = "";
03210    int mf = -1;            /* +1 = Masculin; -1 = Feminin */
03211    ast_verb(3, "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
03212 
03213    if (options && !strncasecmp(options, "m", 1)) {
03214       mf = -1;
03215    }
03216 
03217    ast_verb(3, "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
03218 
03219    while (!res && num) {
03220       if (num < 0) {
03221          snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
03222          if (num > INT_MIN) {
03223             num = -num;
03224          } else {
03225             num = 0;
03226          }
03227       } else if (num < 21) {
03228          if (mf < 0) {
03229             if (num < 10) {
03230                snprintf(fn, sizeof(fn), "digits/f-0%d", num);
03231             } else {
03232                snprintf(fn, sizeof(fn), "digits/f-%d", num);
03233             }
03234          } else {
03235             if (num < 10) {
03236                snprintf(fn, sizeof(fn), "digits/m-0%d", num);
03237             } else {
03238                snprintf(fn, sizeof(fn), "digits/m-%d", num);
03239             }
03240          }
03241          num = 0;
03242       } else if ((num < 100) && num >= 20) {
03243          snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
03244          num = num % 10;
03245       } else if ((num >= 100) && (num < 1000)) {
03246          int tmpnum = num / 100;
03247          snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
03248          num = num - (tmpnum * 100);
03249       } else if ((num >= 1000) && (num < 10000)) {
03250          int tmpnum = num / 1000;
03251          snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
03252          num = num - (tmpnum * 1000);
03253       } else if (num < 20000) {
03254          snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
03255          num = num % 1000;
03256       } else if (num < 1000000) {
03257          res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
03258          if (res) {
03259             return res;
03260          }
03261          snprintf(fn, sizeof(fn), "digits/1k");
03262          num = num % 1000;
03263       } else if (num < 2000000) {
03264          snprintf(fn, sizeof(fn), "digits/1m");
03265          num = num % 1000000;
03266       } else if (num < 3000000) {
03267          snprintf(fn, sizeof(fn), "digits/2m");
03268          num = num - 2000000;
03269       } else if (num < 1000000000) {
03270          res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
03271          if (res) {
03272             return res;
03273          }
03274          snprintf(fn, sizeof(fn), "digits/1m");
03275          num = num % 1000000;
03276       } else {
03277          ast_debug(1, "Number '%d' is too big for me\n", num);
03278          res = -1;
03279       }
03280       if (!res) {
03281          if (!ast_streamfile(chan, fn, language)) {
03282             if ((audiofd > -1) && (ctrlfd > -1)) {
03283                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
03284             } else {
03285                res = ast_waitstream(chan, ints);
03286             }
03287          }
03288          ast_stopstream(chan);
03289       }
03290    }
03291    return res;
03292 }
03293 
03294 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03295 {
03296    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
03297       return ast_say_date_en(chan, t, ints, lang);
03298    } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
03299       return ast_say_date_da(chan, t, ints, lang);
03300    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
03301       return ast_say_date_de(chan, t, ints, lang);
03302    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
03303       return ast_say_date_fr(chan, t, ints, lang);
03304    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
03305       static int deprecation_warning = 0;
03306       if (deprecation_warning++ % 10 == 0) {
03307          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
03308       }
03309       return ast_say_date_ka(chan, t, ints, lang);
03310    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
03311       return ast_say_date_gr(chan, t, ints, lang);
03312    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
03313       return ast_say_date_he(chan, t, ints, lang);
03314    } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
03315       return ast_say_date_hu(chan, t, ints, lang);
03316    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
03317       return ast_say_date_ka(chan, t, ints, lang);
03318    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
03319       return ast_say_date_nl(chan, t, ints, lang);
03320    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
03321       return ast_say_date_pt(chan, t, ints, lang);
03322    } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
03323       return ast_say_date_th(chan, t, ints, lang);
03324    }
03325 
03326    /* Default to English */
03327    return ast_say_date_en(chan, t, ints, lang);
03328 }
03329 
03330 /*! \brief English syntax */
03331 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03332 {
03333    struct ast_tm tm;
03334    struct timeval when = { t, 0 };
03335    char fn[256];
03336    int res = 0;
03337    ast_localtime(&when, &tm, NULL);
03338    if (!res) {
03339       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03340       res = ast_streamfile(chan, fn, lang);
03341       if (!res)
03342          res = ast_waitstream(chan, ints);
03343    }
03344    if (!res) {
03345       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03346       res = ast_streamfile(chan, fn, lang);
03347       if (!res)
03348          res = ast_waitstream(chan, ints);
03349    }
03350    if (!res)
03351       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03352    if (!res)
03353       res = ast_waitstream(chan, ints);
03354    if (!res)
03355       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03356    return res;
03357 }
03358 
03359 /*! \brief Danish syntax */
03360 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03361 {
03362    struct timeval when = { t, 0 };
03363    struct ast_tm tm;
03364    char fn[256];
03365    int res = 0;
03366    ast_localtime(&when, &tm, NULL);
03367    if (!res) {
03368       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03369       res = ast_streamfile(chan, fn, lang);
03370       if (!res)
03371          res = ast_waitstream(chan, ints);
03372    }
03373    if (!res)
03374       res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03375    if (!res)
03376       res = ast_waitstream(chan, ints);
03377    if (!res) {
03378       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03379       res = ast_streamfile(chan, fn, lang);
03380       if (!res)
03381          res = ast_waitstream(chan, ints);
03382    }
03383    if (!res) {
03384       /* Year */
03385       int year = tm.tm_year + 1900;
03386       if (year > 1999) {   /* year 2000 and later */
03387          res = ast_say_number(chan, year, ints, lang, (char *) NULL);
03388       } else {
03389          if (year < 1100) {
03390             /* I'm not going to handle 1100 and prior */
03391             /* We'll just be silent on the year, instead of bombing out. */
03392          } else {
03393              /* year 1100 to 1999. will anybody need this?!? */
03394             snprintf(fn, sizeof(fn), "digits/%d", (year / 100));
03395             res = wait_file(chan, ints, fn, lang);
03396             if (!res) {
03397                res = wait_file(chan, ints, "digits/hundred", lang);
03398                if (!res && year % 100 != 0) {
03399                   res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
03400                }
03401             }
03402          }
03403       }
03404    }
03405    return res;
03406 }
03407 
03408 /*! \brief German syntax */
03409 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03410 {
03411    struct timeval when = { t, 0 };
03412    struct ast_tm tm;
03413    char fn[256];
03414    int res = 0;
03415    ast_localtime(&when, &tm, NULL);
03416    if (!res) {
03417       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03418       res = ast_streamfile(chan, fn, lang);
03419       if (!res)
03420          res = ast_waitstream(chan, ints);
03421    }
03422    if (!res)
03423       res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03424    if (!res)
03425       res = ast_waitstream(chan, ints);
03426    if (!res) {
03427       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03428       res = ast_streamfile(chan, fn, lang);
03429       if (!res)
03430          res = ast_waitstream(chan, ints);
03431    }
03432    if (!res) {
03433       /* Year */
03434       int year = tm.tm_year + 1900;
03435       if (year > 1999) {   /* year 2000 and later */
03436          res = ast_say_number(chan, year, ints, lang, (char *) NULL);
03437       } else {
03438          if (year < 1100) {
03439             /* I'm not going to handle 1100 and prior */
03440             /* We'll just be silent on the year, instead of bombing out. */
03441          } else {
03442              /* year 1100 to 1999. will anybody need this?!? */
03443              /* say 1967 as 'neunzehn hundert sieben und sechzig' */
03444             snprintf(fn, sizeof(fn), "digits/%d", (year / 100) );
03445             res = wait_file(chan, ints, fn, lang);
03446             if (!res) {
03447                res = wait_file(chan, ints, "digits/hundred", lang);
03448                if (!res && year % 100 != 0) {
03449                   res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
03450                }
03451             }
03452          }
03453       }
03454    }
03455    return res;
03456 }
03457 
03458 /*! \brief Hungarian syntax */
03459 int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03460 {
03461    struct timeval when = { t, 0 };
03462    struct ast_tm tm;
03463    char fn[256];
03464    int res = 0;
03465    ast_localtime(&when, &tm, NULL);
03466 
03467    if (!res)
03468       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03469    if (!res)
03470       res = ast_waitstream(chan, ints);
03471    if (!res) {
03472       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03473       res = ast_streamfile(chan, fn, lang);
03474       if (!res)
03475          res = ast_waitstream(chan, ints);
03476    }
03477    if (!res)
03478       ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
03479    if (!res)
03480       res = ast_waitstream(chan, ints);
03481    if (!res) {
03482       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03483       res = ast_streamfile(chan, fn, lang);
03484       if (!res)
03485          res = ast_waitstream(chan, ints);
03486    }
03487    return res;
03488 }
03489 
03490 /*! \brief French syntax */
03491 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03492 {
03493    struct timeval when = { t, 0 };
03494    struct ast_tm tm;
03495    char fn[256];
03496    int res = 0;
03497    ast_localtime(&when, &tm, NULL);
03498    if (!res) {
03499       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03500       res = ast_streamfile(chan, fn, lang);
03501       if (!res)
03502          res = ast_waitstream(chan, ints);
03503    }
03504    if (!res)
03505       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03506    if (!res)
03507       res = ast_waitstream(chan, ints);
03508    if (!res) {
03509       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03510       res = ast_streamfile(chan, fn, lang);
03511       if (!res)
03512          res = ast_waitstream(chan, ints);
03513    }
03514    if (!res)
03515       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03516    return res;
03517 }
03518 
03519 /*! \brief Dutch syntax */
03520 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03521 {
03522    struct timeval when = { t, 0 };
03523    struct ast_tm tm;
03524    char fn[256];
03525    int res = 0;
03526    ast_localtime(&when, &tm, NULL);
03527    if (!res) {
03528       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03529       res = ast_streamfile(chan, fn, lang);
03530       if (!res)
03531          res = ast_waitstream(chan, ints);
03532    }
03533    if (!res)
03534       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03535    if (!res) {
03536       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03537       res = ast_streamfile(chan, fn, lang);
03538       if (!res)
03539          res = ast_waitstream(chan, ints);
03540    }
03541    if (!res)
03542       res = ast_waitstream(chan, ints);
03543    if (!res)
03544       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03545    return res;
03546 }
03547 
03548 /*! \brief Thai syntax */
03549 int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03550 {
03551    struct timeval when = { t, 0 };
03552    struct ast_tm tm;
03553    char fn[256];
03554    int res = 0;
03555    ast_localtime(&when, &tm, NULL);
03556    if (!res) {
03557       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03558       res = ast_streamfile(chan, fn, lang);
03559       ast_copy_string(fn, "digits/tee", sizeof(fn));
03560       res = ast_streamfile(chan, fn, lang);
03561       if (!res)
03562          res = ast_waitstream(chan, ints);
03563    }
03564    if (!res)
03565       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03566    if (!res)
03567       res = ast_waitstream(chan, ints);
03568    if (!res) {
03569       ast_copy_string(fn, "digits/duan", sizeof(fn));
03570       res = ast_streamfile(chan, fn, lang);
03571       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03572       res = ast_streamfile(chan, fn, lang);
03573       if (!res)
03574          res = ast_waitstream(chan, ints);
03575    }
03576    if (!res){
03577       ast_copy_string(fn, "digits/posor", sizeof(fn));
03578       res = ast_streamfile(chan, fn, lang);
03579       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03580    }
03581    return res;
03582 }
03583 
03584 /*! \brief Portuguese syntax */
03585 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03586 {
03587    struct timeval when = { t, 0 };
03588    struct ast_tm tm;
03589    char fn[256];
03590    int res = 0;
03591 
03592    ast_localtime(&when, &tm, NULL);
03593    snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03594    if (!res)
03595       res = wait_file(chan, ints, fn, lang);
03596    if (!res)
03597       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
03598    if (!res)
03599       res = wait_file(chan, ints, "digits/pt-de", lang);
03600    snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03601    if (!res)
03602       res = wait_file(chan, ints, fn, lang);
03603    if (!res)
03604       res = wait_file(chan, ints, "digits/pt-de", lang);
03605    if (!res)
03606       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03607 
03608    return res;
03609 }
03610 
03611 /*! \brief Hebrew syntax */
03612 int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03613 {
03614    struct timeval when = { t, 0 };
03615    struct ast_tm tm;
03616    char fn[256];
03617    int res = 0;
03618    ast_localtime(&when, &tm, NULL);
03619    if (!res) {
03620       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03621       res = ast_streamfile(chan, fn, lang);
03622       if (!res) {
03623          res = ast_waitstream(chan, ints);
03624       }
03625    }
03626    if (!res) {
03627       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03628       res = ast_streamfile(chan, fn, lang);
03629       if (!res) {
03630          res = ast_waitstream(chan, ints);
03631       }
03632    }
03633    if (!res) {
03634       res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
03635    }
03636    if (!res) {
03637       res = ast_waitstream(chan, ints);
03638    }
03639    if (!res) {
03640       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
03641    }
03642    return res;
03643 }
03644 
03645 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)
03646 {
03647    if (!strncasecmp(lang, "en", 2)) {      /* English syntax */
03648       return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
03649    } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
03650       return ast_say_date_with_format_da(chan, t, ints, lang, format, tzone);
03651    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
03652       return ast_say_date_with_format_de(chan, t, ints, lang, format, tzone);
03653    } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
03654       return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
03655    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
03656       return ast_say_date_with_format_he(chan, t, ints, lang, format, tzone);
03657    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
03658       return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone);
03659    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
03660       return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone);
03661    } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
03662       return ast_say_date_with_format_it(chan, t, ints, lang, format, tzone);
03663    } else if (!strncasecmp(lang, "mx", 2)) { /* deprecated Mexican syntax */
03664       static int deprecation_warning = 0;
03665       if (deprecation_warning++ % 10 == 0) {
03666          ast_log(LOG_WARNING, "mx is not a standard language code.  Please switch to using es_MX instead.\n");
03667       }
03668       return ast_say_date_with_format_es(chan, t, ints, lang, format, tzone);
03669    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
03670       return ast_say_date_with_format_nl(chan, t, ints, lang, format, tzone);
03671    } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
03672       return ast_say_date_with_format_pl(chan, t, ints, lang, format, tzone);
03673    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
03674       return ast_say_date_with_format_pt(chan, t, ints, lang, format, tzone);
03675    } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
03676       return ast_say_date_with_format_th(chan, t, ints, lang, format, tzone);
03677    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
03678       static int deprecation_warning = 0;
03679       if (deprecation_warning++ % 10 == 0) {
03680          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
03681       }
03682       return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
03683    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
03684       return ast_say_date_with_format_zh(chan, t, ints, lang, format, tzone);
03685    } else if (!strncasecmp(lang, "vi", 2)) { /* Vietnamese syntax */
03686       return ast_say_date_with_format_vi(chan, t, ints, lang, format, tzone);
03687    }
03688 
03689    /* Default to English */
03690    return ast_say_date_with_format_en(chan, t, ints, lang, format, tzone);
03691 }
03692 
03693 /*! \brief English syntax */
03694 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)
03695 {
03696    struct timeval when = { t, 0 };
03697    struct ast_tm tm;
03698    int res=0, offset, sndoffset;
03699    char sndfile[256], nextmsg[256];
03700 
03701    if (format == NULL)
03702       format = "ABdY 'digits/at' IMp";
03703 
03704    ast_localtime(&when, &tm, tzone);
03705 
03706    for (offset=0 ; format[offset] != '\0' ; offset++) {
03707       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03708       switch (format[offset]) {
03709          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03710          case '\'':
03711             /* Literal name of a sound file */
03712             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03713                sndfile[sndoffset] = format[offset];
03714             }
03715             sndfile[sndoffset] = '\0';
03716             res = wait_file(chan, ints, sndfile, lang);
03717             break;
03718          case 'A':
03719          case 'a':
03720             /* Sunday - Saturday */
03721             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03722             res = wait_file(chan, ints, nextmsg, lang);
03723             break;
03724          case 'B':
03725          case 'b':
03726          case 'h':
03727             /* January - December */
03728             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03729             res = wait_file(chan, ints, nextmsg, lang);
03730             break;
03731          case 'm':
03732             /* Month enumerated */
03733             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
03734             break;
03735          case 'd':
03736          case 'e':
03737             /* First - Thirtyfirst */
03738             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
03739             break;
03740          case 'Y':
03741             /* Year */
03742             if (tm.tm_year > 99) {
03743                res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03744             } else if (tm.tm_year < 1) {
03745                /* I'm not going to handle 1900 and prior */
03746                /* We'll just be silent on the year, instead of bombing out. */
03747             } else {
03748                res = wait_file(chan, ints, "digits/19", lang);
03749                if (!res) {
03750                   if (tm.tm_year <= 9) {
03751                      /* 1901 - 1909 */
03752                      res = wait_file(chan, ints, "digits/oh", lang);
03753                   }
03754 
03755                   res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
03756                }
03757             }
03758             break;
03759          case 'I':
03760          case 'l':
03761             /* 12-Hour */
03762             if (tm.tm_hour == 0)
03763                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
03764             else if (tm.tm_hour > 12)
03765                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
03766             else
03767                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
03768             res = wait_file(chan, ints, nextmsg, lang);
03769             break;
03770          case 'H':
03771          case 'k':
03772             /* 24-Hour */
03773             if (format[offset] == 'H') {
03774                /* e.g. oh-eight */
03775                if (tm.tm_hour < 10) {
03776                   res = wait_file(chan, ints, "digits/oh", lang);
03777                }
03778             } else {
03779                /* e.g. eight */
03780                if (tm.tm_hour == 0) {
03781                   res = wait_file(chan, ints, "digits/oh", lang);
03782                }
03783             }
03784             if (!res) {
03785                if (tm.tm_hour != 0) {
03786                   int remaining = tm.tm_hour;
03787                   if (tm.tm_hour > 20) {
03788                      res = wait_file(chan, ints, "digits/20", lang);
03789                      remaining -= 20;
03790                   }
03791                   if (!res) {
03792                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
03793                      res = wait_file(chan, ints, nextmsg, lang);
03794                   }
03795                }
03796             }
03797             break;
03798          case 'M':
03799          case 'N':
03800             /* Minute */
03801             if (tm.tm_min == 0) {
03802                if (format[offset] == 'M') {
03803                   res = wait_file(chan, ints, "digits/oclock", lang);
03804                } else {
03805                   res = wait_file(chan, ints, "digits/hundred", lang);
03806                }
03807             } else if (tm.tm_min < 10) {
03808                res = wait_file(chan, ints, "digits/oh", lang);
03809                if (!res) {
03810                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
03811                   res = wait_file(chan, ints, nextmsg, lang);
03812                }
03813             } else {
03814                res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
03815             }
03816             break;
03817          case 'P':
03818          case 'p':
03819             /* AM/PM */
03820             if (tm.tm_hour > 11)
03821                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
03822             else
03823                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
03824             res = wait_file(chan, ints, nextmsg, lang);
03825             break;
03826          case 'Q':
03827             /* Shorthand for "Today", "Yesterday", or ABdY */
03828             /* XXX As emphasized elsewhere, this should the native way in your
03829              * language to say the date, with changes in what you say, depending
03830              * upon how recent the date is. XXX */
03831             {
03832                struct timeval now = ast_tvnow();
03833                struct ast_tm tmnow;
03834                time_t beg_today;
03835 
03836                gettimeofday(&now, NULL);
03837                ast_localtime(&now, &tmnow, tzone);
03838                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03839                /* In any case, it saves not having to do ast_mktime() */
03840                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03841                if (beg_today < t) {
03842                   /* Today */
03843                   res = wait_file(chan, ints, "digits/today", lang);
03844                } else if (beg_today - 86400 < t) {
03845                   /* Yesterday */
03846                   res = wait_file(chan, ints, "digits/yesterday", lang);
03847                } else if (beg_today - 86400 * 6 < t) {
03848                   /* Within the last week */
03849                   res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
03850                } else if (beg_today - 2628000 < t) {
03851                   /* Less than a month ago - "Sunday, October third" */
03852                   res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
03853                } else if (beg_today - 15768000 < t) {
03854                   /* Less than 6 months ago - "August seventh" */
03855                   res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
03856                } else {
03857                   /* More than 6 months ago - "April nineteenth two thousand three" */
03858                   res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
03859                }
03860             }
03861             break;
03862          case 'q':
03863             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
03864             /* XXX As emphasized elsewhere, this should the native way in your
03865              * language to say the date, with changes in what you say, depending
03866              * upon how recent the date is. XXX */
03867             {
03868                struct timeval now;
03869                struct ast_tm tmnow;
03870                time_t beg_today;
03871 
03872                now = ast_tvnow();
03873                ast_localtime(&now, &tmnow, tzone);
03874                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03875                /* In any case, it saves not having to do ast_mktime() */
03876                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03877                if (beg_today < t) {
03878                   /* Today */
03879                } else if ((beg_today - 86400) < t) {
03880                   /* Yesterday */
03881                   res = wait_file(chan, ints, "digits/yesterday", lang);
03882                } else if (beg_today - 86400 * 6 < t) {
03883                   /* Within the last week */
03884                   res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
03885                } else if (beg_today - 2628000 < t) {
03886                   /* Less than a month ago - "Sunday, October third" */
03887                   res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
03888                } else if (beg_today - 15768000 < t) {
03889                   /* Less than 6 months ago - "August seventh" */
03890                   res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
03891                } else {
03892                   /* More than 6 months ago - "April nineteenth two thousand three" */
03893                   res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
03894                }
03895             }
03896             break;
03897          case 'R':
03898             res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
03899             break;
03900          case 'S':
03901             /* Seconds */
03902             if (tm.tm_sec == 0) {
03903                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
03904                res = wait_file(chan, ints, nextmsg, lang);
03905             } else if (tm.tm_sec < 10) {
03906                res = wait_file(chan, ints, "digits/oh", lang);
03907                if (!res) {
03908                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
03909                   res = wait_file(chan, ints, nextmsg, lang);
03910                }
03911             } else {
03912                res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
03913             }
03914             break;
03915          case 'T':
03916             res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
03917             break;
03918          case ' ':
03919          case '   ':
03920             /* Just ignore spaces and tabs */
03921             break;
03922          default:
03923             /* Unknown character */
03924             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
03925       }
03926       /* Jump out on DTMF */
03927       if (res) {
03928          break;
03929       }
03930    }
03931    return res;
03932 }
03933 
03934 static char next_item(const char *format)
03935 {
03936    const char *next = ast_skip_blanks(format);
03937    return *next;
03938 }
03939 
03940 /*! \brief Danish syntax */
03941 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)
03942 {
03943    struct timeval when = { t, 0 };
03944    struct ast_tm tm;
03945    int res=0, offset, sndoffset;
03946    char sndfile[256], nextmsg[256];
03947 
03948    if (!format)
03949       format = "A dBY HMS";
03950 
03951    ast_localtime(&when, &tm, tzone);
03952 
03953    for (offset=0 ; format[offset] != '\0' ; offset++) {
03954       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03955       switch (format[offset]) {
03956          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03957          case '\'':
03958             /* Literal name of a sound file */
03959             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03960                sndfile[sndoffset] = format[offset];
03961             }
03962             sndfile[sndoffset] = '\0';
03963             res = wait_file(chan, ints, sndfile, lang);
03964             break;
03965          case 'A':
03966          case 'a':
03967             /* Sunday - Saturday */
03968             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03969             res = wait_file(chan, ints, nextmsg, lang);
03970             break;
03971          case 'B':
03972          case 'b':
03973          case 'h':
03974             /* January - December */
03975             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03976             res = wait_file(chan, ints, nextmsg, lang);
03977             break;
03978          case 'm':
03979             /* Month enumerated */
03980             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
03981             break;
03982          case 'd':
03983          case 'e':
03984             /* First - Thirtyfirst */
03985             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
03986             break;
03987          case 'Y':
03988             /* Year */
03989             {
03990                int year = tm.tm_year + 1900;
03991                if (year > 1999) {   /* year 2000 and later */
03992                   res = ast_say_number(chan, year, ints, lang, (char *) NULL);
03993                } else {
03994                   if (year < 1100) {
03995                      /* I'm not going to handle 1100 and prior */
03996                      /* We'll just be silent on the year, instead of bombing out. */
03997                   } else {
03998                       /* year 1100 to 1999. will anybody need this?!? */
03999                       /* say 1967 as 'nineteen hundred seven and sixty' */
04000                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
04001                      res = wait_file(chan, ints, nextmsg, lang);
04002                      if (!res) {
04003                         res = wait_file(chan, ints, "digits/hundred", lang);
04004                         if (!res && year % 100 != 0) {
04005                            res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
04006                         }
04007                      }
04008                   }
04009                }
04010             }
04011             break;
04012          case 'I':
04013          case 'l':
04014             /* 12-Hour */
04015             res = wait_file(chan, ints, "digits/oclock", lang);
04016             if (tm.tm_hour == 0)
04017                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
04018             else if (tm.tm_hour > 12)
04019                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04020             else
04021                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
04022             if (!res) {
04023                res = wait_file(chan, ints, nextmsg, lang);
04024             }
04025             break;
04026          case 'H':
04027             /* 24-Hour, single digit hours preceded by "oh" (0) */
04028             if (tm.tm_hour < 10 && tm.tm_hour > 0) {
04029                res = wait_file(chan, ints, "digits/0", lang);
04030             }
04031             /* FALLTRHU */
04032          case 'k':
04033             /* 24-Hour */
04034             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04035             break;
04036          case 'M':
04037             /* Minute */
04038             if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
04039                res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
04040             }
04041             if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
04042                if (tm.tm_min == 1) {
04043                   res = wait_file(chan, ints, "digits/minute", lang);
04044                } else {
04045                   res = wait_file(chan, ints, "digits/minutes", lang);
04046                }
04047             }
04048             break;
04049          case 'P':
04050          case 'p':
04051             /* AM/PM */
04052             if (tm.tm_hour > 11)
04053                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
04054             else
04055                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
04056             res = wait_file(chan, ints, nextmsg, lang);
04057             break;
04058          case 'Q':
04059             /* Shorthand for "Today", "Yesterday", or AdBY */
04060             /* XXX As emphasized elsewhere, this should the native way in your
04061              * language to say the date, with changes in what you say, depending
04062              * upon how recent the date is. XXX */
04063             {
04064                struct timeval now = ast_tvnow();
04065                struct ast_tm tmnow;
04066                time_t beg_today;
04067 
04068                ast_localtime(&now, &tmnow, tzone);
04069                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04070                /* In any case, it saves not having to do ast_mktime() */
04071                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04072                if (beg_today < t) {
04073                   /* Today */
04074                   res = wait_file(chan, ints, "digits/today", lang);
04075                } else if (beg_today - 86400 < t) {
04076                   /* Yesterday */
04077                   res = wait_file(chan, ints, "digits/yesterday", lang);
04078                } else {
04079                   res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
04080                }
04081             }
04082             break;
04083          case 'q':
04084             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
04085             /* XXX As emphasized elsewhere, this should the native way in your
04086              * language to say the date, with changes in what you say, depending
04087              * upon how recent the date is. XXX */
04088             {
04089                struct timeval now = ast_tvnow();
04090                struct ast_tm tmnow;
04091                time_t beg_today;
04092 
04093                ast_localtime(&now, &tmnow, tzone);
04094                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04095                /* In any case, it saves not having to do ast_mktime() */
04096                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04097                if (beg_today < t) {
04098                   /* Today */
04099                } else if ((beg_today - 86400) < t) {
04100                   /* Yesterday */
04101                   res = wait_file(chan, ints, "digits/yesterday", lang);
04102                } else if (beg_today - 86400 * 6 < t) {
04103                   /* Within the last week */
04104                   res = ast_say_date_with_format_da(chan, t, ints, lang, "A", tzone);
04105                } else {
04106                   res = ast_say_date_with_format_da(chan, t, ints, lang, "AdBY", tzone);
04107                }
04108             }
04109             break;
04110          case 'R':
04111             res = ast_say_date_with_format_da(chan, t, ints, lang, "HM", tzone);
04112             break;
04113          case 'S':
04114             /* Seconds */
04115             res = wait_file(chan, ints, "digits/and", lang);
04116             if (!res) {
04117                res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
04118                if (!res) {
04119                   res = wait_file(chan, ints, "digits/seconds", lang);
04120                }
04121             }
04122             break;
04123          case 'T':
04124             res = ast_say_date_with_format_da(chan, t, ints, lang, "HMS", tzone);
04125             break;
04126          case ' ':
04127          case '   ':
04128             /* Just ignore spaces and tabs */
04129             break;
04130          default:
04131             /* Unknown character */
04132             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04133       }
04134       /* Jump out on DTMF */
04135       if (res) {
04136          break;
04137       }
04138    }
04139    return res;
04140 }
04141 
04142 /*! \brief German syntax */
04143 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)
04144 {
04145    struct timeval when = { t, 0 };
04146    struct ast_tm tm;
04147    int res=0, offset, sndoffset;
04148    char sndfile[256], nextmsg[256];
04149 
04150    if (!format)
04151       format = "A dBY HMS";
04152 
04153    ast_localtime(&when, &tm, tzone);
04154 
04155    for (offset=0 ; format[offset] != '\0' ; offset++) {
04156       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04157       switch (format[offset]) {
04158          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04159          case '\'':
04160             /* Literal name of a sound file */
04161             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04162                sndfile[sndoffset] = format[offset];
04163             }
04164             sndfile[sndoffset] = '\0';
04165             res = wait_file(chan, ints, sndfile, lang);
04166             break;
04167          case 'A':
04168          case 'a':
04169             /* Sunday - Saturday */
04170             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04171             res = wait_file(chan, ints, nextmsg, lang);
04172             break;
04173          case 'B':
04174          case 'b':
04175          case 'h':
04176             /* January - December */
04177             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04178             res = wait_file(chan, ints, nextmsg, lang);
04179             break;
04180          case 'm':
04181             /* Month enumerated */
04182             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
04183             break;
04184          case 'd':
04185          case 'e':
04186             /* First - Thirtyfirst */
04187             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
04188             break;
04189          case 'Y':
04190             /* Year */
04191             {
04192                int year = tm.tm_year + 1900;
04193                if (year > 1999) {   /* year 2000 and later */
04194                   res = ast_say_number(chan, year, ints, lang, (char *) NULL);
04195                } else {
04196                   if (year < 1100) {
04197                      /* I'm not going to handle 1100 and prior */
04198                      /* We'll just be silent on the year, instead of bombing out. */
04199                   } else {
04200                       /* year 1100 to 1999. will anybody need this?!? */
04201                       /* say 1967 as 'neunzehn hundert sieben und sechzig' */
04202                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) );
04203                      res = wait_file(chan, ints, nextmsg, lang);
04204                      if (!res) {
04205                         res = wait_file(chan, ints, "digits/hundred", lang);
04206                         if (!res && year % 100 != 0) {
04207                            res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
04208                         }
04209                      }
04210                   }
04211                }
04212             }
04213             break;
04214          case 'I':
04215          case 'l':
04216             /* 12-Hour */
04217             if (tm.tm_hour == 0)
04218                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
04219             else if (tm.tm_hour > 12)
04220                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04221             else
04222                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
04223             res = wait_file(chan, ints, nextmsg, lang);
04224             if (!res) {
04225                res = wait_file(chan, ints, "digits/oclock", lang);
04226             }
04227             break;
04228          case 'H':
04229          case 'k':
04230             /* 24-Hour */
04231             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04232             if (!res) {
04233                res = wait_file(chan, ints, "digits/oclock", lang);
04234             }
04235             break;
04236          case 'M':
04237             /* Minute */
04238             if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
04239                res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say digits/minutes */
04240             } else if (tm.tm_min > 0) {
04241                res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
04242             }
04243 
04244             if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
04245                if (tm.tm_min == 1) {
04246                   res = wait_file(chan, ints, "digits/minute", lang);
04247                } else {
04248                   res = wait_file(chan, ints, "digits/minutes", lang);
04249                }
04250             }
04251             break;
04252          case 'P':
04253          case 'p':
04254             /* AM/PM */
04255             if (tm.tm_hour > 11)
04256                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
04257             else
04258                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
04259             res = wait_file(chan, ints, nextmsg, lang);
04260             break;
04261          case 'Q':
04262             /* Shorthand for "Today", "Yesterday", or AdBY */
04263             /* XXX As emphasized elsewhere, this should the native way in your
04264              * language to say the date, with changes in what you say, depending
04265              * upon how recent the date is. XXX */
04266             {
04267                struct timeval now = ast_tvnow();
04268                struct ast_tm tmnow;
04269                time_t beg_today;
04270 
04271                ast_localtime(&now, &tmnow, tzone);
04272                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04273                /* In any case, it saves not having to do ast_mktime() */
04274                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04275                if (beg_today < t) {
04276                   /* Today */
04277                   res = wait_file(chan, ints, "digits/today", lang);
04278                } else if (beg_today - 86400 < t) {
04279                   /* Yesterday */
04280                   res = wait_file(chan, ints, "digits/yesterday", lang);
04281                } else {
04282                   res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
04283                }
04284             }
04285             break;
04286          case 'q':
04287             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
04288             /* XXX As emphasized elsewhere, this should the native way in your
04289              * language to say the date, with changes in what you say, depending
04290              * upon how recent the date is. XXX */
04291             {
04292                struct timeval now = ast_tvnow();
04293                struct ast_tm tmnow;
04294                time_t beg_today;
04295 
04296                ast_localtime(&now, &tmnow, tzone);
04297                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04298                /* In any case, it saves not having to do ast_mktime() */
04299                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04300                if (beg_today < t) {
04301                   /* Today */
04302                } else if ((beg_today - 86400) < t) {
04303                   /* Yesterday */
04304                   res = wait_file(chan, ints, "digits/yesterday", lang);
04305                } else if (beg_today - 86400 * 6 < t) {
04306                   /* Within the last week */
04307                   res = ast_say_date_with_format_de(chan, t, ints, lang, "A", tzone);
04308                } else {
04309                   res = ast_say_date_with_format_de(chan, t, ints, lang, "AdBY", tzone);
04310                }
04311             }
04312             break;
04313          case 'R':
04314             res = ast_say_date_with_format_de(chan, t, ints, lang, "HM", tzone);
04315             break;
04316          case 'S':
04317             /* Seconds */
04318             res = wait_file(chan, ints, "digits/and", lang);
04319             if (!res) {
04320                res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
04321                if (!res) {
04322                   res = wait_file(chan, ints, tm.tm_sec == 1 ? "digits/second" : "digits/seconds", lang);
04323                }
04324             }
04325             break;
04326          case 'T':
04327             res = ast_say_date_with_format_de(chan, t, ints, lang, "HMS", tzone);
04328             break;
04329          case ' ':
04330          case '   ':
04331             /* Just ignore spaces and tabs */
04332             break;
04333          default:
04334             /* Unknown character */
04335             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04336       }
04337       /* Jump out on DTMF */
04338       if (res) {
04339          break;
04340       }
04341    }
04342    return res;
04343 }
04344 
04345 /*! \brief Thai syntax */
04346 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)
04347 {
04348    struct timeval when = { t, 0 };
04349    struct ast_tm tm;
04350    int res=0, offset, sndoffset;
04351    char sndfile[256], nextmsg[256];
04352 
04353    if (format == NULL)
04354       format = "a 'digits/tee' e 'digits/duan' hY  I 'digits/naliga' M 'digits/natee'";
04355 
04356    ast_localtime(&when, &tm, tzone);
04357 
04358    for (offset=0 ; format[offset] != '\0' ; offset++) {
04359       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04360       switch (format[offset]) {
04361          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04362          case '\'':
04363             /* Literal name of a sound file */
04364             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04365                sndfile[sndoffset] = format[offset];
04366             }
04367             sndfile[sndoffset] = '\0';
04368             res = wait_file(chan, ints, sndfile, lang);
04369             break;
04370          case 'A':
04371          case 'a':
04372             /* Sunday - Saturday */
04373             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04374             res = wait_file(chan, ints, nextmsg, lang);
04375             break;
04376          case 'B':
04377          case 'b':
04378          case 'h':
04379             /* January - December */
04380             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04381             res = wait_file(chan, ints, nextmsg, lang);
04382             break;
04383          case 'm':
04384             /* Month enumerated */
04385             res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
04386             break;
04387          case 'd':
04388          case 'e':
04389             /* First - Thirtyfirst */
04390             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
04391             break;
04392          case 'Y':
04393             /* Year */
04394             res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
04395             break;
04396          case 'I':
04397          case 'l':
04398             /* 12-Hour */
04399             if (tm.tm_hour == 0)
04400                ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
04401             snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
04402             res = wait_file(chan, ints, nextmsg, lang);
04403             break;
04404          case 'H':
04405          case 'k':
04406             /* 24-Hour */
04407             if (tm.tm_hour == 0)
04408                ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
04409             snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
04410             res = wait_file(chan, ints, nextmsg, lang);
04411             break;
04412          case 'M':
04413          case 'N':
04414             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
04415             break;
04416          case 'P':
04417          case 'p':
04418             break;
04419          case 'Q':
04420             /* Shorthand for "Today", "Yesterday", or ABdY */
04421             /* XXX As emphasized elsewhere, this should the native way in your
04422              * language to say the date, with changes in what you say, depending
04423              * upon how recent the date is. XXX */
04424             {
04425                struct timeval now = ast_tvnow();
04426                struct ast_tm tmnow;
04427                time_t beg_today;
04428 
04429                ast_localtime(&now, &tmnow, tzone);
04430                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04431                /* In any case, it saves not having to do ast_mktime() */
04432                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04433                if (beg_today < t) {
04434                   /* Today */
04435                   res = wait_file(chan, ints, "digits/today", lang);
04436                } else if (beg_today - 86400 < t) {
04437                   /* Yesterday */
04438                   res = wait_file(chan, ints, "digits/yesterday", lang);
04439                } else if (beg_today - 86400 * 6 < t) {
04440                   /* Within the last week */
04441                   res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
04442                } else if (beg_today - 2628000 < t) {
04443                   /* Less than a month ago - "Sunday, October third" */
04444                   res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
04445                } else if (beg_today - 15768000 < t) {
04446                   /* Less than 6 months ago - "August seventh" */
04447                   res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
04448                } else {
04449                   /* More than 6 months ago - "April nineteenth two thousand three" */
04450                   res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
04451                }
04452             }
04453             break;
04454          case 'q':
04455             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04456             /* XXX As emphasized elsewhere, this should the native way in your
04457              * language to say the date, with changes in what you say, depending
04458              * upon how recent the date is. XXX */
04459             {
04460                struct timeval now = ast_tvnow();
04461                struct ast_tm tmnow;
04462                time_t beg_today;
04463 
04464                ast_localtime(&now, &tmnow, tzone);
04465                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04466                /* In any case, it saves not having to do ast_mktime() */
04467                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04468                if (beg_today < t) {
04469                   /* Today */
04470                } else if ((beg_today - 86400) < t) {
04471                   /* Yesterday */
04472                   res = wait_file(chan, ints, "digits/yesterday", lang);
04473                } else if (beg_today - 86400 * 6 < t) {
04474                   /* Within the last week */
04475                   res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
04476                } else if (beg_today - 2628000 < t) {
04477                   /* Less than a month ago - "Sunday, October third" */
04478                   res = ast_say_date_with_format_en(chan, t, ints, lang, "ABd", tzone);
04479                } else if (beg_today - 15768000 < t) {
04480                   /* Less than 6 months ago - "August seventh" */
04481                   res = ast_say_date_with_format_en(chan, t, ints, lang, "Bd", tzone);
04482                } else {
04483                   /* More than 6 months ago - "April nineteenth two thousand three" */
04484                   res = ast_say_date_with_format_en(chan, t, ints, lang, "BdY", tzone);
04485                }
04486             }
04487             break;
04488          case 'R':
04489             res = ast_say_date_with_format_en(chan, t, ints, lang, "HM", tzone);
04490             break;
04491          case 'S':
04492             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
04493             break;
04494          case 'T':
04495             res = ast_say_date_with_format_en(chan, t, ints, lang, "HMS", tzone);
04496             break;
04497          case ' ':
04498          case '   ':
04499             /* Just ignore spaces and tabs */
04500             break;
04501          default:
04502             /* Unknown character */
04503             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04504       }
04505       /* Jump out on DTMF */
04506       if (res) {
04507          break;
04508       }
04509    }
04510    return res;
04511 }
04512 
04513 /*! \brief ast_say_date_with_format_he Say formatted date in Hebrew
04514  *
04515  * \ref ast_say_date_with_format_en for the details of the options
04516  *
04517  * Changes from the English version:
04518  *
04519  * - don't replicate in here the logic of ast_say_number_full_he
04520  *
04521  * - year is always 4-digit (because it's simpler)
04522  *
04523  * - added c, x, and X. Mainly for my tests
04524  *
04525  * - The standard "long" format used in Hebrew is AdBY, rather than ABdY
04526  *
04527  * \todo
04528  * - A "ha" is missing in the standard date format, before the 'd'.
04529  * - The numbers of 3000--19000 are not handled well
04530  */
04531 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)
04532 {
04533 #define IL_DATE_STR "AdBY"
04534 #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 */
04535 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
04536    /* TODO: This whole function is cut&paste from
04537     * ast_say_date_with_format_en . Is that considered acceptable?
04538     **/
04539    struct timeval when = { t, 0 };
04540    struct ast_tm tm;
04541    int res = 0, offset, sndoffset;
04542    char sndfile[256], nextmsg[256];
04543 
04544    if (!format) {
04545       format = IL_DATE_STR_FULL;
04546    }
04547 
04548    ast_localtime(&when, &tm, tzone);
04549 
04550    for (offset = 0; format[offset] != '\0'; offset++) {
04551       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04552       switch (format[offset]) {
04553          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04554          case '\'':
04555             /* Literal name of a sound file */
04556             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04557                sndfile[sndoffset] = format[offset];
04558             }
04559             sndfile[sndoffset] = '\0';
04560             res = wait_file(chan, ints, sndfile, lang);
04561             break;
04562          case 'A':
04563          case 'a':
04564             /* Sunday - Saturday */
04565             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04566             res = wait_file(chan, ints, nextmsg, lang);
04567             break;
04568          case 'B':
04569          case 'b':
04570          case 'h':
04571             /* January - December */
04572             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04573             res = wait_file(chan, ints, nextmsg, lang);
04574             break;
04575          case 'd':
04576          case 'e': /* Day of the month */
04577             /* I'm not sure exactly what the parameters
04578             * audiofd and ctrlfd to
04579             * ast_say_number_full_he mean, but it seems
04580             * safe to pass -1 there.
04581             *
04582             * At least in one of the pathes :-(
04583             */
04584             res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
04585             break;
04586          case 'Y': /* Year */
04587             res = ast_say_number_full_he(chan, tm.tm_year + 1900, ints, lang, "f", -1, -1);
04588             break;
04589          case 'I':
04590          case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
04591          case 'H':
04592          case 'k': /* 24-Hour */
04593             res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
04594             break;
04595          case 'M': /* Minute */
04596             if (tm.tm_min >= 0 && tm.tm_min <= 9)  /* say a leading zero if needed */
04597                res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
04598             res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
04599             break;
04600          case 'P':
04601          case 'p':
04602             /* AM/PM - There is no AM/PM in Hebrew... */
04603             break;
04604          case 'Q':
04605             /* Shorthand for "Today", "Yesterday", or "date" */
04606          case 'q':
04607             /* Shorthand for "" (today), "Yesterday", A
04608              * (weekday), or "date" */
04609             /* XXX As emphasized elsewhere, this should the native way in your
04610              * language to say the date, with changes in what you say, depending
04611              * upon how recent the date is. XXX */
04612             {
04613                struct timeval now = ast_tvnow();
04614                struct ast_tm tmnow;
04615                time_t beg_today;
04616                char todo = format[offset]; /* The letter to format*/
04617 
04618                ast_localtime(&now, &tmnow, tzone);
04619                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04620                /* In any case, it saves not having to do ast_mktime() */
04621                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04622                if (beg_today < t) {
04623                   /* Today */
04624                   if (todo == 'Q') {
04625                      res = wait_file(chan, ints, "digits/today", lang);
04626                   }
04627                } else if (beg_today - 86400 < t) {
04628                   /* Yesterday */
04629                   res = wait_file(chan, ints, "digits/yesterday", lang);
04630                } else if ((todo != 'Q') && (beg_today - 86400 * 6 < t)) {
04631                   /* Within the last week */
04632                   res = ast_say_date_with_format_he(chan, t, ints, lang, "A", tzone);
04633                } else {
04634                   res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
04635                }
04636             }
04637             break;
04638          case 'R':
04639             res = ast_say_date_with_format_he(chan, t, ints, lang, "HM", tzone);
04640             break;
04641          case 'S': /* Seconds */
04642             res = ast_say_number_full_he(chan, tm.tm_sec,
04643                ints, lang, "f", -1, -1
04644             );
04645             break;
04646          case 'T':
04647             res = ast_say_date_with_format_he(chan, t, ints, lang, "HMS", tzone);
04648             break;
04649          /* c, x, and X seem useful for testing. Not sure
04650           * if they're good for the general public */
04651          case 'c':
04652             res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR_FULL, tzone);
04653             break;
04654          case 'x':
04655             res = ast_say_date_with_format_he(chan, t, ints, lang, IL_DATE_STR, tzone);
04656             break;
04657          case 'X': /* Currently not locale-dependent...*/
04658             res = ast_say_date_with_format_he(chan, t, ints, lang, IL_TIME_STR, tzone);
04659             break;
04660          case ' ':
04661          case '   ':
04662             /* Just ignore spaces and tabs */
04663             break;
04664          default:
04665             /* Unknown character */
04666             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04667       }
04668       /* Jump out on DTMF */
04669       if (res) {
04670          break;
04671       }
04672    }
04673    return res;
04674 }
04675 
04676 
04677 /*! \brief Spanish syntax */
04678 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)
04679 {
04680    struct timeval when = { t, 0 };
04681    struct ast_tm tm;
04682    int res=0, offset, sndoffset;
04683    char sndfile[256], nextmsg[256];
04684 
04685    if (format == NULL)
04686       format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
04687 
04688    ast_localtime(&when, &tm, tzone);
04689 
04690    for (offset=0 ; format[offset] != '\0' ; offset++) {
04691       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04692       switch (format[offset]) {
04693          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04694          case '\'':
04695             /* Literal name of a sound file */
04696             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04697                sndfile[sndoffset] = format[offset];
04698             }
04699             sndfile[sndoffset] = '\0';
04700             snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
04701             res = wait_file(chan, ints, nextmsg, lang);
04702             break;
04703          case 'A':
04704          case 'a':
04705             /* Sunday - Saturday */
04706             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04707             res = wait_file(chan, ints, nextmsg, lang);
04708             break;
04709          case 'B':
04710          case 'b':
04711          case 'h':
04712             /* January - December */
04713             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04714             res = wait_file(chan, ints, nextmsg, lang);
04715             break;
04716          case 'm':
04717             /* First - Twelfth */
04718             snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04719             res = wait_file(chan, ints, nextmsg, lang);
04720             break;
04721          case 'd':
04722          case 'e':
04723             /* First - Thirtyfirst */
04724             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
04725             break;
04726          case 'Y':
04727             /* Year */
04728             res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
04729             break;
04730          case 'I':
04731          case 'l':
04732             /* 12-Hour */
04733             if (tm.tm_hour == 0)
04734                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
04735             else if (tm.tm_hour == 1 || tm.tm_hour == 13)
04736                snprintf(nextmsg,sizeof(nextmsg), "digits/1F");
04737             else if (tm.tm_hour > 12)
04738                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04739             else
04740                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
04741             res = wait_file(chan, ints, nextmsg, lang);
04742             break;
04743          case 'H':
04744          case 'k':
04745             /* 24-Hour */
04746             res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
04747             break;
04748          case 'M':
04749             /* Minute */
04750             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
04751             break;
04752          case 'P':
04753          case 'p':
04754             /* AM/PM */
04755             if (tm.tm_hour > 18)
04756                res = wait_file(chan, ints, "digits/p-m", lang);
04757             else if (tm.tm_hour > 12)
04758                res = wait_file(chan, ints, "digits/afternoon", lang);
04759             else if (tm.tm_hour)
04760                res = wait_file(chan, ints, "digits/a-m", lang);
04761             break;
04762          case 'Q':
04763             /* Shorthand for "Today", "Yesterday", or ABdY */
04764             /* XXX As emphasized elsewhere, this should the native way in your
04765              * language to say the date, with changes in what you say, depending
04766              * upon how recent the date is. XXX */
04767             {
04768                struct timeval now = ast_tvnow();
04769                struct ast_tm tmnow;
04770                time_t beg_today;
04771 
04772                ast_localtime(&now, &tmnow, tzone);
04773                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04774                /* In any case, it saves not having to do ast_mktime() */
04775                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04776                if (beg_today < t) {
04777                   /* Today */
04778                   res = wait_file(chan, ints, "digits/today", lang);
04779                } else if (beg_today - 86400 < t) {
04780                   /* Yesterday */
04781                   res = wait_file(chan, ints, "digits/yesterday", lang);
04782                } else {
04783                   res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
04784                }
04785             }
04786             break;
04787          case 'q':
04788             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04789             /* XXX As emphasized elsewhere, this should the native way in your
04790              * language to say the date, with changes in what you say, depending
04791              * upon how recent the date is. XXX */
04792             {
04793                struct timeval now = ast_tvnow();
04794                struct ast_tm tmnow;
04795                time_t beg_today;
04796 
04797                ast_localtime(&now, &tmnow, tzone);
04798                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04799                /* In any case, it saves not having to do ast_mktime() */
04800                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04801                if (beg_today < t) {
04802                   /* Today */
04803                   res = wait_file(chan, ints, "digits/today", lang);
04804                } else if ((beg_today - 86400) < t) {
04805                   /* Yesterday */
04806                   res = wait_file(chan, ints, "digits/yesterday", lang);
04807                } else if (beg_today - 86400 * 6 < t) {
04808                   /* Within the last week */
04809                   res = ast_say_date_with_format_es(chan, t, ints, lang, "A", tzone);
04810                } else {
04811                   res = ast_say_date_with_format_es(chan, t, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", tzone);
04812                }
04813             }
04814             break;
04815          case 'R':
04816             res = ast_say_date_with_format_es(chan, t, ints, lang, "H 'digits/y' M", tzone);
04817             break;
04818          case 'S':
04819             /* Seconds */
04820             if (tm.tm_sec == 0) {
04821                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
04822                res = wait_file(chan, ints, nextmsg, lang);
04823             } else if (tm.tm_sec < 10) {
04824                res = wait_file(chan, ints, "digits/oh", lang);
04825                if (!res) {
04826                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
04827                   res = wait_file(chan, ints, nextmsg, lang);
04828                }
04829             } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
04830                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
04831                res = wait_file(chan, ints, nextmsg, lang);
04832             } else {
04833                int ten, one;
04834                ten = (tm.tm_sec / 10) * 10;
04835                one = (tm.tm_sec % 10);
04836                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
04837                res = wait_file(chan, ints, nextmsg, lang);
04838                if (!res) {
04839                   /* Fifty, not fifty-zero */
04840                   if (one != 0) {
04841                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
04842                      res = wait_file(chan, ints, nextmsg, lang);
04843                   }
04844                }
04845             }
04846             break;
04847          case 'T':
04848             res = ast_say_date_with_format_es(chan, t, ints, lang, "HMS", tzone);
04849             break;
04850          case ' ':
04851          case '   ':
04852             /* Just ignore spaces and tabs */
04853             break;
04854          default:
04855             /* Unknown character */
04856             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04857       }
04858       /* Jump out on DTMF */
04859       if (res) {
04860          break;
04861       }
04862    }
04863    return res;
04864 }
04865 
04866 /*! \brief French syntax
04867 oclock = heure
04868 */
04869 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)
04870 {
04871    struct timeval when = { t, 0 };
04872    struct ast_tm tm;
04873    int res=0, offset, sndoffset;
04874    char sndfile[256], nextmsg[256];
04875 
04876    if (format == NULL)
04877       format = "AdBY 'digits/at' IMp";
04878 
04879    ast_localtime(&when, &tm, tzone);
04880 
04881    for (offset=0 ; format[offset] != '\0' ; offset++) {
04882       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04883       switch (format[offset]) {
04884          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04885          case '\'':
04886             /* Literal name of a sound file */
04887             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04888                sndfile[sndoffset] = format[offset];
04889             }
04890             sndfile[sndoffset] = '\0';
04891             res = wait_file(chan, ints, sndfile, lang);
04892             break;
04893          case 'A':
04894          case 'a':
04895             /* Sunday - Saturday */
04896             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04897             res = wait_file(chan, ints, nextmsg, lang);
04898             break;
04899          case 'B':
04900          case 'b':
04901          case 'h':
04902             /* January - December */
04903             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04904             res = wait_file(chan, ints, nextmsg, lang);
04905             break;
04906          case 'm':
04907             /* First - Twelfth */
04908             snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04909             res = wait_file(chan, ints, nextmsg, lang);
04910             break;
04911          case 'd':
04912          case 'e':
04913             /* First */
04914             if (tm.tm_mday == 1) {
04915                snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
04916                res = wait_file(chan, ints, nextmsg, lang);
04917             } else {
04918                res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
04919             }
04920             break;
04921          case 'Y':
04922             /* Year */
04923             if (tm.tm_year > 99) {
04924                res = wait_file(chan, ints, "digits/2", lang);
04925                if (!res) {
04926                   res = wait_file(chan, ints, "digits/thousand", lang);
04927                }
04928                if (tm.tm_year > 100) {
04929                   if (!res) {
04930                      res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
04931                   }
04932                }
04933             } else {
04934                if (tm.tm_year < 1) {
04935                   /* I'm not going to handle 1900 and prior */
04936                   /* We'll just be silent on the year, instead of bombing out. */
04937                } else {
04938                   res = wait_file(chan, ints, "digits/thousand", lang);
04939                   if (!res) {
04940                      wait_file(chan, ints, "digits/9", lang);
04941                      wait_file(chan, ints, "digits/hundred", lang);
04942                      res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
04943                   }
04944                }
04945             }
04946             break;
04947          case 'I':
04948          case 'l':
04949             /* 12-Hour */
04950             if (tm.tm_hour == 0)
04951                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
04952             else if (tm.tm_hour > 12)
04953                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04954             else
04955                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
04956             res = wait_file(chan, ints, nextmsg, lang);
04957             if (!res)
04958                res = wait_file(chan, ints, "digits/oclock", lang);
04959             break;
04960          case 'H':
04961          case 'k':
04962             /* 24-Hour */
04963             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
04964             if (!res)
04965                res = wait_file(chan, ints, "digits/oclock", lang);
04966             break;
04967          case 'M':
04968             /* Minute */
04969             if (tm.tm_min == 0) {
04970                break;
04971             }
04972             res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
04973             break;
04974          case 'P':
04975          case 'p':
04976             /* AM/PM */
04977             if (tm.tm_hour > 11)
04978                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
04979             else
04980                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
04981             res = wait_file(chan, ints, nextmsg, lang);
04982             break;
04983          case 'Q':
04984             /* Shorthand for "Today", "Yesterday", or AdBY */
04985             /* XXX As emphasized elsewhere, this should the native way in your
04986              * language to say the date, with changes in what you say, depending
04987              * upon how recent the date is. XXX */
04988             {
04989                struct timeval now = ast_tvnow();
04990                struct ast_tm tmnow;
04991                time_t beg_today;
04992 
04993                ast_localtime(&now, &tmnow, tzone);
04994                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04995                /* In any case, it saves not having to do ast_mktime() */
04996                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04997                if (beg_today < t) {
04998                   /* Today */
04999                   res = wait_file(chan, ints, "digits/today", lang);
05000                } else if (beg_today - 86400 < t) {
05001                   /* Yesterday */
05002                   res = wait_file(chan, ints, "digits/yesterday", lang);
05003                } else {
05004                   res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
05005                }
05006             }
05007             break;
05008          case 'q':
05009             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
05010             /* XXX As emphasized elsewhere, this should the native way in your
05011              * language to say the date, with changes in what you say, depending
05012              * upon how recent the date is. XXX */
05013             {
05014                struct timeval now = ast_tvnow();
05015                struct ast_tm tmnow;
05016                time_t beg_today;
05017 
05018                ast_localtime(&now, &tmnow, tzone);
05019                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05020                /* In any case, it saves not having to do ast_mktime() */
05021                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05022                if (beg_today < t) {
05023                   /* Today */
05024                } else if ((beg_today - 86400) < t) {
05025                   /* Yesterday */
05026                   res = wait_file(chan, ints, "digits/yesterday", lang);
05027                } else if (beg_today - 86400 * 6 < t) {
05028                   /* Within the last week */
05029                   res = ast_say_date_with_format_fr(chan, t, ints, lang, "A", tzone);
05030                } else {
05031                   res = ast_say_date_with_format_fr(chan, t, ints, lang, "AdBY", tzone);
05032                }
05033             }
05034             break;
05035          case 'R':
05036             res = ast_say_date_with_format_fr(chan, t, ints, lang, "HM", tzone);
05037             break;
05038          case 'S':
05039             /* Seconds */
05040             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
05041             if (!res) {
05042                res = wait_file(chan, ints, "digits/second", lang);
05043             }
05044             break;
05045          case 'T':
05046             res = ast_say_date_with_format_fr(chan, t, ints, lang, "HMS", tzone);
05047             break;
05048          case ' ':
05049          case '   ':
05050             /* Just ignore spaces and tabs */
05051             break;
05052          default:
05053             /* Unknown character */
05054             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05055       }
05056       /* Jump out on DTMF */
05057       if (res) {
05058          break;
05059       }
05060    }
05061    return res;
05062 }
05063 
05064 /*! \brief Italian syntax */
05065 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)
05066 {
05067    struct timeval when = { t, 0 };
05068    struct ast_tm tm;
05069    int res=0, offset, sndoffset;
05070    char sndfile[256], nextmsg[256];
05071 
05072    if (format == NULL)
05073       format = "AdB 'digits/at' IMp";
05074 
05075    ast_localtime(&when, &tm, tzone);
05076 
05077    for (offset=0 ; format[offset] != '\0' ; offset++) {
05078       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05079       switch (format[offset]) {
05080          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05081          case '\'':
05082             /* Literal name of a sound file */
05083             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05084                sndfile[sndoffset] = format[offset];
05085             }
05086             sndfile[sndoffset] = '\0';
05087             res = wait_file(chan, ints, sndfile, lang);
05088             break;
05089          case 'A':
05090          case 'a':
05091             /* Sunday - Saturday */
05092             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05093             res = wait_file(chan, ints, nextmsg, lang);
05094             break;
05095          case 'B':
05096          case 'b':
05097          case 'h':
05098             /* January - December */
05099             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05100             res = wait_file(chan, ints, nextmsg, lang);
05101             break;
05102          case 'm':
05103             /* First - Twelfth */
05104             snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
05105             res = wait_file(chan, ints, nextmsg, lang);
05106             break;
05107          case 'd':
05108          case 'e':
05109             /* First day of the month is spelled as ordinal */
05110             if (tm.tm_mday == 1) {
05111                snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
05112                res = wait_file(chan, ints, nextmsg, lang);
05113             } else {
05114                if (!res) {
05115                   res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
05116                }
05117             }
05118             break;
05119          case 'Y':
05120             /* Year */
05121             if (tm.tm_year > 99) {
05122                res = wait_file(chan, ints, "digits/ore-2000", lang);
05123                if (tm.tm_year > 100) {
05124                   if (!res) {
05125                   /* This works until the end of 2021 */
05126                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
05127                   res = wait_file(chan, ints, nextmsg, lang);
05128                   }
05129                }
05130             } else {
05131                if (tm.tm_year < 1) {
05132                   /* I'm not going to handle 1900 and prior */
05133                   /* We'll just be silent on the year, instead of bombing out. */
05134                } else {
05135                   res = wait_file(chan, ints, "digits/ore-1900", lang);
05136                   if ((!res) && (tm.tm_year != 0)) {
05137                      if (tm.tm_year <= 21) {
05138                         /* 1910 - 1921 */
05139                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
05140                         res = wait_file(chan, ints, nextmsg, lang);
05141                      } else {
05142                         /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
05143                         int ten, one;
05144                         ten = tm.tm_year / 10;
05145                         one = tm.tm_year % 10;
05146                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
05147                         res = wait_file(chan, ints, nextmsg, lang);
05148                         if (!res) {
05149                            if (one != 0) {
05150                               snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
05151                               res = wait_file(chan, ints, nextmsg, lang);
05152                            }
05153                         }
05154                      }
05155                   }
05156                }
05157             }
05158             break;
05159          case 'I':
05160          case 'l':
05161             /* 12-Hour */
05162             if (tm.tm_hour == 0)
05163                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
05164             else if (tm.tm_hour > 12)
05165                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
05166             else
05167                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
05168                res = wait_file(chan, ints, nextmsg, lang);
05169             break;
05170          case 'H':
05171          case 'k':
05172             /* 24-Hour */
05173             if (tm.tm_hour == 0) {
05174                res = wait_file(chan, ints, "digits/ore-mezzanotte", lang);
05175             } else if (tm.tm_hour == 1) {
05176                res = wait_file(chan, ints, "digits/ore-una", lang);
05177             } else {
05178                res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
05179             }
05180             break;
05181          case 'M':
05182             /* Minute */
05183             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05184             break;
05185          case 'P':
05186          case 'p':
05187             /* AM/PM */
05188             if (tm.tm_hour > 11)
05189                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
05190             else
05191                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
05192                res = wait_file(chan, ints, nextmsg, lang);
05193             break;
05194          case 'Q':
05195             /* Shorthand for "Today", "Yesterday", or ABdY */
05196             /* XXX As emphasized elsewhere, this should the native way in your
05197              * language to say the date, with changes in what you say, depending
05198              * upon how recent the date is. XXX */
05199             {
05200                struct timeval now = ast_tvnow();
05201                struct ast_tm tmnow;
05202                time_t beg_today;
05203 
05204                ast_localtime(&now, &tmnow, tzone);
05205                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05206                /* In any case, it saves not having to do ast_mktime() */
05207                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05208                if (beg_today < t) {
05209                   /* Today */
05210                   res = wait_file(chan, ints, "digits/today", lang);
05211                } else if (beg_today - 86400 < t) {
05212                   /* Yesterday */
05213                   res = wait_file(chan, ints, "digits/yesterday", lang);
05214                } else {
05215                   res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
05216                }
05217             }
05218             break;
05219          case 'q':
05220             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
05221             {
05222                struct timeval now = ast_tvnow();
05223                struct ast_tm tmnow;
05224                time_t beg_today;
05225 
05226                ast_localtime(&now, &tmnow, tzone);
05227                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05228                /* In any case, it saves not having to do ast_mktime() */
05229                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05230                if (beg_today < t) {
05231                   /* Today */
05232                } else if ((beg_today - 86400) < t) {
05233                   /* Yesterday */
05234                   res = wait_file(chan, ints, "digits/yesterday", lang);
05235                } else if (beg_today - 86400 * 6 < t) {
05236                   /* Within the last week */
05237                   res = ast_say_date_with_format_it(chan, t, ints, lang, "A", tzone);
05238                } else {
05239                   res = ast_say_date_with_format_it(chan, t, ints, lang, "AdB", tzone);
05240                }
05241             }
05242             break;
05243          case 'R':
05244             res = ast_say_date_with_format_it(chan, t, ints, lang, "HM", tzone);
05245             break;
05246          case 'S':
05247             /* Seconds */
05248             if (tm.tm_sec == 0) {
05249                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
05250                res = wait_file(chan, ints, nextmsg, lang);
05251             } else if (tm.tm_sec < 10) {
05252                res = wait_file(chan, ints, "digits/oh", lang);
05253                if (!res) {
05254                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
05255                   res = wait_file(chan, ints, nextmsg, lang);
05256                }
05257             } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
05258                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
05259                res = wait_file(chan, ints, nextmsg, lang);
05260             } else {
05261                int ten, one;
05262                ten = (tm.tm_sec / 10) * 10;
05263                one = (tm.tm_sec % 10);
05264                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
05265                res = wait_file(chan, ints, nextmsg, lang);
05266                if (!res) {
05267                   /* Fifty, not fifty-zero */
05268                   if (one != 0) {
05269                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
05270                      res = wait_file(chan, ints, nextmsg, lang);
05271                   }
05272                }
05273             }
05274             break;
05275          case 'T':
05276             res = ast_say_date_with_format_it(chan, t, ints, lang, "HMS", tzone);
05277             break;
05278          case ' ':
05279          case '   ':
05280             /* Just ignore spaces and tabs */
05281             break;
05282          default:
05283             /* Unknown character */
05284             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05285       }
05286       /* Jump out on DTMF */
05287       if (res) {
05288          break;
05289       }
05290    }
05291    return res;
05292 }
05293 
05294 /*! \brief Dutch syntax */
05295 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)
05296 {
05297    struct timeval when = { t, 0 };
05298    struct ast_tm tm;
05299    int res=0, offset, sndoffset;
05300    char sndfile[256], nextmsg[256];
05301 
05302    if (format == NULL)
05303       format = "AdBY 'digits/at' IMp";
05304 
05305    ast_localtime(&when, &tm, tzone);
05306 
05307    for (offset=0 ; format[offset] != '\0' ; offset++) {
05308       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05309       switch (format[offset]) {
05310          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05311          case '\'':
05312             /* Literal name of a sound file */
05313             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05314                sndfile[sndoffset] = format[offset];
05315             }
05316             sndfile[sndoffset] = '\0';
05317             res = wait_file(chan, ints, sndfile, lang);
05318             break;
05319          case 'A':
05320          case 'a':
05321             /* Sunday - Saturday */
05322             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05323             res = wait_file(chan, ints, nextmsg, lang);
05324             break;
05325          case 'B':
05326          case 'b':
05327          case 'h':
05328             /* January - December */
05329             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05330             res = wait_file(chan, ints, nextmsg, lang);
05331             break;
05332          case 'm':
05333             /* First - Twelfth */
05334             snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
05335             res = wait_file(chan, ints, nextmsg, lang);
05336             break;
05337          case 'd':
05338          case 'e':
05339             /* First - Thirtyfirst */
05340             res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
05341             break;
05342          case 'Y':
05343             /* Year */
05344             if (tm.tm_year > 99) {
05345                res = wait_file(chan, ints, "digits/2", lang);
05346                if (!res) {
05347                   res = wait_file(chan, ints, "digits/thousand", lang);
05348                }
05349                if (tm.tm_year > 100) {
05350                   if (!res) {
05351                      /* This works until the end of 2020 */
05352                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
05353                      res = wait_file(chan, ints, nextmsg, lang);
05354                   }
05355                }
05356             } else {
05357                if (tm.tm_year < 1) {
05358                   /* I'm not going to handle 1900 and prior */
05359                   /* We'll just be silent on the year, instead of bombing out. */
05360                } else {
05361                   res = wait_file(chan, ints, "digits/19", lang);
05362                   if (!res) {
05363                      if (tm.tm_year <= 9) {
05364                         /* 1901 - 1909 */
05365                         res = wait_file(chan, ints, "digits/oh", lang);
05366                         if (!res) {
05367                            snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
05368                            res = wait_file(chan, ints, nextmsg, lang);
05369                         }
05370                      } else if (tm.tm_year <= 20) {
05371                         /* 1910 - 1920 */
05372                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
05373                         res = wait_file(chan, ints, nextmsg, lang);
05374                      } else {
05375                         /* 1921 - 1999 */
05376                         int ten, one;
05377                         ten = tm.tm_year / 10;
05378                         one = tm.tm_year % 10;
05379                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten * 10);
05380                         res = wait_file(chan, ints, nextmsg, lang);
05381                         if (!res) {
05382                            if (one != 0) {
05383                               snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
05384                               res = wait_file(chan, ints, nextmsg, lang);
05385                            }
05386                         }
05387                      }
05388                   }
05389                }
05390             }
05391             break;
05392          case 'I':
05393          case 'l':
05394             /* 12-Hour */
05395             if (tm.tm_hour == 0)
05396                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
05397             else if (tm.tm_hour > 12)
05398                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
05399             else
05400                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
05401             res = wait_file(chan, ints, nextmsg, lang);
05402             break;
05403          case 'H':
05404          case 'k':
05405             /* 24-Hour */
05406             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
05407             if (!res) {
05408                res = wait_file(chan, ints, "digits/nl-uur", lang);
05409             }
05410             break;
05411          case 'M':
05412             /* Minute */
05413             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05414             break;
05415          case 'P':
05416          case 'p':
05417             /* AM/PM */
05418             if (tm.tm_hour > 11)
05419                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
05420             else
05421                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
05422             res = wait_file(chan, ints, nextmsg, lang);
05423             break;
05424          case 'Q':
05425             /* Shorthand for "Today", "Yesterday", or AdBY */
05426             /* XXX As emphasized elsewhere, this should the native way in your
05427              * language to say the date, with changes in what you say, depending
05428              * upon how recent the date is. XXX */
05429             {
05430                struct timeval now = ast_tvnow();
05431                struct ast_tm tmnow;
05432                time_t beg_today;
05433 
05434                ast_localtime(&now, &tmnow, tzone);
05435                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05436                /* In any case, it saves not having to do ast_mktime() */
05437                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05438                if (beg_today < t) {
05439                   /* Today */
05440                   res = wait_file(chan, ints, "digits/today", lang);
05441                } else if (beg_today - 86400 < t) {
05442                   /* Yesterday */
05443                   res = wait_file(chan, ints, "digits/yesterday", lang);
05444                } else {
05445                   res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
05446                }
05447             }
05448             break;
05449          case 'q':
05450             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
05451             {
05452                struct timeval now = ast_tvnow();
05453                struct ast_tm tmnow;
05454                time_t beg_today;
05455 
05456                ast_localtime(&now, &tmnow, tzone);
05457                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05458                /* In any case, it saves not having to do ast_mktime() */
05459                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05460                if (beg_today < t) {
05461                   /* Today */
05462                } else if ((beg_today - 86400) < t) {
05463                   /* Yesterday */
05464                   res = wait_file(chan, ints, "digits/yesterday", lang);
05465                } else if (beg_today - 86400 * 6 < t) {
05466                   /* Within the last week */
05467                   res = ast_say_date_with_format_nl(chan, t, ints, lang, "A", tzone);
05468                } else {
05469                   res = ast_say_date_with_format_nl(chan, t, ints, lang, "AdBY", tzone);
05470                }
05471             }
05472             break;
05473          case 'R':
05474             res = ast_say_date_with_format_nl(chan, t, ints, lang, "HM", tzone);
05475             break;
05476          case 'S':
05477             /* Seconds */
05478             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
05479             break;
05480          case 'T':
05481             res = ast_say_date_with_format_nl(chan, t, ints, lang, "HMS", tzone);
05482             break;
05483          case ' ':
05484          case '   ':
05485             /* Just ignore spaces and tabs */
05486             break;
05487          default:
05488             /* Unknown character */
05489             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05490       }
05491       /* Jump out on DTMF */
05492       if (res) {
05493          break;
05494       }
05495    }
05496    return res;
05497 }
05498 
05499 /*! \brief Polish syntax */
05500 int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const char *ints, const char *lang, const char *format, const char *tzone)
05501 {
05502    struct timeval when = { thetime, 0 };
05503    struct ast_tm tm;
05504    int res=0, offset, sndoffset;
05505    char sndfile[256], nextmsg[256];
05506 
05507    ast_localtime(&when, &tm, tzone);
05508 
05509    for (offset = 0 ; format[offset] != '\0' ; offset++) {
05510       int remaining;
05511       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05512       switch (format[offset]) {
05513          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05514          case '\'':
05515             /* Literal name of a sound file */
05516             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05517                sndfile[sndoffset] = format[offset];
05518             }
05519             sndfile[sndoffset] = '\0';
05520             res = wait_file(chan, ints, sndfile, lang);
05521             break;
05522          case 'A':
05523          case 'a':
05524             /* Sunday - Saturday */
05525             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05526             res = wait_file(chan, ints, nextmsg, lang);
05527             break;
05528          case 'B':
05529          case 'b':
05530          case 'h':
05531             /* January - December */
05532             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05533             res = wait_file(chan, ints, nextmsg, lang);
05534             break;
05535          case 'm':
05536             /* Month enumerated */
05537             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
05538             break;
05539          case 'd':
05540          case 'e':
05541             /* First - Thirtyfirst */
05542             remaining = tm.tm_mday;
05543             if (tm.tm_mday > 30) {
05544                res = wait_file(chan, ints, "digits/h-30", lang);
05545                remaining -= 30;
05546             }
05547             if (tm.tm_mday > 20 && tm.tm_mday < 30) {
05548                res = wait_file(chan, ints, "digits/h-20", lang);
05549                remaining -= 20;
05550             }
05551             if (!res) {
05552                snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remaining);
05553                res = wait_file(chan, ints, nextmsg, lang);
05554             }
05555             break;
05556          case 'Y':
05557             /* Year */
05558             if (tm.tm_year > 100) {
05559                res = wait_file(chan, ints, "digits/2", lang);
05560                if (!res)
05561                   res = wait_file(chan, ints, "digits/1000.2", lang);
05562                if (tm.tm_year > 100) {
05563                   if (!res)
05564                      res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
05565                }
05566             } else if (tm.tm_year == 100) {
05567                res = wait_file(chan, ints, "digits/h-2000", lang);
05568             } else {
05569                if (tm.tm_year < 1) {
05570                   /* I'm not going to handle 1900 and prior */
05571                   /* We'll just be silent on the year, instead of bombing out. */
05572                   break;
05573                } else {
05574                   res = wait_file(chan, ints, "digits/1000", lang);
05575                   if (!res) {
05576                      wait_file(chan, ints, "digits/900", lang);
05577                      res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
05578                   }
05579                }
05580             }
05581             if (!res)
05582                wait_file(chan, ints, "digits/year", lang);
05583             break;
05584          case 'I':
05585          case 'l':
05586             /* 12-Hour */
05587             if (tm.tm_hour == 0)
05588                ast_copy_string(nextmsg, "digits/t-12", sizeof(nextmsg));
05589             else if (tm.tm_hour > 12)
05590                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
05591             else
05592                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
05593 
05594             res = wait_file(chan, ints, nextmsg, lang);
05595             break;
05596          case 'H':
05597          case 'k':
05598             /* 24-Hour */
05599             if (tm.tm_hour != 0) {
05600                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
05601                res = wait_file(chan, ints, nextmsg, lang);
05602             } else
05603                res = wait_file(chan, ints, "digits/t-24", lang);
05604             break;
05605          case 'M':
05606          case 'N':
05607             /* Minute */
05608             if (tm.tm_min == 0) {
05609                if (format[offset] == 'M') {
05610                   res = wait_file(chan, ints, "digits/oclock", lang);
05611                } else {
05612                   res = wait_file(chan, ints, "digits/100", lang);
05613                }
05614             } else
05615                res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
05616             break;
05617          case 'P':
05618          case 'p':
05619             /* AM/PM */
05620             if (tm.tm_hour > 11)
05621                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
05622             else
05623                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
05624             res = wait_file(chan, ints, nextmsg, lang);
05625             break;
05626          case 'Q':
05627             /* Shorthand for "Today", "Yesterday", or AdBY */
05628             {
05629                struct timeval now = ast_tvnow();
05630                struct ast_tm tmnow;
05631                time_t beg_today;
05632 
05633                ast_localtime(&now, &tmnow, tzone);
05634                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05635                /* In any case, it saves not having to do ast_mktime() */
05636                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05637                if (beg_today < thetime) {
05638                   /* Today */
05639                   res = wait_file(chan, ints, "digits/today", lang);
05640                } else if (beg_today - 86400 < thetime) {
05641                   /* Yesterday */
05642                   res = wait_file(chan, ints, "digits/yesterday", lang);
05643                } else {
05644                   res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
05645                }
05646             }
05647             break;
05648          case 'q':
05649             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
05650             {
05651                struct timeval now = ast_tvnow();
05652                struct ast_tm tmnow;
05653                time_t beg_today;
05654 
05655                ast_localtime(&now, &tmnow, tzone);
05656                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05657                /* In any case, it saves not having to do ast_mktime() */
05658                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05659                if (beg_today < thetime) {
05660                   /* Today */
05661                } else if ((beg_today - 86400) < thetime) {
05662                   /* Yesterday */
05663                   res = wait_file(chan, ints, "digits/yesterday", lang);
05664                } else if (beg_today - 86400 * 6 < thetime) {
05665                   /* Within the last week */
05666                   res = ast_say_date_with_format(chan, thetime, ints, lang, "A", tzone);
05667                } else {
05668                   res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", tzone);
05669                }
05670             }
05671             break;
05672          case 'R':
05673             res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", tzone);
05674             break;
05675          case 'S':
05676             /* Seconds */
05677             res = wait_file(chan, ints, "digits/and", lang);
05678             if (!res) {
05679                if (tm.tm_sec == 1) {
05680                   res = wait_file(chan, ints, "digits/1z", lang);
05681                   if (!res)
05682                      res = wait_file(chan, ints, "digits/second-a", lang);
05683                } else {
05684                   res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
05685                   if (!res) {
05686                      int ten, one;
05687                      ten = tm.tm_sec / 10;
05688                      one = tm.tm_sec % 10;
05689 
05690                      if (one > 1 && one < 5 && ten != 1)
05691                         res = wait_file(chan, ints, "digits/seconds", lang);
05692                      else
05693                         res = wait_file(chan, ints, "digits/second", lang);
05694                   }
05695                }
05696             }
05697             break;
05698          case 'T':
05699             res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", tzone);
05700             break;
05701          case ' ':
05702          case '   ':
05703             /* Just ignore spaces and tabs */
05704             break;
05705          default:
05706             /* Unknown character */
05707             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05708       }
05709       /* Jump out on DTMF */
05710       if (res)
05711          break;
05712    }
05713    return res;
05714 }
05715 
05716 /*! \brief Portuguese syntax */
05717 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)
05718 {
05719    struct timeval when = { t, 0 };
05720    struct ast_tm tm;
05721    int res=0, offset, sndoffset;
05722    char sndfile[256], nextmsg[256];
05723 
05724    if (format == NULL)
05725       format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
05726 
05727    ast_localtime(&when, &tm, tzone);
05728 
05729    for (offset=0 ; format[offset] != '\0' ; offset++) {
05730       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05731       switch (format[offset]) {
05732          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05733          case '\'':
05734             /* Literal name of a sound file */
05735             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05736                sndfile[sndoffset] = format[offset];
05737             }
05738             sndfile[sndoffset] = '\0';
05739             snprintf(nextmsg, sizeof(nextmsg), "%s", sndfile);
05740             res = wait_file(chan, ints, nextmsg, lang);
05741             break;
05742          case 'A':
05743          case 'a':
05744             /* Sunday - Saturday */
05745             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05746             res = wait_file(chan, ints, nextmsg, lang);
05747             break;
05748          case 'B':
05749          case 'b':
05750          case 'h':
05751             /* January - December */
05752             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05753             res = wait_file(chan, ints, nextmsg, lang);
05754             break;
05755          case 'm':
05756             /* First - Twelfth */
05757             if (!strcasecmp(lang, "pt_BR")) {
05758                res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
05759             } else {
05760                snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
05761                res = wait_file(chan, ints, nextmsg, lang);
05762             }
05763             break;
05764          case 'd':
05765          case 'e':
05766             /* First - Thirtyfirst */
05767             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
05768             break;
05769          case 'Y':
05770             /* Year */
05771             res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
05772             break;
05773          case 'I':
05774          case 'l':
05775             /* 12-Hour */
05776             if (!strcasecmp(lang, "pt_BR")) {
05777                if (tm.tm_hour == 0) {
05778                   if (format[offset] == 'I')
05779                      res = wait_file(chan, ints, "digits/pt-a", lang);
05780                   if (!res)
05781                      res = wait_file(chan, ints, "digits/pt-meianoite", lang);
05782                } else if (tm.tm_hour == 12) {
05783                   if (format[offset] == 'I')
05784                      res = wait_file(chan, ints, "digits/pt-ao", lang);
05785                   if (!res)
05786                      res = wait_file(chan, ints, "digits/pt-meiodia", lang);
05787                   } else {
05788                   if (format[offset] == 'I') {
05789                      if ((tm.tm_hour % 12) != 1)
05790                         res = wait_file(chan, ints, "digits/pt-as", lang);
05791                      else
05792                         res = wait_file(chan, ints, "digits/pt-a", lang);
05793                   }
05794                   if (!res)
05795                      res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
05796                }
05797             } else {
05798                if (tm.tm_hour == 0) {
05799                   if (format[offset] == 'I')
05800                      res = wait_file(chan, ints, "digits/pt-ah", lang);
05801                   if (!res)
05802                      res = wait_file(chan, ints, "digits/pt-meianoite", lang);
05803                   }
05804                else if (tm.tm_hour == 12) {
05805                   if (format[offset] == 'I')
05806                      res = wait_file(chan, ints, "digits/pt-ao", lang);
05807                   if (!res)
05808                      res = wait_file(chan, ints, "digits/pt-meiodia", lang);
05809                }
05810                else {
05811                   if (format[offset] == 'I') {
05812                      res = wait_file(chan, ints, "digits/pt-ah", lang);
05813                      if ((tm.tm_hour % 12) != 1)
05814                         if (!res)
05815                            res = wait_file(chan, ints, "digits/pt-sss", lang);
05816                   }
05817                   if (!res)
05818                      res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
05819                }
05820             }
05821             break;
05822          case 'H':
05823          case 'k':
05824             /* 24-Hour */
05825             if (!strcasecmp(lang, "pt_BR")) {
05826                res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05827                if ((!res) && (format[offset] == 'H')) {
05828                   if (tm.tm_hour > 1) {
05829                      res = wait_file(chan, ints, "digits/hours", lang);
05830                   } else {
05831                      res = wait_file(chan, ints, "digits/hour", lang);
05832                   }
05833                }
05834             } else {
05835                res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
05836                if (!res) {
05837                   if (tm.tm_hour != 0) {
05838                      int remaining = tm.tm_hour;
05839                      if (tm.tm_hour > 20) {
05840                         res = wait_file(chan, ints, "digits/20", lang);
05841                         remaining -= 20;
05842                      }
05843                      if (!res) {
05844                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
05845                         res = wait_file(chan, ints, nextmsg, lang);
05846                      }
05847                   }
05848                }
05849             }
05850             break;
05851          case 'M':
05852             /* Minute */
05853             if (!strcasecmp(lang, "pt_BR")) {
05854                res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
05855                if (!res) {
05856                   if (tm.tm_min > 1) {
05857                      res = wait_file(chan, ints, "digits/minutes", lang);
05858                   } else {
05859                      res = wait_file(chan, ints, "digits/minute", lang);
05860                   }
05861                }
05862             } else {
05863                if (tm.tm_min == 0) {
05864                   res = wait_file(chan, ints, "digits/pt-hora", lang);
05865                   if (tm.tm_hour != 1)
05866                      if (!res)
05867                         res = wait_file(chan, ints, "digits/pt-sss", lang);
05868                } else {
05869                   res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05870                }
05871             }
05872             break;
05873          case 'P':
05874          case 'p':
05875             /* AM/PM */
05876             if (!strcasecmp(lang, "pt_BR")) {
05877                if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
05878                   res = wait_file(chan, ints, "digits/pt-da", lang);
05879                   if (!res) {
05880                      if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
05881                         res = wait_file(chan, ints, "digits/morning", lang);
05882                      else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
05883                         res = wait_file(chan, ints, "digits/afternoon", lang);
05884                      else res = wait_file(chan, ints, "digits/night", lang);
05885                   }
05886                }
05887             } else {
05888                if (tm.tm_hour > 12)
05889                   res = wait_file(chan, ints, "digits/p-m", lang);
05890                else if (tm.tm_hour  && tm.tm_hour < 12)
05891                   res = wait_file(chan, ints, "digits/a-m", lang);
05892             }
05893             break;
05894          case 'Q':
05895             /* Shorthand for "Today", "Yesterday", or ABdY */
05896             /* XXX As emphasized elsewhere, this should the native way in your
05897              * language to say the date, with changes in what you say, depending
05898              * upon how recent the date is. XXX */
05899             {
05900                struct timeval now = ast_tvnow();
05901                struct ast_tm tmnow;
05902                time_t beg_today;
05903 
05904                ast_localtime(&now, &tmnow, tzone);
05905                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05906                /* In any case, it saves not having to do ast_mktime() */
05907                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05908                if (beg_today < t) {
05909                   /* Today */
05910                   res = wait_file(chan, ints, "digits/today", lang);
05911                } else if (beg_today - 86400 < t) {
05912                   /* Yesterday */
05913                   res = wait_file(chan, ints, "digits/yesterday", lang);
05914                } else {
05915                   res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
05916                }
05917             }
05918             break;
05919          case 'q':
05920             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
05921             /* XXX As emphasized elsewhere, this should the native way in your
05922              * language to say the date, with changes in what you say, depending
05923              * upon how recent the date is. XXX */
05924             {
05925                struct timeval now = ast_tvnow();
05926                struct ast_tm tmnow;
05927                time_t beg_today;
05928 
05929                ast_localtime(&now, &tmnow, tzone);
05930                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05931                /* In any case, it saves not having to do ast_mktime() */
05932                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05933                if (beg_today < t) {
05934                   /* Today */
05935                } else if ((beg_today - 86400) < t) {
05936                   /* Yesterday */
05937                   res = wait_file(chan, ints, "digits/yesterday", lang);
05938                } else if (beg_today - 86400 * 6 < t) {
05939                   /* Within the last week */
05940                   res = ast_say_date_with_format_pt(chan, t, ints, lang, "A", tzone);
05941                } else {
05942                   res = ast_say_date_with_format_pt(chan, t, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", tzone);
05943                }
05944             }
05945             break;
05946          case 'R':
05947             res = ast_say_date_with_format_pt(chan, t, ints, lang, "H 'digits/pt-e' M", tzone);
05948             break;
05949          case 'S':
05950             /* Seconds */
05951             if (!strcasecmp(lang, "pt_BR")) {
05952                res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
05953                if (!res) {
05954                   if (tm.tm_sec > 1) {
05955                      res = wait_file(chan, ints, "digits/seconds", lang);
05956                   } else {
05957                      res = wait_file(chan, ints, "digits/second", lang);
05958                   }
05959                }
05960             } else {
05961                if (tm.tm_sec == 0) {
05962                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
05963                   res = wait_file(chan, ints, nextmsg, lang);
05964                } else if (tm.tm_sec < 10) {
05965                   res = wait_file(chan, ints, "digits/oh", lang);
05966                   if (!res) {
05967                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
05968                      res = wait_file(chan, ints, nextmsg, lang);
05969                   }
05970                } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
05971                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
05972                   res = wait_file(chan, ints, nextmsg, lang);
05973                } else {
05974                   int ten, one;
05975                   ten = (tm.tm_sec / 10) * 10;
05976                   one = (tm.tm_sec % 10);
05977                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", ten);
05978                   res = wait_file(chan, ints, nextmsg, lang);
05979                   if (!res) {
05980                      /* Fifty, not fifty-zero */
05981                      if (one != 0) {
05982                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", one);
05983                         res = wait_file(chan, ints, nextmsg, lang);
05984                      }
05985                   }
05986                }
05987             }
05988             break;
05989          case 'T':
05990             res = ast_say_date_with_format_pt(chan, t, ints, lang, "HMS", tzone);
05991             break;
05992          case ' ':
05993          case '   ':
05994             /* Just ignore spaces and tabs */
05995             break;
05996          default:
05997             /* Unknown character */
05998             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05999       }
06000       /* Jump out on DTMF */
06001       if (res) {
06002          break;
06003       }
06004    }
06005    return res;
06006 }
06007 
06008 /*! \brief Taiwanese / Chinese syntax */
06009 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)
06010 {
06011    struct timeval when = { t, 0 };
06012    struct ast_tm tm;
06013    int res=0, offset, sndoffset;
06014    char sndfile[256], nextmsg[256];
06015 
06016    if (format == NULL)
06017       format = "YBdAkM";
06018 
06019    ast_localtime(&when, &tm, tzone);
06020 
06021    for (offset=0 ; format[offset] != '\0' ; offset++) {
06022       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
06023       switch (format[offset]) {
06024          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
06025          case '\'':
06026             /* Literal name of a sound file */
06027             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
06028                sndfile[sndoffset] = format[offset];
06029             }
06030             sndfile[sndoffset] = '\0';
06031             res = wait_file(chan, ints, sndfile, lang);
06032             break;
06033          case 'A':
06034          case 'a':
06035             /* Sunday - Saturday */
06036             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
06037             res = wait_file(chan, ints, nextmsg, lang);
06038             break;
06039          case 'B':
06040          case 'b':
06041          case 'h':
06042          case 'm':
06043             /* January - December */
06044             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
06045             res = wait_file(chan, ints, nextmsg, lang);
06046             break;
06047          case 'd':
06048          case 'e':
06049             /* First - Thirtyfirst */
06050             if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
06051                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday);
06052                res = wait_file(chan, ints, nextmsg, lang);
06053             } else {
06054                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
06055                res = wait_file(chan, ints, nextmsg, lang);
06056                if (!res) {
06057                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
06058                   res = wait_file(chan, ints, nextmsg, lang);
06059                }
06060             }
06061             if (!res) res = wait_file(chan, ints, "digits/day", lang);
06062             break;
06063          case 'Y':
06064             /* Year */
06065             if (tm.tm_year > 99) {
06066                res = wait_file(chan, ints, "digits/2", lang);
06067                if (!res) {
06068                   res = wait_file(chan, ints, "digits/thousand", lang);
06069                }
06070                if (tm.tm_year > 100) {
06071                   if (!res) {
06072                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
06073                      res = wait_file(chan, ints, nextmsg, lang);
06074                      if (!res) {
06075                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
06076                         res = wait_file(chan, ints, nextmsg, lang);
06077                      }
06078                   }
06079                }
06080                if (!res) {
06081                   res = wait_file(chan, ints, "digits/year", lang);
06082                }
06083             } else {
06084                if (tm.tm_year < 1) {
06085                   /* I'm not going to handle 1900 and prior */
06086                   /* We'll just be silent on the year, instead of bombing out. */
06087                } else {
06088                   res = wait_file(chan, ints, "digits/1", lang);
06089                   if (!res) {
06090                      res = wait_file(chan, ints, "digits/9", lang);
06091                   }
06092                   if (!res) {
06093                      if (tm.tm_year <= 9) {
06094                         /* 1901 - 1909 */
06095                         res = wait_file(chan, ints, "digits/0", lang);
06096                         if (!res) {
06097                            snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year);
06098                            res = wait_file(chan, ints, nextmsg, lang);
06099                         }
06100                      } else {
06101                         /* 1910 - 1999 */
06102                         snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
06103                         res = wait_file(chan, ints, nextmsg, lang);
06104                         if (!res) {
06105                            snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
06106                            res = wait_file(chan, ints, nextmsg, lang);
06107                         }
06108                      }
06109                   }
06110                }
06111                if (!res) {
06112                   res = wait_file(chan, ints, "digits/year", lang);
06113                }
06114             }
06115             break;
06116          case 'I':
06117          case 'l':
06118             /* 12-Hour */
06119             if (tm.tm_hour == 0)
06120                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
06121             else if (tm.tm_hour > 12)
06122                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
06123             else
06124                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
06125             res = wait_file(chan, ints, nextmsg, lang);
06126             if (!res) {
06127                res = wait_file(chan, ints, "digits/oclock", lang);
06128             }
06129             break;
06130          case 'H':
06131             if (tm.tm_hour < 10) {
06132                res = wait_file(chan, ints, "digits/0", lang);
06133             }
06134             /* XXX Static analysis warns of no break here. No idea if this is
06135              * correct or not
06136              */
06137          case 'k':
06138             /* 24-Hour */
06139             if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
06140                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
06141                res = wait_file(chan, ints, nextmsg, lang);
06142             } else {
06143                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
06144                res = wait_file(chan, ints, nextmsg, lang);
06145                if (!res) {
06146                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
06147                   res = wait_file(chan, ints, nextmsg, lang);
06148                }
06149             }
06150             if (!res) {
06151                res = wait_file(chan, ints, "digits/oclock", lang);
06152             }
06153             break;
06154          case 'M':
06155             /* Minute */
06156             if (!(tm.tm_min % 10) || tm.tm_min < 10) {
06157                if (tm.tm_min < 10) {
06158                   res = wait_file(chan, ints, "digits/0", lang);
06159                }
06160                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min);
06161                res = wait_file(chan, ints, nextmsg, lang);
06162             } else {
06163                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
06164                res = wait_file(chan, ints, nextmsg, lang);
06165                if (!res) {
06166                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
06167                   res = wait_file(chan, ints, nextmsg, lang);
06168                }
06169             }
06170             if (!res) {
06171                res = wait_file(chan, ints, "digits/minute", lang);
06172             }
06173             break;
06174          case 'P':
06175          case 'p':
06176             /* AM/PM */
06177             if (tm.tm_hour > 11)
06178                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
06179             else
06180                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
06181             res = wait_file(chan, ints, nextmsg, lang);
06182             break;
06183          case 'Q':
06184             /* Shorthand for "Today", "Yesterday", or ABdY */
06185             /* XXX As emphasized elsewhere, this should the native way in your
06186              * language to say the date, with changes in what you say, depending
06187              * upon how recent the date is. XXX */
06188             {
06189                struct timeval now = ast_tvnow();
06190                struct ast_tm tmnow;
06191                time_t beg_today;
06192 
06193                ast_localtime(&now, &tmnow, tzone);
06194                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
06195                /* In any case, it saves not having to do ast_mktime() */
06196                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
06197                if (beg_today < t) {
06198                   /* Today */
06199                   res = wait_file(chan, ints, "digits/today", lang);
06200                } else if (beg_today - 86400 < t) {
06201                   /* Yesterday */
06202                   res = wait_file(chan, ints, "digits/yesterday", lang);
06203                } else {
06204                   res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
06205                }
06206             }
06207             break;
06208          case 'q':
06209             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
06210             /* XXX As emphasized elsewhere, this should the native way in your
06211              * language to say the date, with changes in what you say, depending
06212              * upon how recent the date is. XXX */
06213             {
06214                struct timeval now = ast_tvnow();
06215                struct ast_tm tmnow;
06216                time_t beg_today;
06217 
06218                ast_localtime(&now, &tmnow, tzone);
06219                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
06220                /* In any case, it saves not having to do ast_mktime() */
06221                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
06222                if (beg_today < t) {
06223                   /* Today */
06224                } else if ((beg_today - 86400) < t) {
06225                   /* Yesterday */
06226                   res = wait_file(chan, ints, "digits/yesterday", lang);
06227                } else if (beg_today - 86400 * 6 < t) {
06228                   /* Within the last week */
06229                   res = ast_say_date_with_format_zh(chan, t, ints, lang, "A", tzone);
06230                } else {
06231                   res = ast_say_date_with_format_zh(chan, t, ints, lang, "YBdA", tzone);
06232                }
06233             }
06234             break;
06235          case 'R':
06236             res = ast_say_date_with_format_zh(chan, t, ints, lang, "kM", tzone);
06237             break;
06238          case 'S':
06239             /* Seconds */
06240             if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
06241                if (tm.tm_sec < 10) {
06242                   res = wait_file(chan, ints, "digits/0", lang);
06243                }
06244                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec);
06245                res = wait_file(chan, ints, nextmsg, lang);
06246             } else {
06247                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
06248                res = wait_file(chan, ints, nextmsg, lang);
06249                if (!res) {
06250                   snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
06251                   res = wait_file(chan, ints, nextmsg, lang);
06252                }
06253             }
06254             if (!res) {
06255                res = wait_file(chan, ints, "digits/second", lang);
06256             }
06257             break;
06258          case 'T':
06259             res = ast_say_date_with_format_zh(chan, t, ints, lang, "HMS", tzone);
06260             break;
06261          case ' ':
06262          case '   ':
06263             /* Just ignore spaces and tabs */
06264          break;
06265          default:
06266             /* Unknown character */
06267             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
06268       }
06269       /* Jump out on DTMF */
06270       if (res) {
06271          break;
06272       }
06273    }
06274    return res;
06275 }
06276 
06277 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06278 {
06279    if (!strncasecmp(lang, "en", 2)) {  /* English syntax */
06280       return ast_say_time_en(chan, t, ints, lang);
06281    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
06282       return ast_say_time_de(chan, t, ints, lang);
06283    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
06284       return ast_say_time_fr(chan, t, ints, lang);
06285    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
06286       static int deprecation_warning = 0;
06287       if (deprecation_warning++ % 10 == 0) {
06288          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
06289       }
06290       return ast_say_time_ka(chan, t, ints, lang);
06291    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
06292       return ast_say_time_gr(chan, t, ints, lang);
06293    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
06294       return ast_say_time_he(chan, t, ints, lang);
06295    } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
06296       return(ast_say_time_hu(chan, t, ints, lang));
06297    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
06298       return ast_say_time_ka(chan, t, ints, lang);
06299    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
06300       return ast_say_time_nl(chan, t, ints, lang);
06301    } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
06302       return ast_say_time_pt_BR(chan, t, ints, lang);
06303    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
06304       return ast_say_time_pt(chan, t, ints, lang);
06305    } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
06306       return(ast_say_time_th(chan, t, ints, lang));
06307    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
06308       static int deprecation_warning = 0;
06309       if (deprecation_warning++ % 10 == 0) {
06310          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
06311       }
06312       return ast_say_time_zh(chan, t, ints, lang);
06313    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
06314       return ast_say_time_zh(chan, t, ints, lang);
06315    }
06316 
06317    /* Default to English */
06318    return ast_say_time_en(chan, t, ints, lang);
06319 }
06320 
06321 /*! \brief English syntax */
06322 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06323 {
06324    struct timeval when = { t, 0 };
06325    struct ast_tm tm;
06326    int res = 0;
06327    int hour, pm=0;
06328 
06329    ast_localtime(&when, &tm, NULL);
06330    hour = tm.tm_hour;
06331    if (!hour)
06332       hour = 12;
06333    else if (hour == 12)
06334       pm = 1;
06335    else if (hour > 12) {
06336       hour -= 12;
06337       pm = 1;
06338    }
06339    if (!res)
06340       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06341 
06342    if (tm.tm_min > 9) {
06343       if (!res)
06344          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06345    } else if (tm.tm_min) {
06346       if (!res)
06347          res = ast_streamfile(chan, "digits/oh", lang);
06348       if (!res)
06349          res = ast_waitstream(chan, ints);
06350       if (!res)
06351          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06352    } else {
06353       if (!res)
06354          res = ast_streamfile(chan, "digits/oclock", lang);
06355       if (!res)
06356          res = ast_waitstream(chan, ints);
06357    }
06358    if (pm) {
06359       if (!res)
06360          res = ast_streamfile(chan, "digits/p-m", lang);
06361    } else {
06362       if (!res)
06363          res = ast_streamfile(chan, "digits/a-m", lang);
06364    }
06365    if (!res)
06366       res = ast_waitstream(chan, ints);
06367    return res;
06368 }
06369 
06370 /*! \brief German syntax */
06371 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06372 {
06373    struct timeval when = { t, 0 };
06374    struct ast_tm tm;
06375    int res = 0;
06376 
06377    ast_localtime(&when, &tm, NULL);
06378    if (!res)
06379       res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
06380    if (!res)
06381       res = ast_streamfile(chan, "digits/oclock", lang);
06382    if (!res)
06383       res = ast_waitstream(chan, ints);
06384    if (!res)
06385        if (tm.tm_min > 0)
06386       res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
06387    return res;
06388 }
06389 
06390 /*! \brief Hungarian syntax */
06391 int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06392 {
06393    struct timeval when = { t, 0 };
06394    struct ast_tm tm;
06395    int res = 0;
06396 
06397    ast_localtime(&when, &tm, NULL);
06398    if (!res)
06399       res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
06400    if (!res)
06401       res = ast_streamfile(chan, "digits/oclock", lang);
06402    if (!res)
06403       res = ast_waitstream(chan, ints);
06404    if (!res)
06405        if (tm.tm_min > 0) {
06406          res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
06407          if (!res)
06408             res = ast_streamfile(chan, "digits/minute", lang);
06409       }
06410    return res;
06411 }
06412 
06413 /*! \brief French syntax */
06414 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06415 {
06416    struct timeval when = { t, 0 };
06417    struct ast_tm tm;
06418    int res = 0;
06419 
06420    ast_localtime(&when, &tm, NULL);
06421 
06422    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
06423    if (!res)
06424       res = ast_streamfile(chan, "digits/oclock", lang);
06425    if (tm.tm_min) {
06426       if (!res)
06427       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06428    }
06429    return res;
06430 }
06431 
06432 /*! \brief Dutch syntax */
06433 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06434 {
06435    struct timeval when = { t, 0 };
06436    struct ast_tm tm;
06437    int res = 0;
06438 
06439    ast_localtime(&when, &tm, NULL);
06440    if (!res)
06441       res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
06442    if (!res)
06443       res = ast_streamfile(chan, "digits/nl-uur", lang);
06444    if (!res)
06445       res = ast_waitstream(chan, ints);
06446    if (!res)
06447        if (tm.tm_min > 0)
06448       res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
06449    return res;
06450 }
06451 
06452 /*! \brief Portuguese syntax */
06453 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06454 {
06455    struct timeval when = { t, 0 };
06456    struct ast_tm tm;
06457    int res = 0;
06458    int hour;
06459 
06460    ast_localtime(&when, &tm, NULL);
06461    hour = tm.tm_hour;
06462    if (!res)
06463       res = ast_say_number(chan, hour, ints, lang, "f");
06464    if (tm.tm_min) {
06465       if (!res)
06466          res = wait_file(chan, ints, "digits/pt-e", lang);
06467       if (!res)
06468          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06469    } else {
06470       if (!res)
06471          res = wait_file(chan, ints, "digits/pt-hora", lang);
06472       if (tm.tm_hour != 1)
06473          if (!res)
06474             res = wait_file(chan, ints, "digits/pt-sss", lang);
06475    }
06476    if (!res)
06477       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06478    return res;
06479 }
06480 
06481 /*! \brief Brazilian Portuguese syntax */
06482 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06483 {
06484    struct timeval when = { t, 0 };
06485    struct ast_tm tm;
06486    int res = 0;
06487 
06488    ast_localtime(&when, &tm, NULL);
06489 
06490    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
06491    if (!res) {
06492       if (tm.tm_hour > 1)
06493          res = wait_file(chan, ints, "digits/hours", lang);
06494       else
06495          res = wait_file(chan, ints, "digits/hour", lang);
06496    }
06497    if ((!res) && (tm.tm_min)) {
06498       res = wait_file(chan, ints, "digits/pt-e", lang);
06499       if (!res)
06500          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06501       if (!res) {
06502          if (tm.tm_min > 1)
06503             res = wait_file(chan, ints, "digits/minutes", lang);
06504          else
06505             res = wait_file(chan, ints, "digits/minute", lang);
06506       }
06507    }
06508    return res;
06509 }
06510 
06511 /*! \brief Thai  syntax */
06512 int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06513 {
06514    struct timeval when = { t, 0 };
06515    struct ast_tm tm;
06516    int res = 0;
06517    int hour;
06518    ast_localtime(&when, &tm, NULL);
06519    hour = tm.tm_hour;
06520    if (!hour)
06521       hour = 24;
06522    if (!res)
06523       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06524    if (!res)
06525       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06526    return res;
06527 }
06528 
06529 /*! \brief Taiwanese / Chinese  syntax */
06530 int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06531 {
06532    struct timeval when = { t, 0 };
06533    struct ast_tm tm;
06534    int res = 0;
06535    int hour, pm=0;
06536 
06537    ast_localtime(&when, &tm, NULL);
06538    hour = tm.tm_hour;
06539    if (!hour)
06540       hour = 12;
06541    else if (hour == 12)
06542       pm = 1;
06543    else if (hour > 12) {
06544       hour -= 12;
06545       pm = 1;
06546    }
06547    if (pm) {
06548       if (!res)
06549          res = ast_streamfile(chan, "digits/p-m", lang);
06550    } else {
06551       if (!res)
06552          res = ast_streamfile(chan, "digits/a-m", lang);
06553    }
06554    if (!res)
06555       res = ast_waitstream(chan, ints);
06556    if (!res)
06557       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06558    if (!res)
06559       res = ast_streamfile(chan, "digits/oclock", lang);
06560    if (!res)
06561       res = ast_waitstream(chan, ints);
06562    if (!res)
06563       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06564    if (!res)
06565       res = ast_streamfile(chan, "digits/minute", lang);
06566    if (!res)
06567       res = ast_waitstream(chan, ints);
06568    return res;
06569 }
06570 
06571 /*! \brief Hebrew syntax */
06572 int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06573 {
06574    struct timeval when = { t, 0 };
06575    struct ast_tm tm;
06576    int res = 0;
06577    int hour;
06578 
06579    ast_localtime(&when, &tm, NULL);
06580    hour = tm.tm_hour;
06581    if (!hour)
06582       hour = 12;
06583 
06584    if (!res)
06585       res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
06586 
06587    if (tm.tm_min > 9) {
06588       if (!res)
06589          res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
06590    } else if (tm.tm_min) {
06591       if (!res) {          /* say a leading zero if needed */
06592          res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
06593       }
06594       if (!res)
06595          res = ast_waitstream(chan, ints);
06596       if (!res)
06597          res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
06598    } else {
06599       if (!res)
06600          res = ast_waitstream(chan, ints);
06601    }
06602    if (!res)
06603       res = ast_waitstream(chan, ints);
06604    return res;
06605 }
06606 
06607 
06608 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06609 {
06610    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
06611       return ast_say_datetime_en(chan, t, ints, lang);
06612    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
06613       return ast_say_datetime_de(chan, t, ints, lang);
06614    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
06615       return ast_say_datetime_fr(chan, t, ints, lang);
06616    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
06617       static int deprecation_warning = 0;
06618       if (deprecation_warning++ % 10 == 0) {
06619          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
06620       }
06621       return ast_say_datetime_ka(chan, t, ints, lang);
06622    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
06623       return ast_say_datetime_gr(chan, t, ints, lang);
06624    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
06625       return ast_say_datetime_he(chan, t, ints, lang);
06626    } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */
06627       return ast_say_datetime_hu(chan, t, ints, lang);
06628    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
06629       return ast_say_datetime_ka(chan, t, ints, lang);
06630    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
06631       return ast_say_datetime_nl(chan, t, ints, lang);
06632    } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
06633       return ast_say_datetime_pt_BR(chan, t, ints, lang);
06634    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
06635       return ast_say_datetime_pt(chan, t, ints, lang);
06636    } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */
06637       return ast_say_datetime_th(chan, t, ints, lang);
06638    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
06639       static int deprecation_warning = 0;
06640       if (deprecation_warning++ % 10 == 0) {
06641          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
06642       }
06643       return ast_say_datetime_zh(chan, t, ints, lang);
06644    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
06645       return ast_say_datetime_zh(chan, t, ints, lang);
06646    }
06647 
06648    /* Default to English */
06649    return ast_say_datetime_en(chan, t, ints, lang);
06650 }
06651 
06652 /*! \brief English syntax */
06653 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06654 {
06655    struct timeval when = { t, 0 };
06656    struct ast_tm tm;
06657    char fn[256];
06658    int res = 0;
06659    int hour, pm=0;
06660 
06661    ast_localtime(&when, &tm, NULL);
06662    if (!res) {
06663       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06664       res = ast_streamfile(chan, fn, lang);
06665       if (!res)
06666          res = ast_waitstream(chan, ints);
06667    }
06668    if (!res) {
06669       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06670       res = ast_streamfile(chan, fn, lang);
06671       if (!res)
06672          res = ast_waitstream(chan, ints);
06673    }
06674    if (!res)
06675       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06676 
06677    hour = tm.tm_hour;
06678    if (!hour)
06679       hour = 12;
06680    else if (hour == 12)
06681       pm = 1;
06682    else if (hour > 12) {
06683       hour -= 12;
06684       pm = 1;
06685    }
06686    if (!res)
06687       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06688 
06689    if (tm.tm_min > 9) {
06690       if (!res)
06691          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06692    } else if (tm.tm_min) {
06693       if (!res)
06694          res = ast_streamfile(chan, "digits/oh", lang);
06695       if (!res)
06696          res = ast_waitstream(chan, ints);
06697       if (!res)
06698          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06699    } else {
06700       if (!res)
06701          res = ast_streamfile(chan, "digits/oclock", lang);
06702       if (!res)
06703          res = ast_waitstream(chan, ints);
06704    }
06705    if (pm) {
06706       if (!res)
06707          res = ast_streamfile(chan, "digits/p-m", lang);
06708    } else {
06709       if (!res)
06710          res = ast_streamfile(chan, "digits/a-m", lang);
06711    }
06712    if (!res)
06713       res = ast_waitstream(chan, ints);
06714    if (!res)
06715       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06716    return res;
06717 }
06718 
06719 /*! \brief German syntax */
06720 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06721 {
06722    struct timeval when = { t, 0 };
06723    struct ast_tm tm;
06724    int res = 0;
06725 
06726    ast_localtime(&when, &tm, NULL);
06727    res = ast_say_date(chan, t, ints, lang);
06728    if (!res)
06729       ast_say_time(chan, t, ints, lang);
06730    return res;
06731 
06732 }
06733 
06734 /*! \brief Hungarian syntax */
06735 int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06736 {
06737    struct timeval when = { t, 0 };
06738    struct ast_tm tm;
06739    int res = 0;
06740 
06741    ast_localtime(&when, &tm, NULL);
06742    res = ast_say_date(chan, t, ints, lang);
06743    if (!res)
06744       ast_say_time(chan, t, ints, lang);
06745    return res;
06746 }
06747 
06748 /*! \brief French syntax */
06749 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06750 {
06751    struct timeval when = { t, 0 };
06752    struct ast_tm tm;
06753    char fn[256];
06754    int res = 0;
06755 
06756    ast_localtime(&when, &tm, NULL);
06757 
06758    if (!res)
06759       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06760 
06761    if (!res) {
06762       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06763       res = ast_streamfile(chan, fn, lang);
06764       if (!res)
06765          res = ast_waitstream(chan, ints);
06766    }
06767    if (!res) {
06768       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06769       res = ast_streamfile(chan, fn, lang);
06770       if (!res)
06771          res = ast_waitstream(chan, ints);
06772    }
06773 
06774    if (!res)
06775       res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
06776    if (!res)
06777          res = ast_streamfile(chan, "digits/oclock", lang);
06778    if (tm.tm_min > 0) {
06779       if (!res)
06780          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06781    }
06782    if (!res)
06783       res = ast_waitstream(chan, ints);
06784    if (!res)
06785       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06786    return res;
06787 }
06788 
06789 /*! \brief Dutch syntax */
06790 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06791 {
06792    struct timeval when = { t, 0 };
06793    struct ast_tm tm;
06794    int res = 0;
06795 
06796    ast_localtime(&when, &tm, NULL);
06797    res = ast_say_date(chan, t, ints, lang);
06798    if (!res) {
06799       res = ast_streamfile(chan, "digits/nl-om", lang);
06800       if (!res)
06801          res = ast_waitstream(chan, ints);
06802    }
06803    if (!res)
06804       ast_say_time(chan, t, ints, lang);
06805    return res;
06806 }
06807 
06808 /*! \brief Portuguese syntax */
06809 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06810 {
06811    struct timeval when = { t, 0 };
06812    struct ast_tm tm;
06813    char fn[256];
06814    int res = 0;
06815    int hour, pm=0;
06816 
06817    ast_localtime(&when, &tm, NULL);
06818    if (!res) {
06819       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06820       res = ast_streamfile(chan, fn, lang);
06821       if (!res)
06822          res = ast_waitstream(chan, ints);
06823    }
06824    if (!res) {
06825       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06826       res = ast_streamfile(chan, fn, lang);
06827       if (!res)
06828          res = ast_waitstream(chan, ints);
06829    }
06830    if (!res)
06831       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06832 
06833    hour = tm.tm_hour;
06834    if (!hour)
06835       hour = 12;
06836    else if (hour == 12)
06837       pm = 1;
06838    else if (hour > 12) {
06839       hour -= 12;
06840       pm = 1;
06841    }
06842    if (!res)
06843       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06844 
06845    if (tm.tm_min > 9) {
06846       if (!res)
06847          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06848    } else if (tm.tm_min) {
06849       if (!res)
06850          res = ast_streamfile(chan, "digits/oh", lang);
06851       if (!res)
06852          res = ast_waitstream(chan, ints);
06853       if (!res)
06854          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06855    } else {
06856       if (!res)
06857          res = ast_streamfile(chan, "digits/oclock", lang);
06858       if (!res)
06859          res = ast_waitstream(chan, ints);
06860    }
06861    if (pm) {
06862       if (!res)
06863          res = ast_streamfile(chan, "digits/p-m", lang);
06864    } else {
06865       if (!res)
06866          res = ast_streamfile(chan, "digits/a-m", lang);
06867    }
06868    if (!res)
06869       res = ast_waitstream(chan, ints);
06870    if (!res)
06871       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06872    return res;
06873 }
06874 
06875 /*! \brief Brazilian Portuguese syntax */
06876 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06877 {
06878    struct timeval when = { t, 0 };
06879    struct ast_tm tm;
06880    int res = 0;
06881 
06882    ast_localtime(&when, &tm, NULL);
06883    res = ast_say_date(chan, t, ints, lang);
06884    if (!res)
06885       res = ast_say_time(chan, t, ints, lang);
06886    return res;
06887 }
06888 
06889 /*! \brief Thai syntax */
06890 int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06891 {
06892    struct timeval when = { t, 0 };
06893    struct ast_tm tm;
06894    char fn[256];
06895    int res = 0;
06896    int hour;
06897    ast_localtime(&when, &tm, NULL);
06898    if (!res) {
06899       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06900       res = ast_streamfile(chan, fn, lang);
06901       if (!res)
06902          res = ast_waitstream(chan, ints);
06903    }
06904    if (!res) {
06905       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06906       res = ast_streamfile(chan, fn, lang);
06907       if (!res)
06908          res = ast_waitstream(chan, ints);
06909    }
06910    if (!res){
06911       ast_copy_string(fn, "digits/posor", sizeof(fn));
06912       res = ast_streamfile(chan, fn, lang);
06913       res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
06914    }
06915    if (!res)
06916       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06917 
06918    hour = tm.tm_hour;
06919    if (!hour)
06920       hour = 24;
06921    if (!res){
06922       ast_copy_string(fn, "digits/wela", sizeof(fn));
06923       res = ast_streamfile(chan, fn, lang);
06924    }
06925    if (!res)
06926       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06927    if (!res)
06928       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06929    return res;
06930 }
06931 
06932 /*! \brief Taiwanese / Chinese syntax */
06933 int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06934 {
06935    struct timeval when = { t, 0 };
06936    struct ast_tm tm;
06937    char fn[256];
06938    int res = 0;
06939    int hour, pm=0;
06940 
06941    ast_localtime(&when, &tm, NULL);
06942    if (!res)
06943       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06944    if (!res) {
06945       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06946       res = ast_streamfile(chan, fn, lang);
06947       if (!res)
06948          res = ast_waitstream(chan, ints);
06949    }
06950    if (!res)
06951       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06952    if (!res) {
06953       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06954       res = ast_streamfile(chan, fn, lang);
06955       if (!res)
06956          res = ast_waitstream(chan, ints);
06957    }
06958 
06959    hour = tm.tm_hour;
06960    if (!hour)
06961       hour = 12;
06962    else if (hour == 12)
06963       pm = 1;
06964    else if (hour > 12) {
06965       hour -= 12;
06966       pm = 1;
06967    }
06968    if (pm) {
06969       if (!res)
06970          res = ast_streamfile(chan, "digits/p-m", lang);
06971    } else {
06972       if (!res)
06973          res = ast_streamfile(chan, "digits/a-m", lang);
06974    }
06975    if (!res)
06976       res = ast_waitstream(chan, ints);
06977    if (!res)
06978       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06979    if (!res)
06980       res = ast_streamfile(chan, "digits/oclock", lang);
06981    if (!res)
06982       res = ast_waitstream(chan, ints);
06983    if (!res)
06984       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06985    if (!res)
06986       res = ast_streamfile(chan, "digits/minute", lang);
06987    if (!res)
06988       res = ast_waitstream(chan, ints);
06989    return res;
06990 }
06991 
06992 /*! \brief Hebrew syntax */
06993 int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06994 {
06995    struct timeval when = { t, 0 };
06996    struct ast_tm tm;
06997    char fn[256];
06998    int res = 0;
06999    int hour;
07000 
07001    ast_localtime(&when, &tm, NULL);
07002    if (!res) {
07003       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07004       res = ast_streamfile(chan, fn, lang);
07005       if (!res) {
07006          res = ast_waitstream(chan, ints);
07007       }
07008    }
07009    if (!res) {
07010       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07011       res = ast_streamfile(chan, fn, lang);
07012       if (!res) {
07013          res = ast_waitstream(chan, ints);
07014       }
07015    }
07016    if (!res) {
07017       res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
07018    }
07019 
07020    hour = tm.tm_hour;
07021    if (!hour) {
07022       hour = 12;
07023    }
07024 
07025    if (!res) {
07026       res = ast_say_number(chan, hour, ints, lang, "f");
07027    }
07028 
07029    if (tm.tm_min > 9) {
07030       if (!res) {
07031          res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
07032       }
07033    } else if (tm.tm_min) {
07034       if (!res) {
07035          /* say a leading zero if needed */
07036          res = ast_say_number(chan, 0, ints, lang, "f");
07037       }
07038       if (!res) {
07039          res = ast_waitstream(chan, ints);
07040       }
07041       if (!res) {
07042          res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
07043       }
07044    } else {
07045       if (!res) {
07046          res = ast_waitstream(chan, ints);
07047       }
07048    }
07049    if (!res) {
07050       res = ast_waitstream(chan, ints);
07051    }
07052    if (!res) {
07053       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
07054    }
07055    return res;
07056 }
07057 
07058 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07059 {
07060    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
07061       return ast_say_datetime_from_now_en(chan, t, ints, lang);
07062    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
07063       return ast_say_datetime_from_now_fr(chan, t, ints, lang);
07064    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
07065       static int deprecation_warning = 0;
07066       if (deprecation_warning++ % 10 == 0) {
07067          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
07068       }
07069       return ast_say_datetime_from_now_ka(chan, t, ints, lang);
07070    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
07071       return ast_say_datetime_from_now_he(chan, t, ints, lang);
07072    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
07073       return ast_say_datetime_from_now_ka(chan, t, ints, lang);
07074    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
07075       return ast_say_datetime_from_now_pt(chan, t, ints, lang);
07076    }
07077 
07078    /* Default to English */
07079    return ast_say_datetime_from_now_en(chan, t, ints, lang);
07080 }
07081 
07082 /*! \brief English syntax */
07083 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07084 {
07085    int res=0;
07086    struct timeval nowtv = ast_tvnow(), when = { t, 0 };
07087    int daydiff;
07088    struct ast_tm tm;
07089    struct ast_tm now;
07090    char fn[256];
07091 
07092    ast_localtime(&when, &tm, NULL);
07093    ast_localtime(&nowtv, &now, NULL);
07094    daydiff = now.tm_yday - tm.tm_yday;
07095    if ((daydiff < 0) || (daydiff > 6)) {
07096       /* Day of month and month */
07097       if (!res) {
07098          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07099          res = ast_streamfile(chan, fn, lang);
07100          if (!res)
07101             res = ast_waitstream(chan, ints);
07102       }
07103       if (!res)
07104          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
07105 
07106    } else if (daydiff) {
07107       /* Just what day of the week */
07108       if (!res) {
07109          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07110          res = ast_streamfile(chan, fn, lang);
07111          if (!res)
07112             res = ast_waitstream(chan, ints);
07113       }
07114    } /* Otherwise, it was today */
07115    if (!res)
07116       res = ast_say_time(chan, t, ints, lang);
07117    return res;
07118 }
07119 
07120 /*! \brief French syntax */
07121 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07122 {
07123    int res=0;
07124    struct timeval nowtv = ast_tvnow(), when = { t, 0 };
07125    int daydiff;
07126    struct ast_tm tm;
07127    struct ast_tm now;
07128    char fn[256];
07129 
07130    ast_localtime(&when, &tm, NULL);
07131    ast_localtime(&nowtv, &now, NULL);
07132    daydiff = now.tm_yday - tm.tm_yday;
07133    if ((daydiff < 0) || (daydiff > 6)) {
07134       /* Day of month and month */
07135       if (!res) {
07136          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07137          res = ast_streamfile(chan, fn, lang);
07138          if (!res)
07139             res = ast_waitstream(chan, ints);
07140       }
07141       if (!res)
07142          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
07143 
07144    } else if (daydiff) {
07145       /* Just what day of the week */
07146       if (!res) {
07147          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07148          res = ast_streamfile(chan, fn, lang);
07149          if (!res)
07150             res = ast_waitstream(chan, ints);
07151       }
07152    } /* Otherwise, it was today */
07153    if (!res)
07154       res = ast_say_time(chan, t, ints, lang);
07155    return res;
07156 }
07157 
07158 /*! \brief Portuguese syntax */
07159 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07160 {
07161    int res=0;
07162    int daydiff;
07163    struct ast_tm tm;
07164    struct ast_tm now;
07165    struct timeval nowtv = ast_tvnow(), when = { t, 0 };
07166    char fn[256];
07167 
07168    ast_localtime(&when, &tm, NULL);
07169    ast_localtime(&nowtv, &now, NULL);
07170    daydiff = now.tm_yday - tm.tm_yday;
07171    if ((daydiff < 0) || (daydiff > 6)) {
07172       /* Day of month and month */
07173       if (!res)
07174          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
07175       if (!res)
07176          res = wait_file(chan, ints, "digits/pt-de", lang);
07177       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07178       if (!res)
07179          res = wait_file(chan, ints, fn, lang);
07180 
07181    } else if (daydiff) {
07182       /* Just what day of the week */
07183       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07184       if (!res)
07185          res = wait_file(chan, ints, fn, lang);
07186    }  /* Otherwise, it was today */
07187    if (!strcasecmp(lang, "pt_BR")) {
07188       if (tm.tm_hour > 1) {
07189          ast_copy_string(fn, "digits/pt-as", sizeof(fn));
07190       } else {
07191          ast_copy_string(fn, "digits/pt-a", sizeof(fn));
07192       }
07193       if (!res)
07194          res = wait_file(chan, ints, fn, lang);
07195    } else {
07196       ast_copy_string(fn, "digits/pt-ah", sizeof(fn));
07197       if (!res)
07198          res = wait_file(chan, ints, fn, lang);
07199       if (tm.tm_hour != 1)
07200       if (!res)
07201          res = wait_file(chan, ints, "digits/pt-sss", lang);
07202       if (!res)
07203          res = ast_say_time(chan, t, ints, lang);
07204    }
07205    return res;
07206 }
07207 
07208 /*! \brief Hebrew syntax */
07209 int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07210 {
07211    int res = 0;
07212    struct timeval nowt = ast_tvnow(), when = { t, 0 };
07213    int daydiff;
07214    struct ast_tm tm;
07215    struct ast_tm now;
07216    char fn[256];
07217 
07218    ast_localtime(&when, &tm, NULL);
07219    ast_localtime(&nowt, &now, NULL);
07220    daydiff = now.tm_yday - tm.tm_yday;
07221    if ((daydiff < 0) || (daydiff > 6)) {
07222       /* Day of month and month */
07223       if (!res) {
07224          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07225          res = ast_streamfile(chan, fn, lang);
07226          if (!res)
07227             res = ast_waitstream(chan, ints);
07228       }
07229       if (!res) {
07230          res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
07231       }
07232    } else if (daydiff) {
07233       /* Just what day of the week */
07234       if (!res) {
07235          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07236          res = ast_streamfile(chan, fn, lang);
07237          if (!res) {
07238             res = ast_waitstream(chan, ints);
07239          }
07240       }
07241    }                    /* Otherwise, it was today */
07242    if (!res) {
07243       res = ast_say_time(chan, t, ints, lang);
07244    }
07245    return res;
07246 }
07247 
07248 
07249 
07250 /*! \brief Greek
07251  * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
07252  */
07253 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
07254    int tmp;
07255    int left;
07256    int res;
07257    char fn[256] = "";
07258 
07259    /* ast_debug(1, "\n\n Saying number female %s %d \n\n", lang, num); */
07260    if (num < 5) {
07261       snprintf(fn, sizeof(fn), "digits/female-%d", num);
07262       res = wait_file(chan, ints, fn, lang);
07263    } else if (num < 13) {
07264       res = ast_say_number(chan, num, ints, lang, (char *) NULL);
07265    } else if (num <100 ) {
07266       tmp = (num/10) * 10;
07267       left = num - tmp;
07268       snprintf(fn, sizeof(fn), "digits/%d", tmp);
07269       res = ast_streamfile(chan, fn, lang);
07270       if (!res)
07271          res = ast_waitstream(chan, ints);
07272       if (left)
07273          gr_say_number_female(left, chan, ints, lang);
07274 
07275    } else {
07276       return -1;
07277    }
07278    return res;
07279 }
07280 
07281 
07282 
07283 /*! \brief Greek support
07284  * A list of the files that you need to create
07285  ->   digits/xilia = "xilia"
07286  ->   digits/myrio = "ekatomyrio"
07287  ->   digits/thousands = "xiliades"
07288  ->   digits/millions = "ektatomyria"
07289  ->   digits/[1..12]   :: A pronunciation of th digits form 1 to 12 e.g. "tria"
07290  ->   digits/[10..100]  :: A pronunciation of the tens from 10 to 90
07291              e.g. 80 = "ogdonta"
07292              Here we must note that we use digits/tens/100 to utter "ekato"
07293              and digits/hundred-100 to utter "ekaton"
07294  ->   digits/hundred-[100...1000] :: A pronunciation of  hundreds from 100 to 1000 e.g 400 =
07295              "terakosia". Here again we use hundreds/1000 for "xilia"
07296              and digits/thousnds for "xiliades"
07297 */
07298 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
07299 {
07300    int res = 0;
07301    char fn[256] = "";
07302    int i=0;
07303 
07304 
07305    if (!num) {
07306       ast_copy_string(fn, "digits/0", sizeof(fn));
07307       res = ast_streamfile(chan, fn, ast_channel_language(chan));
07308       if (!res)
07309          return  ast_waitstream(chan, ints);
07310    }
07311 
07312    while (!res && num ) {
07313       i++;
07314       if (num < 13) {
07315          snprintf(fn, sizeof(fn), "digits/%d", num);
07316          num = 0;
07317       } else if (num <= 100) {
07318          /* 13 < num <= 100  */
07319          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
07320          num %= 10;
07321       } else if (num < 200) {
07322          /* 100 < num < 200 */
07323          snprintf(fn, sizeof(fn), "digits/hundred-100");
07324          num %= 100;
07325       } else if (num < 1000) {
07326          /* 200 < num < 1000 */
07327          snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
07328          num %= 100;
07329       } else if (num < 2000){
07330          snprintf(fn, sizeof(fn), "digits/xilia");
07331          num %= 1000;
07332       } else {
07333          /* num >  1000 */
07334          if (num < 1000000) {
07335             res = ast_say_number_full_gr(chan, (num / 1000), ints, ast_channel_language(chan), audiofd, ctrlfd);
07336             if (res)
07337                return res;
07338             num %= 1000;
07339             snprintf(fn, sizeof(fn), "digits/thousands");
07340          }  else {
07341             if (num < 1000000000) { /* 1,000,000,000 */
07342                res = ast_say_number_full_gr(chan, (num / 1000000), ints, ast_channel_language(chan), audiofd, ctrlfd);
07343                if (res)
07344                   return res;
07345                num %= 1000000;
07346                snprintf(fn, sizeof(fn), "digits/millions");
07347             } else {
07348                ast_debug(1, "Number '%d' is too big for me\n", num);
07349                res = -1;
07350             }
07351          }
07352       }
07353       if (!res) {
07354          if (!ast_streamfile(chan, fn, language)) {
07355             if ((audiofd > -1) && (ctrlfd > -1))
07356                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
07357             else
07358                res = ast_waitstream(chan, ints);
07359          }
07360          ast_stopstream(chan);
07361       }
07362    }
07363    return res;
07364 }
07365 
07366 
07367 /*! \brief Greek support
07368  *
07369  * The format is  weekday - day - month -year
07370  *
07371  * A list of the files that you need to create
07372  * digits/day-[1..7]  : "Deytera .. Paraskeyh"
07373  * digits/months/1..12 : "Ianouariou .. Dekembriou"
07374    Attention the months are in "gekinh klhsh"
07375  */
07376 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07377 {
07378    struct ast_tm tm;
07379    struct timeval when = { t, 0 };
07380 
07381    char fn[256];
07382    int res = 0;
07383 
07384 
07385    ast_localtime(&when, &tm, NULL);
07386    /* W E E K - D A Y */
07387    if (!res) {
07388       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07389       res = ast_streamfile(chan, fn, lang);
07390       if (!res)
07391          res = ast_waitstream(chan, ints);
07392    }
07393    /* D A Y */
07394    if (!res) {
07395       gr_say_number_female(tm.tm_mday, chan, ints, lang);
07396    }
07397    /* M O N T H */
07398    if (!res) {
07399       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07400       res = ast_streamfile(chan, fn, lang);
07401       if (!res)
07402          res = ast_waitstream(chan, ints);
07403    }
07404    /* Y E A R */
07405    if (!res)
07406       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
07407    return res;
07408 }
07409 
07410 
07411 
07412 /*! \brief Greek support
07413  *
07414  * A list of the files that you need to create
07415  * - digits/female/1..4 : "Mia, dyo , treis, tesseris "
07416  * - digits/kai : "KAI"
07417  * - didgits : "h wra"
07418  * - digits/p-m : "meta meshmbrias"
07419  * - digits/a-m : "pro meshmbrias"
07420  */
07421 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07422 {
07423 
07424    struct timeval when = { t, 0 };
07425    struct ast_tm tm;
07426    int res = 0;
07427    int hour, pm=0;
07428 
07429    ast_localtime(&when, &tm, NULL);
07430    hour = tm.tm_hour;
07431 
07432    if (!hour)
07433       hour = 12;
07434    else if (hour == 12)
07435       pm = 1;
07436    else if (hour > 12) {
07437       hour -= 12;
07438       pm = 1;
07439    }
07440 
07441    res = gr_say_number_female(hour, chan, ints, lang);
07442    if (tm.tm_min) {
07443       if (!res)
07444          res = ast_streamfile(chan, "digits/kai", lang);
07445       if (!res)
07446          res = ast_waitstream(chan, ints);
07447       if (!res)
07448          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
07449    } else {
07450       if (!res)
07451          res = ast_streamfile(chan, "digits/hwra", lang);
07452       if (!res)
07453          res = ast_waitstream(chan, ints);
07454    }
07455    if (pm) {
07456       if (!res)
07457          res = ast_streamfile(chan, "digits/p-m", lang);
07458    } else {
07459       if (!res)
07460          res = ast_streamfile(chan, "digits/a-m", lang);
07461    }
07462    if (!res)
07463       res = ast_waitstream(chan, ints);
07464    return res;
07465 }
07466 
07467 
07468 
07469 /*! \brief Greek support
07470  */
07471 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07472 {
07473    struct timeval when = { t, 0 };
07474    struct ast_tm tm;
07475    char fn[256];
07476    int res = 0;
07477 
07478    ast_localtime(&when, &tm, NULL);
07479 
07480    /* W E E K - D A Y */
07481    if (!res) {
07482       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07483       res = ast_streamfile(chan, fn, lang);
07484       if (!res)
07485          res = ast_waitstream(chan, ints);
07486    }
07487    /* D A Y */
07488    if (!res) {
07489       gr_say_number_female(tm.tm_mday, chan, ints, lang);
07490    }
07491    /* M O N T H */
07492    if (!res) {
07493       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07494       res = ast_streamfile(chan, fn, lang);
07495       if (!res)
07496          res = ast_waitstream(chan, ints);
07497    }
07498 
07499    res = ast_say_time_gr(chan, t, ints, lang);
07500    return res;
07501 }
07502 
07503 /*! \brief Greek support
07504  */
07505 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)
07506 {
07507    struct timeval when = { t, 0 };
07508    struct ast_tm tm;
07509    int res=0, offset, sndoffset;
07510    char sndfile[256], nextmsg[256];
07511 
07512    if (!format)
07513       format = "AdBY 'digits/at' IMp";
07514 
07515    ast_localtime(&when, &tm, tzone);
07516 
07517    for (offset=0 ; format[offset] != '\0' ; offset++) {
07518       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
07519       switch (format[offset]) {
07520          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
07521       case '\'':
07522          /* Literal name of a sound file */
07523          for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
07524             sndfile[sndoffset] = format[offset];
07525          }
07526          sndfile[sndoffset] = '\0';
07527          res = wait_file(chan, ints, sndfile, lang);
07528          break;
07529       case 'A':
07530       case 'a':
07531          /* Sunday - Saturday */
07532          snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
07533          res = wait_file(chan, ints, nextmsg, lang);
07534          break;
07535       case 'B':
07536       case 'b':
07537       case 'h':
07538          /* January - December */
07539          snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
07540          res = wait_file(chan, ints, nextmsg, lang);
07541          break;
07542       case 'd':
07543       case 'e':
07544          /* first - thirtyfirst */
07545          gr_say_number_female(tm.tm_mday, chan, ints, lang);
07546          break;
07547       case 'Y':
07548          /* Year */
07549 
07550          ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, ast_channel_language(chan), -1, -1);
07551          break;
07552       case 'I':
07553       case 'l':
07554          /* 12-Hour */
07555          if (tm.tm_hour == 0)
07556             gr_say_number_female(12, chan, ints, lang);
07557          else if (tm.tm_hour > 12)
07558             gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
07559          else
07560             gr_say_number_female(tm.tm_hour, chan, ints, lang);
07561          break;
07562       case 'H':
07563       case 'k':
07564          /* 24-Hour */
07565          gr_say_number_female(tm.tm_hour, chan, ints, lang);
07566          break;
07567       case 'M':
07568          /* Minute */
07569          if (tm.tm_min) {
07570             if (!res)
07571                res = ast_streamfile(chan, "digits/kai", lang);
07572             if (!res)
07573                res = ast_waitstream(chan, ints);
07574             if (!res)
07575                res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
07576          } else {
07577             if (!res)
07578                res = ast_streamfile(chan, "digits/oclock", lang);
07579             if (!res)
07580                res = ast_waitstream(chan, ints);
07581          }
07582          break;
07583       case 'P':
07584       case 'p':
07585          /* AM/PM */
07586          if (tm.tm_hour > 11)
07587             ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
07588          else
07589             ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
07590          res = wait_file(chan, ints, nextmsg, lang);
07591          break;
07592       case 'Q':
07593          /* Shorthand for "Today", "Yesterday", or ABdY */
07594             /* XXX As emphasized elsewhere, this should the native way in your
07595              * language to say the date, with changes in what you say, depending
07596              * upon how recent the date is. XXX */
07597          {
07598             struct timeval now = ast_tvnow();
07599             struct ast_tm tmnow;
07600             time_t beg_today;
07601 
07602             ast_localtime(&now, &tmnow, tzone);
07603             /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
07604             /* In any case, it saves not having to do ast_mktime() */
07605             beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
07606             if (beg_today < t) {
07607                /* Today */
07608                res = wait_file(chan, ints, "digits/today", lang);
07609             } else if (beg_today - 86400 < t) {
07610                /* Yesterday */
07611                res = wait_file(chan, ints, "digits/yesterday", lang);
07612             } else {
07613                res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
07614             }
07615          }
07616          break;
07617       case 'q':
07618          /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
07619             /* XXX As emphasized elsewhere, this should the native way in your
07620              * language to say the date, with changes in what you say, depending
07621              * upon how recent the date is. XXX */
07622          {
07623             struct timeval now = ast_tvnow();
07624             struct ast_tm tmnow;
07625             time_t beg_today;
07626 
07627             ast_localtime(&now, &tmnow, tzone);
07628             /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
07629             /* In any case, it saves not having to do ast_mktime() */
07630             beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
07631             if (beg_today < t) {
07632                /* Today */
07633             } else if ((beg_today - 86400) < t) {
07634                /* Yesterday */
07635                res = wait_file(chan, ints, "digits/yesterday", lang);
07636             } else if (beg_today - 86400 * 6 < t) {
07637                /* Within the last week */
07638                res = ast_say_date_with_format_gr(chan, t, ints, lang, "A", tzone);
07639             } else {
07640                res = ast_say_date_with_format_gr(chan, t, ints, lang, "AdBY", tzone);
07641             }
07642          }
07643          break;
07644       case 'R':
07645          res = ast_say_date_with_format_gr(chan, t, ints, lang, "HM", tzone);
07646          break;
07647       case 'S':
07648          /* Seconds */
07649          ast_copy_string(nextmsg, "digits/kai", sizeof(nextmsg));
07650          res = wait_file(chan, ints, nextmsg, lang);
07651          if (!res)
07652             res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
07653          if (!res)
07654             ast_copy_string(nextmsg, "digits/seconds", sizeof(nextmsg));
07655          res = wait_file(chan, ints, nextmsg, lang);
07656          break;
07657       case 'T':
07658          res = ast_say_date_with_format_gr(chan, t, ints, lang, "HMS", tzone);
07659          break;
07660       case ' ':
07661       case '   ':
07662          /* Just ignore spaces and tabs */
07663          break;
07664       default:
07665          /* Unknown character */
07666          ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
07667       }
07668       /* Jump out on DTMF */
07669       if (res) {
07670          break;
07671       }
07672    }
07673    return res;
07674 }
07675 
07676 /*! \brief Vietnamese syntax */
07677 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)
07678 {
07679    struct timeval when = { t, 0 };
07680    struct ast_tm tm;
07681    int res = 0, offset, sndoffset;
07682    char sndfile[256], nextmsg[256];
07683 
07684    if (format == NULL)
07685       format = "A 'digits/day' eB 'digits/year' Y 'digits/at' k 'hours' M 'minutes' p";
07686 
07687    ast_localtime(&when, &tm, tzone);
07688 
07689    for (offset=0 ; format[offset] != '\0' ; offset++) {
07690       ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
07691       switch (format[offset]) {
07692          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
07693          case '\'':
07694             /* Literal name of a sound file */
07695             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
07696                sndfile[sndoffset] = format[offset];
07697             }
07698             sndfile[sndoffset] = '\0';
07699             res = wait_file(chan, ints, sndfile, lang);
07700             break;
07701          case 'A':
07702          case 'a':
07703             /* Sunday - Saturday */
07704             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
07705             res = wait_file(chan, ints, nextmsg, lang);
07706             break;
07707          case 'B':
07708          case 'b':
07709          case 'h':
07710             /* January - December */
07711             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
07712             res = wait_file(chan, ints, nextmsg, lang);
07713             break;
07714          case 'm':
07715             /* Month enumerated */
07716             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
07717             break;
07718          case 'd':
07719          case 'e':
07720             /* 1 - 31 */
07721             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
07722             break;
07723          case 'Y':
07724             /* Year */
07725             if (tm.tm_year > 99) {
07726                res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
07727             } else if (tm.tm_year < 1) {
07728                /* I'm not going to handle 1900 and prior */
07729                /* We'll just be silent on the year, instead of bombing out. */
07730             } else {
07731                res = wait_file(chan, ints, "digits/19", lang);
07732                if (!res) {
07733                   if (tm.tm_year <= 9) {
07734                      /* 1901 - 1909 */
07735                      res = wait_file(chan, ints, "digits/odd", lang);
07736                   }
07737 
07738                   res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
07739                }
07740             }
07741             break;
07742          case 'I':
07743          case 'l':
07744             /* 12-Hour */
07745             if (tm.tm_hour == 0)
07746                ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
07747             else if (tm.tm_hour > 12)
07748                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
07749             else
07750                snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
07751             res = wait_file(chan, ints, nextmsg, lang);
07752             break;
07753          case 'H':
07754          case 'k':
07755             /* 24-Hour */
07756             if (format[offset] == 'H') {
07757                /* e.g. oh-eight */
07758                if (tm.tm_hour < 10) {
07759                   res = wait_file(chan, ints, "digits/0", lang);
07760                }
07761             } else {
07762                /* e.g. eight */
07763                if (tm.tm_hour == 0) {
07764                   res = wait_file(chan, ints, "digits/0", lang);
07765                }
07766             }
07767             if (!res) {
07768                if (tm.tm_hour != 0) {
07769                   int remaining = tm.tm_hour;
07770                   if (tm.tm_hour > 20) {
07771                      res = wait_file(chan, ints, "digits/20", lang);
07772                      remaining -= 20;
07773                   }
07774                   if (!res) {
07775                      snprintf(nextmsg, sizeof(nextmsg), "digits/%d", remaining);
07776                      res = wait_file(chan, ints, nextmsg, lang);
07777                   }
07778                }
07779             }
07780             break;
07781          case 'M':
07782          case 'N':
07783             /* Minute */
07784             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
07785             break;
07786          case 'P':
07787          case 'p':
07788             /* AM/PM */
07789             if (tm.tm_hour > 11)
07790                ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
07791             else
07792                ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
07793             res = wait_file(chan, ints, nextmsg, lang);
07794             break;
07795          case 'Q':
07796             /* Shorthand for "Today", "Yesterday", or ABdY */
07797             /* XXX As emphasized elsewhere, this should the native way in your
07798              * language to say the date, with changes in what you say, depending
07799              * upon how recent the date is. XXX */
07800             {
07801                struct timeval now = ast_tvnow();
07802                struct ast_tm tmnow;
07803                time_t beg_today;
07804 
07805                gettimeofday(&now, NULL);
07806                ast_localtime(&now, &tmnow, tzone);
07807                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
07808                /* In any case, it saves not having to do ast_mktime() */
07809                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
07810                if (beg_today < t) {
07811                   /* Today */
07812                   res = wait_file(chan, ints, "digits/today", lang);
07813                } else if (beg_today - 86400 < t) {
07814                   /* Yesterday */
07815                   res = wait_file(chan, ints, "digits/yesterday", lang);
07816                } else if (beg_today - 86400 * 6 < t) {
07817                   /* Within the last week */
07818                   res = ast_say_date_with_format_vi(chan, t, ints, lang, "A", tzone);
07819                } else if (beg_today - 2628000 < t) {
07820                   /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
07821                   res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
07822                } else if (beg_today - 15768000 < t) {
07823                   /* Less than 6 months ago - "August seventh" */
07824                   res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
07825                } else {
07826                   /* More than 6 months ago - "April nineteenth two thousand three" */
07827                   res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
07828                }
07829             }
07830             break;
07831          case 'q':
07832             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
07833             /* XXX As emphasized elsewhere, this should the native way in your
07834              * language to say the date, with changes in what you say, depending
07835              * upon how recent the date is. XXX */
07836             {
07837                struct timeval now;
07838                struct ast_tm tmnow;
07839                time_t beg_today;
07840 
07841                now = ast_tvnow();
07842                ast_localtime(&now, &tmnow, tzone);
07843                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
07844                /* In any case, it saves not having to do ast_mktime() */
07845                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
07846                if (beg_today < t) {
07847                   /* Today */
07848                } else if ((beg_today - 86400) < t) {
07849                   /* Yesterday */
07850                   res = wait_file(chan, ints, "digits/yesterday", lang);
07851                } else if (beg_today - 86400 * 6 < t) {
07852                   /* Within the last week */
07853                   res = ast_say_date_with_format_en(chan, t, ints, lang, "A", tzone);
07854                } else if (beg_today - 2628000 < t) {
07855                   /* Less than a month ago - "Chu nhat ngay 13 thang 2" */
07856                   res = ast_say_date_with_format_vi(chan, t, ints, lang, "A 'digits/day' dB", tzone);
07857                } else if (beg_today - 15768000 < t) {
07858                   /* Less than 6 months ago - "August seventh" */
07859                   res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB", tzone);
07860                } else {
07861                   /* More than 6 months ago - "April nineteenth two thousand three" */
07862                   res = ast_say_date_with_format_vi(chan, t, ints, lang, "'digits/day' dB 'digits/year' Y", tzone);
07863                }
07864             }
07865             break;
07866          case 'R':
07867             res = ast_say_date_with_format_vi(chan, t, ints, lang, "HM", tzone);
07868             break;
07869          case 'S':
07870             /* Seconds */
07871             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
07872             break;
07873          case 'T':
07874             res = ast_say_date_with_format_vi(chan, t, ints, lang, "H 'hours' M 'minutes' S 'seconds'", tzone);
07875             break;
07876          case ' ':
07877          case '   ':
07878             /* Just ignore spaces and tabs */
07879             break;
07880          default:
07881             /* Unknown character */
07882             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
07883       }
07884       /* Jump out on DTMF */
07885       if (res) {
07886          break;
07887       }
07888    }
07889    return res;
07890 }
07891 
07892 /*! \brief Georgian support
07893 
07894    Convert a number into a semi-localized string. Only for Georgian.
07895    res must be of at least 256 bytes, preallocated.
07896    The output corresponds to Georgian spoken numbers, so
07897    it may be either converted to real words by applying a direct conversion
07898    table, or played just by substituting the entities with played files.
07899 
07900    Output may consist of the following tokens (separated by spaces):
07901    0, minus.
07902    1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
07903    10-19.
07904    20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
07905    100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
07906    1000, 1000_. (atasi, atas).
07907    1000000, 1000000_. (milioni, milion).
07908    1000000000, 1000000000_. (miliardi, miliard).
07909 
07910    To be able to play the sounds, each of the above tokens needs
07911    a corresponding sound file. (e.g. 200_.gsm).
07912 */
07913 static char* ast_translate_number_ka(int num, char* res, int res_len)
07914 {
07915    char buf[256];
07916    int digit = 0;
07917    int remaining = 0;
07918 
07919 
07920    if (num < 0) {
07921       strncat(res, "minus ", res_len - strlen(res) - 1);
07922       if ( num > INT_MIN ) {
07923          num = -num;
07924       } else {
07925          num = 0;
07926       }
07927    }
07928 
07929 
07930    /* directly read the numbers */
07931    if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
07932       snprintf(buf, sizeof(buf), "%d", num);
07933       strncat(res, buf, res_len - strlen(res) - 1);
07934       return res;
07935    }
07936 
07937 
07938    if (num < 40) {  /* ocda... */
07939       strncat(res, "20_ ", res_len - strlen(res) - 1);
07940       return ast_translate_number_ka(num - 20, res, res_len);
07941    }
07942 
07943    if (num < 60) {  /* ormocda... */
07944       strncat(res, "40_ ", res_len - strlen(res) - 1);
07945       return ast_translate_number_ka(num - 40, res, res_len);
07946    }
07947 
07948    if (num < 80) {  /* samocda... */
07949       strncat(res, "60_ ", res_len - strlen(res) - 1);
07950       return ast_translate_number_ka(num - 60, res, res_len);
07951    }
07952 
07953    if (num < 100) {  /* otxmocda... */
07954       strncat(res, "80_ ", res_len - strlen(res) - 1);
07955       return ast_translate_number_ka(num - 80, res, res_len);
07956    }
07957 
07958 
07959    if (num < 1000) {  /*  as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
07960       remaining = num % 100;
07961       digit = (num - remaining) / 100;
07962 
07963       if (remaining == 0) {
07964          snprintf(buf, sizeof(buf), "%d", num);
07965          strncat(res, buf, res_len - strlen(res) - 1);
07966          return res;
07967       } else {
07968          snprintf(buf, sizeof(buf), "%d_ ", digit*100);
07969          strncat(res, buf, res_len - strlen(res) - 1);
07970          return ast_translate_number_ka(remaining, res, res_len);
07971       }
07972    }
07973 
07974 
07975    if (num == 1000) {
07976       strncat(res, "1000", res_len - strlen(res) - 1);
07977       return res;
07978    }
07979 
07980 
07981    if (num < 1000000) {
07982       remaining = num % 1000;
07983       digit = (num - remaining) / 1000;
07984 
07985       if (remaining == 0) {
07986          ast_translate_number_ka(digit, res, res_len);
07987          strncat(res, " 1000", res_len - strlen(res) - 1);
07988          return res;
07989       }
07990 
07991       if (digit == 1) {
07992          strncat(res, "1000_ ", res_len - strlen(res) - 1);
07993          return ast_translate_number_ka(remaining, res, res_len);
07994       }
07995 
07996       ast_translate_number_ka(digit, res, res_len);
07997       strncat(res, " 1000_ ", res_len - strlen(res) - 1);
07998       return ast_translate_number_ka(remaining, res, res_len);
07999    }
08000 
08001 
08002    if (num == 1000000) {
08003       strncat(res, "1 1000000", res_len - strlen(res) - 1);
08004       return res;
08005    }
08006 
08007 
08008    if (num < 1000000000) {
08009       remaining = num % 1000000;
08010       digit = (num - remaining) / 1000000;
08011 
08012       if (remaining == 0) {
08013          ast_translate_number_ka(digit, res, res_len);
08014          strncat(res, " 1000000", res_len - strlen(res) - 1);
08015          return res;
08016       }
08017 
08018       ast_translate_number_ka(digit, res, res_len);
08019       strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
08020       return ast_translate_number_ka(remaining, res, res_len);
08021    }
08022 
08023 
08024    if (num == 1000000000) {
08025       strncat(res, "1 1000000000", res_len - strlen(res) - 1);
08026       return res;
08027    }
08028 
08029 
08030    if (num > 1000000000) {
08031       remaining = num % 1000000000;
08032       digit = (num - remaining) / 1000000000;
08033 
08034       if (remaining == 0) {
08035          ast_translate_number_ka(digit, res, res_len);
08036          strncat(res, " 1000000000", res_len - strlen(res) - 1);
08037          return res;
08038       }
08039 
08040       ast_translate_number_ka(digit, res, res_len);
08041       strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
08042       return ast_translate_number_ka(remaining, res, res_len);
08043    }
08044 
08045    return res;
08046 
08047 }
08048 
08049 
08050 
08051 /*! \brief  ast_say_number_full_ka: Georgian syntax */
08052 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)
08053 {
08054    int res = 0;
08055    char fn[512] = "";
08056    char* s = 0;
08057    const char* remaining = fn;
08058 
08059    if (!num) {
08060       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
08061    }
08062 
08063 
08064    ast_translate_number_ka(num, fn, 512);
08065 
08066 
08067 
08068    while (res == 0 && (s = strstr(remaining, " "))) {
08069       size_t len = s - remaining;
08070       char* new_string = ast_malloc(len + 1 + strlen("digits/"));
08071 
08072       sprintf(new_string, "digits/");
08073       strncat(new_string, remaining, len);  /* we can't sprintf() it, it's not null-terminated. */
08074 /*    new_string[len + strlen("digits/")] = '\0'; */
08075 
08076       if (!ast_streamfile(chan, new_string, language)) {
08077          if ((audiofd  > -1) && (ctrlfd > -1)) {
08078             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
08079          } else {
08080             res = ast_waitstream(chan, ints);
08081          }
08082       }
08083       ast_stopstream(chan);
08084 
08085       ast_free(new_string);
08086 
08087       remaining = s + 1;  /* position just after the found space char. */
08088       while (*remaining == ' ') {  /* skip multiple spaces */
08089          remaining++;
08090       }
08091    }
08092 
08093 
08094    /* the last chunk. */
08095    if (res == 0 && *remaining) {
08096 
08097       char* new_string = ast_malloc(strlen(remaining) + 1 + strlen("digits/"));
08098       sprintf(new_string, "digits/%s", remaining);
08099 
08100       if (!ast_streamfile(chan, new_string, language)) {
08101          if ((audiofd  > -1) && (ctrlfd > -1)) {
08102             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
08103          } else {
08104             res = ast_waitstream(chan, ints);
08105          }
08106       }
08107       ast_stopstream(chan);
08108 
08109       ast_free(new_string);
08110 
08111    }
08112 
08113 
08114    return res;
08115 
08116 }
08117 
08118 
08119 
08120 /*! \brief Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi".
08121 
08122 Georgian support for date/time requires the following files (*.gsm):
08123 
08124  - mon-1, mon-2, ... (ianvari, tebervali, ...)
08125  - day-1, day-2, ... (orshabati, samshabati, ...)
08126  - saati_da
08127  - tsuti
08128  - tslis
08129 */
08130 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
08131 {
08132    struct timeval when = { t, 0 };
08133    struct ast_tm tm;
08134    char fn[256];
08135    int res = 0;
08136    ast_localtime(&when, &tm, NULL);
08137 
08138    if (!res) {
08139       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
08140    }
08141 
08142    if (!res) {
08143       snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
08144       res = ast_streamfile(chan, fn, lang);
08145       if (!res) {
08146          res = ast_waitstream(chan, ints);
08147       }
08148    }
08149 
08150    if (!res) {
08151       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
08152 /*    if (!res)
08153          res = ast_waitstream(chan, ints);
08154 */
08155    }
08156 
08157    if (!res) {
08158       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
08159       res = ast_streamfile(chan, fn, lang);
08160       if (!res) {
08161          res = ast_waitstream(chan, ints);
08162       }
08163    }
08164    return res;
08165 
08166 }
08167 
08168 
08169 
08170 
08171 
08172 /*! \brief Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
08173 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
08174 {
08175    struct timeval when = { t, 0 };
08176    struct ast_tm tm;
08177    int res = 0;
08178 
08179    ast_localtime(&when, &tm, NULL);
08180 
08181    res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
08182    if (!res) {
08183       res = ast_streamfile(chan, "digits/saati_da", lang);
08184       if (!res) {
08185          res = ast_waitstream(chan, ints);
08186       }
08187    }
08188 
08189    if (tm.tm_min) {
08190       if (!res) {
08191          res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
08192 
08193          if (!res) {
08194             res = ast_streamfile(chan, "digits/tsuti", lang);
08195             if (!res) {
08196                res = ast_waitstream(chan, ints);
08197             }
08198          }
08199       }
08200    }
08201    return res;
08202 }
08203 
08204 
08205 
08206 /*! \brief Georgian syntax. Say date, then say time. */
08207 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
08208 {
08209    struct timeval when = { t, 0 };
08210    struct ast_tm tm;
08211    int res = 0;
08212 
08213    ast_localtime(&when, &tm, NULL);
08214    res = ast_say_date(chan, t, ints, lang);
08215    if (!res) {
08216       ast_say_time(chan, t, ints, lang);
08217    }
08218    return res;
08219 
08220 }
08221 
08222 
08223 
08224 
08225 /*! \brief Georgian syntax */
08226 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
08227 {
08228    int res=0;
08229    int daydiff;
08230    struct ast_tm tm;
08231    struct ast_tm now;
08232    struct timeval when = { t, 0 }, nowt = ast_tvnow();
08233    char fn[256];
08234 
08235    ast_localtime(&when, &tm, NULL);
08236    ast_localtime(&nowt, &now, NULL);
08237    daydiff = now.tm_yday - tm.tm_yday;
08238    if ((daydiff < 0) || (daydiff > 6)) {
08239       /* Day of month and month */
08240       if (!res) {
08241          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
08242       }
08243       if (!res) {
08244          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
08245          res = ast_streamfile(chan, fn, lang);
08246          if (!res) {
08247             res = ast_waitstream(chan, ints);
08248          }
08249       }
08250 
08251    } else if (daydiff) {
08252       /* Just what day of the week */
08253       if (!res) {
08254          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
08255          res = ast_streamfile(chan, fn, lang);
08256          if (!res) {
08257             res = ast_waitstream(chan, ints);
08258          }
08259       }
08260    } /* Otherwise, it was today */
08261    if (!res) {
08262       res = ast_say_time(chan, t, ints, lang);
08263    }
08264 
08265    return res;
08266 }
08267 
08268 /*! \brief
08269  * In English, we use the plural for everything but one. For example:
08270  *  - 1 degree
08271  *  - 2 degrees
08272  *  - 5 degrees
08273  * The filename for the plural form is generated by appending "s". Note that
08274  * purpose is to generate a unique filename, not to implement irregular
08275  * declensions. Thus:
08276  *  - 1 man
08277  *  - 2 mans (the "mans" soundfile will of course say "men")
08278  */
08279 static const char *counted_noun_ending_en(int num)
08280 {
08281    if (num == 1 || num == -1) {
08282       return "";
08283    } else {
08284       return "s";
08285    }
08286 }
08287 
08288 /*! \brief
08289  * Counting of objects in slavic languages such as Russian and Ukrainian the
08290  * rules are more complicated. There are two plural forms used in counting.
08291  * They are the genative singular which we represent with the suffix "x1" and
08292  * the genative plural which we represent with the suffix "x2". The base names
08293  * of the soundfiles remain in English. For example:
08294  *  - 1 degree (soudfile says "gradus")
08295  *  - 2 degreex1 (soundfile says "gradusa")
08296  *  - 5 degreex2 (soundfile says "gradusov")
08297  */
08298 static const char *counted_noun_ending_slavic(int num)
08299 {
08300    if (num < 0) {
08301        num *= -1;
08302    }
08303    num %= 100;       /* never pay attention to more than two digits */
08304    if (num >= 20) {     /* for numbers 20 and above, pay attention to only last digit */
08305        num %= 10;
08306    }
08307    if (num == 1) {         /* singular */
08308        return "";
08309    }
08310    if (num > 0 && num < 5) {  /* 2--4 get genative singular */
08311        return "x1";
08312    } else {       /* 5--19 get genative plural */
08313        return "x2";
08314    }
08315 }
08316 
08317 int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
08318 {
08319    char *temp;
08320    int temp_len;
08321    const char *ending;
08322    if (!strncasecmp(ast_channel_language(chan), "ru", 2)) {        /* Russian */
08323       ending = counted_noun_ending_slavic(num);
08324    } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian */
08325       ending = counted_noun_ending_slavic(num);
08326    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* Polish */
08327       ending = counted_noun_ending_slavic(num);
08328    } else {                                            /* English and default */
08329       ending = counted_noun_ending_en(num);
08330    }
08331    temp = ast_alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
08332    snprintf(temp, temp_len, "%s%s", noun, ending);
08333    return ast_play_and_wait(chan, temp);
08334 }
08335 
08336 /*! \brief
08337  * In slavic languages such as Russian and Ukrainian the rules for declining
08338  * adjectives are simpler than those for nouns.  When counting we use only
08339  * the singular (to which we give no suffix) and the genative plural (which
08340  * we represent by adding an "x").  Oh, an in the singular gender matters
08341  * so we append the supplied gender suffix ("m", "f", "n").
08342  */
08343 static const char *counted_adjective_ending_ru(int num, const char gender[])
08344 {
08345    if (num < 0) {
08346        num *= -1;
08347    }
08348    num %= 100;    /* never pay attention to more than two digits */
08349    if (num >= 20) {  /* at 20 and beyond only the last digit matters */
08350        num %= 10;
08351    }
08352    if (num == 1) {
08353        return gender ? gender : "";
08354    } else {    /* all other numbers get the genative plural */
08355        return "x";
08356    }
08357 }
08358 
08359 int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
08360 {
08361    char *temp;
08362    int temp_len;
08363    const char *ending;
08364    if (!strncasecmp(ast_channel_language(chan), "ru", 2)) {           /* Russian */
08365       ending = counted_adjective_ending_ru(num, gender);
08366    } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) {    /* Ukrainian */
08367       ending = counted_adjective_ending_ru(num, gender);
08368    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {    /* Polish */
08369       ending = counted_adjective_ending_ru(num, gender);
08370    } else {                                               /* English and default */
08371       ending = "";
08372    }
08373    temp = ast_alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
08374    snprintf(temp, temp_len, "%s%s", adjective, ending);
08375    return ast_play_and_wait(chan, temp);
08376 }
08377 
08378 
08379 
08380 /*! \brief
08381  * remap the 'say' functions to use those in this file
08382  */
08383 static void __attribute__((constructor)) __say_init(void)
08384 {
08385    ast_say_number_full = say_number_full;
08386    ast_say_enumeration_full = say_enumeration_full;
08387    ast_say_digit_str_full = say_digit_str_full;
08388    ast_say_character_str_full = say_character_str_full;
08389    ast_say_phonetic_str_full = say_phonetic_str_full;
08390    ast_say_datetime = say_datetime;
08391    ast_say_time = say_time;
08392    ast_say_date = say_date;
08393    ast_say_datetime_from_now = say_datetime_from_now;
08394    ast_say_date_with_format = say_date_with_format;
08395 }