Wed Aug 18 22:33:56 2010

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

Generated on Wed Aug 18 22:33:56 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7