Thu May 14 14:49:10 2009

Asterisk developer's documentation


say.c

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

Generated on Thu May 14 14:49:10 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7