Fri Jun 19 12:09:52 2009

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

Generated on Fri Jun 19 12:09:52 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7