Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:55 2007

Asterisk developer's documentation :: Codename Pineapple


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

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen