Mon Mar 31 07:38:05 2008

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

Generated on Mon Mar 31 07:38:05 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1