Sun Jun 12 16:37:48 2011

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

Generated on Sun Jun 12 16:37:48 2011 for Asterisk - the Open Source PBX by  doxygen 1.5.6