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

Generated on Tue Jul 14 23:09:55 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7