Wed Aug 15 01:24:24 2007

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

Generated on Wed Aug 15 01:24:24 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3