Sun Oct 16 2011 08:41:46

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