Sun Aug 15 20:33:31 2010

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: 237573 $")
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             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03291                sndfile[sndoffset] = format[offset];
03292             }
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 static char next_item(const char *format)
03515 {
03516    const char *next = ast_skip_blanks(format);
03517    return *next;
03518 }
03519 
03520 /* Danish syntax */
03521 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)
03522 {
03523    struct tm tm;
03524    int res=0, offset, sndoffset;
03525    char sndfile[256], nextmsg[256];
03526 
03527    if (!format)
03528       format = "A dBY HMS";
03529 
03530    ast_localtime(&time,&tm,timezone);
03531 
03532    for (offset=0 ; format[offset] != '\0' ; offset++) {
03533       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03534       switch (format[offset]) {
03535          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03536          case '\'':
03537             /* Literal name of a sound file */
03538             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03539                sndfile[sndoffset] = format[offset];
03540             }
03541             sndfile[sndoffset] = '\0';
03542             res = wait_file(chan,ints,sndfile,lang);
03543             break;
03544          case 'A':
03545          case 'a':
03546             /* Sunday - Saturday */
03547             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03548             res = wait_file(chan,ints,nextmsg,lang);
03549             break;
03550          case 'B':
03551          case 'b':
03552          case 'h':
03553             /* January - December */
03554             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03555             res = wait_file(chan,ints,nextmsg,lang);
03556             break;
03557          case 'm':
03558             /* Month enumerated */
03559             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");   
03560             break;
03561          case 'd':
03562          case 'e':
03563             /* First - Thirtyfirst */
03564             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");  
03565             break;
03566          case 'Y':
03567             /* Year */
03568             {
03569                int year = tm.tm_year + 1900;
03570                if (year > 1999) {   /* year 2000 and later */
03571                   res = ast_say_number(chan, year, ints, lang, (char *) NULL);   
03572                } else {
03573                   if (year < 1100) {
03574                      /* I'm not going to handle 1100 and prior */
03575                      /* We'll just be silent on the year, instead of bombing out. */
03576                   } else {
03577                       /* year 1100 to 1999. will anybody need this?!? */
03578                       /* say 1967 as 'nineteen hundred seven and sixty' */
03579                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
03580                      res = wait_file(chan,ints,nextmsg,lang);
03581                      if (!res) {
03582                         res = wait_file(chan,ints, "digits/hundred",lang);
03583                         if (!res && year % 100 != 0) {
03584                            res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); 
03585                         }
03586                      }
03587                   }
03588                }
03589             }
03590             break;
03591          case 'I':
03592          case 'l':
03593             /* 12-Hour */
03594             res = wait_file(chan,ints,"digits/oclock",lang);
03595             if (tm.tm_hour == 0)
03596                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
03597             else if (tm.tm_hour > 12)
03598                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
03599             else
03600                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
03601             if (!res) {
03602                res = wait_file(chan,ints,nextmsg,lang);
03603             }
03604             break;
03605          case 'H':
03606             /* 24-Hour, single digit hours preceeded by "oh" (0) */
03607             if (tm.tm_hour < 10 && tm.tm_hour > 0) {
03608                res = wait_file(chan,ints, "digits/0",lang);
03609             }
03610             /* FALLTRHU */
03611          case 'k':
03612             /* 24-Hour */
03613             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
03614             break;
03615          case 'M':
03616             /* Minute */
03617             if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
03618                res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
03619             }
03620             if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
03621                if (tm.tm_min == 1) {
03622                   res = wait_file(chan,ints,"digits/minute",lang);
03623                } else {
03624                   res = wait_file(chan,ints,"digits/minutes",lang);
03625                }
03626             }
03627             break;
03628          case 'P':
03629          case 'p':
03630             /* AM/PM */
03631             if (tm.tm_hour > 11)
03632                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
03633             else
03634                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
03635             res = wait_file(chan,ints,nextmsg,lang);
03636             break;
03637          case 'Q':
03638             /* Shorthand for "Today", "Yesterday", or AdBY */
03639             /* XXX As emphasized elsewhere, this should the native way in your
03640              * language to say the date, with changes in what you say, depending
03641              * upon how recent the date is. XXX */
03642             {
03643                struct timeval now;
03644                struct tm tmnow;
03645                time_t beg_today, tt;
03646 
03647                gettimeofday(&now,NULL);
03648                tt = now.tv_sec;
03649                ast_localtime(&tt,&tmnow,timezone);
03650                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03651                /* In any case, it saves not having to do ast_mktime() */
03652                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03653                if (beg_today < time) {
03654                   /* Today */
03655                   res = wait_file(chan,ints, "digits/today",lang);
03656                } else if (beg_today - 86400 < time) {
03657                   /* Yesterday */
03658                   res = wait_file(chan,ints, "digits/yesterday",lang);
03659                } else {
03660                   res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
03661                }
03662             }
03663             break;
03664          case 'q':
03665             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
03666             /* XXX As emphasized elsewhere, this should the native way in your
03667              * language to say the date, with changes in what you say, depending
03668              * upon how recent the date is. XXX */
03669             {
03670                struct timeval now;
03671                struct tm tmnow;
03672                time_t beg_today, tt;
03673 
03674                gettimeofday(&now,NULL);
03675                tt = now.tv_sec;
03676                ast_localtime(&tt,&tmnow,timezone);
03677                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03678                /* In any case, it saves not having to do ast_mktime() */
03679                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03680                if (beg_today < time) {
03681                   /* Today */
03682                } else if ((beg_today - 86400) < time) {
03683                   /* Yesterday */
03684                   res = wait_file(chan,ints, "digits/yesterday",lang);
03685                } else if (beg_today - 86400 * 6 < time) {
03686                   /* Within the last week */
03687                   res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
03688                } else {
03689                   res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
03690                }
03691             }
03692             break;
03693          case 'R':
03694             res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
03695             break;
03696          case 'S':
03697             /* Seconds */
03698             res = wait_file(chan,ints, "digits/and",lang);
03699             if (!res) {
03700                res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");  
03701                if (!res) {
03702                   res = wait_file(chan,ints, "digits/seconds",lang);
03703                }
03704             }
03705             break;
03706          case 'T':
03707             res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
03708             break;
03709          case ' ':
03710          case '   ':
03711             /* Just ignore spaces and tabs */
03712             break;
03713          default:
03714             /* Unknown character */
03715             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
03716       }
03717       /* Jump out on DTMF */
03718       if (res) {
03719          break;
03720       }
03721    }
03722    return res;
03723 }
03724 
03725 /* German syntax */
03726 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)
03727 {
03728    struct tm tm;
03729    int res=0, offset, sndoffset;
03730    char sndfile[256], nextmsg[256];
03731 
03732    if (!format)
03733       format = "A dBY HMS";
03734 
03735    ast_localtime(&time,&tm,timezone);
03736 
03737    for (offset=0 ; format[offset] != '\0' ; offset++) {
03738       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03739       switch (format[offset]) {
03740          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03741          case '\'':
03742             /* Literal name of a sound file */
03743             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03744                sndfile[sndoffset] = format[offset];
03745             }
03746             sndfile[sndoffset] = '\0';
03747             res = wait_file(chan,ints,sndfile,lang);
03748             break;
03749          case 'A':
03750          case 'a':
03751             /* Sunday - Saturday */
03752             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03753             res = wait_file(chan,ints,nextmsg,lang);
03754             break;
03755          case 'B':
03756          case 'b':
03757          case 'h':
03758             /* January - December */
03759             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03760             res = wait_file(chan,ints,nextmsg,lang);
03761             break;
03762          case 'm':
03763             /* Month enumerated */
03764             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");   
03765             break;
03766          case 'd':
03767          case 'e':
03768             /* First - Thirtyfirst */
03769             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");  
03770             break;
03771          case 'Y':
03772             /* Year */
03773             {
03774                int year = tm.tm_year + 1900;
03775                if (year > 1999) {   /* year 2000 and later */
03776                   res = ast_say_number(chan, year, ints, lang, (char *) NULL);   
03777                } else {
03778                   if (year < 1100) {
03779                      /* I'm not going to handle 1100 and prior */
03780                      /* We'll just be silent on the year, instead of bombing out. */
03781                   } else {
03782                       /* year 1100 to 1999. will anybody need this?!? */
03783                       /* say 1967 as 'neunzehn hundert sieben und sechzig' */
03784                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
03785                      res = wait_file(chan,ints,nextmsg,lang);
03786                      if (!res) {
03787                         res = wait_file(chan,ints, "digits/hundred",lang);
03788                         if (!res && year % 100 != 0) {
03789                            res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); 
03790                         }
03791                      }
03792                   }
03793                }
03794             }
03795             break;
03796          case 'I':
03797          case 'l':
03798             /* 12-Hour */
03799             if (tm.tm_hour == 0)
03800                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
03801             else if (tm.tm_hour > 12)
03802                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
03803             else
03804                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
03805             res = wait_file(chan,ints,nextmsg,lang);
03806             if (!res) {
03807                res = wait_file(chan,ints,"digits/oclock",lang);
03808             }
03809             break;
03810          case 'H':
03811          case 'k':
03812             /* 24-Hour */
03813             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);   
03814             if (!res) {
03815                res = wait_file(chan,ints,"digits/oclock",lang);
03816             }
03817             break;
03818          case 'M':
03819             /* Minute */
03820             if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
03821                res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say digits/minutes */
03822             } else if (tm.tm_min > 0) {
03823                res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
03824             }
03825 
03826             if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
03827                if (tm.tm_min == 1) {
03828                   res = wait_file(chan,ints,"digits/minute",lang);
03829                } else {
03830                   res = wait_file(chan,ints,"digits/minutes",lang);
03831                }
03832             }
03833             break;
03834          case 'P':
03835          case 'p':
03836             /* AM/PM */
03837             if (tm.tm_hour > 11)
03838                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
03839             else
03840                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
03841             res = wait_file(chan,ints,nextmsg,lang);
03842             break;
03843          case 'Q':
03844             /* Shorthand for "Today", "Yesterday", or AdBY */
03845             /* XXX As emphasized elsewhere, this should the native way in your
03846              * language to say the date, with changes in what you say, depending
03847              * upon how recent the date is. XXX */
03848             {
03849                struct timeval now;
03850                struct tm tmnow;
03851                time_t beg_today, tt;
03852 
03853                gettimeofday(&now,NULL);
03854                tt = now.tv_sec;
03855                ast_localtime(&tt,&tmnow,timezone);
03856                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03857                /* In any case, it saves not having to do ast_mktime() */
03858                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03859                if (beg_today < time) {
03860                   /* Today */
03861                   res = wait_file(chan,ints, "digits/today",lang);
03862                } else if (beg_today - 86400 < time) {
03863                   /* Yesterday */
03864                   res = wait_file(chan,ints, "digits/yesterday",lang);
03865                } else {
03866                   res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
03867                }
03868             }
03869             break;
03870          case 'q':
03871             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
03872             /* XXX As emphasized elsewhere, this should the native way in your
03873              * language to say the date, with changes in what you say, depending
03874              * upon how recent the date is. XXX */
03875             {
03876                struct timeval now;
03877                struct tm tmnow;
03878                time_t beg_today, tt;
03879 
03880                gettimeofday(&now,NULL);
03881                tt = now.tv_sec;
03882                ast_localtime(&tt,&tmnow,timezone);
03883                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03884                /* In any case, it saves not having to do ast_mktime() */
03885                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03886                if (beg_today < time) {
03887                   /* Today */
03888                } else if ((beg_today - 86400) < time) {
03889                   /* Yesterday */
03890                   res = wait_file(chan,ints, "digits/yesterday",lang);
03891                } else if (beg_today - 86400 * 6 < time) {
03892                   /* Within the last week */
03893                   res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
03894                } else {
03895                   res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
03896                }
03897             }
03898             break;
03899          case 'R':
03900             res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
03901             break;
03902          case 'S':
03903             /* Seconds */
03904             res = wait_file(chan,ints, "digits/and",lang);
03905             if (!res) {
03906                res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");  
03907                if (!res) {
03908                   res = wait_file(chan, ints, tm.tm_sec == 1 ? "digits/second" : "digits/seconds", lang);
03909                }
03910             }
03911             break;
03912          case 'T':
03913             res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
03914             break;
03915          case ' ':
03916          case '   ':
03917             /* Just ignore spaces and tabs */
03918             break;
03919          default:
03920             /* Unknown character */
03921             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
03922       }
03923       /* Jump out on DTMF */
03924       if (res) {
03925          break;
03926       }
03927    }
03928    return res;
03929 }
03930 
03931 /* TODO: this probably is not the correct format for doxygen remarks */
03932 
03933 /** ast_say_date_with_format_he Say formmated date in Hebrew
03934  *
03935  * \ref ast_say_date_with_format_en for the details of the options 
03936  *
03937  * Changes from the English version: 
03938  *
03939  * * don't replicate in here the logic of ast_say_number_full_he
03940  *
03941  * * year is always 4-digit (because it's simpler)
03942  *
03943  * * added c, x, and X. Mainly for my tests
03944  *
03945  * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
03946  *
03947  * TODO: 
03948  * * A "ha" is missing in the standard date format, before the 'd'.
03949  * * The numbers of 3000--19000 are not handled well
03950  **/
03951 #define IL_DATE_STR "AdBY"
03952 #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 */
03953 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
03954 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)
03955 {
03956    /* TODO: This whole function is cut&paste from 
03957     * ast_say_date_with_format_en . Is that considered acceptable?
03958     **/
03959    struct tm tm;
03960    int res = 0, offset, sndoffset;
03961    char sndfile[256], nextmsg[256];
03962 
03963    if (!format) {
03964       format = IL_DATE_STR_FULL;
03965    }
03966 
03967    ast_localtime(&time, &tm, timezone);
03968 
03969    for (offset = 0; format[offset] != '\0'; offset++) {
03970       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03971       switch (format[offset]) {
03972          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03973          case '\'':
03974             /* Literal name of a sound file */
03975             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03976                sndfile[sndoffset] = format[offset];
03977             }
03978             sndfile[sndoffset] = '\0';
03979             res = wait_file(chan,ints,sndfile,lang);
03980             break;
03981          case 'A':
03982          case 'a':
03983             /* Sunday - Saturday */
03984             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03985             res = wait_file(chan,ints,nextmsg,lang);
03986             break;
03987          case 'B':
03988          case 'b':
03989          case 'h':
03990             /* January - December */
03991             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03992             res = wait_file(chan,ints,nextmsg,lang);
03993             break;
03994          case 'd':
03995          case 'e': /* Day of the month */
03996                                 /* I'm not sure exactly what the parameters 
03997                                  * audiofd and ctrlfd to 
03998                                  * ast_say_number_full_he mean, but it seems
03999                                  * safe to pass -1 there. 
04000                                  *
04001                                  * At least in one of the pathes :-( 
04002                                  */
04003             res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
04004             break;
04005          case 'Y': /* Year */
04006             res = ast_say_number_full_he(chan, tm.tm_year+1900,
04007                ints, lang, "f", -1, -1
04008             );
04009             break;
04010          case 'I':
04011          case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
04012          case 'H':
04013          case 'k': /* 24-Hour */
04014             res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
04015             break;
04016          case 'M': /* Minute */
04017             if (tm.tm_min >= 0 && tm.tm_min <= 9)  /* say a leading zero if needed */
04018                res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
04019             res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
04020             break;
04021          case 'P':
04022          case 'p':
04023             /* AM/PM - There is no AM/PM in Hebrew... */
04024             break;
04025          case 'Q':
04026             /* Shorthand for "Today", "Yesterday", or "date" */
04027          case 'q':
04028             /* Shorthand for "" (today), "Yesterday", A 
04029                                  * (weekday), or "date" */
04030             /* XXX As emphasized elsewhere, this should the native way in your
04031              * language to say the date, with changes in what you say, depending
04032              * upon how recent the date is. XXX */
04033             {
04034                struct timeval now;
04035                struct tm tmnow;
04036                time_t beg_today, tt;
04037                char todo = format[offset]; /* The letter to format*/
04038 
04039                gettimeofday(&now,NULL);
04040                tt = now.tv_sec;
04041                ast_localtime(&tt,&tmnow,timezone);
04042                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04043                /* In any case, it saves not having to do ast_mktime() */
04044                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04045                if (beg_today < time) {
04046                   /* Today */
04047                   if (todo == 'Q') {
04048                      res = wait_file(chan,
04049                            ints, 
04050                            "digits/today",
04051                            lang);
04052                   }
04053                } else if (beg_today - 86400 < time) {
04054                   /* Yesterday */
04055                   res = wait_file(chan,ints, "digits/yesterday",lang);
04056                } else if ((todo != 'Q') &&
04057                   (beg_today - 86400 * 6 < time))
04058                {
04059                   /* Within the last week */
04060                   res = ast_say_date_with_format_he(chan,
04061                                 time, ints, lang, 
04062                                 "A", timezone);
04063                } else {
04064                   res = ast_say_date_with_format_he(chan,
04065                                 time, ints, lang, 
04066                                 IL_DATE_STR, timezone);
04067                }
04068             }
04069             break;
04070          case 'R':
04071             res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
04072             break;
04073          case 'S': /* Seconds */
04074             res = ast_say_number_full_he(chan, tm.tm_sec,
04075                ints, lang, "f", -1, -1
04076             );
04077             break;
04078          case 'T':
04079             res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
04080             break;
04081          /* c, x, and X seem useful for testing. Not sure
04082                          * if thiey're good for the general public */
04083          case 'c':
04084             res = ast_say_date_with_format_he(chan, time, 
04085                                     ints, lang, IL_DATE_STR_FULL, timezone);
04086             break;
04087          case 'x':
04088             res = ast_say_date_with_format_he(chan, time, 
04089                                     ints, lang, IL_DATE_STR, timezone);
04090             break;
04091          case 'X': /* Currently not locale-dependent...*/
04092             res = ast_say_date_with_format_he(chan, time, 
04093                                     ints, lang, IL_TIME_STR, timezone);
04094             break;
04095          case ' ':
04096          case '   ':
04097             /* Just ignore spaces and tabs */
04098             break;
04099          default:
04100             /* Unknown character */
04101             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04102       }
04103       /* Jump out on DTMF */
04104       if (res) {
04105          break;
04106       }
04107    }
04108    return res;
04109 }
04110 
04111 
04112 /* Spanish syntax */
04113 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)
04114 {
04115    struct tm tm;
04116    int res=0, offset, sndoffset;
04117    char sndfile[256], nextmsg[256];
04118 
04119    if (format == NULL)
04120       format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
04121 
04122    ast_localtime(&time,&tm,timezone);
04123 
04124    for (offset=0 ; format[offset] != '\0' ; offset++) {
04125       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04126       switch (format[offset]) {
04127          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04128          case '\'':
04129             /* Literal name of a sound file */
04130             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04131                sndfile[sndoffset] = format[offset];
04132             }
04133             sndfile[sndoffset] = '\0';
04134             snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
04135             res = wait_file(chan,ints,nextmsg,lang);
04136             break;
04137          case 'A':
04138          case 'a':
04139             /* Sunday - Saturday */
04140             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04141             res = wait_file(chan,ints,nextmsg,lang);
04142             break;
04143          case 'B':
04144          case 'b':
04145          case 'h':
04146             /* January - December */
04147             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04148             res = wait_file(chan,ints,nextmsg,lang);
04149             break;
04150          case 'm':
04151             /* First - Twelfth */
04152             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04153             res = wait_file(chan,ints,nextmsg,lang);
04154             break;
04155          case 'd':
04156          case 'e':
04157             /* First - Thirtyfirst */
04158             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
04159             break;
04160          case 'Y':
04161             /* Year */
04162             res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
04163             break;
04164          case 'I':
04165          case 'l':
04166             /* 12-Hour */
04167             if (tm.tm_hour == 0)
04168                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04169             else if (tm.tm_hour > 12)
04170                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04171             else
04172                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04173             res = wait_file(chan,ints,nextmsg,lang);
04174             break;
04175          case 'H':
04176          case 'k':
04177             /* 24-Hour */
04178             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04179             if ((!res) && (format[offset] == 'H')) {
04180                if (tm.tm_hour == 1) {
04181                   res = wait_file(chan,ints,"digits/hour",lang);
04182                } else {
04183                   res = wait_file(chan,ints,"digits/hours",lang);
04184                }
04185             }
04186             break;
04187             break;
04188          case 'M':
04189             /* Minute */
04190             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL); 
04191             if (!res) {
04192                if (tm.tm_min == 1) {
04193                   res = wait_file(chan,ints,"digits/minute",lang);
04194                } else {
04195                   res = wait_file(chan,ints,"digits/minutes",lang);
04196                }
04197             }
04198             break;
04199          case 'P':
04200          case 'p':
04201             /* AM/PM */
04202             if (tm.tm_hour > 18)
04203                res = wait_file(chan, ints, "digits/p-m", lang);
04204             else if (tm.tm_hour > 12)
04205                res = wait_file(chan, ints, "digits/afternoon", lang);
04206             else if (tm.tm_hour)
04207                res = wait_file(chan, ints, "digits/a-m", lang);
04208             break;
04209          case 'Q':
04210             /* Shorthand for "Today", "Yesterday", or ABdY */
04211             /* XXX As emphasized elsewhere, this should the native way in your
04212              * language to say the date, with changes in what you say, depending
04213              * upon how recent the date is. XXX */
04214             {
04215                struct timeval now;
04216                struct tm tmnow;
04217                time_t beg_today, tt;
04218 
04219                gettimeofday(&now,NULL);
04220                tt = now.tv_sec;
04221                ast_localtime(&tt,&tmnow,timezone);
04222                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04223                /* In any case, it saves not having to do ast_mktime() */
04224                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04225                if (beg_today < time) {
04226                   /* Today */
04227                   res = wait_file(chan,ints, "digits/today",lang);
04228                } else if (beg_today - 86400 < time) {
04229                   /* Yesterday */
04230                   res = wait_file(chan,ints, "digits/yesterday",lang);
04231                } else {
04232                   res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
04233                }
04234             }
04235             break;
04236          case 'q':
04237             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04238             /* XXX As emphasized elsewhere, this should the native way in your
04239              * language to say the date, with changes in what you say, depending
04240              * upon how recent the date is. XXX */
04241             {
04242                struct timeval now;
04243                struct tm tmnow;
04244                time_t beg_today, tt;
04245 
04246                gettimeofday(&now,NULL);
04247                tt = now.tv_sec;
04248                ast_localtime(&tt,&tmnow,timezone);
04249                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04250                /* In any case, it saves not having to do ast_mktime() */
04251                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04252                if (beg_today < time) {
04253                   /* Today */
04254                   res = wait_file(chan,ints, "digits/today",lang);
04255                } else if ((beg_today - 86400) < time) {
04256                   /* Yesterday */
04257                   res = wait_file(chan,ints, "digits/yesterday",lang);
04258                } else if (beg_today - 86400 * 6 < time) {
04259                   /* Within the last week */
04260                   res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
04261                } else {
04262                   res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
04263                }
04264             }
04265             break;
04266          case 'R':
04267             res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/and' M", timezone);
04268             break;
04269          case 'S':
04270             /* Seconds */
04271             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
04272             if (!res) {
04273                if (tm.tm_sec == 1) {
04274                   res = wait_file(chan,ints,"digits/second",lang);
04275                } else {
04276                   res = wait_file(chan,ints,"digits/seconds",lang);
04277                }
04278             }
04279             break;
04280          case 'T':
04281             res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
04282             break;
04283          case ' ':
04284          case '   ':
04285             /* Just ignore spaces and tabs */
04286             break;
04287          default:
04288             /* Unknown character */
04289             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04290       }
04291       /* Jump out on DTMF */
04292       if (res) {
04293          break;
04294       }
04295    }
04296    return res;
04297 }
04298 
04299 /* French syntax 
04300 oclock = heure
04301 */
04302 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)
04303 {
04304    struct tm tm;
04305    int res=0, offset, sndoffset;
04306    char sndfile[256], nextmsg[256];
04307 
04308    if (format == NULL)
04309       format = "AdBY 'digits/at' IMp";
04310 
04311    ast_localtime(&time,&tm,timezone);
04312 
04313    for (offset=0 ; format[offset] != '\0' ; offset++) {
04314       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04315       switch (format[offset]) {
04316          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04317          case '\'':
04318             /* Literal name of a sound file */
04319             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04320                sndfile[sndoffset] = format[offset];
04321             }
04322             sndfile[sndoffset] = '\0';
04323             res = wait_file(chan,ints,sndfile,lang);
04324             break;
04325          case 'A':
04326          case 'a':
04327             /* Sunday - Saturday */
04328             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04329             res = wait_file(chan,ints,nextmsg,lang);
04330             break;
04331          case 'B':
04332          case 'b':
04333          case 'h':
04334             /* January - December */
04335             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04336             res = wait_file(chan,ints,nextmsg,lang);
04337             break;
04338          case 'm':
04339             /* First - Twelfth */
04340             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04341             res = wait_file(chan,ints,nextmsg,lang);
04342             break;
04343          case 'd':
04344          case 'e':
04345             /* First */
04346             if (tm.tm_mday == 1) {
04347                snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
04348                res = wait_file(chan,ints,nextmsg,lang);
04349             } else {
04350                res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
04351             }
04352             break;
04353          case 'Y':
04354             /* Year */
04355             if (tm.tm_year > 99) {
04356                res = wait_file(chan,ints, "digits/2",lang);
04357                if (!res) {
04358                   res = wait_file(chan,ints, "digits/thousand",lang);
04359                }
04360                if (tm.tm_year > 100) {
04361                   if (!res) {
04362                      res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
04363                   }
04364                }
04365             } else {
04366                if (tm.tm_year < 1) {
04367                   /* I'm not going to handle 1900 and prior */
04368                   /* We'll just be silent on the year, instead of bombing out. */
04369                } else {
04370                   res = wait_file(chan,ints, "digits/thousand",lang);
04371                   if (!res) {
04372                      wait_file(chan,ints, "digits/9",lang);
04373                      wait_file(chan,ints, "digits/hundred",lang);
04374                      res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
04375                   }
04376                }
04377             }
04378             break;
04379          case 'I':
04380          case 'l':
04381             /* 12-Hour */
04382             if (tm.tm_hour == 0)
04383                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04384             else if (tm.tm_hour > 12)
04385                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04386             else
04387                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04388             res = wait_file(chan,ints,nextmsg,lang);
04389             if (!res)
04390                res = wait_file(chan,ints, "digits/oclock",lang);
04391             break;
04392          case 'H':
04393          case 'k':
04394             /* 24-Hour */
04395             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
04396             if (!res)
04397                res = wait_file(chan,ints, "digits/oclock",lang);
04398             break;
04399          case 'M':
04400             /* Minute */
04401             if (tm.tm_min == 0) {
04402                break;
04403             }
04404             res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
04405             break;
04406          case 'P':
04407          case 'p':
04408             /* AM/PM */
04409             if (tm.tm_hour > 11)
04410                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
04411             else
04412                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
04413             res = wait_file(chan,ints,nextmsg,lang);
04414             break;
04415          case 'Q':
04416             /* Shorthand for "Today", "Yesterday", or AdBY */
04417             /* XXX As emphasized elsewhere, this should the native way in your
04418              * language to say the date, with changes in what you say, depending
04419              * upon how recent the date is. XXX */
04420             {
04421                struct timeval now;
04422                struct tm tmnow;
04423                time_t beg_today, tt;
04424 
04425                gettimeofday(&now,NULL);
04426                tt = now.tv_sec;
04427                ast_localtime(&tt,&tmnow,timezone);
04428                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04429                /* In any case, it saves not having to do ast_mktime() */
04430                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04431                if (beg_today < time) {
04432                   /* Today */
04433                   res = wait_file(chan,ints, "digits/today",lang);
04434                } else if (beg_today - 86400 < time) {
04435                   /* Yesterday */
04436                   res = wait_file(chan,ints, "digits/yesterday",lang);
04437                } else {
04438                   res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
04439                }
04440             }
04441             break;
04442          case 'q':
04443             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
04444             /* XXX As emphasized elsewhere, this should the native way in your
04445              * language to say the date, with changes in what you say, depending
04446              * upon how recent the date is. XXX */
04447             {
04448                struct timeval now;
04449                struct tm tmnow;
04450                time_t beg_today, tt;
04451 
04452                gettimeofday(&now,NULL);
04453                tt = now.tv_sec;
04454                ast_localtime(&tt,&tmnow,timezone);
04455                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04456                /* In any case, it saves not having to do ast_mktime() */
04457                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04458                if (beg_today < time) {
04459                   /* Today */
04460                } else if ((beg_today - 86400) < time) {
04461                   /* Yesterday */
04462                   res = wait_file(chan,ints, "digits/yesterday",lang);
04463                } else if (beg_today - 86400 * 6 < time) {
04464                   /* Within the last week */
04465                   res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
04466                } else {
04467                   res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
04468                }
04469             }
04470             break;
04471          case 'R':
04472             res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
04473             break;
04474          case 'S':
04475             /* Seconds */
04476             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
04477             if (!res) {
04478                res = wait_file(chan,ints, "digits/second",lang);
04479             }
04480             break;
04481          case 'T':
04482             res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
04483             break;
04484          case ' ':
04485          case '   ':
04486             /* Just ignore spaces and tabs */
04487             break;
04488          default:
04489             /* Unknown character */
04490             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04491       }
04492       /* Jump out on DTMF */
04493       if (res) {
04494          break;
04495       }
04496    }
04497    return res;
04498 }
04499 
04500 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)
04501 {
04502    struct tm tm;
04503    int res=0, offset, sndoffset;
04504    char sndfile[256], nextmsg[256];
04505 
04506    if (format == NULL)
04507       format = "AdB 'digits/at' IMp";
04508 
04509    ast_localtime(&time,&tm,timezone);
04510 
04511    for (offset=0 ; format[offset] != '\0' ; offset++) {
04512       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04513       switch (format[offset]) {
04514          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04515          case '\'':
04516             /* Literal name of a sound file */
04517             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04518                sndfile[sndoffset] = format[offset];
04519             }
04520             sndfile[sndoffset] = '\0';
04521             res = wait_file(chan,ints,sndfile,lang);
04522             break;
04523          case 'A':
04524          case 'a':
04525             /* Sunday - Saturday */
04526             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04527             res = wait_file(chan,ints,nextmsg,lang);
04528             break;
04529          case 'B':
04530          case 'b':
04531          case 'h':
04532             /* January - December */
04533             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04534             res = wait_file(chan,ints,nextmsg,lang);
04535             break;
04536          case 'm':
04537             /* First - Twelfth */
04538             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04539             res = wait_file(chan,ints,nextmsg,lang);
04540             break;
04541          case 'd':
04542          case 'e':
04543             /* First day of the month is spelled as ordinal */
04544             if (tm.tm_mday == 1) {
04545                snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
04546                res = wait_file(chan,ints,nextmsg,lang);
04547             } else {
04548                if (!res) {
04549                   res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
04550                }
04551             }
04552             break;
04553          case 'Y':
04554             /* Year */
04555             if (tm.tm_year > 99) {
04556                res = wait_file(chan,ints, "digits/ore-2000",lang);
04557                if (tm.tm_year > 100) {
04558                   if (!res) {
04559                   /* This works until the end of 2021 */
04560                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
04561                   res = wait_file(chan,ints,nextmsg,lang);
04562                   }
04563                }
04564             } else {
04565                if (tm.tm_year < 1) {
04566                   /* I'm not going to handle 1900 and prior */
04567                   /* We'll just be silent on the year, instead of bombing out. */
04568                } else {
04569                   res = wait_file(chan,ints, "digits/ore-1900",lang);
04570                   if ((!res) && (tm.tm_year != 0)) {
04571                      if (tm.tm_year <= 21) {
04572                         /* 1910 - 1921 */
04573                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
04574                         res = wait_file(chan,ints,nextmsg,lang);
04575                      } else {
04576                         /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
04577                         int ten, one;
04578                         ten = tm.tm_year / 10;
04579                         one = tm.tm_year % 10;
04580                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
04581                         res = wait_file(chan,ints,nextmsg,lang);
04582                         if (!res) {
04583                            if (one != 0) {
04584                               snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
04585                               res = wait_file(chan,ints,nextmsg,lang);
04586                            }
04587                         }
04588                      }
04589                   }
04590                }
04591             }
04592             break;
04593          case 'I':
04594          case 'l':
04595             /* 12-Hour */
04596             if (tm.tm_hour == 0)
04597                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04598             else if (tm.tm_hour > 12)
04599                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04600             else
04601                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04602                res = wait_file(chan,ints,nextmsg,lang);
04603             break;
04604          case 'H':
04605          case 'k':
04606             /* 24-Hour */
04607             if (tm.tm_hour == 0) {
04608                res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
04609             } else if (tm.tm_hour == 1) {
04610                res = wait_file(chan,ints, "digits/ore-una",lang);
04611             } else {
04612                res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04613             }
04614             break;
04615          case 'M':
04616             /* Minute */
04617             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
04618             break;
04619          case 'P':
04620          case 'p':
04621             /* AM/PM */
04622             if (tm.tm_hour > 11)
04623                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
04624             else
04625                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
04626                res = wait_file(chan,ints,nextmsg,lang);
04627             break;
04628          case 'Q':
04629             /* Shorthand for "Today", "Yesterday", or ABdY */
04630             /* XXX As emphasized elsewhere, this should the native way in your
04631              * language to say the date, with changes in what you say, depending
04632              * upon how recent the date is. XXX */
04633             {
04634                struct timeval now;
04635                struct tm tmnow;
04636                time_t beg_today, tt;
04637    
04638                gettimeofday(&now,NULL);
04639                tt = now.tv_sec;
04640                ast_localtime(&tt,&tmnow,timezone);
04641                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04642                /* In any case, it saves not having to do ast_mktime() */
04643                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04644                if (beg_today < time) {
04645                   /* Today */
04646                   res = wait_file(chan,ints, "digits/today",lang);
04647                } else if (beg_today - 86400 < time) {
04648                   /* Yesterday */
04649                   res = wait_file(chan,ints, "digits/yesterday",lang);
04650                } else {
04651                   res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
04652                }
04653             }
04654             break;
04655          case 'q':
04656             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04657             {
04658                struct timeval now;
04659                struct tm tmnow;
04660                time_t beg_today, tt;
04661    
04662                gettimeofday(&now,NULL);
04663                tt = now.tv_sec;
04664                ast_localtime(&tt,&tmnow,timezone);
04665                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04666                /* In any case, it saves not having to do ast_mktime() */
04667                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04668                if (beg_today < time) {
04669                   /* Today */
04670                } else if ((beg_today - 86400) < time) {
04671                   /* Yesterday */
04672                   res = wait_file(chan,ints, "digits/yesterday",lang);
04673                } else if (beg_today - 86400 * 6 < time) {
04674                   /* Within the last week */
04675                   res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
04676                } else {
04677                   res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
04678                }
04679             }
04680             break;
04681          case 'R':
04682             res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
04683             break;
04684          case 'S':
04685             /* Seconds */
04686             if (tm.tm_sec == 0) {
04687                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
04688                res = wait_file(chan,ints,nextmsg,lang);
04689             } else if (tm.tm_sec < 10) {
04690                res = wait_file(chan,ints, "digits/oh",lang);
04691                if (!res) {
04692                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
04693                   res = wait_file(chan,ints,nextmsg,lang);
04694                }
04695             } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
04696                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
04697                res = wait_file(chan,ints,nextmsg,lang);
04698             } else {
04699                int ten, one;
04700                ten = (tm.tm_sec / 10) * 10;
04701                one = (tm.tm_sec % 10);
04702                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
04703                res = wait_file(chan,ints,nextmsg,lang);
04704                if (!res) {
04705                   /* Fifty, not fifty-zero */
04706                   if (one != 0) {
04707                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
04708                      res = wait_file(chan,ints,nextmsg,lang);
04709                   }
04710                }
04711             }
04712               break;
04713          case 'T':
04714             res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
04715             break;
04716          case ' ':
04717          case '   ':
04718             /* Just ignore spaces and tabs */
04719             break;
04720          default:
04721             /* Unknown character */
04722             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04723       }
04724       /* Jump out on DTMF */
04725       if (res) {
04726          break;
04727       }
04728    }
04729    return res;
04730 }
04731 
04732 /* Dutch syntax */
04733 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)
04734 {
04735    struct tm tm;
04736    int res=0, offset, sndoffset;
04737    char sndfile[256], nextmsg[256];
04738 
04739    if (format == NULL)
04740       format = "ABdY 'digits/at' IMp";
04741 
04742    ast_localtime(&time,&tm,timezone);
04743 
04744    for (offset=0 ; format[offset] != '\0' ; offset++) {
04745       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04746       switch (format[offset]) {
04747          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04748          case '\'':
04749             /* Literal name of a sound file */
04750             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04751                sndfile[sndoffset] = format[offset];
04752             }
04753             sndfile[sndoffset] = '\0';
04754             res = wait_file(chan,ints,sndfile,lang);
04755             break;
04756          case 'A':
04757          case 'a':
04758             /* Sunday - Saturday */
04759             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04760             res = wait_file(chan,ints,nextmsg,lang);
04761             break;
04762          case 'B':
04763          case 'b':
04764          case 'h':
04765             /* January - December */
04766             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04767             res = wait_file(chan,ints,nextmsg,lang);
04768             break;
04769          case 'm':
04770             /* First - Twelfth */
04771             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04772             res = wait_file(chan,ints,nextmsg,lang);
04773             break;
04774          case 'd':
04775          case 'e':
04776             /* First - Thirtyfirst */
04777             res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
04778             break;
04779          case 'Y':
04780             /* Year */
04781             if (tm.tm_year > 99) {
04782                res = wait_file(chan,ints, "digits/2",lang);
04783                if (!res) {
04784                   res = wait_file(chan,ints, "digits/thousand",lang);
04785                }
04786                if (tm.tm_year > 100) {
04787                   if (!res) {
04788                      /* This works until the end of 2020 */
04789                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
04790                      res = wait_file(chan,ints,nextmsg,lang);
04791                   }
04792                }
04793             } else {
04794                if (tm.tm_year < 1) {
04795                   /* I'm not going to handle 1900 and prior */
04796                   /* We'll just be silent on the year, instead of bombing out. */
04797                } else {
04798                   res = wait_file(chan,ints, "digits/19",lang);
04799                   if (!res) {
04800                      if (tm.tm_year <= 9) {
04801                         /* 1901 - 1909 */
04802                         res = wait_file(chan,ints, "digits/oh",lang);
04803                         if (!res) {
04804                            snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
04805                            res = wait_file(chan,ints,nextmsg,lang);
04806                         }
04807                      } else if (tm.tm_year <= 20) {
04808                         /* 1910 - 1920 */
04809                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
04810                         res = wait_file(chan,ints,nextmsg,lang);
04811                      } else {
04812                         /* 1921 - 1999 */
04813                         int ten, one;
04814                         ten = tm.tm_year / 10;
04815                         one = tm.tm_year % 10;
04816                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
04817                         res = wait_file(chan,ints,nextmsg,lang);
04818                         if (!res) {
04819                            if (one != 0) {
04820                               snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
04821                               res = wait_file(chan,ints,nextmsg,lang);
04822                            }
04823                         }
04824                      }
04825                   }
04826                }
04827             }
04828             break;
04829          case 'I':
04830          case 'l':
04831             /* 12-Hour */
04832             if (tm.tm_hour == 0)
04833                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04834             else if (tm.tm_hour > 12)
04835                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04836             else
04837                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04838             res = wait_file(chan,ints,nextmsg,lang);
04839             break;
04840          case 'H':
04841          case 'k':
04842             /* 24-Hour */
04843             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04844             if (!res) {
04845                res = wait_file(chan,ints, "digits/nl-uur",lang);
04846             }
04847             break;
04848          case 'M':
04849             /* Minute */
04850             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
04851             break;
04852          case 'P':
04853          case 'p':
04854             /* AM/PM */
04855             if (tm.tm_hour > 11)
04856                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
04857             else
04858                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
04859             res = wait_file(chan,ints,nextmsg,lang);
04860             break;
04861          case 'Q':
04862             /* Shorthand for "Today", "Yesterday", or ABdY */
04863             /* XXX As emphasized elsewhere, this should the native way in your
04864              * language to say the date, with changes in what you say, depending
04865              * upon how recent the date is. XXX */
04866             {
04867                struct timeval now;
04868                struct tm tmnow;
04869                time_t beg_today, tt;
04870 
04871                gettimeofday(&now,NULL);
04872                tt = now.tv_sec;
04873                ast_localtime(&tt,&tmnow,timezone);
04874                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04875                /* In any case, it saves not having to do ast_mktime() */
04876                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04877                if (beg_today < time) {
04878                   /* Today */
04879                   res = wait_file(chan,ints, "digits/today",lang);
04880                } else if (beg_today - 86400 < time) {
04881                   /* Yesterday */
04882                   res = wait_file(chan,ints, "digits/yesterday",lang);
04883                } else {
04884                   res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
04885                }
04886             }
04887             break;
04888          case 'q':
04889             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04890             {
04891                struct timeval now;
04892                struct tm tmnow;
04893                time_t beg_today, tt;
04894 
04895                gettimeofday(&now,NULL);
04896                tt = now.tv_sec;
04897                ast_localtime(&tt,&tmnow,timezone);
04898                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04899                /* In any case, it saves not having to do ast_mktime() */
04900                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04901                if (beg_today < time) {
04902                   /* Today */
04903                } else if ((beg_today - 86400) < time) {
04904                   /* Yesterday */
04905                   res = wait_file(chan,ints, "digits/yesterday",lang);
04906                } else if (beg_today - 86400 * 6 < time) {
04907                   /* Within the last week */
04908                   res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
04909                } else {
04910                   res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
04911                }
04912             }
04913             break;
04914          case 'R':
04915             res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
04916             break;
04917          case 'S':
04918             /* Seconds */
04919             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
04920             break;
04921          case 'T':
04922             res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
04923             break;
04924          case ' ':
04925          case '   ':
04926             /* Just ignore spaces and tabs */
04927             break;
04928          default:
04929             /* Unknown character */
04930             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04931       }
04932       /* Jump out on DTMF */
04933       if (res) {
04934          break;
04935       }
04936    }
04937    return res;
04938 }
04939 
04940 /* Polish syntax */
04941 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)
04942 {
04943    struct tm tm;
04944    int res=0, offset, sndoffset;
04945    char sndfile[256], nextmsg[256];
04946 
04947    ast_localtime(&thetime, &tm, timezone);
04948 
04949    for (offset = 0 ; format[offset] != '\0' ; offset++) {
04950       int remainder;
04951       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04952       switch (format[offset]) {
04953          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04954          case '\'':
04955             /* Literal name of a sound file */
04956             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04957                sndfile[sndoffset] = format[offset];
04958             }
04959             sndfile[sndoffset] = '\0';
04960             res = wait_file(chan, ints, sndfile, lang);
04961             break;
04962          case 'A':
04963          case 'a':
04964             /* Sunday - Saturday */
04965             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04966             res = wait_file(chan, ints, nextmsg, lang);
04967             break;
04968          case 'B':
04969          case 'b':
04970          case 'h':
04971             /* January - December */
04972             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04973             res = wait_file(chan, ints, nextmsg, lang);
04974             break;
04975          case 'm':
04976             /* Month enumerated */
04977             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
04978             break;
04979          case 'd':
04980          case 'e':
04981             /* First - Thirtyfirst */
04982             remainder = tm.tm_mday;
04983             if (tm.tm_mday > 30) {
04984                res = wait_file(chan, ints, "digits/h-30", lang);
04985                remainder -= 30;
04986             }
04987             if (tm.tm_mday > 20 && tm.tm_mday < 30) {
04988                res = wait_file(chan, ints, "digits/h-20", lang);
04989                remainder -= 20;
04990             }
04991             if (!res) {
04992                snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
04993                res = wait_file(chan, ints, nextmsg, lang);
04994             }
04995             break;
04996          case 'Y':
04997             /* Year */
04998             if (tm.tm_year > 100) {
04999                res = wait_file(chan, ints, "digits/2", lang);
05000                if (!res)
05001                   res = wait_file(chan, ints, "digits/1000.2",lang);
05002                if (tm.tm_year > 100) {
05003                   if (!res)
05004                      res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
05005                }
05006             } else if (tm.tm_year == 100) {
05007                res = wait_file(chan, ints, "digits/h-2000", lang);
05008             } else {
05009                if (tm.tm_year < 1) {
05010                   /* I'm not going to handle 1900 and prior */
05011                   /* We'll just be silent on the year, instead of bombing out. */
05012                   break;
05013                } else {
05014                   res = wait_file(chan, ints, "digits/1000", lang);
05015                   if (!res) {
05016                      wait_file(chan, ints, "digits/900", lang);
05017                      res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
05018                   }
05019                }
05020             }
05021             if (!res)
05022                wait_file(chan, ints, "digits/year", lang);
05023             break;
05024          case 'I':
05025          case 'l':
05026             /* 12-Hour */
05027             if (tm.tm_hour == 0)
05028                snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
05029             else if (tm.tm_hour > 12)
05030                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
05031             else 
05032                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
05033 
05034             res = wait_file(chan, ints, nextmsg, lang);
05035             break;
05036          case 'H':
05037          case 'k':
05038             /* 24-Hour */
05039             if (tm.tm_hour != 0) {
05040                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
05041                res = wait_file(chan, ints, nextmsg, lang);
05042             } else 
05043                res = wait_file(chan, ints, "digits/t-24", lang);
05044             break;
05045          case 'M':
05046          case 'N':
05047             /* Minute */
05048             if (tm.tm_min == 0) {
05049                if (format[offset] == 'M') {
05050                   res = wait_file(chan, ints, "digits/oclock", lang);
05051                } else {
05052                   res = wait_file(chan, ints, "digits/100", lang);
05053                }
05054             } else
05055                res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); 
05056             break;
05057          case 'P':
05058          case 'p':
05059             /* AM/PM */
05060             if (tm.tm_hour > 11)
05061                snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
05062             else
05063                snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
05064             res = wait_file(chan, ints, nextmsg, lang);
05065             break;
05066          case 'Q':
05067             /* Shorthand for "Today", "Yesterday", or AdBY */
05068             {
05069                time_t tv_sec = time(NULL);
05070                struct tm tmnow;
05071                time_t beg_today;
05072 
05073                ast_localtime(&tv_sec,&tmnow, timezone);
05074                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05075                /* In any case, it saves not having to do ast_mktime() */
05076                beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05077                if (beg_today < thetime) {
05078                   /* Today */
05079                   res = wait_file(chan, ints, "digits/today", lang);
05080                } else if (beg_today - 86400 < thetime) {
05081                   /* Yesterday */
05082                   res = wait_file(chan, ints, "digits/yesterday", lang);
05083                } else {
05084                   res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
05085                }
05086             }
05087             break;
05088          case 'q':
05089             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
05090             {
05091                time_t tv_sec = time(NULL);
05092                struct tm tmnow;
05093                time_t beg_today;
05094 
05095                ast_localtime(&tv_sec, &tmnow, timezone);
05096                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05097                /* In any case, it saves not having to do ast_mktime() */
05098                beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05099                if (beg_today < thetime) {
05100                   /* Today */
05101                } else if ((beg_today - 86400) < thetime) {
05102                   /* Yesterday */
05103                   res = wait_file(chan, ints, "digits/yesterday", lang);
05104                } else if (beg_today - 86400 * 6 < thetime) {
05105                   /* Within the last week */
05106                   res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
05107                } else {
05108                   res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
05109                }
05110             }
05111             break;
05112          case 'R':
05113             res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
05114             break;
05115          case 'S':
05116             /* Seconds */
05117             res = wait_file(chan, ints, "digits/and", lang);
05118             if (!res) {
05119                if (tm.tm_sec == 1) {
05120                   res = wait_file(chan, ints, "digits/1z", lang);
05121                   if (!res)
05122                      res = wait_file(chan, ints, "digits/second-a", lang);
05123                } else {
05124                   res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
05125                   if (!res) {
05126                      int ten, one;
05127                      ten = tm.tm_sec / 10;
05128                      one = tm.tm_sec % 10;
05129                      
05130                      if (one > 1 && one < 5 && ten != 1)
05131                         res = wait_file(chan,ints, "digits/seconds",lang);
05132                      else
05133                         res = wait_file(chan,ints, "digits/second",lang);
05134                   }
05135                }
05136             }
05137             break;
05138          case 'T':
05139             res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
05140             break;
05141          case ' ':
05142          case '   ':
05143             /* Just ignore spaces and tabs */
05144             break;
05145          default:
05146             /* Unknown character */
05147             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05148       }
05149       /* Jump out on DTMF */
05150       if (res)
05151          break;
05152    }
05153    return res;
05154 }
05155 
05156 /* Portuguese syntax */
05157 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)
05158 {
05159    struct tm tm;
05160    int res=0, offset, sndoffset;
05161    char sndfile[256], nextmsg[256];
05162 
05163    if (format == NULL)
05164       format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
05165 
05166    ast_localtime(&time,&tm,timezone);
05167 
05168    for (offset=0 ; format[offset] != '\0' ; offset++) {
05169       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05170       switch (format[offset]) {
05171          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05172          case '\'':
05173             /* Literal name of a sound file */
05174             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05175                sndfile[sndoffset] = format[offset];
05176             }
05177             sndfile[sndoffset] = '\0';
05178             snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
05179             res = wait_file(chan,ints,nextmsg,lang);
05180             break;
05181          case 'A':
05182          case 'a':
05183             /* Sunday - Saturday */
05184             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05185             res = wait_file(chan,ints,nextmsg,lang);
05186             break;
05187          case 'B':
05188          case 'b':
05189          case 'h':
05190             /* January - December */
05191             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05192             res = wait_file(chan,ints,nextmsg,lang);
05193             break;
05194          case 'm':
05195             /* First - Twelfth */
05196             if (!strcasecmp(lang, "pt_BR")) {
05197                res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
05198             } else {
05199                snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
05200                res = wait_file(chan,ints,nextmsg,lang);
05201             }
05202             break;
05203          case 'd':
05204          case 'e':
05205             /* First - Thirtyfirst */
05206             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
05207             break;
05208          case 'Y':
05209             /* Year */
05210             res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
05211             break;
05212          case 'I':
05213          case 'l':
05214             /* 12-Hour */
05215             if (tm.tm_hour == 0) {
05216                if (format[offset] == 'I')
05217                   res = wait_file(chan, ints, "digits/pt-a", lang);
05218                if (!res)
05219                   res = wait_file(chan, ints, "digits/pt-meianoite", lang);
05220             } else if (tm.tm_hour == 12) {
05221                if (format[offset] == 'I')
05222                   res = wait_file(chan, ints, "digits/pt-ao", lang);
05223                if (!res)
05224                   res = wait_file(chan, ints, "digits/pt-meiodia", lang);
05225             } else {
05226                if (format[offset] == 'I') {
05227                   if ((tm.tm_hour % 12) != 1)
05228                      res = wait_file(chan, ints, "digits/pt-as", lang);
05229                   else
05230                      res = wait_file(chan, ints, "digits/pt-a", lang);
05231                }
05232                if (!res)
05233                   res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
05234             }
05235             break;
05236          case 'H':
05237          case 'k':
05238             /* 24-Hour */
05239             res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05240             if ((!res) && (format[offset] == 'H')) {
05241                if (tm.tm_hour > 1) {
05242                   res = wait_file(chan,ints,"digits/hours",lang);
05243                } else {
05244                   res = wait_file(chan,ints,"digits/hour",lang);
05245                }
05246             }
05247             break;
05248          case 'M':
05249             /* Minute */
05250             res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
05251             if (!res) {
05252                if (tm.tm_min > 1) {
05253                   res = wait_file(chan,ints,"digits/minutes",lang);
05254                } else {
05255                   res = wait_file(chan,ints,"digits/minute",lang);
05256                }
05257             }
05258             break;
05259          case 'P':
05260          case 'p':
05261             /* AM/PM */
05262             if (!strcasecmp(lang, "pt_BR")) {
05263                if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
05264                   res = wait_file(chan, ints, "digits/pt-da", lang);
05265                   if (!res) {
05266                      if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
05267                         res = wait_file(chan, ints, "digits/morning", lang);
05268                      else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
05269                         res = wait_file(chan, ints, "digits/afternoon", lang);
05270                      else res = wait_file(chan, ints, "digits/night", lang);
05271                   }
05272                }
05273             } else {
05274                if (tm.tm_hour > 12)
05275                   res = wait_file(chan, ints, "digits/p-m", lang);
05276                else if (tm.tm_hour  && tm.tm_hour < 12)
05277                   res = wait_file(chan, ints, "digits/a-m", lang);
05278             }
05279             break;
05280          case 'Q':
05281             /* Shorthand for "Today", "Yesterday", or ABdY */
05282             /* XXX As emphasized elsewhere, this should the native way in your
05283              * language to say the date, with changes in what you say, depending
05284              * upon how recent the date is. XXX */
05285             {
05286                struct timeval now;
05287                struct tm tmnow;
05288                time_t beg_today, tt;
05289 
05290                gettimeofday(&now,NULL);
05291                tt = now.tv_sec;
05292                ast_localtime(&tt,&tmnow,timezone);
05293                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05294                /* In any case, it saves not having to do ast_mktime() */
05295                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05296                if (beg_today < time) {
05297                   /* Today */
05298                   res = wait_file(chan,ints, "digits/today",lang);
05299                } else if (beg_today - 86400 < time) {
05300                   /* Yesterday */
05301                   res = wait_file(chan,ints, "digits/yesterday",lang);
05302                } else {
05303                   res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
05304                }
05305             }
05306             break;
05307          case 'q':
05308             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
05309             /* XXX As emphasized elsewhere, this should the native way in your
05310              * language to say the date, with changes in what you say, depending
05311              * upon how recent the date is. XXX */
05312             {
05313                struct timeval now;
05314                struct tm tmnow;
05315                time_t beg_today, tt;
05316 
05317                gettimeofday(&now,NULL);
05318                tt = now.tv_sec;
05319                ast_localtime(&tt,&tmnow,timezone);
05320                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05321                /* In any case, it saves not having to do ast_mktime() */
05322                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05323                if (beg_today < time) {
05324                   /* Today */
05325                } else if ((beg_today - 86400) < time) {
05326                   /* Yesterday */
05327                   res = wait_file(chan,ints, "digits/yesterday",lang);
05328                } else if (beg_today - 86400 * 6 < time) {
05329                   /* Within the last week */
05330                   res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
05331                } else {
05332                   res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
05333                }
05334             }
05335             break;
05336          case 'R':
05337             res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/and' M", timezone);
05338             break;
05339          case 'S':
05340             /* Seconds */
05341             res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
05342             if (!res) {
05343                if (tm.tm_sec > 1) {
05344                   res = wait_file(chan,ints,"digits/seconds",lang);
05345                } else {
05346                   res = wait_file(chan,ints,"digits/second",lang);
05347                }
05348             }
05349             break;
05350          case 'T':
05351             res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
05352             break;
05353          case ' ':
05354          case '   ':
05355             /* Just ignore spaces and tabs */
05356             break;
05357          default:
05358             /* Unknown character */
05359             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05360       }
05361       /* Jump out on DTMF */
05362       if (res) {
05363          break;
05364       }
05365    }
05366    return res;
05367 }
05368 
05369 /* Taiwanese / Chinese syntax */
05370 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)
05371 {
05372    struct tm tm;
05373    int res=0, offset, sndoffset;
05374    char sndfile[256], nextmsg[256];
05375 
05376    if (format == NULL)
05377       format = "YBdAkM";
05378 
05379    ast_localtime(&time,&tm,timezone);
05380 
05381    for (offset=0 ; format[offset] != '\0' ; offset++) {
05382       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05383       switch (format[offset]) {
05384          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05385          case '\'':
05386             /* Literal name of a sound file */
05387             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05388                sndfile[sndoffset] = format[offset];
05389             }
05390             sndfile[sndoffset] = '\0';
05391             res = wait_file(chan,ints,sndfile,lang);
05392             break;
05393          case 'A':
05394          case 'a':
05395             /* Sunday - Saturday */
05396             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05397             res = wait_file(chan,ints,nextmsg,lang);
05398             break;
05399          case 'B':
05400          case 'b':
05401          case 'h':
05402          case 'm':
05403             /* January - December */
05404             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05405             res = wait_file(chan,ints,nextmsg,lang);
05406             break;
05407          case 'd':
05408          case 'e':
05409             /* First - Thirtyfirst */
05410             if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
05411                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
05412                res = wait_file(chan,ints,nextmsg,lang);
05413             } else {
05414                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
05415                res = wait_file(chan,ints,nextmsg,lang);
05416                if (!res) {
05417                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
05418                   res = wait_file(chan,ints,nextmsg,lang);
05419                }
05420             }
05421             if (!res) res = wait_file(chan,ints,"digits/day",lang);
05422             break;
05423          case 'Y':
05424             /* Year */
05425             if (tm.tm_year > 99) {
05426                res = wait_file(chan,ints, "digits/2",lang);
05427                if (!res) {
05428                   res = wait_file(chan,ints, "digits/thousand",lang);
05429                }
05430                if (tm.tm_year > 100) {
05431                   if (!res) {
05432                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
05433                      res = wait_file(chan,ints,nextmsg,lang);
05434                      if (!res) {
05435                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
05436                         res = wait_file(chan,ints,nextmsg,lang);
05437                      }
05438                   }
05439                }
05440                if (!res) {
05441                   res = wait_file(chan,ints, "digits/year",lang);
05442                }
05443             } else {
05444                if (tm.tm_year < 1) {
05445                   /* I'm not going to handle 1900 and prior */
05446                   /* We'll just be silent on the year, instead of bombing out. */
05447                } else {
05448                   res = wait_file(chan,ints, "digits/1",lang);
05449                   if (!res) {
05450                      res = wait_file(chan,ints, "digits/9",lang);
05451                   }
05452                   if (!res) {
05453                      if (tm.tm_year <= 9) {
05454                         /* 1901 - 1909 */
05455                         res = wait_file(chan,ints, "digits/0",lang);
05456                         if (!res) {
05457                            snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
05458                            res = wait_file(chan,ints,nextmsg,lang);
05459                         }
05460                      } else {
05461                         /* 1910 - 1999 */
05462                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
05463                         res = wait_file(chan,ints,nextmsg,lang);
05464                         if (!res) {
05465                            snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
05466                            res = wait_file(chan,ints,nextmsg,lang);
05467                         }
05468                      }
05469                   }
05470                }
05471                if (!res) {
05472                   res = wait_file(chan,ints, "digits/year",lang);
05473                }
05474             }
05475             break;
05476          case 'I':
05477          case 'l':
05478             /* 12-Hour */
05479             if (tm.tm_hour == 0)
05480                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
05481             else if (tm.tm_hour > 12)
05482                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
05483             else
05484                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
05485             res = wait_file(chan,ints,nextmsg,lang);
05486             if (!res) {
05487                res = wait_file(chan,ints, "digits/oclock",lang);
05488             }
05489             break;
05490          case 'H':
05491                 if (tm.tm_hour < 10) {
05492                     res = wait_file(chan, ints, "digits/0", lang);
05493                 }
05494          case 'k':
05495             /* 24-Hour */
05496             if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
05497                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
05498                res = wait_file(chan,ints,nextmsg,lang);
05499             } else {
05500                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
05501                res = wait_file(chan,ints,nextmsg,lang);
05502                if (!res) {
05503                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
05504                   res = wait_file(chan,ints,nextmsg,lang);
05505                }
05506             }
05507             if (!res) {
05508                res = wait_file(chan,ints, "digits/oclock",lang);
05509             }
05510             break;
05511          case 'M':
05512             /* Minute */
05513             if (!(tm.tm_min % 10) || tm.tm_min < 10) {
05514                if (tm.tm_min < 10) {
05515                   res = wait_file(chan, ints, "digits/0", lang);
05516                }
05517                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
05518                res = wait_file(chan,ints,nextmsg,lang);
05519             } else {
05520                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
05521                res = wait_file(chan,ints,nextmsg,lang);
05522                if (!res) {
05523                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
05524                   res = wait_file(chan,ints,nextmsg,lang);
05525                }
05526             }
05527             if (!res) {
05528                res = wait_file(chan,ints, "digits/minute",lang);
05529             }
05530             break;
05531          case 'P':
05532          case 'p':
05533             /* AM/PM */
05534             if (tm.tm_hour > 11)
05535                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
05536             else
05537                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
05538             res = wait_file(chan,ints,nextmsg,lang);
05539             break;
05540          case 'Q':
05541             /* Shorthand for "Today", "Yesterday", or ABdY */
05542             /* XXX As emphasized elsewhere, this should the native way in your
05543              * language to say the date, with changes in what you say, depending
05544              * upon how recent the date is. XXX */
05545             {
05546                struct timeval now;
05547                struct tm tmnow;
05548                time_t beg_today, tt;
05549 
05550                gettimeofday(&now,NULL);
05551                tt = now.tv_sec;
05552                ast_localtime(&tt,&tmnow,timezone);
05553                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05554                /* In any case, it saves not having to do ast_mktime() */
05555                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05556                if (beg_today < time) {
05557                   /* Today */
05558                   res = wait_file(chan,ints, "digits/today",lang);
05559                } else if (beg_today - 86400 < time) {
05560                   /* Yesterday */
05561                   res = wait_file(chan,ints, "digits/yesterday",lang);
05562                } else {
05563                   res = ast_say_date_with_format_zh(chan, time, ints, lang, "YBdA", timezone);
05564                }
05565             }
05566             break;
05567          case 'q':
05568             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
05569             /* XXX As emphasized elsewhere, this should the native way in your
05570              * language to say the date, with changes in what you say, depending
05571              * upon how recent the date is. XXX */
05572             {
05573                struct timeval now;
05574                struct tm tmnow;
05575                time_t beg_today, tt;
05576 
05577                gettimeofday(&now,NULL);
05578                tt = now.tv_sec;
05579                ast_localtime(&tt,&tmnow,timezone);
05580                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05581                /* In any case, it saves not having to do ast_mktime() */
05582                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05583                if (beg_today < time) {
05584                   /* Today */
05585                } else if ((beg_today - 86400) < time) {
05586                   /* Yesterday */
05587                   res = wait_file(chan,ints, "digits/yesterday",lang);
05588                } else if (beg_today - 86400 * 6 < time) {
05589                   /* Within the last week */
05590                   res = ast_say_date_with_format_zh(chan, time, ints, lang, "A", timezone);
05591                } else {
05592                   res = ast_say_date_with_format_zh(chan, time, ints, lang, "YBdA", timezone);
05593                }
05594             }
05595             break;
05596          case 'R':
05597             res = ast_say_date_with_format_zh(chan, time, ints, lang, "kM", timezone);
05598             break;
05599          case 'S':
05600             /* Seconds */
05601             if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
05602                if (tm.tm_sec < 10) {
05603                   res = wait_file(chan, ints, "digits/0", lang);
05604                }
05605                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
05606                res = wait_file(chan,ints,nextmsg,lang);
05607             } else {
05608                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
05609                res = wait_file(chan,ints,nextmsg,lang);
05610                if (!res) {
05611                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
05612                   res = wait_file(chan,ints,nextmsg,lang);
05613                }
05614             }
05615             if (!res) {
05616                res = wait_file(chan,ints, "digits/second",lang);
05617             }
05618             break;
05619          case 'T':
05620             res = ast_say_date_with_format_zh(chan, time, ints, lang, "HMS", timezone);
05621             break;
05622          case ' ':
05623          case '   ':
05624             /* Just ignore spaces and tabs */
05625          break;
05626          default:
05627             /* Unknown character */
05628             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05629       }
05630       /* Jump out on DTMF */
05631       if (res) {
05632          break;
05633       }
05634    }
05635    return res;
05636 }
05637 
05638 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05639 {
05640    if (!strncasecmp(lang, "en", 2)) {  /* English syntax */
05641       return ast_say_time_en(chan, t, ints, lang);
05642    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
05643       return ast_say_time_de(chan, t, ints, lang);
05644    } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
05645       return(ast_say_time_es(chan, t, ints, lang));
05646    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
05647       return ast_say_time_fr(chan, t, ints, lang);
05648    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
05649       static int deprecation_warning = 0;
05650       if (deprecation_warning++ % 10 == 0) {
05651          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
05652       }
05653       return ast_say_time_ka(chan, t, ints, lang);
05654    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
05655       return ast_say_time_gr(chan, t, ints, lang);
05656    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
05657       return ast_say_time_he(chan, t, ints, lang);
05658    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
05659       return ast_say_time_ka(chan, t, ints, lang);
05660    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
05661       return ast_say_time_nl(chan, t, ints, lang);
05662    } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
05663       return ast_say_time_pt_BR(chan, t, ints, lang);
05664    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
05665       return ast_say_time_pt(chan, t, ints, lang);
05666    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
05667       static int deprecation_warning = 0;
05668       if (deprecation_warning++ % 10 == 0) {
05669          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
05670       }
05671       return ast_say_time_zh(chan, t, ints, lang);
05672    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
05673       return ast_say_time_zh(chan, t, ints, lang);
05674    }
05675 
05676    /* Default to English */
05677    return ast_say_time_en(chan, t, ints, lang);
05678 }
05679 
05680 /* English syntax */
05681 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05682 {
05683    struct tm tm;
05684    int res = 0;
05685    int hour, pm=0;
05686 
05687    ast_localtime(&t, &tm, NULL);
05688    hour = tm.tm_hour;
05689    if (!hour)
05690       hour = 12;
05691    else if (hour == 12)
05692       pm = 1;
05693    else if (hour > 12) {
05694       hour -= 12;
05695       pm = 1;
05696    }
05697    if (!res)
05698       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
05699    if (tm.tm_min > 9) {
05700       if (!res)
05701          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05702    } else if (tm.tm_min) {
05703       if (!res)
05704          res = ast_streamfile(chan, "digits/oh", lang);
05705       if (!res)
05706          res = ast_waitstream(chan, ints);
05707       if (!res)
05708          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05709    } else {
05710       if (!res)
05711          res = ast_streamfile(chan, "digits/oclock", lang);
05712       if (!res)
05713          res = ast_waitstream(chan, ints);
05714    }
05715    if (pm) {
05716       if (!res)
05717          res = ast_streamfile(chan, "digits/p-m", lang);
05718    } else {
05719       if (!res)
05720          res = ast_streamfile(chan, "digits/a-m", lang);
05721    }
05722    if (!res)
05723       res = ast_waitstream(chan, ints);
05724    return res;
05725 }
05726 
05727 /* German syntax */
05728 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05729 {
05730    struct tm tm;
05731    int res = 0;
05732 
05733    ast_localtime(&t, &tm, NULL);
05734    if (!res)
05735       res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
05736    if (!res)
05737       res = ast_streamfile(chan, "digits/oclock", lang);
05738    if (!res)
05739       res = ast_waitstream(chan, ints);
05740    if (!res)
05741        if (tm.tm_min > 0) 
05742       res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
05743    return res;
05744 }
05745 
05746 /* Spanish syntax */
05747 int ast_say_time_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05748 {
05749    struct tm tm;
05750    int res = 0;
05751    ast_localtime(&t, &tm, NULL);
05752    
05753    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05754    if (!res) {
05755       if (tm.tm_hour != 1)
05756          res = wait_file(chan, ints, "digits/hours", lang);
05757       else
05758          res = wait_file(chan, ints, "digits/hour", lang);
05759    }
05760    if ((!res) && (tm.tm_min)) {
05761       res = wait_file(chan, ints, "digits/and", lang);
05762       if (!res)
05763          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05764       if (!res) {
05765          if (tm.tm_min > 1)
05766             res = wait_file(chan, ints, "digits/minutes", lang);
05767          else
05768             res = wait_file(chan, ints, "digits/minute", lang);
05769       }
05770    }
05771    return res;
05772 }
05773 
05774 /* French syntax */
05775 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05776 {
05777    struct tm tm;
05778    int res = 0;
05779 
05780    ast_localtime(&t, &tm, NULL);
05781 
05782    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05783    if (!res)
05784       res = ast_streamfile(chan, "digits/oclock", lang);
05785    if (tm.tm_min) {
05786       if (!res)
05787       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05788    }
05789    return res;
05790 }
05791 
05792 /* Dutch syntax */
05793 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05794 {
05795    struct tm tm;
05796    int res = 0;
05797 
05798    ast_localtime(&t, &tm, NULL);
05799    if (!res)
05800       res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
05801    if (!res)
05802       res = ast_streamfile(chan, "digits/nl-uur", lang);
05803    if (!res)
05804       res = ast_waitstream(chan, ints);
05805    if (!res)
05806        if (tm.tm_min > 0) 
05807       res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
05808    return res;
05809 }
05810 
05811 /* Portuguese syntax */
05812 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05813 {
05814    struct tm tm;
05815    int res = 0;
05816    int hour;
05817 
05818    ast_localtime(&t, &tm, NULL);
05819    hour = tm.tm_hour;
05820    if (!res)
05821       res = ast_say_number(chan, hour, ints, lang, "f");
05822    if (tm.tm_min) {
05823       if (!res)
05824          res = wait_file(chan, ints, "digits/and", lang);
05825       if (!res)
05826          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05827    } else {
05828       if (!res) {
05829          if (tm.tm_hour == 1)
05830             res = wait_file(chan, ints, "digits/hour", lang);
05831          else
05832             res = wait_file(chan, ints, "digits/hours", lang);
05833       }
05834    }
05835    if (!res)
05836       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
05837    return res;
05838 }
05839 
05840 /* Brazilian Portuguese syntax */
05841 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05842 {
05843    struct tm tm;
05844    int res = 0;
05845 
05846    ast_localtime(&t, &tm, NULL);
05847 
05848    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05849    if (!res) {
05850       if (tm.tm_hour > 1)
05851          res = wait_file(chan, ints, "digits/hours", lang);
05852       else
05853          res = wait_file(chan, ints, "digits/hour", lang);
05854    }
05855    if ((!res) && (tm.tm_min)) {
05856       res = wait_file(chan, ints, "digits/and", lang);
05857       if (!res)
05858          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05859       if (!res) {
05860          if (tm.tm_min > 1)
05861             res = wait_file(chan, ints, "digits/minutes", lang);
05862          else
05863             res = wait_file(chan, ints, "digits/minute", lang);
05864       }
05865    }
05866    return res;
05867 }
05868 
05869 /* Taiwanese / Chinese  syntax */
05870 int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05871 {
05872    struct tm tm;
05873    int res = 0;
05874    int hour, pm=0;
05875 
05876    ast_localtime(&t, &tm, NULL);
05877    hour = tm.tm_hour;
05878    if (!hour)
05879       hour = 12;
05880    else if (hour == 12)
05881       pm = 1;
05882    else if (hour > 12) {
05883       hour -= 12;
05884       pm = 1;
05885    }
05886    if (pm) {
05887       if (!res)
05888          res = ast_streamfile(chan, "digits/p-m", lang);
05889    } else {
05890       if (!res)
05891          res = ast_streamfile(chan, "digits/a-m", lang);
05892    }
05893    if (!res)
05894       res = ast_waitstream(chan, ints);
05895    if (!res)
05896       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
05897    if (!res)
05898       res = ast_streamfile(chan, "digits/oclock", lang);
05899    if (!res)
05900       res = ast_waitstream(chan, ints);
05901    if (!res)
05902       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05903    if (!res)
05904       res = ast_streamfile(chan, "digits/minute", lang);
05905    if (!res)
05906       res = ast_waitstream(chan, ints);
05907    return res;
05908 }
05909 
05910 /* Hebrew syntax */
05911 int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05912 {
05913    struct tm tm;
05914    int res = 0;
05915    int hour;
05916 
05917    ast_localtime(&t, &tm, NULL);
05918    hour = tm.tm_hour;
05919    if (!hour)
05920       hour = 12;
05921 
05922    if (!res)
05923       res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
05924 
05925    if (tm.tm_min > 9) {
05926       if (!res)
05927          res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
05928    } else if (tm.tm_min) {
05929       if (!res) {          /* say a leading zero if needed */
05930          res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
05931       }
05932       if (!res)
05933          res = ast_waitstream(chan, ints);
05934       if (!res)
05935          res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
05936    } else {
05937       if (!res)
05938          res = ast_waitstream(chan, ints);
05939    }
05940    if (!res)
05941       res = ast_waitstream(chan, ints);
05942    return res;
05943 }
05944 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05945 {
05946    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
05947       return ast_say_datetime_en(chan, t, ints, lang);
05948    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
05949       return ast_say_datetime_de(chan, t, ints, lang);
05950    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
05951       return ast_say_datetime_fr(chan, t, ints, lang);
05952    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
05953       static int deprecation_warning = 0;
05954       if (deprecation_warning++ % 10 == 0) {
05955          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
05956       }
05957       return ast_say_datetime_ka(chan, t, ints, lang);
05958    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
05959       return ast_say_datetime_gr(chan, t, ints, lang);
05960    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
05961       return ast_say_datetime_he(chan, t, ints, lang);
05962    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
05963       return ast_say_datetime_ka(chan, t, ints, lang);
05964    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
05965       return ast_say_datetime_nl(chan, t, ints, lang);
05966    } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
05967       return ast_say_datetime_pt_BR(chan, t, ints, lang);
05968    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
05969       return ast_say_datetime_pt(chan, t, ints, lang);
05970    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
05971       static int deprecation_warning = 0;
05972       if (deprecation_warning++ % 10 == 0) {
05973          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
05974       }
05975       return ast_say_datetime_zh(chan, t, ints, lang);
05976    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
05977       return ast_say_datetime_zh(chan, t, ints, lang);
05978    }
05979 
05980    /* Default to English */
05981    return ast_say_datetime_en(chan, t, ints, lang);
05982 }
05983 
05984 /* English syntax */
05985 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05986 {
05987    struct tm tm;
05988    char fn[256];
05989    int res = 0;
05990    int hour, pm=0;
05991 
05992    ast_localtime(&t, &tm, NULL);
05993    if (!res) {
05994       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
05995       res = ast_streamfile(chan, fn, lang);
05996       if (!res)
05997          res = ast_waitstream(chan, ints);
05998    }
05999    if (!res) {
06000       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06001       res = ast_streamfile(chan, fn, lang);
06002       if (!res)
06003          res = ast_waitstream(chan, ints);
06004    }
06005    if (!res)
06006       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06007 
06008    hour = tm.tm_hour;
06009    if (!hour)
06010       hour = 12;
06011    else if (hour == 12)
06012       pm = 1;
06013    else if (hour > 12) {
06014       hour -= 12;
06015       pm = 1;
06016    }
06017    if (!res)
06018       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06019 
06020    if (tm.tm_min > 9) {
06021       if (!res)
06022          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06023    } else if (tm.tm_min) {
06024       if (!res)
06025          res = ast_streamfile(chan, "digits/oh", lang);
06026       if (!res)
06027          res = ast_waitstream(chan, ints);
06028       if (!res)
06029          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06030    } else {
06031       if (!res)
06032          res = ast_streamfile(chan, "digits/oclock", lang);
06033       if (!res)
06034          res = ast_waitstream(chan, ints);
06035    }
06036    if (pm) {
06037       if (!res)
06038          res = ast_streamfile(chan, "digits/p-m", lang);
06039    } else {
06040       if (!res)
06041          res = ast_streamfile(chan, "digits/a-m", lang);
06042    }
06043    if (!res)
06044       res = ast_waitstream(chan, ints);
06045    if (!res)
06046       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06047    return res;
06048 }
06049 
06050 /* German syntax */
06051 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06052 {
06053    struct tm tm;
06054    int res = 0;
06055 
06056    ast_localtime(&t, &tm, NULL);
06057    res = ast_say_date(chan, t, ints, lang);
06058    if (!res) 
06059       ast_say_time(chan, t, ints, lang);
06060    return res;
06061 
06062 }
06063 
06064 /* French syntax */
06065 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06066 {
06067    struct tm tm;
06068    char fn[256];
06069    int res = 0;
06070 
06071    ast_localtime(&t, &tm, NULL);
06072 
06073    if (!res)
06074       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06075 
06076    if (!res) {
06077       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06078       res = ast_streamfile(chan, fn, lang);
06079       if (!res)
06080          res = ast_waitstream(chan, ints);
06081    }
06082    if (!res) {
06083       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06084       res = ast_streamfile(chan, fn, lang);
06085       if (!res)
06086          res = ast_waitstream(chan, ints);
06087    }
06088 
06089    if (!res)
06090       res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
06091    if (!res)
06092          res = ast_streamfile(chan, "digits/oclock", lang);
06093    if (tm.tm_min > 0) {
06094       if (!res)
06095          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06096    } 
06097    if (!res)
06098       res = ast_waitstream(chan, ints);
06099    if (!res)
06100       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06101    return res;
06102 }
06103 
06104 /* Dutch syntax */
06105 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06106 {
06107    struct tm tm;
06108    int res = 0;
06109 
06110    ast_localtime(&t, &tm, NULL);
06111    res = ast_say_date(chan, t, ints, lang);
06112    if (!res) {
06113       res = ast_streamfile(chan, "digits/nl-om", lang);
06114       if (!res)
06115          res = ast_waitstream(chan, ints);
06116    }
06117    if (!res) 
06118       ast_say_time(chan, t, ints, lang);
06119    return res;
06120 }
06121 
06122 /* Portuguese syntax */
06123 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06124 {
06125    struct tm tm;
06126    char fn[256];
06127    int res = 0;
06128    int hour, pm=0;
06129 
06130    ast_localtime(&t, &tm, NULL);
06131    if (!res) {
06132       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06133       res = ast_streamfile(chan, fn, lang);
06134       if (!res)
06135          res = ast_waitstream(chan, ints);
06136    }
06137    if (!res) {
06138       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06139       res = ast_streamfile(chan, fn, lang);
06140       if (!res)
06141          res = ast_waitstream(chan, ints);
06142    }
06143    if (!res)
06144       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06145 
06146    hour = tm.tm_hour;
06147    if (!hour)
06148       hour = 12;
06149    else if (hour == 12)
06150       pm = 1;
06151    else if (hour > 12) {
06152       hour -= 12;
06153       pm = 1;
06154    }
06155    if (!res)
06156       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06157 
06158    if (tm.tm_min > 9) {
06159       if (!res)
06160          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06161    } else if (tm.tm_min) {
06162       if (!res)
06163          res = ast_streamfile(chan, "digits/oh", lang);
06164       if (!res)
06165          res = ast_waitstream(chan, ints);
06166       if (!res)
06167          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06168    } else {
06169       if (!res)
06170          res = ast_streamfile(chan, "digits/oclock", lang);
06171       if (!res)
06172          res = ast_waitstream(chan, ints);
06173    }
06174    if (pm) {
06175       if (!res)
06176          res = ast_streamfile(chan, "digits/p-m", lang);
06177    } else {
06178       if (!res)
06179          res = ast_streamfile(chan, "digits/a-m", lang);
06180    }
06181    if (!res)
06182       res = ast_waitstream(chan, ints);
06183    if (!res)
06184       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06185    return res;
06186 }
06187 
06188 /* Brazilian Portuguese syntax */
06189 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06190 {
06191    struct tm tm;
06192    int res = 0;
06193 
06194    ast_localtime(&t, &tm, NULL);
06195    res = ast_say_date(chan, t, ints, lang);
06196    if (!res)
06197       res = ast_say_time(chan, t, ints, lang);
06198    return res;
06199 }
06200 
06201 /* Taiwanese / Chinese syntax */
06202 int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06203 {
06204    struct tm tm;
06205    char fn[256];
06206    int res = 0;
06207    int hour, pm=0;
06208 
06209    ast_localtime(&t, &tm, NULL);
06210    if (!res)
06211       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06212    if (!res) {
06213       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06214       res = ast_streamfile(chan, fn, lang);
06215       if (!res)
06216          res = ast_waitstream(chan, ints);
06217    }
06218    if (!res)
06219       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06220    if (!res) {
06221       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06222       res = ast_streamfile(chan, fn, lang);
06223       if (!res)
06224          res = ast_waitstream(chan, ints);
06225    }
06226 
06227    hour = tm.tm_hour;
06228    if (!hour)
06229       hour = 12;
06230    else if (hour == 12)
06231       pm = 1;
06232    else if (hour > 12) {
06233       hour -= 12;
06234       pm = 1;
06235    }
06236    if (pm) {
06237       if (!res)
06238          res = ast_streamfile(chan, "digits/p-m", lang);
06239    } else {
06240       if (!res)
06241          res = ast_streamfile(chan, "digits/a-m", lang);
06242    }
06243    if (!res)
06244       res = ast_waitstream(chan, ints);
06245    if (!res)
06246       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06247    if (!res)
06248       res = ast_streamfile(chan, "digits/oclock", lang);
06249    if (!res)
06250       res = ast_waitstream(chan, ints);
06251    if (!res)
06252       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06253    if (!res)
06254       res = ast_streamfile(chan, "digits/minute", lang);
06255    if (!res)
06256       res = ast_waitstream(chan, ints);
06257    return res;
06258 }
06259 
06260 /* Hebrew syntax */
06261 int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06262 {
06263    struct tm tm;
06264    char fn[256];
06265    int res = 0;
06266    int hour;
06267 
06268    ast_localtime(&t, &tm, NULL);
06269    if (!res) {
06270       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06271       res = ast_streamfile(chan, fn, lang);
06272       if (!res) {
06273          res = ast_waitstream(chan, ints);
06274       }
06275    }
06276    if (!res) {
06277       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06278       res = ast_streamfile(chan, fn, lang);
06279       if (!res) {
06280          res = ast_waitstream(chan, ints);
06281       }
06282    }
06283    if (!res) {
06284       res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
06285    }
06286 
06287    hour = tm.tm_hour;
06288    if (!hour) {
06289       hour = 12;
06290    }
06291 
06292    if (!res) {
06293       res = ast_say_number(chan, hour, ints, lang, "f");
06294    }
06295 
06296    if (tm.tm_min > 9) {
06297       if (!res) {
06298          res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
06299       }
06300    } else if (tm.tm_min) {
06301       if (!res) {
06302          /* say a leading zero if needed */
06303          res = ast_say_number(chan, 0, ints, lang, "f");
06304       }
06305       if (!res) {
06306          res = ast_waitstream(chan, ints);
06307       }
06308       if (!res) {
06309          res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
06310       }
06311    } else {
06312       if (!res) {
06313          res = ast_waitstream(chan, ints);
06314       }
06315    }
06316    if (!res) {
06317       res = ast_waitstream(chan, ints);
06318    }
06319    if (!res) {
06320       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
06321    }
06322    return res;
06323 }
06324 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06325 {
06326    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
06327       return ast_say_datetime_from_now_en(chan, t, ints, lang);
06328    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
06329       return ast_say_datetime_from_now_fr(chan, t, ints, lang);
06330    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
06331       static int deprecation_warning = 0;
06332       if (deprecation_warning++ % 10 == 0) {
06333          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
06334       }
06335       return ast_say_datetime_from_now_ka(chan, t, ints, lang);
06336    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
06337       return ast_say_datetime_from_now_he(chan, t, ints, lang);
06338    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
06339       return ast_say_datetime_from_now_ka(chan, t, ints, lang);
06340    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
06341       return ast_say_datetime_from_now_pt(chan, t, ints, lang);
06342    }
06343 
06344    /* Default to English */
06345    return ast_say_datetime_from_now_en(chan, t, ints, lang);
06346 }
06347 
06348 /* English syntax */
06349 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06350 {
06351    int res=0;
06352    time_t nowt;
06353    int daydiff;
06354    struct tm tm;
06355    struct tm now;
06356    char fn[256];
06357 
06358    time(&nowt);
06359 
06360    ast_localtime(&t, &tm, NULL);
06361    ast_localtime(&nowt,&now, NULL);
06362    daydiff = now.tm_yday - tm.tm_yday;
06363    if ((daydiff < 0) || (daydiff > 6)) {
06364       /* Day of month and month */
06365       if (!res) {
06366          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06367          res = ast_streamfile(chan, fn, lang);
06368          if (!res)
06369             res = ast_waitstream(chan, ints);
06370       }
06371       if (!res)
06372          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06373 
06374    } else if (daydiff) {
06375       /* Just what day of the week */
06376       if (!res) {
06377          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06378          res = ast_streamfile(chan, fn, lang);
06379          if (!res)
06380             res = ast_waitstream(chan, ints);
06381       }
06382    } /* Otherwise, it was today */
06383    if (!res)
06384       res = ast_say_time(chan, t, ints, lang);
06385    return res;
06386 }
06387 
06388 /* French syntax */
06389 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06390 {
06391    int res=0;
06392    time_t nowt;
06393    int daydiff;
06394    struct tm tm;
06395    struct tm now;
06396    char fn[256];
06397 
06398    time(&nowt);
06399 
06400    ast_localtime(&t, &tm, NULL);
06401    ast_localtime(&nowt, &now, NULL);
06402    daydiff = now.tm_yday - tm.tm_yday;
06403    if ((daydiff < 0) || (daydiff > 6)) {
06404       /* Day of month and month */
06405       if (!res) {
06406          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06407          res = ast_streamfile(chan, fn, lang);
06408          if (!res)
06409             res = ast_waitstream(chan, ints);
06410       }
06411       if (!res)
06412          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06413 
06414    } else if (daydiff) {
06415       /* Just what day of the week */
06416       if (!res) {
06417          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06418          res = ast_streamfile(chan, fn, lang);
06419          if (!res)
06420             res = ast_waitstream(chan, ints);
06421       }
06422    } /* Otherwise, it was today */
06423    if (!res)
06424       res = ast_say_time(chan, t, ints, lang);
06425    return res;
06426 }
06427 
06428 /* Portuguese syntax */
06429 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06430 {
06431    int res=0;
06432    time_t nowt;
06433    int daydiff;
06434    struct tm tm;
06435    struct tm now;
06436    char fn[256];
06437 
06438    time(&nowt);
06439 
06440    ast_localtime(&t, &tm, NULL);
06441    ast_localtime(&nowt, &now, NULL);
06442    daydiff = now.tm_yday - tm.tm_yday;
06443    if ((daydiff < 0) || (daydiff > 6)) {
06444       /* Day of month and month */
06445       if (!res)
06446          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06447       if (!res)
06448          res = wait_file(chan, ints, "digits/pt-de", lang);
06449       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06450       if (!res)
06451          res = wait_file(chan, ints, fn, lang);
06452    
06453    } else if (daydiff) {
06454       /* Just what day of the week */
06455       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06456       if (!res)
06457          res = wait_file(chan, ints, fn, lang);
06458    }  /* Otherwise, it was today */
06459    if (tm.tm_hour > 1)
06460       snprintf(fn, sizeof(fn), "digits/pt-as");
06461    else
06462       snprintf(fn, sizeof(fn), "digits/pt-a");
06463    if (!res)
06464       res = wait_file(chan, ints, fn, lang);
06465    if (!res)
06466       res = ast_say_time(chan, t, ints, lang);
06467    return res;
06468 }
06469 
06470 /* Hebrew syntax */
06471 int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06472 {
06473    int res = 0;
06474    time_t nowt;
06475    int daydiff;
06476    struct tm tm;
06477    struct tm now;
06478    char fn[256];
06479 
06480    time(&nowt);
06481 
06482    ast_localtime(&t, &tm, NULL);
06483    ast_localtime(&nowt, &now, NULL);
06484    daydiff = now.tm_yday - tm.tm_yday;
06485    if ((daydiff < 0) || (daydiff > 6)) {
06486       /* Day of month and month */
06487       if (!res) {
06488          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06489          res = ast_streamfile(chan, fn, lang);
06490          if (!res)
06491             res = ast_waitstream(chan, ints);
06492       }
06493       if (!res) {
06494          res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
06495       }
06496    } else if (daydiff) {
06497       /* Just what day of the week */
06498       if (!res) {
06499          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06500          res = ast_streamfile(chan, fn, lang);
06501          if (!res) {
06502             res = ast_waitstream(chan, ints);
06503          }
06504       }
06505    }                    /* Otherwise, it was today */
06506    if (!res) {
06507       res = ast_say_time(chan, t, ints, lang);
06508    }
06509    return res;
06510 }
06511 
06512 /*********************************** GREEK SUPPORT ***************************************/
06513 
06514 
06515 /*
06516  * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
06517  */
06518 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
06519    int tmp;
06520    int left;
06521    int res;
06522    char fn[256] = "";
06523 
06524    /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
06525    if (num < 5) {
06526       snprintf(fn, sizeof(fn), "digits/female-%d", num);
06527       res = wait_file(chan, ints, fn, lang);
06528    } else if (num < 13) {
06529       res = ast_say_number(chan, num, ints, lang, (char *) NULL);
06530    } else if (num <100 ) { 
06531       tmp = (num/10) * 10;
06532       left = num - tmp;
06533       snprintf(fn, sizeof(fn), "digits/%d", tmp);
06534       res = ast_streamfile(chan, fn, lang);
06535       if (!res)
06536          res = ast_waitstream(chan, ints);
06537       if (left)
06538          gr_say_number_female(left, chan, ints, lang);
06539          
06540    } else {
06541       return -1;
06542    }
06543    return res;
06544 }
06545 
06546 
06547 
06548 /*
06549  *    A list of the files that you need to create
06550  ->   digits/xilia = "xilia"
06551  ->   digits/myrio = "ekatomyrio"
06552  ->   digits/thousands = "xiliades"
06553  ->   digits/millions = "ektatomyria"
06554  ->   digits/[1..12]   :: A pronunciation of th digits form 1 to 12 e.g. "tria"
06555  ->   digits/[10..100]  :: A pronunciation of the tens from 10 to 90 
06556                                               e,g 80 = "ogdonta" 
06557                    Here we must note that we use digits/tens/100 to utter "ekato"
06558                    and digits/hundred-100 to utter "ekaton"
06559  ->   digits/hundred-[100...1000] :: A pronunciation of  hundreds from 100 to 1000 e.g 400 = 
06560                                                        "terakosia". Here again we use hundreds/1000 for "xilia" 
06561                    and digits/thousnds for "xiliades"
06562 */
06563 
06564 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
06565 {
06566    int res = 0;
06567    char fn[256] = "";
06568    int i=0;
06569 
06570  
06571    if (!num) {
06572       snprintf(fn, sizeof(fn), "digits/0");
06573       res = ast_streamfile(chan, fn, chan->language);
06574       if (!res)
06575          return  ast_waitstream(chan, ints);
06576    }
06577 
06578    while (!res && num ) {
06579       i++;
06580       if (num < 13) {
06581          snprintf(fn, sizeof(fn), "digits/%d", num);
06582          num = 0;
06583       } else if (num <= 100) {
06584          /* 13 < num <= 100  */
06585          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
06586          num -= ((num / 10) * 10); 
06587       } else if (num < 200) {
06588          /* 100 < num < 200 */
06589          snprintf(fn, sizeof(fn), "digits/hundred-100");
06590          num -= ((num / 100) * 100);
06591       } else if (num < 1000) {
06592          /* 200 < num < 1000 */
06593          snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
06594          num -= ((num / 100) * 100);
06595       } else if (num < 2000){
06596          snprintf(fn, sizeof(fn), "digits/xilia");
06597          num -= ((num / 1000) * 1000);
06598       } else {
06599          /* num >  1000 */ 
06600          if (num < 1000000) {
06601             res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
06602             if (res)
06603                return res;
06604             num = num % 1000;
06605             snprintf(fn, sizeof(fn), "digits/thousands");
06606          }  else {
06607             if (num < 1000000000) { /* 1,000,000,000 */
06608                res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
06609                if (res)
06610                   return res;
06611                num = num % 1000000;
06612                snprintf(fn, sizeof(fn), "digits/millions");
06613             } else {
06614                ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
06615                res = -1;
06616             }
06617          }
06618       } 
06619       if (!res) {
06620          if (!ast_streamfile(chan, fn, language)) {
06621             if ((audiofd > -1) && (ctrlfd > -1))
06622                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
06623             else
06624                res = ast_waitstream(chan, ints);
06625          }
06626          ast_stopstream(chan);
06627       }
06628    }
06629    return res;
06630 }
06631 
06632 
06633 /*
06634  * The format is  weekday - day - month -year
06635  * 
06636  * A list of the files that you need to create
06637  * digits/day-[1..7]  : "Deytera .. Paraskeyh"
06638  * digits/months/1..12 : "Ianouariou .. Dekembriou"  
06639                                        Attention the months are in 
06640             "gekinh klhsh"
06641  */
06642 
06643 
06644 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06645 {
06646    struct tm tm;
06647    
06648    char fn[256];
06649    int res = 0;
06650    
06651 
06652    ast_localtime(&t,&tm,NULL);
06653    /* W E E K - D A Y */
06654    if (!res) {
06655       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06656       res = ast_streamfile(chan, fn, lang);
06657       if (!res)
06658          res = ast_waitstream(chan, ints);
06659    }
06660    /* D A Y */
06661    if (!res) {
06662       gr_say_number_female(tm.tm_mday, chan, ints, lang);
06663    }
06664    /* M O N T H */
06665    if (!res) {
06666       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06667       res = ast_streamfile(chan, fn, lang);
06668       if (!res)
06669          res = ast_waitstream(chan, ints);
06670    }
06671    /* Y E A R */
06672    if (!res)
06673       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06674    return res; 
06675 }
06676 
06677 
06678  
06679 /* A list of the files that you need to create
06680  * digits/female/1..4 : "Mia, dyo , treis, tesseris "
06681  * digits/kai : "KAI"
06682  * didgits : "h wra"
06683  * digits/p-m : "meta meshmbrias" 
06684  * digits/a-m : "pro meshmbrias"
06685  */
06686 
06687 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06688 {
06689 
06690    struct tm tm;
06691    int res = 0;
06692    int hour, pm=0;
06693 
06694    ast_localtime(&t, &tm, NULL);
06695    hour = tm.tm_hour;
06696 
06697    if (!hour)
06698       hour = 12;
06699    else if (hour == 12)
06700       pm = 1;
06701    else if (hour > 12) {
06702       hour -= 12;
06703       pm = 1;
06704    }
06705  
06706    res = gr_say_number_female(hour, chan, ints, lang);
06707    if (tm.tm_min) {
06708       if (!res)
06709          res = ast_streamfile(chan, "digits/kai", lang);
06710       if (!res)
06711          res = ast_waitstream(chan, ints);
06712       if (!res)
06713          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06714    } else {
06715       if (!res)
06716          res = ast_streamfile(chan, "digits/hwra", lang);
06717       if (!res)
06718          res = ast_waitstream(chan, ints);
06719    }
06720    if (pm) {
06721       if (!res)
06722          res = ast_streamfile(chan, "digits/p-m", lang);
06723    } else {
06724       if (!res)
06725          res = ast_streamfile(chan, "digits/a-m", lang);
06726    }
06727    if (!res)
06728       res = ast_waitstream(chan, ints);
06729    return res;
06730 }
06731 
06732 
06733 
06734 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06735 {
06736    struct tm tm;
06737    char fn[256];
06738    int res = 0;
06739 
06740    ast_localtime(&t, &tm, NULL);
06741 
06742    
06743    /* W E E K - D A Y */
06744    if (!res) {
06745       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06746       res = ast_streamfile(chan, fn, lang);
06747       if (!res)
06748          res = ast_waitstream(chan, ints);
06749    }
06750    /* D A Y */
06751    if (!res) {
06752       gr_say_number_female(tm.tm_mday, chan, ints, lang);
06753    }
06754    /* M O N T H */
06755    if (!res) {
06756       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06757       res = ast_streamfile(chan, fn, lang);
06758       if (!res)
06759          res = ast_waitstream(chan, ints);
06760    }
06761 
06762    res = ast_say_time_gr(chan, t, ints, lang);
06763    return res;
06764 }
06765 
06766 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)
06767 {
06768    
06769    struct tm tm;
06770    int res=0, offset, sndoffset;
06771    char sndfile[256], nextmsg[256];
06772 
06773    if (!format)
06774       format = "AdBY 'digits/at' IMp";
06775 
06776    ast_localtime(&time,&tm,timezone);
06777    
06778    for (offset=0 ; format[offset] != '\0' ; offset++) {
06779       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
06780       switch (format[offset]) {
06781          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
06782       case '\'':
06783          /* Literal name of a sound file */
06784          for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
06785             sndfile[sndoffset] = format[offset];
06786          }
06787          sndfile[sndoffset] = '\0';
06788          res = wait_file(chan,ints,sndfile,lang);
06789          break;
06790       case 'A':
06791       case 'a':
06792          /* Sunday - Saturday */
06793          snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
06794          res = wait_file(chan,ints,nextmsg,lang);
06795          break;
06796       case 'B':
06797       case 'b':
06798       case 'h':
06799          /* January - December */
06800          snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
06801          res = wait_file(chan,ints,nextmsg,lang);
06802          break;
06803       case 'd':
06804       case 'e':
06805          /* first - thirtyfirst */
06806          gr_say_number_female(tm.tm_mday, chan, ints, lang);
06807          break;
06808       case 'Y':
06809          /* Year */
06810          
06811          ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
06812          break;
06813       case 'I':
06814       case 'l':
06815          /* 12-Hour */
06816          if (tm.tm_hour == 0)
06817             gr_say_number_female(12, chan, ints, lang);
06818          else if (tm.tm_hour > 12)
06819             gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
06820          else
06821             gr_say_number_female(tm.tm_hour, chan, ints, lang);
06822          break;
06823       case 'H':
06824       case 'k':
06825          /* 24-Hour */
06826          gr_say_number_female(tm.tm_hour, chan, ints, lang);
06827          break;
06828       case 'M':
06829          /* Minute */
06830          if (tm.tm_min) {
06831             if (!res)
06832                res = ast_streamfile(chan, "digits/kai", lang);
06833             if (!res)
06834                res = ast_waitstream(chan, ints);
06835             if (!res)
06836                res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
06837          } else {
06838             if (!res)
06839                res = ast_streamfile(chan, "digits/oclock", lang);
06840             if (!res)
06841                res = ast_waitstream(chan, ints);
06842          }
06843          break;
06844       case 'P':
06845       case 'p':
06846          /* AM/PM */
06847          if (tm.tm_hour > 11)
06848             snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
06849          else
06850             snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
06851          res = wait_file(chan,ints,nextmsg,lang);
06852          break;
06853       case 'Q':
06854          /* Shorthand for "Today", "Yesterday", or ABdY */
06855             /* XXX As emphasized elsewhere, this should the native way in your
06856              * language to say the date, with changes in what you say, depending
06857              * upon how recent the date is. XXX */
06858          {
06859             struct timeval now;
06860             struct tm tmnow;
06861             time_t beg_today, tt;
06862             
06863             gettimeofday(&now,NULL);
06864             tt = now.tv_sec;
06865             ast_localtime(&tt,&tmnow,timezone);
06866             /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
06867             /* In any case, it saves not having to do ast_mktime() */
06868             beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
06869             if (beg_today < time) {
06870                /* Today */
06871                res = wait_file(chan,ints, "digits/today",lang);
06872             } else if (beg_today - 86400 < time) {
06873                /* Yesterday */
06874                res = wait_file(chan,ints, "digits/yesterday",lang);
06875             } else {
06876                res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
06877             }
06878          }
06879          break;
06880       case 'q':
06881          /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
06882             /* XXX As emphasized elsewhere, this should the native way in your
06883              * language to say the date, with changes in what you say, depending
06884              * upon how recent the date is. XXX */
06885          {
06886             struct timeval now;
06887             struct tm tmnow;
06888             time_t beg_today, tt;
06889             
06890             gettimeofday(&now,NULL);
06891             tt = now.tv_sec;
06892             ast_localtime(&tt,&tmnow,timezone);
06893             /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
06894             /* In any case, it saves not having to do ast_mktime() */
06895             beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
06896             if (beg_today < time) {
06897                /* Today */
06898             } else if ((beg_today - 86400) < time) {
06899                /* Yesterday */
06900                res = wait_file(chan,ints, "digits/yesterday",lang);
06901             } else if (beg_today - 86400 * 6 < time) {
06902                /* Within the last week */
06903                res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
06904             } else {
06905                res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
06906             }
06907          }
06908          break;
06909       case 'R':
06910          res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
06911          break;
06912       case 'S':
06913          /* Seconds */
06914          snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
06915          res = wait_file(chan,ints,nextmsg,lang);
06916          if (!res)
06917             res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
06918          if (!res)
06919             snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
06920          res = wait_file(chan,ints,nextmsg,lang);
06921          break;
06922       case 'T':
06923          res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
06924          break;
06925       case ' ':
06926       case '   ':
06927          /* Just ignore spaces and tabs */
06928          break;
06929       default:
06930          /* Unknown character */
06931          ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
06932       }
06933       /* Jump out on DTMF */
06934       if (res) {
06935          break;
06936       }
06937    }
06938    return res;
06939 }
06940 
06941 
06942 
06943 
06944 /*********************************** Georgian Support ***************************************/
06945 
06946 
06947 /*
06948    Convert a number into a semi-localized string. Only for Georgian.
06949    res must be of at least 256 bytes, preallocated.
06950    The output corresponds to Georgian spoken numbers, so
06951    it may be either converted to real words by applying a direct conversion
06952    table, or played just by substituting the entities with played files.
06953 
06954    Output may consist of the following tokens (separated by spaces):
06955    0, minus.
06956    1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
06957    10-19.
06958    20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
06959    100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
06960    1000, 1000_. (atasi, atas).
06961    1000000, 1000000_. (milioni, milion).
06962    1000000000, 1000000000_. (miliardi, miliard).
06963 
06964    To be able to play the sounds, each of the above tokens needs
06965    a corresponding sound file. (e.g. 200_.gsm).
06966 */
06967 static char* ast_translate_number_ka(int num, char* res, int res_len)
06968 {
06969    char buf[256];
06970    int digit = 0;
06971    int remainder = 0;
06972 
06973 
06974    if (num < 0) {
06975       strncat(res, "minus ", res_len - strlen(res) - 1);
06976       if ( num > INT_MIN ) {
06977          num = -num;
06978       } else {
06979          num = 0;
06980       }
06981    }
06982 
06983 
06984    /* directly read the numbers */
06985    if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
06986       snprintf(buf, sizeof(buf), "%d", num);
06987       strncat(res, buf, res_len - strlen(res) - 1);
06988       return res;
06989    }
06990 
06991 
06992    if (num < 40) {  /* ocda... */
06993       strncat(res, "20_ ", res_len - strlen(res) - 1);
06994       return ast_translate_number_ka(num - 20, res, res_len);
06995    }
06996 
06997    if (num < 60) {  /* ormocda... */
06998       strncat(res, "40_ ", res_len - strlen(res) - 1);
06999       return ast_translate_number_ka(num - 40, res, res_len);
07000    }
07001 
07002    if (num < 80) {  /* samocda... */
07003       strncat(res, "60_ ", res_len - strlen(res) - 1);
07004       return ast_translate_number_ka(num - 60, res, res_len);
07005    }
07006 
07007    if (num < 100) {  /* otxmocda... */
07008       strncat(res, "80_ ", res_len - strlen(res) - 1);
07009       return ast_translate_number_ka(num - 80, res, res_len);
07010    }
07011 
07012 
07013    if (num < 1000) {  /*  as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
07014       remainder = num % 100;
07015       digit = (num - remainder) / 100;
07016 
07017       if (remainder == 0) {
07018          snprintf(buf, sizeof(buf), "%d", num);
07019          strncat(res, buf, res_len - strlen(res) - 1);
07020          return res;
07021       } else {
07022          snprintf(buf, sizeof(buf), "%d_ ", digit*100);
07023          strncat(res, buf, res_len - strlen(res) - 1);
07024          return ast_translate_number_ka(remainder, res, res_len);
07025       }
07026    }
07027 
07028 
07029    if (num == 1000) {
07030       strncat(res, "1000", res_len - strlen(res) - 1);
07031       return res;
07032    }
07033 
07034 
07035    if (num < 1000000) {
07036       remainder = num % 1000;
07037       digit = (num - remainder) / 1000;
07038 
07039       if (remainder == 0) {
07040          ast_translate_number_ka(digit, res, res_len);
07041          strncat(res, " 1000", res_len - strlen(res) - 1);
07042          return res;
07043       }
07044 
07045       if (digit == 1) {
07046          strncat(res, "1000_ ", res_len - strlen(res) - 1);
07047          return ast_translate_number_ka(remainder, res, res_len);
07048       }
07049 
07050       ast_translate_number_ka(digit, res, res_len);
07051       strncat(res, " 1000_ ", res_len - strlen(res) - 1);
07052       return ast_translate_number_ka(remainder, res, res_len);
07053 
07054    }
07055 
07056 
07057    if (num == 1000000) {
07058       strncat(res, "1 1000000", res_len - strlen(res) - 1);
07059       return res;
07060    }
07061 
07062 
07063    if (num < 1000000000) {
07064       remainder = num % 1000000;
07065       digit = (num - remainder) / 1000000;
07066 
07067       if (remainder == 0) {
07068          ast_translate_number_ka(digit, res, res_len);
07069          strncat(res, " 1000000", res_len - strlen(res) - 1);
07070          return res;
07071       }
07072 
07073       ast_translate_number_ka(digit, res, res_len);
07074       strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
07075       return ast_translate_number_ka(remainder, res, res_len);
07076 
07077    }
07078 
07079 
07080    if (num == 1000000000) {
07081       strncat(res, "1 1000000000", res_len - strlen(res) - 1);
07082       return res;
07083    }
07084 
07085 
07086    if (num > 1000000000) {
07087       remainder = num % 1000000000;
07088       digit = (num - remainder) / 1000000000;
07089 
07090       if (remainder == 0) {
07091          ast_translate_number_ka(digit, res, res_len);
07092          strncat(res, " 1000000000", res_len - strlen(res) - 1);
07093          return res;
07094       }
07095 
07096       ast_translate_number_ka(digit, res, res_len);
07097       strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
07098       return ast_translate_number_ka(remainder, res, res_len);
07099 
07100    }
07101 
07102    return res;
07103 
07104 }
07105 
07106 
07107 
07108 /*! \brief  ast_say_number_full_ka: Georgian syntax */
07109 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)
07110 {
07111    int res = 0;
07112    char fn[512] = "";
07113    char* s = 0;
07114    const char* remainder = fn;
07115 
07116    if (!num)
07117       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
07118 
07119 
07120    ast_translate_number_ka(num, fn, 512);
07121 
07122 
07123 
07124    while (res == 0 && (s = strstr(remainder, " "))) {
07125       size_t len = s - remainder;
07126       char* new_string = malloc(len + 1 + strlen("digits/"));
07127 
07128       sprintf(new_string, "digits/");
07129       strncat(new_string, remainder, len);  /* we can't sprintf() it, it's not null-terminated. */
07130 /*       new_string[len + strlen("digits/")] = '\0'; */
07131 
07132       if (!ast_streamfile(chan, new_string, language)) {
07133          if ((audiofd  > -1) && (ctrlfd > -1))
07134             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
07135          else
07136             res = ast_waitstream(chan, ints);
07137       }
07138       ast_stopstream(chan);
07139 
07140       free(new_string);
07141 
07142       remainder = s + 1;  /* position just after the found space char. */
07143       while (*remainder == ' ')  /* skip multiple spaces */
07144          remainder++;
07145    }
07146 
07147 
07148    /* the last chunk. */
07149    if (res == 0 && *remainder) {
07150 
07151       char* new_string = malloc(strlen(remainder) + 1 + strlen("digits/"));
07152       sprintf(new_string, "digits/%s", remainder);
07153 
07154       if (!ast_streamfile(chan, new_string, language)) {
07155          if ((audiofd  > -1) && (ctrlfd > -1))
07156             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
07157          else
07158             res = ast_waitstream(chan, ints);
07159       }
07160       ast_stopstream(chan);
07161 
07162       free(new_string);
07163 
07164    }
07165 
07166 
07167    return res;
07168 
07169 }
07170 
07171 
07172 
07173 /*
07174 Georgian support for date/time requires the following files (*.gsm):
07175 
07176 mon-1, mon-2, ... (ianvari, tebervali, ...)
07177 day-1, day-2, ... (orshabati, samshabati, ...)
07178 saati_da
07179 tsuti
07180 tslis
07181 */
07182 
07183 
07184 
07185 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
07186 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07187 {
07188    struct tm tm;
07189    char fn[256];
07190    int res = 0;
07191    ast_localtime(&t,&tm,NULL);
07192 
07193    if (!res)
07194       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
07195 
07196    if (!res) {
07197       snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
07198       res = ast_streamfile(chan, fn, lang);
07199       if (!res)
07200          res = ast_waitstream(chan, ints);
07201    }
07202 
07203    if (!res) {
07204       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
07205 /*       if (!res)
07206          res = ast_waitstream(chan, ints);
07207 */
07208    }
07209 
07210    if (!res) {
07211       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07212       res = ast_streamfile(chan, fn, lang);
07213       if (!res)
07214          res = ast_waitstream(chan, ints);
07215    }
07216    return res;
07217 
07218 }
07219 
07220 
07221 
07222 
07223 
07224 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
07225 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07226 {
07227    struct tm tm;
07228    int res = 0;
07229 
07230    ast_localtime(&t, &tm, NULL);
07231 
07232    res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
07233    if (!res) {
07234       res = ast_streamfile(chan, "digits/saati_da", lang);
07235       if (!res)
07236          res = ast_waitstream(chan, ints);
07237    }
07238 
07239    if (tm.tm_min) {
07240       if (!res) {
07241          res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
07242 
07243          if (!res) {
07244             res = ast_streamfile(chan, "digits/tsuti", lang);
07245             if (!res)
07246                res = ast_waitstream(chan, ints);
07247          }
07248       }
07249    }
07250    return res;
07251 }
07252 
07253 
07254 
07255 /* Georgian syntax. Say date, then say time. */
07256 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07257 {
07258    struct tm tm;
07259    int res = 0;
07260 
07261    ast_localtime(&t, &tm, NULL);
07262    res = ast_say_date(chan, t, ints, lang);
07263    if (!res)
07264       ast_say_time(chan, t, ints, lang);
07265    return res;
07266 
07267 }
07268 
07269 
07270 
07271 
07272 /* Georgian syntax */
07273 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07274 {
07275    int res=0;
07276    time_t nowt;
07277    int daydiff;
07278    struct tm tm;
07279    struct tm now;
07280    char fn[256];
07281 
07282    time(&nowt);
07283 
07284    ast_localtime(&t, &tm, NULL);
07285    ast_localtime(&nowt, &now, NULL);
07286    daydiff = now.tm_yday - tm.tm_yday;
07287    if ((daydiff < 0) || (daydiff > 6)) {
07288       /* Day of month and month */
07289       if (!res)
07290          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
07291       if (!res) {
07292          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07293          res = ast_streamfile(chan, fn, lang);
07294          if (!res)
07295             res = ast_waitstream(chan, ints);
07296       }
07297 
07298    } else if (daydiff) {
07299       /* Just what day of the week */
07300       if (!res) {
07301          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07302          res = ast_streamfile(chan, fn, lang);
07303          if (!res)
07304             res = ast_waitstream(chan, ints);
07305       }
07306    } /* Otherwise, it was today */
07307    if (!res)
07308       res = ast_say_time(chan, t, ints, lang);
07309 
07310    return res;
07311 }
07312 
07313 /* In English, we use the plural for everything but one. For example:
07314  *  1 degree
07315  *  2 degrees
07316  *  5 degrees
07317  * The filename for the plural form is generated by appending "s". Note that
07318  * purpose is to generate a unique filename, not to implement irregular 
07319  * declensions. Thus:
07320  *  1 man
07321  *  2 mans (the "mans" soundfile will of course say "men")
07322  */
07323 static const char *counted_noun_ending_en(int num)
07324 {
07325    if (num == 1 || num == -1) {
07326       return "";
07327    } else {
07328       return "s";
07329    }
07330 }
07331 
07332 /* Counting of objects in slavic languages such as Russian and Ukrainian the
07333  * rules are more complicated. There are two plural forms used in counting.
07334  * They are the genative singular which we represent with the suffix "x1" and 
07335  * the genative plural which we represent with the suffix "x2". The base names
07336  * of the soundfiles remain in English. For example:
07337  *  1 degree (soudfile says "gradus")
07338  *  2 degreex1 (soundfile says "gradusa")
07339  *  5 degreex2 (soundfile says "gradusov")
07340  */
07341 static const char *counted_noun_ending_slavic(int num)
07342 {
07343       if (num < 0) {
07344        num *= -1;
07345    }
07346    num %= 100;       /* never pay attention to more than two digits */
07347    if (num >= 20) {     /* for numbers 20 and above, pay attention to only last digit */
07348        num %= 10;
07349    }
07350    if (num == 1) {         /* singular */
07351        return "";
07352    }
07353    if (num > 0 && num < 5) {  /* 2--4 get genative singular */
07354        return "x1";
07355    } else {       /* 5--19 get genative plural */
07356        return "x2";
07357    }
07358 }
07359 
07360 int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
07361 {
07362    char *temp;
07363    int temp_len;
07364    const char *ending;
07365    if (!strncasecmp(chan->language, "ru", 2)) {        /* Russian */
07366       ending = counted_noun_ending_slavic(num);
07367    } else if (!strncasecmp(chan->language, "ua", 2)) { /* Ukrainian */
07368       ending = counted_noun_ending_slavic(num);
07369    } else if (!strncasecmp(chan->language, "pl", 2)) { /* Polish */
07370       ending = counted_noun_ending_slavic(num);
07371    } else {                                            /* English and default */
07372       ending = counted_noun_ending_en(num);
07373    }
07374    temp = alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
07375    snprintf(temp, temp_len, "%s%s", noun, ending);
07376    return ast_play_and_wait(chan, temp);
07377 }
07378 
07379 /*
07380  * In slavic languages such as Russian and Ukrainian the rules for declining
07381  * adjectives are simpler than those for nouns.  When counting we use only
07382  * the singular (to which we give no suffix) and the genative plural (which
07383  * we represent by adding an "x").  Oh, an in the singular gender matters
07384  * so we append the supplied gender suffix ("m", "f", "n").
07385  */
07386 static const char *counted_adjective_ending_ru(int num, const char gender[])
07387 {
07388    if (num < 0) {
07389        num *= -1;
07390    }
07391    num %= 100;    /* never pay attention to more than two digits */
07392    if (num >= 20) {  /* at 20 and beyond only the last digit matters */
07393        num %= 10;
07394    }
07395    if (num == 1) {
07396        return gender ? gender : "";
07397    } else {    /* all other numbers get the genative plural */
07398        return "x";
07399    }
07400 }
07401 
07402 int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
07403 {
07404    char *temp;
07405    int temp_len;
07406    const char *ending;
07407    if (!strncasecmp(chan->language, "ru", 2)) {           /* Russian */
07408       ending = counted_adjective_ending_ru(num, gender);
07409    } else if (!strncasecmp(chan->language, "ua", 2)) {    /* Ukrainian */
07410       ending = counted_adjective_ending_ru(num, gender);
07411    } else if (!strncasecmp(chan->language, "pl", 2)) {    /* Polish */
07412       ending = counted_adjective_ending_ru(num, gender);
07413    } else {                                               /* English and default */
07414       ending = "";
07415    }
07416    temp = alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
07417    snprintf(temp, temp_len, "%s%s", adjective, ending);
07418    return ast_play_and_wait(chan, temp);
07419 }
07420 
07421 
07422 
07423 /*
07424  * remap the 'say' functions to use those in this file
07425  */
07426 static void __attribute__((constructor)) __say_init(void)
07427 {
07428    ast_say_number_full = say_number_full;
07429    ast_say_enumeration_full = say_enumeration_full;
07430    ast_say_digit_str_full = say_digit_str_full;
07431    ast_say_character_str_full = say_character_str_full;
07432    ast_say_phonetic_str_full = say_phonetic_str_full;
07433    ast_say_datetime = say_datetime;
07434    ast_say_time = say_time;
07435    ast_say_date = say_date;
07436    ast_say_datetime_from_now = say_datetime_from_now;
07437    ast_say_date_with_format = say_date_with_format;
07438 }

Generated on Sun Aug 15 20:33:31 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7