Sat Aug 6 00:39:32 2011

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: 296309 $")
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 
02105 /*! \brief  ast_say_number_full_se: Swedish syntax */
02106 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)
02107 {
02108    int playh = 0;
02109    int start = 1;
02110    char fn[256] = "";
02111    int cn = 1;    /* +1 = commune; -1 = neuter */
02112    int res = 0;
02113 
02114    if (!num) 
02115       return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
02116    if (options && !strncasecmp(options, "n",1)) cn = -1;
02117 
02118    while (num || playh) {
02119       if (num < 0) {
02120          snprintf(fn, sizeof(fn), "digits/minus");
02121          if ( num > INT_MIN ) {
02122             num = -num;
02123          } else {
02124             num = 0;
02125          }
02126       } else if (playh) {
02127          snprintf(fn, sizeof(fn), "digits/hundred");
02128          playh = 0;
02129       } else if (start  && num < 200 && num > 99 && cn == -1) {
02130          /* Don't say "en hundra" just say "hundra". */
02131          snprintf(fn, sizeof(fn), "digits/hundred");
02132          num -= 100;
02133       } else if (num == 1 && cn == -1) {  /* En eller ett? */
02134          snprintf(fn, sizeof(fn), "digits/1N");
02135          num = 0;
02136       } else if (num < 20) {
02137          snprintf(fn, sizeof(fn), "digits/%d", num);
02138          num = 0;
02139       } else if (num < 100) { /* Below hundreds - teens and tens */
02140          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
02141          num -= ((num / 10) * 10);
02142       } else if (num < 1000) {
02143          /* Hundreds */
02144          snprintf(fn, sizeof(fn), "digits/%d", (num/100));
02145          playh++;
02146          num -= ((num / 100) * 100);
02147       } else if (num < 1000000) { /* 1,000,000 */
02148          /* Always say "ett hundra tusen", not "en hundra tusen" */
02149          res = ast_say_number_full_se(chan, num / 1000, ints, language, "c", audiofd, ctrlfd);
02150          if (res) {
02151             return res;
02152          }
02153          num = num % 1000;
02154          snprintf(fn, sizeof(fn), "digits/thousand");
02155       } else if (num < 1000000000) {   /* 1,000,000,000 */
02156          /* Always say "en miljon", not "ett miljon" */
02157          res = ast_say_number_full_se(chan, num / 1000000, ints, language, "n", audiofd, ctrlfd);
02158          if (res) {
02159             return res;
02160          }
02161          num = num % 1000000;
02162          snprintf(fn, sizeof(fn), "digits/million");
02163       } else { /* Miljarder - Billions */
02164          ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
02165          return -1;
02166       }
02167 
02168       if (!ast_streamfile(chan, fn, language)) {
02169          if ((audiofd > -1) && (ctrlfd > -1))
02170             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02171          else
02172             res = ast_waitstream(chan, ints);
02173          ast_stopstream(chan);
02174          if (res) {
02175             return res;
02176          }
02177       }
02178       start = 0;
02179    }
02180    return 0;
02181 }
02182 
02183 /*! \brief  ast_say_number_full_zh: Taiwanese / Chinese syntax */
02184 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
02185 {
02186    int res = 0;
02187    int playh = 0;
02188    int playt = 0;
02189    int playz = 0;
02190    int last_length = 0;
02191    char buf[20] = "";
02192    char fn[256] = "";
02193    if (!num)
02194       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
02195 
02196    while (!res && (num || playh || playt || playz)) {
02197          if (num < 0) {
02198             snprintf(fn, sizeof(fn), "digits/minus");
02199             if ( num > INT_MIN ) {
02200                num = -num;
02201             } else {
02202                num = 0;
02203             }  
02204          } else if (playz) {
02205             snprintf(fn, sizeof(fn), "digits/0");
02206             last_length = 0;
02207             playz = 0;
02208          } else if (playh) {
02209             snprintf(fn, sizeof(fn), "digits/hundred");
02210             playh = 0;
02211          } else if (playt) {
02212             snprintf(fn, sizeof(fn), "digits/thousand");
02213             playt = 0;
02214          } else   if (num < 10) {
02215             snprintf(buf, sizeof(buf), "%d", num);
02216             if (last_length - strlen(buf) > 1 && last_length != 0) {
02217                last_length = strlen(buf);
02218                playz++;
02219                continue;
02220             }
02221             snprintf(fn, sizeof(fn), "digits/%d", num);
02222             num = 0;
02223          } else   if (num < 100) {
02224             snprintf(buf, sizeof(buf), "%d", num);
02225             if (last_length - strlen(buf) > 1 && last_length != 0) {
02226                last_length = strlen(buf);
02227                playz++;
02228                continue;
02229             }
02230             last_length = strlen(buf);
02231             snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
02232             num -= ((num / 10) * 10);
02233          } else {
02234             if (num < 1000){
02235                snprintf(buf, sizeof(buf), "%d", num);
02236                if (last_length - strlen(buf) > 1 && last_length != 0) {
02237                   last_length = strlen(buf);
02238                   playz++;
02239                   continue;
02240                }
02241                snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
02242                playh++;
02243                snprintf(buf, sizeof(buf), "%d", num);
02244                ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02245                last_length = strlen(buf);
02246                num -= ((num / 100) * 100);
02247             } else if (num < 10000){
02248                snprintf(buf, sizeof(buf), "%d", num);
02249                snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
02250                playt++;
02251                snprintf(buf, sizeof(buf), "%d", num);
02252                ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02253                last_length = strlen(buf);
02254                num -= ((num / 1000) * 1000);
02255             } else if (num < 100000000) { /* 100,000,000 */
02256                   res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
02257                   if (res)
02258                      return res;
02259                   snprintf(buf, sizeof(buf), "%d", num);
02260                   ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02261                   num -= ((num / 10000) * 10000);
02262                   last_length = strlen(buf);
02263                   snprintf(fn, sizeof(fn), "digits/wan");
02264             } else {
02265                if (num < 1000000000) { /* 1000,000,000 */
02266                   res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
02267                   if (res)
02268                      return res;
02269                   snprintf(buf, sizeof(buf), "%d", num);
02270                   ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
02271                   last_length = strlen(buf);
02272                   num -= ((num / 100000000) * 100000000);
02273                   snprintf(fn, sizeof(fn), "digits/yi");
02274                } else {
02275                      ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
02276                      res = -1;
02277                }
02278             }
02279          }
02280          if (!res) {
02281             if (!ast_streamfile(chan, fn, language)) {
02282                if ((audiofd > -1) && (ctrlfd > -1))
02283                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02284                else
02285                   res = ast_waitstream(chan, ints);
02286             }
02287             ast_stopstream(chan);
02288          }
02289    }
02290    return res;
02291 }
02292 
02293 
02294 /*! \brief  determine last digits for thousands/millions (ru) */
02295 static int get_lastdigits_ru(int num) {
02296    if (num < 20) {
02297       return num;
02298    } else if (num < 100) {
02299       return get_lastdigits_ru(num % 10);
02300    } else if (num < 1000) {
02301       return get_lastdigits_ru(num % 100);
02302    }
02303    return 0;   /* number too big */
02304 }
02305 
02306 
02307 /*! \brief  ast_say_number_full_ru: Russian syntax */
02308 /*! \brief  additional files:
02309    n00.gsm        (one hundred, two hundred, ...)
02310    thousand.gsm
02311    million.gsm
02312    thousands-i.gsm      (tisyachi)
02313    million-a.gsm     (milliona)
02314    thousands.gsm
02315    millions.gsm
02316    1f.gsm         (odna)
02317    2f.gsm         (dve)
02318     
02319    where 'n' from 1 to 9
02320 */
02321 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)
02322 {
02323    int res = 0;
02324    int lastdigits = 0;
02325    char fn[256] = "";
02326    if (!num) 
02327       return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
02328 
02329    while (!res && (num)) {
02330       if (num < 0) {
02331          snprintf(fn, sizeof(fn), "digits/minus");
02332          if ( num > INT_MIN ) {
02333             num = -num;
02334          } else {
02335             num = 0;
02336          }  
02337       } else   if (num < 20) {
02338          if (options && strlen(options) == 1 && num < 3) {
02339              snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
02340          } else {
02341              snprintf(fn, sizeof(fn), "digits/%d", num);
02342          }
02343          num = 0;
02344       } else   if (num < 100) {
02345          snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
02346          num %= 10;
02347       } else   if (num < 1000){
02348          snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
02349          num %= 100;
02350       } else   if (num < 1000000) { /* 1,000,000 */
02351          lastdigits = get_lastdigits_ru(num / 1000);
02352          /* say thousands */
02353          if (lastdigits < 3) {
02354             res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
02355          } else {
02356             res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
02357          }
02358          if (res)
02359             return res;
02360          if (lastdigits == 1) {
02361             snprintf(fn, sizeof(fn), "digits/thousand");
02362          } else if (lastdigits > 1 && lastdigits < 5) {
02363             snprintf(fn, sizeof(fn), "digits/thousands-i");
02364          } else {
02365             snprintf(fn, sizeof(fn), "digits/thousands");
02366          }
02367          num %= 1000;
02368       } else   if (num < 1000000000) { /* 1,000,000,000 */
02369          lastdigits = get_lastdigits_ru(num / 1000000);
02370          /* say millions */
02371          res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
02372          if (res)
02373             return res;
02374          if (lastdigits == 1) {
02375             snprintf(fn, sizeof(fn), "digits/million");
02376          } else if (lastdigits > 1 && lastdigits < 5) {
02377             snprintf(fn, sizeof(fn), "digits/million-a");
02378          } else {
02379             snprintf(fn, sizeof(fn), "digits/millions");
02380          }
02381          num %= 1000000;
02382       } else {
02383          ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
02384             res = -1;
02385       }
02386       if (!res) {
02387          if (!ast_streamfile(chan, fn, language)) {
02388             if ((audiofd  > -1) && (ctrlfd > -1))
02389                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02390             else
02391                res = ast_waitstream(chan, ints);
02392          }
02393          ast_stopstream(chan);
02394       }
02395    }
02396    return res;
02397 }
02398 
02399 
02400 /*! \brief  ast_say_enumeration_full: call language-specific functions */
02401 /* Called from AGI */
02402 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
02403 {
02404    if (!strncasecmp(language, "en", 2)) {        /* English syntax */
02405       return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
02406    } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
02407       return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
02408    } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
02409       return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
02410    } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
02411       return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
02412    }
02413 
02414    /* Default to english */
02415    return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
02416 }
02417 
02418 /*! \brief  ast_say_enumeration_full_en: English syntax */
02419 /* This is the default syntax, if no other syntax defined in this file is used */
02420 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
02421 {
02422    int res = 0, t = 0;
02423    char fn[256] = "";
02424    
02425    while (!res && num) {
02426       if (num < 0) {
02427          snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
02428          if ( num > INT_MIN ) {
02429             num = -num;
02430          } else {
02431             num = 0;
02432          }  
02433       } else if (num < 20) {
02434          snprintf(fn, sizeof(fn), "digits/h-%d", num);
02435          num = 0;
02436       } else if (num < 100) { 
02437          int tens = num / 10;
02438          num = num % 10;
02439          if (num == 0) {
02440             snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
02441          } else {
02442             snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
02443          }
02444       } else if (num < 1000) {
02445          int hundreds = num / 100;
02446          num = num % 100;
02447          if (hundreds > 1 || t == 1) {
02448             res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
02449          }        
02450          if (res)
02451             return res;
02452          if (num) {
02453             snprintf(fn, sizeof(fn), "digits/hundred");
02454          } else {
02455             snprintf(fn, sizeof(fn), "digits/h-hundred");
02456          }
02457       } else if (num < 1000000) {
02458          int thousands = num / 1000;
02459          num = num % 1000;
02460          if (thousands > 1 || t == 1) {
02461             res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
02462          }
02463          if (res)
02464             return res;
02465          if (num) {              
02466             snprintf(fn, sizeof(fn), "digits/thousand");
02467          } else {
02468             snprintf(fn, sizeof(fn), "digits/h-thousand");
02469          }
02470          t = 1;
02471       } else if (num < 1000000000) {
02472          int millions = num / 1000000;
02473          num = num % 1000000;
02474          t = 1;
02475          res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
02476          if (res)
02477             return res;
02478          if (num) {              
02479             snprintf(fn, sizeof(fn), "digits/million");
02480          } else {
02481             snprintf(fn, sizeof(fn), "digits/h-million");
02482          }
02483       } else if (num < INT_MAX) {
02484          int billions = num / 1000000000;
02485          num = num % 1000000000;
02486          t = 1;
02487          res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
02488          if (res)
02489             return res;
02490          if (num) {              
02491             snprintf(fn, sizeof(fn), "digits/billion");
02492          } else {
02493             snprintf(fn, sizeof(fn), "digits/h-billion");
02494          }
02495       } else if (num == INT_MAX) {
02496          snprintf(fn, sizeof(fn), "digits/h-last");
02497          num = 0;
02498       } else {
02499          ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
02500          res = -1;
02501       }
02502 
02503       if (!res) {
02504          if (!ast_streamfile(chan, fn, language)) {
02505             if ((audiofd > -1) && (ctrlfd > -1)) {
02506                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02507             } else {
02508                res = ast_waitstream(chan, ints);
02509             }
02510          }
02511          ast_stopstream(chan);
02512       }
02513    }
02514    return res;
02515 }
02516 
02517 /*! \brief  ast_say_enumeration_full_da: Danish syntax */
02518 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)
02519 {
02520    /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
02521    int res = 0, t = 0;
02522    char fn[256] = "", fna[256] = "";
02523    char *gender;
02524 
02525    if (options && !strncasecmp(options, "f",1)) {
02526       gender = "F";
02527    } else if (options && !strncasecmp(options, "n",1)) {
02528       gender = "N";
02529    } else {
02530       gender = "";
02531    }
02532 
02533    if (!num) 
02534       return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
02535 
02536    while (!res && num) {
02537       if (num < 0) {
02538          snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
02539          if ( num > INT_MIN ) {
02540             num = -num;
02541          } else {
02542             num = 0;
02543          }  
02544       } else if (num < 100 && t) {
02545          snprintf(fn, sizeof(fn), "digits/and");
02546          t = 0;
02547       } else if (num < 20) {
02548          snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
02549          num = 0;
02550       } else if (num < 100) {
02551          int ones = num % 10;
02552          if (ones) {
02553             snprintf(fn, sizeof(fn), "digits/%d-and", ones);
02554             num -= ones;
02555          } else {
02556             snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
02557             num = 0;
02558          }
02559       } else if (num == 100 && t == 0) {
02560          snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
02561          num = 0;
02562       } else if (num < 1000) {
02563          int hundreds = num / 100;
02564          num = num % 100;
02565          if (hundreds == 1) {
02566             snprintf(fn, sizeof(fn), "digits/1N");
02567          } else {
02568             snprintf(fn, sizeof(fn), "digits/%d", hundreds);
02569          }
02570          if (num) {              
02571             snprintf(fna, sizeof(fna), "digits/hundred");
02572          } else {
02573             snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
02574          }
02575          t = 1;
02576       } else   if (num < 1000000) {
02577          int thousands = num / 1000;
02578          num = num % 1000;
02579          if (thousands == 1) {
02580             if (num) {              
02581                snprintf(fn, sizeof(fn), "digits/1N");
02582                snprintf(fna, sizeof(fna), "digits/thousand");
02583             } else {
02584                if (t) {
02585                   snprintf(fn, sizeof(fn), "digits/1N");
02586                   snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
02587                } else {
02588                   snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
02589                }
02590             }
02591          } else {
02592             res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
02593             if (res) {
02594                return res;
02595             }
02596             if (num) {              
02597                snprintf(fn, sizeof(fn), "digits/thousand");
02598             } else {
02599                snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
02600             }
02601          }
02602          t = 1;
02603       } else if (num < 1000000000) {
02604          int millions = num / 1000000;
02605          num = num % 1000000;
02606          if (millions == 1) {
02607             if (num) {              
02608                snprintf(fn, sizeof(fn), "digits/1F");
02609                snprintf(fna, sizeof(fna), "digits/million");
02610             } else {
02611                snprintf(fn, sizeof(fn), "digits/1N");
02612                snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
02613             }
02614          } else {
02615             res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
02616             if (res) {
02617                return res;
02618             }
02619             if (num) {              
02620                snprintf(fn, sizeof(fn), "digits/millions");
02621             } else {
02622                snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
02623             }
02624          }
02625          t = 1;
02626       } else if (num < INT_MAX) {
02627          int billions = num / 1000000000;
02628          num = num % 1000000000;
02629          if (billions == 1) {
02630             if (num) {              
02631                snprintf(fn, sizeof(fn), "digits/1F");
02632                snprintf(fna, sizeof(fna), "digits/milliard");
02633             } else {
02634                snprintf(fn, sizeof(fn), "digits/1N");
02635                snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
02636             }
02637          } else {
02638             res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
02639             if (res)
02640                return res;
02641             if (num) {              
02642                snprintf(fn, sizeof(fna), "digits/milliards");
02643             } else {
02644                snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
02645             }
02646          }
02647          t = 1;
02648       } else if (num == INT_MAX) {
02649          snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
02650          num = 0;
02651       } else {
02652          ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
02653          res = -1;
02654       }
02655 
02656       if (!res) {
02657          if (!ast_streamfile(chan, fn, language)) {
02658             if ((audiofd > -1) && (ctrlfd > -1)) 
02659                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02660             else  
02661                res = ast_waitstream(chan, ints);
02662          }
02663          ast_stopstream(chan);
02664          if (!res) {
02665             if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
02666                if ((audiofd > -1) && (ctrlfd > -1)) {
02667                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02668                } else {
02669                   res = ast_waitstream(chan, ints);
02670                }
02671             }
02672             ast_stopstream(chan);
02673             strcpy(fna, "");
02674          }
02675       }
02676    }
02677    return res;
02678 }
02679 
02680 /*! \brief  ast_say_enumeration_full_de: German syntax */
02681 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)
02682 {
02683    /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
02684    int res = 0, t = 0;
02685    char fn[256] = "", fna[256] = "";
02686    char *gender;
02687 
02688    if (options && !strncasecmp(options, "f",1)) {
02689       gender = "F";
02690    } else if (options && !strncasecmp(options, "n",1)) {
02691       gender = "N";
02692    } else {
02693       gender = "";
02694    }
02695 
02696    if (!num) 
02697       return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
02698 
02699    while (!res && num) {
02700       if (num < 0) {
02701          snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
02702          if ( num > INT_MIN ) {
02703             num = -num;
02704          } else {
02705             num = 0;
02706          }  
02707       } else if (num < 100 && t) {
02708          snprintf(fn, sizeof(fn), "digits/and");
02709          t = 0;
02710       } else if (num < 20) {
02711          snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
02712          num = 0;
02713       } else if (num < 100) {
02714          int ones = num % 10;
02715          if (ones) {
02716             snprintf(fn, sizeof(fn), "digits/%d-and", ones);
02717             num -= ones;
02718          } else {
02719             snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
02720             num = 0;
02721          }
02722       } else if (num == 100 && t == 0) {
02723          snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
02724          num = 0;
02725       } else if (num < 1000) {
02726          int hundreds = num / 100;
02727          num = num % 100;
02728          if (hundreds == 1) {
02729             snprintf(fn, sizeof(fn), "digits/1N");
02730          } else {
02731             snprintf(fn, sizeof(fn), "digits/%d", hundreds);
02732          }
02733          if (num) {              
02734             snprintf(fna, sizeof(fna), "digits/hundred");
02735          } else {
02736             snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
02737          }
02738          t = 1;
02739       } else   if (num < 1000000) {
02740          int thousands = num / 1000;
02741          num = num % 1000;
02742          if (thousands == 1) {
02743             if (num) {              
02744                snprintf(fn, sizeof(fn), "digits/1N");
02745                snprintf(fna, sizeof(fna), "digits/thousand");
02746             } else {
02747                if (t) {
02748                   snprintf(fn, sizeof(fn), "digits/1N");
02749                   snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
02750                } else {
02751                   snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
02752                }
02753             }
02754          } else {
02755             res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
02756             if (res) {
02757                return res;
02758             }
02759             if (num) {              
02760                snprintf(fn, sizeof(fn), "digits/thousand");
02761             } else {
02762                snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
02763             }
02764          }
02765          t = 1;
02766       } else if (num < 1000000000) {
02767          int millions = num / 1000000;
02768          num = num % 1000000;
02769          if (millions == 1) {
02770             if (num) {              
02771                snprintf(fn, sizeof(fn), "digits/1F");
02772                snprintf(fna, sizeof(fna), "digits/million");
02773             } else {
02774                snprintf(fn, sizeof(fn), "digits/1N");
02775                snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
02776             }
02777          } else {
02778             res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
02779             if (res) {
02780                return res;
02781             }
02782             if (num) {              
02783                snprintf(fn, sizeof(fn), "digits/millions");
02784             } else {
02785                snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
02786             }
02787          }
02788          t = 1;
02789       } else if (num < INT_MAX) {
02790          int billions = num / 1000000000;
02791          num = num % 1000000000;
02792          if (billions == 1) {
02793             if (num) {              
02794                snprintf(fn, sizeof(fn), "digits/1F");
02795                snprintf(fna, sizeof(fna), "digits/milliard");
02796             } else {
02797                snprintf(fn, sizeof(fn), "digits/1N");
02798                snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
02799             }
02800          } else {
02801             res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
02802             if (res)
02803                return res;
02804             if (num) {              
02805                snprintf(fn, sizeof(fna), "digits/milliards");
02806             } else {
02807                snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
02808             }
02809          }
02810          t = 1;
02811       } else if (num == INT_MAX) {
02812          snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
02813          num = 0;
02814       } else {
02815          ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
02816          res = -1;
02817       }
02818 
02819       if (!res) {
02820          if (!ast_streamfile(chan, fn, language)) {
02821             if ((audiofd > -1) && (ctrlfd > -1)) 
02822                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02823             else  
02824                res = ast_waitstream(chan, ints);
02825          }
02826          ast_stopstream(chan);
02827          if (!res) {
02828             if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
02829                if ((audiofd > -1) && (ctrlfd > -1)) {
02830                   res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02831                } else {
02832                   res = ast_waitstream(chan, ints);
02833                }
02834             }
02835             ast_stopstream(chan);
02836             strcpy(fna, "");
02837          }
02838       }
02839    }
02840    return res;
02841 }
02842 
02843 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)
02844 {
02845    int res = 0;
02846    char fn[256] = "";
02847    int mf = -1;            /* +1 = Masculin; -1 = Feminin */
02848    ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
02849 
02850    if (options && !strncasecmp(options, "m", 1)) {
02851       mf = -1;
02852    }
02853 
02854    ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
02855 
02856    while (!res && num) {
02857       if (num < 0) {
02858          snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
02859          if (num > INT_MIN) {
02860             num = -num;
02861          } else {
02862             num = 0;
02863          }
02864       } else if (num < 21) {
02865          if (mf < 0) {
02866             if (num < 10) {
02867                snprintf(fn, sizeof(fn), "digits/f-0%d", num);
02868             } else {
02869                snprintf(fn, sizeof(fn), "digits/f-%d", num);
02870             }
02871          } else {
02872             if (num < 10) {
02873                snprintf(fn, sizeof(fn), "digits/m-0%d", num);
02874             } else {
02875                snprintf(fn, sizeof(fn), "digits/m-%d", num);
02876             }
02877          }
02878          num = 0;
02879       } else if ((num < 100) && num >= 20) {
02880          snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
02881          num = num % 10;
02882       } else if ((num >= 100) && (num < 1000)) {
02883          int tmpnum = num / 100;
02884          snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
02885          num = num - (tmpnum * 100);
02886       } else if ((num >= 1000) && (num < 10000)) {
02887          int tmpnum = num / 1000;
02888          snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
02889          num = num - (tmpnum * 1000);
02890       } else if (num < 20000) {
02891          snprintf(fn, sizeof(fn), "digits/m-%d", (num / 1000));
02892          num = num % 1000;
02893       } else if (num < 1000000) {
02894          res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
02895          if (res) {
02896             return res;
02897          }
02898          snprintf(fn, sizeof(fn), "digits/1k");
02899          num = num % 1000;
02900       } else if (num < 2000000) {
02901          snprintf(fn, sizeof(fn), "digits/1m");
02902          num = num % 1000000;
02903       } else if (num < 3000000) {
02904          snprintf(fn, sizeof(fn), "digits/2m");
02905          num = num - 2000000;
02906       } else if (num < 1000000000) {
02907          res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
02908          if (res) {
02909             return res;
02910          }
02911          snprintf(fn, sizeof(fn), "digits/1m");
02912          num = num % 1000000;
02913       } else {
02914          ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
02915          res = -1;
02916       }
02917       if (!res) {
02918          if (!ast_streamfile(chan, fn, language)) {
02919             if ((audiofd > -1) && (ctrlfd > -1)) {
02920                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
02921             } else {
02922                res = ast_waitstream(chan, ints);
02923             }
02924          }
02925          ast_stopstream(chan);
02926       }
02927    }
02928    return res;
02929 }
02930 
02931 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
02932 {
02933    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
02934       return ast_say_date_en(chan, t, ints, lang);
02935    } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
02936       return ast_say_date_da(chan, t, ints, lang);
02937    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
02938       return ast_say_date_de(chan, t, ints, lang);
02939    } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
02940       return(ast_say_date_es(chan, t, ints, lang));
02941    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
02942       return ast_say_date_fr(chan, t, ints, lang);
02943    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
02944       static int deprecation_warning = 0;
02945       if (deprecation_warning++ % 10 == 0) {
02946          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
02947       }
02948       return ast_say_date_ka(chan, t, ints, lang);
02949    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
02950       return ast_say_date_gr(chan, t, ints, lang);
02951    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
02952       return ast_say_date_he(chan, t, ints, lang);
02953    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
02954       return ast_say_date_ka(chan, t, ints, lang);
02955    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
02956       return ast_say_date_nl(chan, t, ints, lang);
02957    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
02958       return ast_say_date_pt(chan, t, ints, lang);
02959    }
02960 
02961    /* Default to English */
02962    return ast_say_date_en(chan, t, ints, lang);
02963 }
02964 
02965 /* English syntax */
02966 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
02967 {
02968    struct tm tm;
02969    char fn[256];
02970    int res = 0;
02971    ast_localtime(&t,&tm,NULL);
02972    if (!res) {
02973       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
02974       res = ast_streamfile(chan, fn, lang);
02975       if (!res)
02976          res = ast_waitstream(chan, ints);
02977    }
02978    if (!res) {
02979       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
02980       res = ast_streamfile(chan, fn, lang);
02981       if (!res)
02982          res = ast_waitstream(chan, ints);
02983    }
02984    if (!res)
02985       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
02986    if (!res)
02987       res = ast_waitstream(chan, ints);
02988    if (!res)
02989       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
02990    return res;
02991 }
02992 
02993 /* Danish syntax */
02994 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
02995 {
02996    struct tm tm;
02997    char fn[256];
02998    int res = 0;
02999    ast_localtime(&t,&tm,NULL);
03000    if (!res) {
03001       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03002       res = ast_streamfile(chan, fn, lang);
03003       if (!res)
03004          res = ast_waitstream(chan, ints);
03005    }
03006    if (!res)
03007       res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03008    if (!res)
03009       res = ast_waitstream(chan, ints);
03010    if (!res) {
03011       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03012       res = ast_streamfile(chan, fn, lang);
03013       if (!res)
03014          res = ast_waitstream(chan, ints);
03015    }
03016    if (!res) {
03017       /* Year */
03018       int year = tm.tm_year + 1900;
03019       if (year > 1999) {   /* year 2000 and later */
03020          res = ast_say_number(chan, year, ints, lang, (char *) NULL);   
03021       } else {
03022          if (year < 1100) {
03023             /* I'm not going to handle 1100 and prior */
03024             /* We'll just be silent on the year, instead of bombing out. */
03025          } else {
03026              /* year 1100 to 1999. will anybody need this?!? */
03027             snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
03028             res = wait_file(chan, ints, fn, lang);
03029             if (!res) {
03030                res = wait_file(chan,ints, "digits/hundred", lang);
03031                if (!res && year % 100 != 0) {
03032                   res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); 
03033                }
03034             }
03035          }
03036       }
03037    }
03038    return res;
03039 }
03040 
03041 /* German syntax */
03042 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03043 {
03044    struct tm tm;
03045    char fn[256];
03046    int res = 0;
03047    ast_localtime(&t,&tm,NULL);
03048    if (!res) {
03049       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03050       res = ast_streamfile(chan, fn, lang);
03051       if (!res)
03052          res = ast_waitstream(chan, ints);
03053    }
03054    if (!res)
03055       res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03056    if (!res)
03057       res = ast_waitstream(chan, ints);
03058    if (!res) {
03059       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03060       res = ast_streamfile(chan, fn, lang);
03061       if (!res)
03062          res = ast_waitstream(chan, ints);
03063    }
03064    if (!res) {
03065       /* Year */
03066       int year = tm.tm_year + 1900;
03067       if (year > 1999) {   /* year 2000 and later */
03068          res = ast_say_number(chan, year, ints, lang, (char *) NULL);   
03069       } else {
03070          if (year < 1100) {
03071             /* I'm not going to handle 1100 and prior */
03072             /* We'll just be silent on the year, instead of bombing out. */
03073          } else {
03074              /* year 1100 to 1999. will anybody need this?!? */
03075              /* say 1967 as 'neunzehn hundert sieben und sechzig' */
03076             snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
03077             res = wait_file(chan, ints, fn, lang);
03078             if (!res) {
03079                res = wait_file(chan,ints, "digits/hundred", lang);
03080                if (!res && year % 100 != 0) {
03081                   res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); 
03082                }
03083             }
03084          }
03085       }
03086    }
03087    return res;
03088 }
03089 
03090 /* Spanish syntax */
03091 int ast_say_date_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03092 {
03093    struct tm tm;
03094    char fn[256];
03095    int res = 0;
03096    ast_localtime(&t,&tm,NULL);
03097    snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03098    res = wait_file(chan, ints, fn, lang);
03099    if (!res) {
03100       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03101       if (!res)
03102          res = ast_waitstream(chan, ints);
03103    }
03104    if (!res)
03105       res = wait_file(chan, ints, "digits/es-de", lang);
03106    if (!res) {
03107       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03108       res = wait_file(chan, ints, fn, lang);
03109    }
03110    if (!res)
03111       res = wait_file(chan, ints, "digits/es-de", lang);
03112    if (!res) {
03113       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03114       if (!res)
03115          res = ast_waitstream(chan, ints);
03116    }
03117    return res;
03118 }
03119 
03120 /* French syntax */
03121 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03122 {
03123    struct tm tm;
03124    char fn[256];
03125    int res = 0;
03126    ast_localtime(&t,&tm,NULL);
03127    if (!res) {
03128       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03129       res = ast_streamfile(chan, fn, lang);
03130       if (!res)
03131          res = ast_waitstream(chan, ints);
03132    }
03133    if (!res)
03134       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03135    if (!res)
03136       res = ast_waitstream(chan, ints);
03137    if (!res) {
03138       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03139       res = ast_streamfile(chan, fn, lang);
03140       if (!res)
03141          res = ast_waitstream(chan, ints);
03142    }
03143    if (!res)
03144       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03145    return res;
03146 }
03147 
03148 /* Dutch syntax */
03149 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03150 {
03151    struct tm tm;
03152    char fn[256];
03153    int res = 0;
03154    ast_localtime(&t,&tm,NULL);
03155    if (!res) {
03156       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03157       res = ast_streamfile(chan, fn, lang);
03158       if (!res)
03159          res = ast_waitstream(chan, ints);
03160    }
03161    if (!res)
03162       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
03163    if (!res) {
03164       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03165       res = ast_streamfile(chan, fn, lang);
03166       if (!res)
03167          res = ast_waitstream(chan, ints);
03168    }
03169    if (!res)
03170       res = ast_waitstream(chan, ints);
03171    if (!res)
03172       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03173    return res;
03174 }
03175 
03176 /* Portuguese syntax */
03177 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03178 {
03179    struct tm tm;
03180    char fn[256];
03181    int res = 0;
03182 
03183    ast_localtime(&t, &tm, NULL);
03184    snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03185    if (!res)
03186       res = wait_file(chan, ints, fn, lang);
03187    if (!res)
03188       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
03189    if (!res)
03190       res = wait_file(chan, ints, "digits/pt-de", lang);
03191    snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03192    if (!res)
03193       res = wait_file(chan, ints, fn, lang);
03194    if (!res)
03195       res = wait_file(chan, ints, "digits/pt-de", lang);
03196    if (!res)
03197       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03198 
03199    return res;
03200 }
03201 
03202 /* Hebrew syntax */
03203 int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
03204 {
03205    struct tm tm;
03206    char fn[256];
03207    int res = 0;
03208    ast_localtime(&t, &tm, NULL);
03209    if (!res) {
03210       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
03211       res = ast_streamfile(chan, fn, lang);
03212       if (!res) {
03213          res = ast_waitstream(chan, ints);
03214       }
03215    }
03216    if (!res) {
03217       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
03218       res = ast_streamfile(chan, fn, lang);
03219       if (!res) {
03220          res = ast_waitstream(chan, ints);
03221       }
03222    }
03223    if (!res) {
03224       res = ast_say_number(chan, tm.tm_mday, ints, lang, "m");
03225    }
03226    if (!res) {
03227       res = ast_waitstream(chan, ints);
03228    }
03229    if (!res) {
03230       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "m");
03231    }
03232    return res;
03233 }
03234 
03235 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)
03236 {
03237    if (!strncasecmp(lang, "en", 2)) {      /* English syntax */
03238       return ast_say_date_with_format_en(chan, time, ints, lang, format, timezone);
03239    } else if (!strncasecmp(lang, "da", 2)) { /* Danish syntax */
03240       return ast_say_date_with_format_da(chan, time, ints, lang, format, timezone);
03241    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
03242       return ast_say_date_with_format_de(chan, time, ints, lang, format, timezone);
03243    } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
03244       return ast_say_date_with_format_es(chan, time, ints, lang, format, timezone);
03245    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
03246       return ast_say_date_with_format_he(chan, time, ints, lang, format, timezone);
03247    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
03248       return ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone);
03249    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
03250       return ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone);
03251    } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */
03252       return ast_say_date_with_format_it(chan, time, ints, lang, format, timezone);
03253    } else if (!strncasecmp(lang, "mx", 2)) { /* deprecated Mexican syntax */
03254       static int deprecation_warning = 0;
03255       if (deprecation_warning++ % 10 == 0) {
03256          ast_log(LOG_WARNING, "mx is not a standard language code.  Please switch to using es_MX instead.\n");
03257       }
03258       return ast_say_date_with_format_es(chan, time, ints, lang, format, timezone);
03259    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
03260       return ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone);
03261    } else if (!strncasecmp(lang, "pl", 2)) { /* Polish syntax */
03262       return ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone);
03263    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
03264       return ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone);
03265    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
03266       static int deprecation_warning = 0;
03267       if (deprecation_warning++ % 10 == 0) {
03268          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
03269       }
03270       return ast_say_date_with_format_zh(chan, time, ints, lang, format, timezone);
03271    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
03272       return ast_say_date_with_format_zh(chan, time, ints, lang, format, timezone);
03273    }
03274 
03275    /* Default to English */
03276    return ast_say_date_with_format_en(chan, time, ints, lang, format, timezone);
03277 }
03278 
03279 /* English syntax */
03280 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)
03281 {
03282    struct tm tm;
03283    int res=0, offset, sndoffset;
03284    char sndfile[256], nextmsg[256];
03285 
03286    if (format == NULL)
03287       format = "ABdY 'digits/at' IMp";
03288 
03289    ast_localtime(&time,&tm,timezone);
03290 
03291    for (offset=0 ; format[offset] != '\0' ; offset++) {
03292       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03293       switch (format[offset]) {
03294          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03295          case '\'':
03296             /* Literal name of a sound file */
03297             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03298                sndfile[sndoffset] = format[offset];
03299             }
03300             sndfile[sndoffset] = '\0';
03301             res = wait_file(chan,ints,sndfile,lang);
03302             break;
03303          case 'A':
03304          case 'a':
03305             /* Sunday - Saturday */
03306             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03307             res = wait_file(chan,ints,nextmsg,lang);
03308             break;
03309          case 'B':
03310          case 'b':
03311          case 'h':
03312             /* January - December */
03313             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03314             res = wait_file(chan,ints,nextmsg,lang);
03315             break;
03316          case 'm':
03317             /* Month enumerated */
03318             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);  
03319             break;
03320          case 'd':
03321          case 'e':
03322             /* First - Thirtyfirst */
03323             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL); 
03324             break;
03325          case 'Y':
03326             /* Year */
03327             if (tm.tm_year > 99) {
03328                     res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
03329             } else if (tm.tm_year < 1) {
03330                /* I'm not going to handle 1900 and prior */
03331                /* We'll just be silent on the year, instead of bombing out. */
03332             } else {
03333                res = wait_file(chan, ints, "digits/19", lang);
03334                if (!res) {
03335                   if (tm.tm_year <= 9) {
03336                      /* 1901 - 1909 */
03337                      res = wait_file(chan,ints, "digits/oh", lang);
03338                   }
03339 
03340                   res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
03341                }
03342             }
03343             break;
03344          case 'I':
03345          case 'l':
03346             /* 12-Hour */
03347             if (tm.tm_hour == 0)
03348                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
03349             else if (tm.tm_hour > 12)
03350                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
03351             else
03352                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
03353             res = wait_file(chan,ints,nextmsg,lang);
03354             break;
03355          case 'H':
03356          case 'k':
03357             /* 24-Hour */
03358             if (format[offset] == 'H') {
03359                /* e.g. oh-eight */
03360                if (tm.tm_hour < 10) {
03361                   res = wait_file(chan,ints, "digits/oh",lang);
03362                }
03363             } else {
03364                /* e.g. eight */
03365                if (tm.tm_hour == 0) {
03366                   res = wait_file(chan,ints, "digits/oh",lang);
03367                }
03368             }
03369             if (!res) {
03370                if (tm.tm_hour != 0) {
03371                   int remainder = tm.tm_hour;
03372                   if (tm.tm_hour > 20) {
03373                      res = wait_file(chan,ints, "digits/20",lang);
03374                      remainder -= 20;
03375                   }
03376                   if (!res) {
03377                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
03378                      res = wait_file(chan,ints,nextmsg,lang);
03379                   }
03380                }
03381             }
03382             break;
03383          case 'M':
03384          case 'N':
03385             /* Minute */
03386             if (tm.tm_min == 0) {
03387                if (format[offset] == 'M') {
03388                   res = wait_file(chan, ints, "digits/oclock", lang);
03389                } else {
03390                   res = wait_file(chan, ints, "digits/hundred", lang);
03391                }
03392             } else if (tm.tm_min < 10) {
03393                res = wait_file(chan,ints, "digits/oh",lang);
03394                if (!res) {
03395                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
03396                   res = wait_file(chan,ints,nextmsg,lang);
03397                }
03398             } else {
03399                res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
03400             }
03401             break;
03402          case 'P':
03403          case 'p':
03404             /* AM/PM */
03405             if (tm.tm_hour > 11)
03406                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
03407             else
03408                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
03409             res = wait_file(chan,ints,nextmsg,lang);
03410             break;
03411          case 'Q':
03412             /* Shorthand for "Today", "Yesterday", or ABdY */
03413             /* XXX As emphasized elsewhere, this should the native way in your
03414              * language to say the date, with changes in what you say, depending
03415              * upon how recent the date is. XXX */
03416             {
03417                struct timeval now;
03418                struct tm tmnow;
03419                time_t beg_today, tt;
03420 
03421                gettimeofday(&now,NULL);
03422                tt = now.tv_sec;
03423                ast_localtime(&tt,&tmnow,timezone);
03424                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03425                /* In any case, it saves not having to do ast_mktime() */
03426                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03427                if (beg_today < time) {
03428                   /* Today */
03429                   res = wait_file(chan,ints, "digits/today",lang);
03430                } else if (beg_today - 86400 < time) {
03431                   /* Yesterday */
03432                   res = wait_file(chan,ints, "digits/yesterday",lang);
03433                } else if (beg_today - 86400 * 6 < time) {
03434                   /* Within the last week */
03435                   res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
03436                } else if (beg_today - 2628000 < time) {
03437                   /* Less than a month ago - "Sunday, October third" */
03438                   res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
03439                } else if (beg_today - 15768000 < time) {
03440                   /* Less than 6 months ago - "August seventh" */
03441                   res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
03442                } else {
03443                   /* More than 6 months ago - "April nineteenth two thousand three" */
03444                   res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
03445                }
03446             }
03447             break;
03448          case 'q':
03449             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
03450             /* XXX As emphasized elsewhere, this should the native way in your
03451              * language to say the date, with changes in what you say, depending
03452              * upon how recent the date is. XXX */
03453             {
03454                struct timeval now;
03455                struct tm tmnow;
03456                time_t beg_today, tt;
03457 
03458                gettimeofday(&now,NULL);
03459                tt = now.tv_sec;
03460                ast_localtime(&tt,&tmnow,timezone);
03461                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03462                /* In any case, it saves not having to do ast_mktime() */
03463                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03464                if (beg_today < time) {
03465                   /* Today */
03466                } else if ((beg_today - 86400) < time) {
03467                   /* Yesterday */
03468                   res = wait_file(chan,ints, "digits/yesterday",lang);
03469                } else if (beg_today - 86400 * 6 < time) {
03470                   /* Within the last week */
03471                   res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
03472                } else if (beg_today - 2628000 < time) {
03473                   /* Less than a month ago - "Sunday, October third" */
03474                   res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
03475                } else if (beg_today - 15768000 < time) {
03476                   /* Less than 6 months ago - "August seventh" */
03477                   res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
03478                } else {
03479                   /* More than 6 months ago - "April nineteenth two thousand three" */
03480                   res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
03481                }
03482             }
03483             break;
03484          case 'R':
03485             res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
03486             break;
03487          case 'S':
03488             /* Seconds */
03489             if (tm.tm_sec == 0) {
03490                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
03491                res = wait_file(chan,ints,nextmsg,lang);
03492             } else if (tm.tm_sec < 10) {
03493                res = wait_file(chan,ints, "digits/oh",lang);
03494                if (!res) {
03495                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
03496                   res = wait_file(chan,ints,nextmsg,lang);
03497                }
03498             } else {
03499                res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
03500             }
03501             break;
03502          case 'T':
03503             res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
03504             break;
03505          case ' ':
03506          case '   ':
03507             /* Just ignore spaces and tabs */
03508             break;
03509          default:
03510             /* Unknown character */
03511             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
03512       }
03513       /* Jump out on DTMF */
03514       if (res) {
03515          break;
03516       }
03517    }
03518    return res;
03519 }
03520 
03521 static char next_item(const char *format)
03522 {
03523    const char *next = ast_skip_blanks(format);
03524    return *next;
03525 }
03526 
03527 /* Danish syntax */
03528 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)
03529 {
03530    struct tm tm;
03531    int res=0, offset, sndoffset;
03532    char sndfile[256], nextmsg[256];
03533 
03534    if (!format)
03535       format = "A dBY HMS";
03536 
03537    ast_localtime(&time,&tm,timezone);
03538 
03539    for (offset=0 ; format[offset] != '\0' ; offset++) {
03540       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03541       switch (format[offset]) {
03542          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03543          case '\'':
03544             /* Literal name of a sound file */
03545             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03546                sndfile[sndoffset] = format[offset];
03547             }
03548             sndfile[sndoffset] = '\0';
03549             res = wait_file(chan,ints,sndfile,lang);
03550             break;
03551          case 'A':
03552          case 'a':
03553             /* Sunday - Saturday */
03554             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03555             res = wait_file(chan,ints,nextmsg,lang);
03556             break;
03557          case 'B':
03558          case 'b':
03559          case 'h':
03560             /* January - December */
03561             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03562             res = wait_file(chan,ints,nextmsg,lang);
03563             break;
03564          case 'm':
03565             /* Month enumerated */
03566             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");   
03567             break;
03568          case 'd':
03569          case 'e':
03570             /* First - Thirtyfirst */
03571             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");  
03572             break;
03573          case 'Y':
03574             /* Year */
03575             {
03576                int year = tm.tm_year + 1900;
03577                if (year > 1999) {   /* year 2000 and later */
03578                   res = ast_say_number(chan, year, ints, lang, (char *) NULL);   
03579                } else {
03580                   if (year < 1100) {
03581                      /* I'm not going to handle 1100 and prior */
03582                      /* We'll just be silent on the year, instead of bombing out. */
03583                   } else {
03584                       /* year 1100 to 1999. will anybody need this?!? */
03585                       /* say 1967 as 'nineteen hundred seven and sixty' */
03586                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
03587                      res = wait_file(chan,ints,nextmsg,lang);
03588                      if (!res) {
03589                         res = wait_file(chan,ints, "digits/hundred",lang);
03590                         if (!res && year % 100 != 0) {
03591                            res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); 
03592                         }
03593                      }
03594                   }
03595                }
03596             }
03597             break;
03598          case 'I':
03599          case 'l':
03600             /* 12-Hour */
03601             res = wait_file(chan,ints,"digits/oclock",lang);
03602             if (tm.tm_hour == 0)
03603                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
03604             else if (tm.tm_hour > 12)
03605                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
03606             else
03607                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
03608             if (!res) {
03609                res = wait_file(chan,ints,nextmsg,lang);
03610             }
03611             break;
03612          case 'H':
03613             /* 24-Hour, single digit hours preceeded by "oh" (0) */
03614             if (tm.tm_hour < 10 && tm.tm_hour > 0) {
03615                res = wait_file(chan,ints, "digits/0",lang);
03616             }
03617             /* FALLTRHU */
03618          case 'k':
03619             /* 24-Hour */
03620             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
03621             break;
03622          case 'M':
03623             /* Minute */
03624             if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
03625                res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
03626             }
03627             if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
03628                if (tm.tm_min == 1) {
03629                   res = wait_file(chan,ints,"digits/minute",lang);
03630                } else {
03631                   res = wait_file(chan,ints,"digits/minutes",lang);
03632                }
03633             }
03634             break;
03635          case 'P':
03636          case 'p':
03637             /* AM/PM */
03638             if (tm.tm_hour > 11)
03639                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
03640             else
03641                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
03642             res = wait_file(chan,ints,nextmsg,lang);
03643             break;
03644          case 'Q':
03645             /* Shorthand for "Today", "Yesterday", or AdBY */
03646             /* XXX As emphasized elsewhere, this should the native way in your
03647              * language to say the date, with changes in what you say, depending
03648              * upon how recent the date is. XXX */
03649             {
03650                struct timeval now;
03651                struct tm tmnow;
03652                time_t beg_today, tt;
03653 
03654                gettimeofday(&now,NULL);
03655                tt = now.tv_sec;
03656                ast_localtime(&tt,&tmnow,timezone);
03657                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03658                /* In any case, it saves not having to do ast_mktime() */
03659                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03660                if (beg_today < time) {
03661                   /* Today */
03662                   res = wait_file(chan,ints, "digits/today",lang);
03663                } else if (beg_today - 86400 < time) {
03664                   /* Yesterday */
03665                   res = wait_file(chan,ints, "digits/yesterday",lang);
03666                } else {
03667                   res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
03668                }
03669             }
03670             break;
03671          case 'q':
03672             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
03673             /* XXX As emphasized elsewhere, this should the native way in your
03674              * language to say the date, with changes in what you say, depending
03675              * upon how recent the date is. XXX */
03676             {
03677                struct timeval now;
03678                struct tm tmnow;
03679                time_t beg_today, tt;
03680 
03681                gettimeofday(&now,NULL);
03682                tt = now.tv_sec;
03683                ast_localtime(&tt,&tmnow,timezone);
03684                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03685                /* In any case, it saves not having to do ast_mktime() */
03686                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03687                if (beg_today < time) {
03688                   /* Today */
03689                } else if ((beg_today - 86400) < time) {
03690                   /* Yesterday */
03691                   res = wait_file(chan,ints, "digits/yesterday",lang);
03692                } else if (beg_today - 86400 * 6 < time) {
03693                   /* Within the last week */
03694                   res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
03695                } else {
03696                   res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
03697                }
03698             }
03699             break;
03700          case 'R':
03701             res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
03702             break;
03703          case 'S':
03704             /* Seconds */
03705             res = wait_file(chan,ints, "digits/and",lang);
03706             if (!res) {
03707                res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");  
03708                if (!res) {
03709                   res = wait_file(chan,ints, "digits/seconds",lang);
03710                }
03711             }
03712             break;
03713          case 'T':
03714             res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
03715             break;
03716          case ' ':
03717          case '   ':
03718             /* Just ignore spaces and tabs */
03719             break;
03720          default:
03721             /* Unknown character */
03722             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
03723       }
03724       /* Jump out on DTMF */
03725       if (res) {
03726          break;
03727       }
03728    }
03729    return res;
03730 }
03731 
03732 /* German syntax */
03733 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)
03734 {
03735    struct tm tm;
03736    int res=0, offset, sndoffset;
03737    char sndfile[256], nextmsg[256];
03738 
03739    if (!format)
03740       format = "A dBY HMS";
03741 
03742    ast_localtime(&time,&tm,timezone);
03743 
03744    for (offset=0 ; format[offset] != '\0' ; offset++) {
03745       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03746       switch (format[offset]) {
03747          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03748          case '\'':
03749             /* Literal name of a sound file */
03750             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03751                sndfile[sndoffset] = format[offset];
03752             }
03753             sndfile[sndoffset] = '\0';
03754             res = wait_file(chan,ints,sndfile,lang);
03755             break;
03756          case 'A':
03757          case 'a':
03758             /* Sunday - Saturday */
03759             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03760             res = wait_file(chan,ints,nextmsg,lang);
03761             break;
03762          case 'B':
03763          case 'b':
03764          case 'h':
03765             /* January - December */
03766             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03767             res = wait_file(chan,ints,nextmsg,lang);
03768             break;
03769          case 'm':
03770             /* Month enumerated */
03771             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");   
03772             break;
03773          case 'd':
03774          case 'e':
03775             /* First - Thirtyfirst */
03776             res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");  
03777             break;
03778          case 'Y':
03779             /* Year */
03780             {
03781                int year = tm.tm_year + 1900;
03782                if (year > 1999) {   /* year 2000 and later */
03783                   res = ast_say_number(chan, year, ints, lang, (char *) NULL);   
03784                } else {
03785                   if (year < 1100) {
03786                      /* I'm not going to handle 1100 and prior */
03787                      /* We'll just be silent on the year, instead of bombing out. */
03788                   } else {
03789                       /* year 1100 to 1999. will anybody need this?!? */
03790                       /* say 1967 as 'neunzehn hundert sieben und sechzig' */
03791                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
03792                      res = wait_file(chan,ints,nextmsg,lang);
03793                      if (!res) {
03794                         res = wait_file(chan,ints, "digits/hundred",lang);
03795                         if (!res && year % 100 != 0) {
03796                            res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); 
03797                         }
03798                      }
03799                   }
03800                }
03801             }
03802             break;
03803          case 'I':
03804          case 'l':
03805             /* 12-Hour */
03806             if (tm.tm_hour == 0)
03807                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
03808             else if (tm.tm_hour > 12)
03809                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
03810             else
03811                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
03812             res = wait_file(chan,ints,nextmsg,lang);
03813             if (!res) {
03814                res = wait_file(chan,ints,"digits/oclock",lang);
03815             }
03816             break;
03817          case 'H':
03818          case 'k':
03819             /* 24-Hour */
03820             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);   
03821             if (!res) {
03822                res = wait_file(chan,ints,"digits/oclock",lang);
03823             }
03824             break;
03825          case 'M':
03826             /* Minute */
03827             if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
03828                res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say digits/minutes */
03829             } else if (tm.tm_min > 0) {
03830                res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
03831             }
03832 
03833             if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
03834                if (tm.tm_min == 1) {
03835                   res = wait_file(chan,ints,"digits/minute",lang);
03836                } else {
03837                   res = wait_file(chan,ints,"digits/minutes",lang);
03838                }
03839             }
03840             break;
03841          case 'P':
03842          case 'p':
03843             /* AM/PM */
03844             if (tm.tm_hour > 11)
03845                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
03846             else
03847                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
03848             res = wait_file(chan,ints,nextmsg,lang);
03849             break;
03850          case 'Q':
03851             /* Shorthand for "Today", "Yesterday", or AdBY */
03852             /* XXX As emphasized elsewhere, this should the native way in your
03853              * language to say the date, with changes in what you say, depending
03854              * upon how recent the date is. XXX */
03855             {
03856                struct timeval now;
03857                struct tm tmnow;
03858                time_t beg_today, tt;
03859 
03860                gettimeofday(&now,NULL);
03861                tt = now.tv_sec;
03862                ast_localtime(&tt,&tmnow,timezone);
03863                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03864                /* In any case, it saves not having to do ast_mktime() */
03865                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03866                if (beg_today < time) {
03867                   /* Today */
03868                   res = wait_file(chan,ints, "digits/today",lang);
03869                } else if (beg_today - 86400 < time) {
03870                   /* Yesterday */
03871                   res = wait_file(chan,ints, "digits/yesterday",lang);
03872                } else {
03873                   res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
03874                }
03875             }
03876             break;
03877          case 'q':
03878             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
03879             /* XXX As emphasized elsewhere, this should the native way in your
03880              * language to say the date, with changes in what you say, depending
03881              * upon how recent the date is. XXX */
03882             {
03883                struct timeval now;
03884                struct tm tmnow;
03885                time_t beg_today, tt;
03886 
03887                gettimeofday(&now,NULL);
03888                tt = now.tv_sec;
03889                ast_localtime(&tt,&tmnow,timezone);
03890                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
03891                /* In any case, it saves not having to do ast_mktime() */
03892                beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
03893                if (beg_today < time) {
03894                   /* Today */
03895                } else if ((beg_today - 86400) < time) {
03896                   /* Yesterday */
03897                   res = wait_file(chan,ints, "digits/yesterday",lang);
03898                } else if (beg_today - 86400 * 6 < time) {
03899                   /* Within the last week */
03900                   res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
03901                } else {
03902                   res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
03903                }
03904             }
03905             break;
03906          case 'R':
03907             res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
03908             break;
03909          case 'S':
03910             /* Seconds */
03911             res = wait_file(chan,ints, "digits/and",lang);
03912             if (!res) {
03913                res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");  
03914                if (!res) {
03915                   res = wait_file(chan, ints, tm.tm_sec == 1 ? "digits/second" : "digits/seconds", lang);
03916                }
03917             }
03918             break;
03919          case 'T':
03920             res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
03921             break;
03922          case ' ':
03923          case '   ':
03924             /* Just ignore spaces and tabs */
03925             break;
03926          default:
03927             /* Unknown character */
03928             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
03929       }
03930       /* Jump out on DTMF */
03931       if (res) {
03932          break;
03933       }
03934    }
03935    return res;
03936 }
03937 
03938 /* TODO: this probably is not the correct format for doxygen remarks */
03939 
03940 /** ast_say_date_with_format_he Say formmated date in Hebrew
03941  *
03942  * \ref ast_say_date_with_format_en for the details of the options 
03943  *
03944  * Changes from the English version: 
03945  *
03946  * * don't replicate in here the logic of ast_say_number_full_he
03947  *
03948  * * year is always 4-digit (because it's simpler)
03949  *
03950  * * added c, x, and X. Mainly for my tests
03951  *
03952  * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
03953  *
03954  * TODO: 
03955  * * A "ha" is missing in the standard date format, before the 'd'.
03956  * * The numbers of 3000--19000 are not handled well
03957  **/
03958 #define IL_DATE_STR "AdBY"
03959 #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 */
03960 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
03961 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)
03962 {
03963    /* TODO: This whole function is cut&paste from 
03964     * ast_say_date_with_format_en . Is that considered acceptable?
03965     **/
03966    struct tm tm;
03967    int res = 0, offset, sndoffset;
03968    char sndfile[256], nextmsg[256];
03969 
03970    if (!format) {
03971       format = IL_DATE_STR_FULL;
03972    }
03973 
03974    ast_localtime(&time, &tm, timezone);
03975 
03976    for (offset = 0; format[offset] != '\0'; offset++) {
03977       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
03978       switch (format[offset]) {
03979          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
03980          case '\'':
03981             /* Literal name of a sound file */
03982             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
03983                sndfile[sndoffset] = format[offset];
03984             }
03985             sndfile[sndoffset] = '\0';
03986             res = wait_file(chan,ints,sndfile,lang);
03987             break;
03988          case 'A':
03989          case 'a':
03990             /* Sunday - Saturday */
03991             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
03992             res = wait_file(chan,ints,nextmsg,lang);
03993             break;
03994          case 'B':
03995          case 'b':
03996          case 'h':
03997             /* January - December */
03998             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
03999             res = wait_file(chan,ints,nextmsg,lang);
04000             break;
04001          case 'd':
04002          case 'e': /* Day of the month */
04003                                 /* I'm not sure exactly what the parameters 
04004                                  * audiofd and ctrlfd to 
04005                                  * ast_say_number_full_he mean, but it seems
04006                                  * safe to pass -1 there. 
04007                                  *
04008                                  * At least in one of the pathes :-( 
04009                                  */
04010             res = ast_say_number_full_he(chan, tm.tm_mday, ints, lang, "m", -1, -1);
04011             break;
04012          case 'Y': /* Year */
04013             res = ast_say_number_full_he(chan, tm.tm_year+1900,
04014                ints, lang, "f", -1, -1
04015             );
04016             break;
04017          case 'I':
04018          case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
04019          case 'H':
04020          case 'k': /* 24-Hour */
04021             res = ast_say_number_full_he(chan, tm.tm_hour, ints, lang, "f", -1, -1);
04022             break;
04023          case 'M': /* Minute */
04024             if (tm.tm_min >= 0 && tm.tm_min <= 9)  /* say a leading zero if needed */
04025                res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
04026             res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
04027             break;
04028          case 'P':
04029          case 'p':
04030             /* AM/PM - There is no AM/PM in Hebrew... */
04031             break;
04032          case 'Q':
04033             /* Shorthand for "Today", "Yesterday", or "date" */
04034          case 'q':
04035             /* Shorthand for "" (today), "Yesterday", A 
04036                                  * (weekday), or "date" */
04037             /* XXX As emphasized elsewhere, this should the native way in your
04038              * language to say the date, with changes in what you say, depending
04039              * upon how recent the date is. XXX */
04040             {
04041                struct timeval now;
04042                struct tm tmnow;
04043                time_t beg_today, tt;
04044                char todo = format[offset]; /* The letter to format*/
04045 
04046                gettimeofday(&now,NULL);
04047                tt = now.tv_sec;
04048                ast_localtime(&tt,&tmnow,timezone);
04049                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04050                /* In any case, it saves not having to do ast_mktime() */
04051                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04052                if (beg_today < time) {
04053                   /* Today */
04054                   if (todo == 'Q') {
04055                      res = wait_file(chan,
04056                            ints, 
04057                            "digits/today",
04058                            lang);
04059                   }
04060                } else if (beg_today - 86400 < time) {
04061                   /* Yesterday */
04062                   res = wait_file(chan,ints, "digits/yesterday",lang);
04063                } else if ((todo != 'Q') &&
04064                   (beg_today - 86400 * 6 < time))
04065                {
04066                   /* Within the last week */
04067                   res = ast_say_date_with_format_he(chan,
04068                                 time, ints, lang, 
04069                                 "A", timezone);
04070                } else {
04071                   res = ast_say_date_with_format_he(chan,
04072                                 time, ints, lang, 
04073                                 IL_DATE_STR, timezone);
04074                }
04075             }
04076             break;
04077          case 'R':
04078             res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
04079             break;
04080          case 'S': /* Seconds */
04081             res = ast_say_number_full_he(chan, tm.tm_sec,
04082                ints, lang, "f", -1, -1
04083             );
04084             break;
04085          case 'T':
04086             res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
04087             break;
04088          /* c, x, and X seem useful for testing. Not sure
04089                          * if thiey're good for the general public */
04090          case 'c':
04091             res = ast_say_date_with_format_he(chan, time, 
04092                                     ints, lang, IL_DATE_STR_FULL, timezone);
04093             break;
04094          case 'x':
04095             res = ast_say_date_with_format_he(chan, time, 
04096                                     ints, lang, IL_DATE_STR, timezone);
04097             break;
04098          case 'X': /* Currently not locale-dependent...*/
04099             res = ast_say_date_with_format_he(chan, time, 
04100                                     ints, lang, IL_TIME_STR, timezone);
04101             break;
04102          case ' ':
04103          case '   ':
04104             /* Just ignore spaces and tabs */
04105             break;
04106          default:
04107             /* Unknown character */
04108             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04109       }
04110       /* Jump out on DTMF */
04111       if (res) {
04112          break;
04113       }
04114    }
04115    return res;
04116 }
04117 
04118 
04119 /* Spanish syntax */
04120 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)
04121 {
04122    struct tm tm;
04123    int res=0, offset, sndoffset;
04124    char sndfile[256], nextmsg[256];
04125 
04126    if (format == NULL)
04127       format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
04128 
04129    ast_localtime(&time,&tm,timezone);
04130 
04131    for (offset=0 ; format[offset] != '\0' ; offset++) {
04132       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04133       switch (format[offset]) {
04134          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04135          case '\'':
04136             /* Literal name of a sound file */
04137             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04138                sndfile[sndoffset] = format[offset];
04139             }
04140             sndfile[sndoffset] = '\0';
04141             snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
04142             res = wait_file(chan,ints,nextmsg,lang);
04143             break;
04144          case 'A':
04145          case 'a':
04146             /* Sunday - Saturday */
04147             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04148             res = wait_file(chan,ints,nextmsg,lang);
04149             break;
04150          case 'B':
04151          case 'b':
04152          case 'h':
04153             /* January - December */
04154             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04155             res = wait_file(chan,ints,nextmsg,lang);
04156             break;
04157          case 'm':
04158             /* First - Twelfth */
04159             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04160             res = wait_file(chan,ints,nextmsg,lang);
04161             break;
04162          case 'd':
04163          case 'e':
04164             /* First - Thirtyfirst */
04165             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
04166             break;
04167          case 'Y':
04168             /* Year */
04169             res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
04170             break;
04171          case 'I':
04172          case 'l':
04173             /* 12-Hour */
04174             if (tm.tm_hour == 0)
04175                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04176             else if (tm.tm_hour > 12)
04177                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04178             else
04179                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04180             res = wait_file(chan,ints,nextmsg,lang);
04181             break;
04182          case 'H':
04183          case 'k':
04184             /* 24-Hour */
04185             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04186             if ((!res) && (format[offset] == 'H')) {
04187                if (tm.tm_hour == 1) {
04188                   res = wait_file(chan,ints,"digits/hour",lang);
04189                } else {
04190                   res = wait_file(chan,ints,"digits/hours",lang);
04191                }
04192             }
04193             break;
04194             break;
04195          case 'M':
04196             /* Minute */
04197             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL); 
04198             if (!res) {
04199                if (tm.tm_min == 1) {
04200                   res = wait_file(chan,ints,"digits/minute",lang);
04201                } else {
04202                   res = wait_file(chan,ints,"digits/minutes",lang);
04203                }
04204             }
04205             break;
04206          case 'P':
04207          case 'p':
04208             /* AM/PM */
04209             if (tm.tm_hour > 18)
04210                res = wait_file(chan, ints, "digits/p-m", lang);
04211             else if (tm.tm_hour > 12)
04212                res = wait_file(chan, ints, "digits/afternoon", lang);
04213             else if (tm.tm_hour)
04214                res = wait_file(chan, ints, "digits/a-m", lang);
04215             break;
04216          case 'Q':
04217             /* Shorthand for "Today", "Yesterday", or ABdY */
04218             /* XXX As emphasized elsewhere, this should the native way in your
04219              * language to say the date, with changes in what you say, depending
04220              * upon how recent the date is. XXX */
04221             {
04222                struct timeval now;
04223                struct tm tmnow;
04224                time_t beg_today, tt;
04225 
04226                gettimeofday(&now,NULL);
04227                tt = now.tv_sec;
04228                ast_localtime(&tt,&tmnow,timezone);
04229                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04230                /* In any case, it saves not having to do ast_mktime() */
04231                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04232                if (beg_today < time) {
04233                   /* Today */
04234                   res = wait_file(chan,ints, "digits/today",lang);
04235                } else if (beg_today - 86400 < time) {
04236                   /* Yesterday */
04237                   res = wait_file(chan,ints, "digits/yesterday",lang);
04238                } else {
04239                   res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
04240                }
04241             }
04242             break;
04243          case 'q':
04244             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04245             /* XXX As emphasized elsewhere, this should the native way in your
04246              * language to say the date, with changes in what you say, depending
04247              * upon how recent the date is. XXX */
04248             {
04249                struct timeval now;
04250                struct tm tmnow;
04251                time_t beg_today, tt;
04252 
04253                gettimeofday(&now,NULL);
04254                tt = now.tv_sec;
04255                ast_localtime(&tt,&tmnow,timezone);
04256                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04257                /* In any case, it saves not having to do ast_mktime() */
04258                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04259                if (beg_today < time) {
04260                   /* Today */
04261                   res = wait_file(chan,ints, "digits/today",lang);
04262                } else if ((beg_today - 86400) < time) {
04263                   /* Yesterday */
04264                   res = wait_file(chan,ints, "digits/yesterday",lang);
04265                } else if (beg_today - 86400 * 6 < time) {
04266                   /* Within the last week */
04267                   res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
04268                } else {
04269                   res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
04270                }
04271             }
04272             break;
04273          case 'R':
04274             res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/and' M", timezone);
04275             break;
04276          case 'S':
04277             /* Seconds */
04278             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
04279             if (!res) {
04280                if (tm.tm_sec == 1) {
04281                   res = wait_file(chan,ints,"digits/second",lang);
04282                } else {
04283                   res = wait_file(chan,ints,"digits/seconds",lang);
04284                }
04285             }
04286             break;
04287          case 'T':
04288             res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
04289             break;
04290          case ' ':
04291          case '   ':
04292             /* Just ignore spaces and tabs */
04293             break;
04294          default:
04295             /* Unknown character */
04296             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04297       }
04298       /* Jump out on DTMF */
04299       if (res) {
04300          break;
04301       }
04302    }
04303    return res;
04304 }
04305 
04306 /* French syntax 
04307 oclock = heure
04308 */
04309 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)
04310 {
04311    struct tm tm;
04312    int res=0, offset, sndoffset;
04313    char sndfile[256], nextmsg[256];
04314 
04315    if (format == NULL)
04316       format = "AdBY 'digits/at' IMp";
04317 
04318    ast_localtime(&time,&tm,timezone);
04319 
04320    for (offset=0 ; format[offset] != '\0' ; offset++) {
04321       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04322       switch (format[offset]) {
04323          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04324          case '\'':
04325             /* Literal name of a sound file */
04326             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04327                sndfile[sndoffset] = format[offset];
04328             }
04329             sndfile[sndoffset] = '\0';
04330             res = wait_file(chan,ints,sndfile,lang);
04331             break;
04332          case 'A':
04333          case 'a':
04334             /* Sunday - Saturday */
04335             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04336             res = wait_file(chan,ints,nextmsg,lang);
04337             break;
04338          case 'B':
04339          case 'b':
04340          case 'h':
04341             /* January - December */
04342             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04343             res = wait_file(chan,ints,nextmsg,lang);
04344             break;
04345          case 'm':
04346             /* First - Twelfth */
04347             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04348             res = wait_file(chan,ints,nextmsg,lang);
04349             break;
04350          case 'd':
04351          case 'e':
04352             /* First */
04353             if (tm.tm_mday == 1) {
04354                snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
04355                res = wait_file(chan,ints,nextmsg,lang);
04356             } else {
04357                res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
04358             }
04359             break;
04360          case 'Y':
04361             /* Year */
04362             if (tm.tm_year > 99) {
04363                res = wait_file(chan,ints, "digits/2",lang);
04364                if (!res) {
04365                   res = wait_file(chan,ints, "digits/thousand",lang);
04366                }
04367                if (tm.tm_year > 100) {
04368                   if (!res) {
04369                      res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
04370                   }
04371                }
04372             } else {
04373                if (tm.tm_year < 1) {
04374                   /* I'm not going to handle 1900 and prior */
04375                   /* We'll just be silent on the year, instead of bombing out. */
04376                } else {
04377                   res = wait_file(chan,ints, "digits/thousand",lang);
04378                   if (!res) {
04379                      wait_file(chan,ints, "digits/9",lang);
04380                      wait_file(chan,ints, "digits/hundred",lang);
04381                      res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
04382                   }
04383                }
04384             }
04385             break;
04386          case 'I':
04387          case 'l':
04388             /* 12-Hour */
04389             if (tm.tm_hour == 0)
04390                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04391             else if (tm.tm_hour > 12)
04392                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04393             else
04394                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04395             res = wait_file(chan,ints,nextmsg,lang);
04396             if (!res)
04397                res = wait_file(chan,ints, "digits/oclock",lang);
04398             break;
04399          case 'H':
04400          case 'k':
04401             /* 24-Hour */
04402             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
04403             if (!res)
04404                res = wait_file(chan,ints, "digits/oclock",lang);
04405             break;
04406          case 'M':
04407             /* Minute */
04408             if (tm.tm_min == 0) {
04409                break;
04410             }
04411             res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
04412             break;
04413          case 'P':
04414          case 'p':
04415             /* AM/PM */
04416             if (tm.tm_hour > 11)
04417                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
04418             else
04419                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
04420             res = wait_file(chan,ints,nextmsg,lang);
04421             break;
04422          case 'Q':
04423             /* Shorthand for "Today", "Yesterday", or AdBY */
04424             /* XXX As emphasized elsewhere, this should the native way in your
04425              * language to say the date, with changes in what you say, depending
04426              * upon how recent the date is. XXX */
04427             {
04428                struct timeval now;
04429                struct tm tmnow;
04430                time_t beg_today, tt;
04431 
04432                gettimeofday(&now,NULL);
04433                tt = now.tv_sec;
04434                ast_localtime(&tt,&tmnow,timezone);
04435                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04436                /* In any case, it saves not having to do ast_mktime() */
04437                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04438                if (beg_today < time) {
04439                   /* Today */
04440                   res = wait_file(chan,ints, "digits/today",lang);
04441                } else if (beg_today - 86400 < time) {
04442                   /* Yesterday */
04443                   res = wait_file(chan,ints, "digits/yesterday",lang);
04444                } else {
04445                   res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
04446                }
04447             }
04448             break;
04449          case 'q':
04450             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
04451             /* XXX As emphasized elsewhere, this should the native way in your
04452              * language to say the date, with changes in what you say, depending
04453              * upon how recent the date is. XXX */
04454             {
04455                struct timeval now;
04456                struct tm tmnow;
04457                time_t beg_today, tt;
04458 
04459                gettimeofday(&now,NULL);
04460                tt = now.tv_sec;
04461                ast_localtime(&tt,&tmnow,timezone);
04462                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04463                /* In any case, it saves not having to do ast_mktime() */
04464                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04465                if (beg_today < time) {
04466                   /* Today */
04467                } else if ((beg_today - 86400) < time) {
04468                   /* Yesterday */
04469                   res = wait_file(chan,ints, "digits/yesterday",lang);
04470                } else if (beg_today - 86400 * 6 < time) {
04471                   /* Within the last week */
04472                   res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
04473                } else {
04474                   res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
04475                }
04476             }
04477             break;
04478          case 'R':
04479             res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
04480             break;
04481          case 'S':
04482             /* Seconds */
04483             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
04484             if (!res) {
04485                res = wait_file(chan,ints, "digits/second",lang);
04486             }
04487             break;
04488          case 'T':
04489             res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
04490             break;
04491          case ' ':
04492          case '   ':
04493             /* Just ignore spaces and tabs */
04494             break;
04495          default:
04496             /* Unknown character */
04497             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04498       }
04499       /* Jump out on DTMF */
04500       if (res) {
04501          break;
04502       }
04503    }
04504    return res;
04505 }
04506 
04507 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)
04508 {
04509    struct tm tm;
04510    int res=0, offset, sndoffset;
04511    char sndfile[256], nextmsg[256];
04512 
04513    if (format == NULL)
04514       format = "AdB 'digits/at' IMp";
04515 
04516    ast_localtime(&time,&tm,timezone);
04517 
04518    for (offset=0 ; format[offset] != '\0' ; offset++) {
04519       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04520       switch (format[offset]) {
04521          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04522          case '\'':
04523             /* Literal name of a sound file */
04524             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04525                sndfile[sndoffset] = format[offset];
04526             }
04527             sndfile[sndoffset] = '\0';
04528             res = wait_file(chan,ints,sndfile,lang);
04529             break;
04530          case 'A':
04531          case 'a':
04532             /* Sunday - Saturday */
04533             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04534             res = wait_file(chan,ints,nextmsg,lang);
04535             break;
04536          case 'B':
04537          case 'b':
04538          case 'h':
04539             /* January - December */
04540             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04541             res = wait_file(chan,ints,nextmsg,lang);
04542             break;
04543          case 'm':
04544             /* First - Twelfth */
04545             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04546             res = wait_file(chan,ints,nextmsg,lang);
04547             break;
04548          case 'd':
04549          case 'e':
04550             /* First day of the month is spelled as ordinal */
04551             if (tm.tm_mday == 1) {
04552                snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
04553                res = wait_file(chan,ints,nextmsg,lang);
04554             } else {
04555                if (!res) {
04556                   res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
04557                }
04558             }
04559             break;
04560          case 'Y':
04561             /* Year */
04562             if (tm.tm_year > 99) {
04563                res = wait_file(chan,ints, "digits/ore-2000",lang);
04564                if (tm.tm_year > 100) {
04565                   if (!res) {
04566                   /* This works until the end of 2021 */
04567                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
04568                   res = wait_file(chan,ints,nextmsg,lang);
04569                   }
04570                }
04571             } else {
04572                if (tm.tm_year < 1) {
04573                   /* I'm not going to handle 1900 and prior */
04574                   /* We'll just be silent on the year, instead of bombing out. */
04575                } else {
04576                   res = wait_file(chan,ints, "digits/ore-1900",lang);
04577                   if ((!res) && (tm.tm_year != 0)) {
04578                      if (tm.tm_year <= 21) {
04579                         /* 1910 - 1921 */
04580                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
04581                         res = wait_file(chan,ints,nextmsg,lang);
04582                      } else {
04583                         /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
04584                         int ten, one;
04585                         ten = tm.tm_year / 10;
04586                         one = tm.tm_year % 10;
04587                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
04588                         res = wait_file(chan,ints,nextmsg,lang);
04589                         if (!res) {
04590                            if (one != 0) {
04591                               snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
04592                               res = wait_file(chan,ints,nextmsg,lang);
04593                            }
04594                         }
04595                      }
04596                   }
04597                }
04598             }
04599             break;
04600          case 'I':
04601          case 'l':
04602             /* 12-Hour */
04603             if (tm.tm_hour == 0)
04604                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04605             else if (tm.tm_hour > 12)
04606                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04607             else
04608                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04609                res = wait_file(chan,ints,nextmsg,lang);
04610             break;
04611          case 'H':
04612          case 'k':
04613             /* 24-Hour */
04614             if (tm.tm_hour == 0) {
04615                res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
04616             } else if (tm.tm_hour == 1) {
04617                res = wait_file(chan,ints, "digits/ore-una",lang);
04618             } else {
04619                res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04620             }
04621             break;
04622          case 'M':
04623             /* Minute */
04624             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
04625             break;
04626          case 'P':
04627          case 'p':
04628             /* AM/PM */
04629             if (tm.tm_hour > 11)
04630                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
04631             else
04632                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
04633                res = wait_file(chan,ints,nextmsg,lang);
04634             break;
04635          case 'Q':
04636             /* Shorthand for "Today", "Yesterday", or ABdY */
04637             /* XXX As emphasized elsewhere, this should the native way in your
04638              * language to say the date, with changes in what you say, depending
04639              * upon how recent the date is. XXX */
04640             {
04641                struct timeval now;
04642                struct tm tmnow;
04643                time_t beg_today, tt;
04644    
04645                gettimeofday(&now,NULL);
04646                tt = now.tv_sec;
04647                ast_localtime(&tt,&tmnow,timezone);
04648                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04649                /* In any case, it saves not having to do ast_mktime() */
04650                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04651                if (beg_today < time) {
04652                   /* Today */
04653                   res = wait_file(chan,ints, "digits/today",lang);
04654                } else if (beg_today - 86400 < time) {
04655                   /* Yesterday */
04656                   res = wait_file(chan,ints, "digits/yesterday",lang);
04657                } else {
04658                   res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
04659                }
04660             }
04661             break;
04662          case 'q':
04663             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04664             {
04665                struct timeval now;
04666                struct tm tmnow;
04667                time_t beg_today, tt;
04668    
04669                gettimeofday(&now,NULL);
04670                tt = now.tv_sec;
04671                ast_localtime(&tt,&tmnow,timezone);
04672                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04673                /* In any case, it saves not having to do ast_mktime() */
04674                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04675                if (beg_today < time) {
04676                   /* Today */
04677                } else if ((beg_today - 86400) < time) {
04678                   /* Yesterday */
04679                   res = wait_file(chan,ints, "digits/yesterday",lang);
04680                } else if (beg_today - 86400 * 6 < time) {
04681                   /* Within the last week */
04682                   res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
04683                } else {
04684                   res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
04685                }
04686             }
04687             break;
04688          case 'R':
04689             res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
04690             break;
04691          case 'S':
04692             /* Seconds */
04693             if (tm.tm_sec == 0) {
04694                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
04695                res = wait_file(chan,ints,nextmsg,lang);
04696             } else if (tm.tm_sec < 10) {
04697                res = wait_file(chan,ints, "digits/oh",lang);
04698                if (!res) {
04699                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
04700                   res = wait_file(chan,ints,nextmsg,lang);
04701                }
04702             } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
04703                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
04704                res = wait_file(chan,ints,nextmsg,lang);
04705             } else {
04706                int ten, one;
04707                ten = (tm.tm_sec / 10) * 10;
04708                one = (tm.tm_sec % 10);
04709                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
04710                res = wait_file(chan,ints,nextmsg,lang);
04711                if (!res) {
04712                   /* Fifty, not fifty-zero */
04713                   if (one != 0) {
04714                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
04715                      res = wait_file(chan,ints,nextmsg,lang);
04716                   }
04717                }
04718             }
04719               break;
04720          case 'T':
04721             res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
04722             break;
04723          case ' ':
04724          case '   ':
04725             /* Just ignore spaces and tabs */
04726             break;
04727          default:
04728             /* Unknown character */
04729             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04730       }
04731       /* Jump out on DTMF */
04732       if (res) {
04733          break;
04734       }
04735    }
04736    return res;
04737 }
04738 
04739 /* Dutch syntax */
04740 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)
04741 {
04742    struct tm tm;
04743    int res=0, offset, sndoffset;
04744    char sndfile[256], nextmsg[256];
04745 
04746    if (format == NULL)
04747       format = "ABdY 'digits/at' IMp";
04748 
04749    ast_localtime(&time,&tm,timezone);
04750 
04751    for (offset=0 ; format[offset] != '\0' ; offset++) {
04752       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04753       switch (format[offset]) {
04754          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04755          case '\'':
04756             /* Literal name of a sound file */
04757             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04758                sndfile[sndoffset] = format[offset];
04759             }
04760             sndfile[sndoffset] = '\0';
04761             res = wait_file(chan,ints,sndfile,lang);
04762             break;
04763          case 'A':
04764          case 'a':
04765             /* Sunday - Saturday */
04766             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04767             res = wait_file(chan,ints,nextmsg,lang);
04768             break;
04769          case 'B':
04770          case 'b':
04771          case 'h':
04772             /* January - December */
04773             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04774             res = wait_file(chan,ints,nextmsg,lang);
04775             break;
04776          case 'm':
04777             /* First - Twelfth */
04778             snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
04779             res = wait_file(chan,ints,nextmsg,lang);
04780             break;
04781          case 'd':
04782          case 'e':
04783             /* First - Thirtyfirst */
04784             res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
04785             break;
04786          case 'Y':
04787             /* Year */
04788             if (tm.tm_year > 99) {
04789                res = wait_file(chan,ints, "digits/2",lang);
04790                if (!res) {
04791                   res = wait_file(chan,ints, "digits/thousand",lang);
04792                }
04793                if (tm.tm_year > 100) {
04794                   if (!res) {
04795                      /* This works until the end of 2020 */
04796                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
04797                      res = wait_file(chan,ints,nextmsg,lang);
04798                   }
04799                }
04800             } else {
04801                if (tm.tm_year < 1) {
04802                   /* I'm not going to handle 1900 and prior */
04803                   /* We'll just be silent on the year, instead of bombing out. */
04804                } else {
04805                   res = wait_file(chan,ints, "digits/19",lang);
04806                   if (!res) {
04807                      if (tm.tm_year <= 9) {
04808                         /* 1901 - 1909 */
04809                         res = wait_file(chan,ints, "digits/oh",lang);
04810                         if (!res) {
04811                            snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
04812                            res = wait_file(chan,ints,nextmsg,lang);
04813                         }
04814                      } else if (tm.tm_year <= 20) {
04815                         /* 1910 - 1920 */
04816                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
04817                         res = wait_file(chan,ints,nextmsg,lang);
04818                      } else {
04819                         /* 1921 - 1999 */
04820                         int ten, one;
04821                         ten = tm.tm_year / 10;
04822                         one = tm.tm_year % 10;
04823                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
04824                         res = wait_file(chan,ints,nextmsg,lang);
04825                         if (!res) {
04826                            if (one != 0) {
04827                               snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
04828                               res = wait_file(chan,ints,nextmsg,lang);
04829                            }
04830                         }
04831                      }
04832                   }
04833                }
04834             }
04835             break;
04836          case 'I':
04837          case 'l':
04838             /* 12-Hour */
04839             if (tm.tm_hour == 0)
04840                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
04841             else if (tm.tm_hour > 12)
04842                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
04843             else
04844                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
04845             res = wait_file(chan,ints,nextmsg,lang);
04846             break;
04847          case 'H':
04848          case 'k':
04849             /* 24-Hour */
04850             res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
04851             if (!res) {
04852                res = wait_file(chan,ints, "digits/nl-uur",lang);
04853             }
04854             break;
04855          case 'M':
04856             /* Minute */
04857             res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
04858             break;
04859          case 'P':
04860          case 'p':
04861             /* AM/PM */
04862             if (tm.tm_hour > 11)
04863                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
04864             else
04865                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
04866             res = wait_file(chan,ints,nextmsg,lang);
04867             break;
04868          case 'Q':
04869             /* Shorthand for "Today", "Yesterday", or ABdY */
04870             /* XXX As emphasized elsewhere, this should the native way in your
04871              * language to say the date, with changes in what you say, depending
04872              * upon how recent the date is. XXX */
04873             {
04874                struct timeval now;
04875                struct tm tmnow;
04876                time_t beg_today, tt;
04877 
04878                gettimeofday(&now,NULL);
04879                tt = now.tv_sec;
04880                ast_localtime(&tt,&tmnow,timezone);
04881                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04882                /* In any case, it saves not having to do ast_mktime() */
04883                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04884                if (beg_today < time) {
04885                   /* Today */
04886                   res = wait_file(chan,ints, "digits/today",lang);
04887                } else if (beg_today - 86400 < time) {
04888                   /* Yesterday */
04889                   res = wait_file(chan,ints, "digits/yesterday",lang);
04890                } else {
04891                   res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
04892                }
04893             }
04894             break;
04895          case 'q':
04896             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
04897             {
04898                struct timeval now;
04899                struct tm tmnow;
04900                time_t beg_today, tt;
04901 
04902                gettimeofday(&now,NULL);
04903                tt = now.tv_sec;
04904                ast_localtime(&tt,&tmnow,timezone);
04905                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
04906                /* In any case, it saves not having to do ast_mktime() */
04907                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
04908                if (beg_today < time) {
04909                   /* Today */
04910                } else if ((beg_today - 86400) < time) {
04911                   /* Yesterday */
04912                   res = wait_file(chan,ints, "digits/yesterday",lang);
04913                } else if (beg_today - 86400 * 6 < time) {
04914                   /* Within the last week */
04915                   res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
04916                } else {
04917                   res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
04918                }
04919             }
04920             break;
04921          case 'R':
04922             res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
04923             break;
04924          case 'S':
04925             /* Seconds */
04926             res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
04927             break;
04928          case 'T':
04929             res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
04930             break;
04931          case ' ':
04932          case '   ':
04933             /* Just ignore spaces and tabs */
04934             break;
04935          default:
04936             /* Unknown character */
04937             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
04938       }
04939       /* Jump out on DTMF */
04940       if (res) {
04941          break;
04942       }
04943    }
04944    return res;
04945 }
04946 
04947 /* Polish syntax */
04948 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)
04949 {
04950    struct tm tm;
04951    int res=0, offset, sndoffset;
04952    char sndfile[256], nextmsg[256];
04953 
04954    ast_localtime(&thetime, &tm, timezone);
04955 
04956    for (offset = 0 ; format[offset] != '\0' ; offset++) {
04957       int remainder;
04958       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
04959       switch (format[offset]) {
04960          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
04961          case '\'':
04962             /* Literal name of a sound file */
04963             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
04964                sndfile[sndoffset] = format[offset];
04965             }
04966             sndfile[sndoffset] = '\0';
04967             res = wait_file(chan, ints, sndfile, lang);
04968             break;
04969          case 'A':
04970          case 'a':
04971             /* Sunday - Saturday */
04972             snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
04973             res = wait_file(chan, ints, nextmsg, lang);
04974             break;
04975          case 'B':
04976          case 'b':
04977          case 'h':
04978             /* January - December */
04979             snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
04980             res = wait_file(chan, ints, nextmsg, lang);
04981             break;
04982          case 'm':
04983             /* Month enumerated */
04984             res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
04985             break;
04986          case 'd':
04987          case 'e':
04988             /* First - Thirtyfirst */
04989             remainder = tm.tm_mday;
04990             if (tm.tm_mday > 30) {
04991                res = wait_file(chan, ints, "digits/h-30", lang);
04992                remainder -= 30;
04993             }
04994             if (tm.tm_mday > 20 && tm.tm_mday < 30) {
04995                res = wait_file(chan, ints, "digits/h-20", lang);
04996                remainder -= 20;
04997             }
04998             if (!res) {
04999                snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
05000                res = wait_file(chan, ints, nextmsg, lang);
05001             }
05002             break;
05003          case 'Y':
05004             /* Year */
05005             if (tm.tm_year > 100) {
05006                res = wait_file(chan, ints, "digits/2", lang);
05007                if (!res)
05008                   res = wait_file(chan, ints, "digits/1000.2",lang);
05009                if (tm.tm_year > 100) {
05010                   if (!res)
05011                      res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
05012                }
05013             } else if (tm.tm_year == 100) {
05014                res = wait_file(chan, ints, "digits/h-2000", lang);
05015             } else {
05016                if (tm.tm_year < 1) {
05017                   /* I'm not going to handle 1900 and prior */
05018                   /* We'll just be silent on the year, instead of bombing out. */
05019                   break;
05020                } else {
05021                   res = wait_file(chan, ints, "digits/1000", lang);
05022                   if (!res) {
05023                      wait_file(chan, ints, "digits/900", lang);
05024                      res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
05025                   }
05026                }
05027             }
05028             if (!res)
05029                wait_file(chan, ints, "digits/year", lang);
05030             break;
05031          case 'I':
05032          case 'l':
05033             /* 12-Hour */
05034             if (tm.tm_hour == 0)
05035                snprintf(nextmsg, sizeof(nextmsg), "digits/t-12");
05036             else if (tm.tm_hour > 12)
05037                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
05038             else 
05039                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
05040 
05041             res = wait_file(chan, ints, nextmsg, lang);
05042             break;
05043          case 'H':
05044          case 'k':
05045             /* 24-Hour */
05046             if (tm.tm_hour != 0) {
05047                snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
05048                res = wait_file(chan, ints, nextmsg, lang);
05049             } else 
05050                res = wait_file(chan, ints, "digits/t-24", lang);
05051             break;
05052          case 'M':
05053          case 'N':
05054             /* Minute */
05055             if (tm.tm_min == 0) {
05056                if (format[offset] == 'M') {
05057                   res = wait_file(chan, ints, "digits/oclock", lang);
05058                } else {
05059                   res = wait_file(chan, ints, "digits/100", lang);
05060                }
05061             } else
05062                res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); 
05063             break;
05064          case 'P':
05065          case 'p':
05066             /* AM/PM */
05067             if (tm.tm_hour > 11)
05068                snprintf(nextmsg, sizeof(nextmsg), "digits/p-m");
05069             else
05070                snprintf(nextmsg, sizeof(nextmsg), "digits/a-m");
05071             res = wait_file(chan, ints, nextmsg, lang);
05072             break;
05073          case 'Q':
05074             /* Shorthand for "Today", "Yesterday", or AdBY */
05075             {
05076                time_t tv_sec = time(NULL);
05077                struct tm tmnow;
05078                time_t beg_today;
05079 
05080                ast_localtime(&tv_sec,&tmnow, timezone);
05081                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05082                /* In any case, it saves not having to do ast_mktime() */
05083                beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05084                if (beg_today < thetime) {
05085                   /* Today */
05086                   res = wait_file(chan, ints, "digits/today", lang);
05087                } else if (beg_today - 86400 < thetime) {
05088                   /* Yesterday */
05089                   res = wait_file(chan, ints, "digits/yesterday", lang);
05090                } else {
05091                   res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
05092                }
05093             }
05094             break;
05095          case 'q':
05096             /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
05097             {
05098                time_t tv_sec = time(NULL);
05099                struct tm tmnow;
05100                time_t beg_today;
05101 
05102                ast_localtime(&tv_sec, &tmnow, timezone);
05103                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05104                /* In any case, it saves not having to do ast_mktime() */
05105                beg_today = tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05106                if (beg_today < thetime) {
05107                   /* Today */
05108                } else if ((beg_today - 86400) < thetime) {
05109                   /* Yesterday */
05110                   res = wait_file(chan, ints, "digits/yesterday", lang);
05111                } else if (beg_today - 86400 * 6 < thetime) {
05112                   /* Within the last week */
05113                   res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
05114                } else {
05115                   res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
05116                }
05117             }
05118             break;
05119          case 'R':
05120             res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
05121             break;
05122          case 'S':
05123             /* Seconds */
05124             res = wait_file(chan, ints, "digits/and", lang);
05125             if (!res) {
05126                if (tm.tm_sec == 1) {
05127                   res = wait_file(chan, ints, "digits/1z", lang);
05128                   if (!res)
05129                      res = wait_file(chan, ints, "digits/second-a", lang);
05130                } else {
05131                   res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
05132                   if (!res) {
05133                      int ten, one;
05134                      ten = tm.tm_sec / 10;
05135                      one = tm.tm_sec % 10;
05136                      
05137                      if (one > 1 && one < 5 && ten != 1)
05138                         res = wait_file(chan,ints, "digits/seconds",lang);
05139                      else
05140                         res = wait_file(chan,ints, "digits/second",lang);
05141                   }
05142                }
05143             }
05144             break;
05145          case 'T':
05146             res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
05147             break;
05148          case ' ':
05149          case '   ':
05150             /* Just ignore spaces and tabs */
05151             break;
05152          default:
05153             /* Unknown character */
05154             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05155       }
05156       /* Jump out on DTMF */
05157       if (res)
05158          break;
05159    }
05160    return res;
05161 }
05162 
05163 /* Portuguese syntax */
05164 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)
05165 {
05166    struct tm tm;
05167    int res=0, offset, sndoffset;
05168    char sndfile[256], nextmsg[256];
05169 
05170    if (format == NULL)
05171       format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
05172 
05173    ast_localtime(&time,&tm,timezone);
05174 
05175    for (offset=0 ; format[offset] != '\0' ; offset++) {
05176       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05177       switch (format[offset]) {
05178          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05179          case '\'':
05180             /* Literal name of a sound file */
05181             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05182                sndfile[sndoffset] = format[offset];
05183             }
05184             sndfile[sndoffset] = '\0';
05185             snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
05186             res = wait_file(chan,ints,nextmsg,lang);
05187             break;
05188          case 'A':
05189          case 'a':
05190             /* Sunday - Saturday */
05191             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05192             res = wait_file(chan,ints,nextmsg,lang);
05193             break;
05194          case 'B':
05195          case 'b':
05196          case 'h':
05197             /* January - December */
05198             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05199             res = wait_file(chan,ints,nextmsg,lang);
05200             break;
05201          case 'm':
05202             /* First - Twelfth */
05203             if (!strcasecmp(lang, "pt_BR")) {
05204                res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
05205             } else {
05206                snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
05207                res = wait_file(chan,ints,nextmsg,lang);
05208             }
05209             break;
05210          case 'd':
05211          case 'e':
05212             /* First - Thirtyfirst */
05213             res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
05214             break;
05215          case 'Y':
05216             /* Year */
05217             res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
05218             break;
05219          case 'I':
05220          case 'l':
05221             /* 12-Hour */
05222             if (tm.tm_hour == 0) {
05223                if (format[offset] == 'I')
05224                   res = wait_file(chan, ints, "digits/pt-a", lang);
05225                if (!res)
05226                   res = wait_file(chan, ints, "digits/pt-meianoite", lang);
05227             } else if (tm.tm_hour == 12) {
05228                if (format[offset] == 'I')
05229                   res = wait_file(chan, ints, "digits/pt-ao", lang);
05230                if (!res)
05231                   res = wait_file(chan, ints, "digits/pt-meiodia", lang);
05232             } else {
05233                if (format[offset] == 'I') {
05234                   if ((tm.tm_hour % 12) != 1)
05235                      res = wait_file(chan, ints, "digits/pt-as", lang);
05236                   else
05237                      res = wait_file(chan, ints, "digits/pt-a", lang);
05238                }
05239                if (!res)
05240                   res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
05241             }
05242             break;
05243          case 'H':
05244          case 'k':
05245             /* 24-Hour */
05246             res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05247             if ((!res) && (format[offset] == 'H')) {
05248                if (tm.tm_hour > 1) {
05249                   res = wait_file(chan,ints,"digits/hours",lang);
05250                } else {
05251                   res = wait_file(chan,ints,"digits/hour",lang);
05252                }
05253             }
05254             break;
05255          case 'M':
05256             /* Minute */
05257             res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
05258             if (!res) {
05259                if (tm.tm_min > 1) {
05260                   res = wait_file(chan,ints,"digits/minutes",lang);
05261                } else {
05262                   res = wait_file(chan,ints,"digits/minute",lang);
05263                }
05264             }
05265             break;
05266          case 'P':
05267          case 'p':
05268             /* AM/PM */
05269             if (!strcasecmp(lang, "pt_BR")) {
05270                if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
05271                   res = wait_file(chan, ints, "digits/pt-da", lang);
05272                   if (!res) {
05273                      if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
05274                         res = wait_file(chan, ints, "digits/morning", lang);
05275                      else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
05276                         res = wait_file(chan, ints, "digits/afternoon", lang);
05277                      else res = wait_file(chan, ints, "digits/night", lang);
05278                   }
05279                }
05280             } else {
05281                if (tm.tm_hour > 12)
05282                   res = wait_file(chan, ints, "digits/p-m", lang);
05283                else if (tm.tm_hour  && tm.tm_hour < 12)
05284                   res = wait_file(chan, ints, "digits/a-m", lang);
05285             }
05286             break;
05287          case 'Q':
05288             /* Shorthand for "Today", "Yesterday", or ABdY */
05289             /* XXX As emphasized elsewhere, this should the native way in your
05290              * language to say the date, with changes in what you say, depending
05291              * upon how recent the date is. XXX */
05292             {
05293                struct timeval now;
05294                struct tm tmnow;
05295                time_t beg_today, tt;
05296 
05297                gettimeofday(&now,NULL);
05298                tt = now.tv_sec;
05299                ast_localtime(&tt,&tmnow,timezone);
05300                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05301                /* In any case, it saves not having to do ast_mktime() */
05302                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05303                if (beg_today < time) {
05304                   /* Today */
05305                   res = wait_file(chan,ints, "digits/today",lang);
05306                } else if (beg_today - 86400 < time) {
05307                   /* Yesterday */
05308                   res = wait_file(chan,ints, "digits/yesterday",lang);
05309                } else {
05310                   res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
05311                }
05312             }
05313             break;
05314          case 'q':
05315             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
05316             /* XXX As emphasized elsewhere, this should the native way in your
05317              * language to say the date, with changes in what you say, depending
05318              * upon how recent the date is. XXX */
05319             {
05320                struct timeval now;
05321                struct tm tmnow;
05322                time_t beg_today, tt;
05323 
05324                gettimeofday(&now,NULL);
05325                tt = now.tv_sec;
05326                ast_localtime(&tt,&tmnow,timezone);
05327                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05328                /* In any case, it saves not having to do ast_mktime() */
05329                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05330                if (beg_today < time) {
05331                   /* Today */
05332                } else if ((beg_today - 86400) < time) {
05333                   /* Yesterday */
05334                   res = wait_file(chan,ints, "digits/yesterday",lang);
05335                } else if (beg_today - 86400 * 6 < time) {
05336                   /* Within the last week */
05337                   res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
05338                } else {
05339                   res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
05340                }
05341             }
05342             break;
05343          case 'R':
05344             res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/and' M", timezone);
05345             break;
05346          case 'S':
05347             /* Seconds */
05348             res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
05349             if (!res) {
05350                if (tm.tm_sec > 1) {
05351                   res = wait_file(chan,ints,"digits/seconds",lang);
05352                } else {
05353                   res = wait_file(chan,ints,"digits/second",lang);
05354                }
05355             }
05356             break;
05357          case 'T':
05358             res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
05359             break;
05360          case ' ':
05361          case '   ':
05362             /* Just ignore spaces and tabs */
05363             break;
05364          default:
05365             /* Unknown character */
05366             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05367       }
05368       /* Jump out on DTMF */
05369       if (res) {
05370          break;
05371       }
05372    }
05373    return res;
05374 }
05375 
05376 /* Taiwanese / Chinese syntax */
05377 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)
05378 {
05379    struct tm tm;
05380    int res=0, offset, sndoffset;
05381    char sndfile[256], nextmsg[256];
05382 
05383    if (format == NULL)
05384       format = "YBdAkM";
05385 
05386    ast_localtime(&time,&tm,timezone);
05387 
05388    for (offset=0 ; format[offset] != '\0' ; offset++) {
05389       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
05390       switch (format[offset]) {
05391          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
05392          case '\'':
05393             /* Literal name of a sound file */
05394             for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
05395                sndfile[sndoffset] = format[offset];
05396             }
05397             sndfile[sndoffset] = '\0';
05398             res = wait_file(chan,ints,sndfile,lang);
05399             break;
05400          case 'A':
05401          case 'a':
05402             /* Sunday - Saturday */
05403             snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
05404             res = wait_file(chan,ints,nextmsg,lang);
05405             break;
05406          case 'B':
05407          case 'b':
05408          case 'h':
05409          case 'm':
05410             /* January - December */
05411             snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
05412             res = wait_file(chan,ints,nextmsg,lang);
05413             break;
05414          case 'd':
05415          case 'e':
05416             /* First - Thirtyfirst */
05417             if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
05418                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
05419                res = wait_file(chan,ints,nextmsg,lang);
05420             } else {
05421                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
05422                res = wait_file(chan,ints,nextmsg,lang);
05423                if (!res) {
05424                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
05425                   res = wait_file(chan,ints,nextmsg,lang);
05426                }
05427             }
05428             if (!res) res = wait_file(chan,ints,"digits/day",lang);
05429             break;
05430          case 'Y':
05431             /* Year */
05432             if (tm.tm_year > 99) {
05433                res = wait_file(chan,ints, "digits/2",lang);
05434                if (!res) {
05435                   res = wait_file(chan,ints, "digits/thousand",lang);
05436                }
05437                if (tm.tm_year > 100) {
05438                   if (!res) {
05439                      snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
05440                      res = wait_file(chan,ints,nextmsg,lang);
05441                      if (!res) {
05442                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
05443                         res = wait_file(chan,ints,nextmsg,lang);
05444                      }
05445                   }
05446                }
05447                if (!res) {
05448                   res = wait_file(chan,ints, "digits/year",lang);
05449                }
05450             } else {
05451                if (tm.tm_year < 1) {
05452                   /* I'm not going to handle 1900 and prior */
05453                   /* We'll just be silent on the year, instead of bombing out. */
05454                } else {
05455                   res = wait_file(chan,ints, "digits/1",lang);
05456                   if (!res) {
05457                      res = wait_file(chan,ints, "digits/9",lang);
05458                   }
05459                   if (!res) {
05460                      if (tm.tm_year <= 9) {
05461                         /* 1901 - 1909 */
05462                         res = wait_file(chan,ints, "digits/0",lang);
05463                         if (!res) {
05464                            snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
05465                            res = wait_file(chan,ints,nextmsg,lang);
05466                         }
05467                      } else {
05468                         /* 1910 - 1999 */
05469                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
05470                         res = wait_file(chan,ints,nextmsg,lang);
05471                         if (!res) {
05472                            snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
05473                            res = wait_file(chan,ints,nextmsg,lang);
05474                         }
05475                      }
05476                   }
05477                }
05478                if (!res) {
05479                   res = wait_file(chan,ints, "digits/year",lang);
05480                }
05481             }
05482             break;
05483          case 'I':
05484          case 'l':
05485             /* 12-Hour */
05486             if (tm.tm_hour == 0)
05487                snprintf(nextmsg,sizeof(nextmsg), "digits/12");
05488             else if (tm.tm_hour > 12)
05489                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
05490             else
05491                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
05492             res = wait_file(chan,ints,nextmsg,lang);
05493             if (!res) {
05494                res = wait_file(chan,ints, "digits/oclock",lang);
05495             }
05496             break;
05497          case 'H':
05498                 if (tm.tm_hour < 10) {
05499                     res = wait_file(chan, ints, "digits/0", lang);
05500                 }
05501          case 'k':
05502             /* 24-Hour */
05503             if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
05504                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
05505                res = wait_file(chan,ints,nextmsg,lang);
05506             } else {
05507                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
05508                res = wait_file(chan,ints,nextmsg,lang);
05509                if (!res) {
05510                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
05511                   res = wait_file(chan,ints,nextmsg,lang);
05512                }
05513             }
05514             if (!res) {
05515                res = wait_file(chan,ints, "digits/oclock",lang);
05516             }
05517             break;
05518          case 'M':
05519             /* Minute */
05520             if (!(tm.tm_min % 10) || tm.tm_min < 10) {
05521                if (tm.tm_min < 10) {
05522                   res = wait_file(chan, ints, "digits/0", lang);
05523                }
05524                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
05525                res = wait_file(chan,ints,nextmsg,lang);
05526             } else {
05527                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
05528                res = wait_file(chan,ints,nextmsg,lang);
05529                if (!res) {
05530                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
05531                   res = wait_file(chan,ints,nextmsg,lang);
05532                }
05533             }
05534             if (!res) {
05535                res = wait_file(chan,ints, "digits/minute",lang);
05536             }
05537             break;
05538          case 'P':
05539          case 'p':
05540             /* AM/PM */
05541             if (tm.tm_hour > 11)
05542                snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
05543             else
05544                snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
05545             res = wait_file(chan,ints,nextmsg,lang);
05546             break;
05547          case 'Q':
05548             /* Shorthand for "Today", "Yesterday", or ABdY */
05549             /* XXX As emphasized elsewhere, this should the native way in your
05550              * language to say the date, with changes in what you say, depending
05551              * upon how recent the date is. XXX */
05552             {
05553                struct timeval now;
05554                struct tm tmnow;
05555                time_t beg_today, tt;
05556 
05557                gettimeofday(&now,NULL);
05558                tt = now.tv_sec;
05559                ast_localtime(&tt,&tmnow,timezone);
05560                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05561                /* In any case, it saves not having to do ast_mktime() */
05562                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05563                if (beg_today < time) {
05564                   /* Today */
05565                   res = wait_file(chan,ints, "digits/today",lang);
05566                } else if (beg_today - 86400 < time) {
05567                   /* Yesterday */
05568                   res = wait_file(chan,ints, "digits/yesterday",lang);
05569                } else {
05570                   res = ast_say_date_with_format_zh(chan, time, ints, lang, "YBdA", timezone);
05571                }
05572             }
05573             break;
05574          case 'q':
05575             /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
05576             /* XXX As emphasized elsewhere, this should the native way in your
05577              * language to say the date, with changes in what you say, depending
05578              * upon how recent the date is. XXX */
05579             {
05580                struct timeval now;
05581                struct tm tmnow;
05582                time_t beg_today, tt;
05583 
05584                gettimeofday(&now,NULL);
05585                tt = now.tv_sec;
05586                ast_localtime(&tt,&tmnow,timezone);
05587                /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
05588                /* In any case, it saves not having to do ast_mktime() */
05589                beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
05590                if (beg_today < time) {
05591                   /* Today */
05592                } else if ((beg_today - 86400) < time) {
05593                   /* Yesterday */
05594                   res = wait_file(chan,ints, "digits/yesterday",lang);
05595                } else if (beg_today - 86400 * 6 < time) {
05596                   /* Within the last week */
05597                   res = ast_say_date_with_format_zh(chan, time, ints, lang, "A", timezone);
05598                } else {
05599                   res = ast_say_date_with_format_zh(chan, time, ints, lang, "YBdA", timezone);
05600                }
05601             }
05602             break;
05603          case 'R':
05604             res = ast_say_date_with_format_zh(chan, time, ints, lang, "kM", timezone);
05605             break;
05606          case 'S':
05607             /* Seconds */
05608             if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
05609                if (tm.tm_sec < 10) {
05610                   res = wait_file(chan, ints, "digits/0", lang);
05611                }
05612                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
05613                res = wait_file(chan,ints,nextmsg,lang);
05614             } else {
05615                snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
05616                res = wait_file(chan,ints,nextmsg,lang);
05617                if (!res) {
05618                   snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
05619                   res = wait_file(chan,ints,nextmsg,lang);
05620                }
05621             }
05622             if (!res) {
05623                res = wait_file(chan,ints, "digits/second",lang);
05624             }
05625             break;
05626          case 'T':
05627             res = ast_say_date_with_format_zh(chan, time, ints, lang, "HMS", timezone);
05628             break;
05629          case ' ':
05630          case '   ':
05631             /* Just ignore spaces and tabs */
05632          break;
05633          default:
05634             /* Unknown character */
05635             ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
05636       }
05637       /* Jump out on DTMF */
05638       if (res) {
05639          break;
05640       }
05641    }
05642    return res;
05643 }
05644 
05645 static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05646 {
05647    if (!strncasecmp(lang, "en", 2)) {  /* English syntax */
05648       return ast_say_time_en(chan, t, ints, lang);
05649    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
05650       return ast_say_time_de(chan, t, ints, lang);
05651    } else if (!strncasecmp(lang, "es", 2)) { /* Spanish syntax */
05652       return(ast_say_time_es(chan, t, ints, lang));
05653    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
05654       return ast_say_time_fr(chan, t, ints, lang);
05655    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
05656       static int deprecation_warning = 0;
05657       if (deprecation_warning++ % 10 == 0) {
05658          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
05659       }
05660       return ast_say_time_ka(chan, t, ints, lang);
05661    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
05662       return ast_say_time_gr(chan, t, ints, lang);
05663    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
05664       return ast_say_time_he(chan, t, ints, lang);
05665    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
05666       return ast_say_time_ka(chan, t, ints, lang);
05667    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
05668       return ast_say_time_nl(chan, t, ints, lang);
05669    } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
05670       return ast_say_time_pt_BR(chan, t, ints, lang);
05671    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
05672       return ast_say_time_pt(chan, t, ints, lang);
05673    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
05674       static int deprecation_warning = 0;
05675       if (deprecation_warning++ % 10 == 0) {
05676          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
05677       }
05678       return ast_say_time_zh(chan, t, ints, lang);
05679    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
05680       return ast_say_time_zh(chan, t, ints, lang);
05681    }
05682 
05683    /* Default to English */
05684    return ast_say_time_en(chan, t, ints, lang);
05685 }
05686 
05687 /* English syntax */
05688 int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05689 {
05690    struct tm tm;
05691    int res = 0;
05692    int hour, pm=0;
05693 
05694    ast_localtime(&t, &tm, NULL);
05695    hour = tm.tm_hour;
05696    if (!hour)
05697       hour = 12;
05698    else if (hour == 12)
05699       pm = 1;
05700    else if (hour > 12) {
05701       hour -= 12;
05702       pm = 1;
05703    }
05704    if (!res)
05705       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
05706    if (tm.tm_min > 9) {
05707       if (!res)
05708          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05709    } else if (tm.tm_min) {
05710       if (!res)
05711          res = ast_streamfile(chan, "digits/oh", lang);
05712       if (!res)
05713          res = ast_waitstream(chan, ints);
05714       if (!res)
05715          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05716    } else {
05717       if (!res)
05718          res = ast_streamfile(chan, "digits/oclock", lang);
05719       if (!res)
05720          res = ast_waitstream(chan, ints);
05721    }
05722    if (pm) {
05723       if (!res)
05724          res = ast_streamfile(chan, "digits/p-m", lang);
05725    } else {
05726       if (!res)
05727          res = ast_streamfile(chan, "digits/a-m", lang);
05728    }
05729    if (!res)
05730       res = ast_waitstream(chan, ints);
05731    return res;
05732 }
05733 
05734 /* German syntax */
05735 int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05736 {
05737    struct tm tm;
05738    int res = 0;
05739 
05740    ast_localtime(&t, &tm, NULL);
05741    if (!res)
05742       res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
05743    if (!res)
05744       res = ast_streamfile(chan, "digits/oclock", lang);
05745    if (!res)
05746       res = ast_waitstream(chan, ints);
05747    if (!res)
05748        if (tm.tm_min > 0) 
05749       res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
05750    return res;
05751 }
05752 
05753 /* Spanish syntax */
05754 int ast_say_time_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05755 {
05756    struct tm tm;
05757    int res = 0;
05758    ast_localtime(&t, &tm, NULL);
05759    
05760    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05761    if (!res) {
05762       if (tm.tm_hour != 1)
05763          res = wait_file(chan, ints, "digits/hours", lang);
05764       else
05765          res = wait_file(chan, ints, "digits/hour", lang);
05766    }
05767    if ((!res) && (tm.tm_min)) {
05768       res = wait_file(chan, ints, "digits/and", lang);
05769       if (!res)
05770          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05771       if (!res) {
05772          if (tm.tm_min > 1)
05773             res = wait_file(chan, ints, "digits/minutes", lang);
05774          else
05775             res = wait_file(chan, ints, "digits/minute", lang);
05776       }
05777    }
05778    return res;
05779 }
05780 
05781 /* French syntax */
05782 int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05783 {
05784    struct tm tm;
05785    int res = 0;
05786 
05787    ast_localtime(&t, &tm, NULL);
05788 
05789    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05790    if (!res)
05791       res = ast_streamfile(chan, "digits/oclock", lang);
05792    if (tm.tm_min) {
05793       if (!res)
05794       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05795    }
05796    return res;
05797 }
05798 
05799 /* Dutch syntax */
05800 int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05801 {
05802    struct tm tm;
05803    int res = 0;
05804 
05805    ast_localtime(&t, &tm, NULL);
05806    if (!res)
05807       res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
05808    if (!res)
05809       res = ast_streamfile(chan, "digits/nl-uur", lang);
05810    if (!res)
05811       res = ast_waitstream(chan, ints);
05812    if (!res)
05813        if (tm.tm_min > 0) 
05814       res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
05815    return res;
05816 }
05817 
05818 /* Portuguese syntax */
05819 int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05820 {
05821    struct tm tm;
05822    int res = 0;
05823    int hour;
05824 
05825    ast_localtime(&t, &tm, NULL);
05826    hour = tm.tm_hour;
05827    if (!res)
05828       res = ast_say_number(chan, hour, ints, lang, "f");
05829    if (tm.tm_min) {
05830       if (!res)
05831          res = wait_file(chan, ints, "digits/and", lang);
05832       if (!res)
05833          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05834    } else {
05835       if (!res) {
05836          if (tm.tm_hour == 1)
05837             res = wait_file(chan, ints, "digits/hour", lang);
05838          else
05839             res = wait_file(chan, ints, "digits/hours", lang);
05840       }
05841    }
05842    if (!res)
05843       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
05844    return res;
05845 }
05846 
05847 /* Brazilian Portuguese syntax */
05848 int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05849 {
05850    struct tm tm;
05851    int res = 0;
05852 
05853    ast_localtime(&t, &tm, NULL);
05854 
05855    res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
05856    if (!res) {
05857       if (tm.tm_hour > 1)
05858          res = wait_file(chan, ints, "digits/hours", lang);
05859       else
05860          res = wait_file(chan, ints, "digits/hour", lang);
05861    }
05862    if ((!res) && (tm.tm_min)) {
05863       res = wait_file(chan, ints, "digits/and", lang);
05864       if (!res)
05865          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05866       if (!res) {
05867          if (tm.tm_min > 1)
05868             res = wait_file(chan, ints, "digits/minutes", lang);
05869          else
05870             res = wait_file(chan, ints, "digits/minute", lang);
05871       }
05872    }
05873    return res;
05874 }
05875 
05876 /* Taiwanese / Chinese  syntax */
05877 int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05878 {
05879    struct tm tm;
05880    int res = 0;
05881    int hour, pm=0;
05882 
05883    ast_localtime(&t, &tm, NULL);
05884    hour = tm.tm_hour;
05885    if (!hour)
05886       hour = 12;
05887    else if (hour == 12)
05888       pm = 1;
05889    else if (hour > 12) {
05890       hour -= 12;
05891       pm = 1;
05892    }
05893    if (pm) {
05894       if (!res)
05895          res = ast_streamfile(chan, "digits/p-m", lang);
05896    } else {
05897       if (!res)
05898          res = ast_streamfile(chan, "digits/a-m", lang);
05899    }
05900    if (!res)
05901       res = ast_waitstream(chan, ints);
05902    if (!res)
05903       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
05904    if (!res)
05905       res = ast_streamfile(chan, "digits/oclock", lang);
05906    if (!res)
05907       res = ast_waitstream(chan, ints);
05908    if (!res)
05909       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
05910    if (!res)
05911       res = ast_streamfile(chan, "digits/minute", lang);
05912    if (!res)
05913       res = ast_waitstream(chan, ints);
05914    return res;
05915 }
05916 
05917 /* Hebrew syntax */
05918 int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05919 {
05920    struct tm tm;
05921    int res = 0;
05922    int hour;
05923 
05924    ast_localtime(&t, &tm, NULL);
05925    hour = tm.tm_hour;
05926    if (!hour)
05927       hour = 12;
05928 
05929    if (!res)
05930       res = ast_say_number_full_he(chan, hour, ints, lang, "f", -1, -1);
05931 
05932    if (tm.tm_min > 9) {
05933       if (!res)
05934          res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
05935    } else if (tm.tm_min) {
05936       if (!res) {          /* say a leading zero if needed */
05937          res = ast_say_number_full_he(chan, 0, ints, lang, "f", -1, -1);
05938       }
05939       if (!res)
05940          res = ast_waitstream(chan, ints);
05941       if (!res)
05942          res = ast_say_number_full_he(chan, tm.tm_min, ints, lang, "f", -1, -1);
05943    } else {
05944       if (!res)
05945          res = ast_waitstream(chan, ints);
05946    }
05947    if (!res)
05948       res = ast_waitstream(chan, ints);
05949    return res;
05950 }
05951 static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05952 {
05953    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
05954       return ast_say_datetime_en(chan, t, ints, lang);
05955    } else if (!strncasecmp(lang, "de", 2)) { /* German syntax */
05956       return ast_say_datetime_de(chan, t, ints, lang);
05957    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
05958       return ast_say_datetime_fr(chan, t, ints, lang);
05959    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
05960       static int deprecation_warning = 0;
05961       if (deprecation_warning++ % 10 == 0) {
05962          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
05963       }
05964       return ast_say_datetime_ka(chan, t, ints, lang);
05965    } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */
05966       return ast_say_datetime_gr(chan, t, ints, lang);
05967    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
05968       return ast_say_datetime_he(chan, t, ints, lang);
05969    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
05970       return ast_say_datetime_ka(chan, t, ints, lang);
05971    } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */
05972       return ast_say_datetime_nl(chan, t, ints, lang);
05973    } else if (!strncasecmp(lang, "pt_BR", 5)) { /* Brazilian Portuguese syntax */
05974       return ast_say_datetime_pt_BR(chan, t, ints, lang);
05975    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
05976       return ast_say_datetime_pt(chan, t, ints, lang);
05977    } else if (!strncasecmp(lang, "tw", 2)) { /* deprecated Taiwanese syntax */
05978       static int deprecation_warning = 0;
05979       if (deprecation_warning++ % 10 == 0) {
05980          ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese.  Please switch to using zh_TW instead.\n");
05981       }
05982       return ast_say_datetime_zh(chan, t, ints, lang);
05983    } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */
05984       return ast_say_datetime_zh(chan, t, ints, lang);
05985    }
05986 
05987    /* Default to English */
05988    return ast_say_datetime_en(chan, t, ints, lang);
05989 }
05990 
05991 /* English syntax */
05992 int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
05993 {
05994    struct tm tm;
05995    char fn[256];
05996    int res = 0;
05997    int hour, pm=0;
05998 
05999    ast_localtime(&t, &tm, NULL);
06000    if (!res) {
06001       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06002       res = ast_streamfile(chan, fn, lang);
06003       if (!res)
06004          res = ast_waitstream(chan, ints);
06005    }
06006    if (!res) {
06007       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06008       res = ast_streamfile(chan, fn, lang);
06009       if (!res)
06010          res = ast_waitstream(chan, ints);
06011    }
06012    if (!res)
06013       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06014 
06015    hour = tm.tm_hour;
06016    if (!hour)
06017       hour = 12;
06018    else if (hour == 12)
06019       pm = 1;
06020    else if (hour > 12) {
06021       hour -= 12;
06022       pm = 1;
06023    }
06024    if (!res)
06025       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06026 
06027    if (tm.tm_min > 9) {
06028       if (!res)
06029          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06030    } else if (tm.tm_min) {
06031       if (!res)
06032          res = ast_streamfile(chan, "digits/oh", lang);
06033       if (!res)
06034          res = ast_waitstream(chan, ints);
06035       if (!res)
06036          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06037    } else {
06038       if (!res)
06039          res = ast_streamfile(chan, "digits/oclock", lang);
06040       if (!res)
06041          res = ast_waitstream(chan, ints);
06042    }
06043    if (pm) {
06044       if (!res)
06045          res = ast_streamfile(chan, "digits/p-m", lang);
06046    } else {
06047       if (!res)
06048          res = ast_streamfile(chan, "digits/a-m", lang);
06049    }
06050    if (!res)
06051       res = ast_waitstream(chan, ints);
06052    if (!res)
06053       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06054    return res;
06055 }
06056 
06057 /* German syntax */
06058 int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06059 {
06060    struct tm tm;
06061    int res = 0;
06062 
06063    ast_localtime(&t, &tm, NULL);
06064    res = ast_say_date(chan, t, ints, lang);
06065    if (!res) 
06066       ast_say_time(chan, t, ints, lang);
06067    return res;
06068 
06069 }
06070 
06071 /* French syntax */
06072 int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06073 {
06074    struct tm tm;
06075    char fn[256];
06076    int res = 0;
06077 
06078    ast_localtime(&t, &tm, NULL);
06079 
06080    if (!res)
06081       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06082 
06083    if (!res) {
06084       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06085       res = ast_streamfile(chan, fn, lang);
06086       if (!res)
06087          res = ast_waitstream(chan, ints);
06088    }
06089    if (!res) {
06090       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06091       res = ast_streamfile(chan, fn, lang);
06092       if (!res)
06093          res = ast_waitstream(chan, ints);
06094    }
06095 
06096    if (!res)
06097       res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
06098    if (!res)
06099          res = ast_streamfile(chan, "digits/oclock", lang);
06100    if (tm.tm_min > 0) {
06101       if (!res)
06102          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06103    } 
06104    if (!res)
06105       res = ast_waitstream(chan, ints);
06106    if (!res)
06107       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06108    return res;
06109 }
06110 
06111 /* Dutch syntax */
06112 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06113 {
06114    struct tm tm;
06115    int res = 0;
06116 
06117    ast_localtime(&t, &tm, NULL);
06118    res = ast_say_date(chan, t, ints, lang);
06119    if (!res) {
06120       res = ast_streamfile(chan, "digits/nl-om", lang);
06121       if (!res)
06122          res = ast_waitstream(chan, ints);
06123    }
06124    if (!res) 
06125       ast_say_time(chan, t, ints, lang);
06126    return res;
06127 }
06128 
06129 /* Portuguese syntax */
06130 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06131 {
06132    struct tm tm;
06133    char fn[256];
06134    int res = 0;
06135    int hour, pm=0;
06136 
06137    ast_localtime(&t, &tm, NULL);
06138    if (!res) {
06139       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06140       res = ast_streamfile(chan, fn, lang);
06141       if (!res)
06142          res = ast_waitstream(chan, ints);
06143    }
06144    if (!res) {
06145       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06146       res = ast_streamfile(chan, fn, lang);
06147       if (!res)
06148          res = ast_waitstream(chan, ints);
06149    }
06150    if (!res)
06151       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06152 
06153    hour = tm.tm_hour;
06154    if (!hour)
06155       hour = 12;
06156    else if (hour == 12)
06157       pm = 1;
06158    else if (hour > 12) {
06159       hour -= 12;
06160       pm = 1;
06161    }
06162    if (!res)
06163       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06164 
06165    if (tm.tm_min > 9) {
06166       if (!res)
06167          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06168    } else if (tm.tm_min) {
06169       if (!res)
06170          res = ast_streamfile(chan, "digits/oh", lang);
06171       if (!res)
06172          res = ast_waitstream(chan, ints);
06173       if (!res)
06174          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06175    } else {
06176       if (!res)
06177          res = ast_streamfile(chan, "digits/oclock", lang);
06178       if (!res)
06179          res = ast_waitstream(chan, ints);
06180    }
06181    if (pm) {
06182       if (!res)
06183          res = ast_streamfile(chan, "digits/p-m", lang);
06184    } else {
06185       if (!res)
06186          res = ast_streamfile(chan, "digits/a-m", lang);
06187    }
06188    if (!res)
06189       res = ast_waitstream(chan, ints);
06190    if (!res)
06191       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06192    return res;
06193 }
06194 
06195 /* Brazilian Portuguese syntax */
06196 int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06197 {
06198    struct tm tm;
06199    int res = 0;
06200 
06201    ast_localtime(&t, &tm, NULL);
06202    res = ast_say_date(chan, t, ints, lang);
06203    if (!res)
06204       res = ast_say_time(chan, t, ints, lang);
06205    return res;
06206 }
06207 
06208 /* Taiwanese / Chinese syntax */
06209 int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06210 {
06211    struct tm tm;
06212    char fn[256];
06213    int res = 0;
06214    int hour, pm=0;
06215 
06216    ast_localtime(&t, &tm, NULL);
06217    if (!res)
06218       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06219    if (!res) {
06220       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06221       res = ast_streamfile(chan, fn, lang);
06222       if (!res)
06223          res = ast_waitstream(chan, ints);
06224    }
06225    if (!res)
06226       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06227    if (!res) {
06228       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06229       res = ast_streamfile(chan, fn, lang);
06230       if (!res)
06231          res = ast_waitstream(chan, ints);
06232    }
06233 
06234    hour = tm.tm_hour;
06235    if (!hour)
06236       hour = 12;
06237    else if (hour == 12)
06238       pm = 1;
06239    else if (hour > 12) {
06240       hour -= 12;
06241       pm = 1;
06242    }
06243    if (pm) {
06244       if (!res)
06245          res = ast_streamfile(chan, "digits/p-m", lang);
06246    } else {
06247       if (!res)
06248          res = ast_streamfile(chan, "digits/a-m", lang);
06249    }
06250    if (!res)
06251       res = ast_waitstream(chan, ints);
06252    if (!res)
06253       res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
06254    if (!res)
06255       res = ast_streamfile(chan, "digits/oclock", lang);
06256    if (!res)
06257       res = ast_waitstream(chan, ints);
06258    if (!res)
06259       res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06260    if (!res)
06261       res = ast_streamfile(chan, "digits/minute", lang);
06262    if (!res)
06263       res = ast_waitstream(chan, ints);
06264    return res;
06265 }
06266 
06267 /* Hebrew syntax */
06268 int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06269 {
06270    struct tm tm;
06271    char fn[256];
06272    int res = 0;
06273    int hour;
06274 
06275    ast_localtime(&t, &tm, NULL);
06276    if (!res) {
06277       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06278       res = ast_streamfile(chan, fn, lang);
06279       if (!res) {
06280          res = ast_waitstream(chan, ints);
06281       }
06282    }
06283    if (!res) {
06284       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06285       res = ast_streamfile(chan, fn, lang);
06286       if (!res) {
06287          res = ast_waitstream(chan, ints);
06288       }
06289    }
06290    if (!res) {
06291       res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
06292    }
06293 
06294    hour = tm.tm_hour;
06295    if (!hour) {
06296       hour = 12;
06297    }
06298 
06299    if (!res) {
06300       res = ast_say_number(chan, hour, ints, lang, "f");
06301    }
06302 
06303    if (tm.tm_min > 9) {
06304       if (!res) {
06305          res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
06306       }
06307    } else if (tm.tm_min) {
06308       if (!res) {
06309          /* say a leading zero if needed */
06310          res = ast_say_number(chan, 0, ints, lang, "f");
06311       }
06312       if (!res) {
06313          res = ast_waitstream(chan, ints);
06314       }
06315       if (!res) {
06316          res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
06317       }
06318    } else {
06319       if (!res) {
06320          res = ast_waitstream(chan, ints);
06321       }
06322    }
06323    if (!res) {
06324       res = ast_waitstream(chan, ints);
06325    }
06326    if (!res) {
06327       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, "f");
06328    }
06329    return res;
06330 }
06331 static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06332 {
06333    if (!strncasecmp(lang, "en", 2)) {        /* English syntax */
06334       return ast_say_datetime_from_now_en(chan, t, ints, lang);
06335    } else if (!strncasecmp(lang, "fr", 2)) { /* French syntax */
06336       return ast_say_datetime_from_now_fr(chan, t, ints, lang);
06337    } else if (!strncasecmp(lang, "ge", 2)) { /* deprecated Georgian syntax */
06338       static int deprecation_warning = 0;
06339       if (deprecation_warning++ % 10 == 0) {
06340          ast_log(LOG_WARNING, "ge is not a standard language code.  Please switch to using ka instead.\n");
06341       }
06342       return ast_say_datetime_from_now_ka(chan, t, ints, lang);
06343    } else if (!strncasecmp(lang, "he", 2)) { /* Hebrew syntax */
06344       return ast_say_datetime_from_now_he(chan, t, ints, lang);
06345    } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */
06346       return ast_say_datetime_from_now_ka(chan, t, ints, lang);
06347    } else if (!strncasecmp(lang, "pt", 2)) { /* Portuguese syntax */
06348       return ast_say_datetime_from_now_pt(chan, t, ints, lang);
06349    }
06350 
06351    /* Default to English */
06352    return ast_say_datetime_from_now_en(chan, t, ints, lang);
06353 }
06354 
06355 /* English syntax */
06356 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06357 {
06358    int res=0;
06359    time_t nowt;
06360    int daydiff;
06361    struct tm tm;
06362    struct tm now;
06363    char fn[256];
06364 
06365    time(&nowt);
06366 
06367    ast_localtime(&t, &tm, NULL);
06368    ast_localtime(&nowt,&now, NULL);
06369    daydiff = now.tm_yday - tm.tm_yday;
06370    if ((daydiff < 0) || (daydiff > 6)) {
06371       /* Day of month and month */
06372       if (!res) {
06373          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06374          res = ast_streamfile(chan, fn, lang);
06375          if (!res)
06376             res = ast_waitstream(chan, ints);
06377       }
06378       if (!res)
06379          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06380 
06381    } else if (daydiff) {
06382       /* Just what day of the week */
06383       if (!res) {
06384          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06385          res = ast_streamfile(chan, fn, lang);
06386          if (!res)
06387             res = ast_waitstream(chan, ints);
06388       }
06389    } /* Otherwise, it was today */
06390    if (!res)
06391       res = ast_say_time(chan, t, ints, lang);
06392    return res;
06393 }
06394 
06395 /* French syntax */
06396 int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06397 {
06398    int res=0;
06399    time_t nowt;
06400    int daydiff;
06401    struct tm tm;
06402    struct tm now;
06403    char fn[256];
06404 
06405    time(&nowt);
06406 
06407    ast_localtime(&t, &tm, NULL);
06408    ast_localtime(&nowt, &now, NULL);
06409    daydiff = now.tm_yday - tm.tm_yday;
06410    if ((daydiff < 0) || (daydiff > 6)) {
06411       /* Day of month and month */
06412       if (!res) {
06413          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06414          res = ast_streamfile(chan, fn, lang);
06415          if (!res)
06416             res = ast_waitstream(chan, ints);
06417       }
06418       if (!res)
06419          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06420 
06421    } else if (daydiff) {
06422       /* Just what day of the week */
06423       if (!res) {
06424          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06425          res = ast_streamfile(chan, fn, lang);
06426          if (!res)
06427             res = ast_waitstream(chan, ints);
06428       }
06429    } /* Otherwise, it was today */
06430    if (!res)
06431       res = ast_say_time(chan, t, ints, lang);
06432    return res;
06433 }
06434 
06435 /* Portuguese syntax */
06436 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06437 {
06438    int res=0;
06439    time_t nowt;
06440    int daydiff;
06441    struct tm tm;
06442    struct tm now;
06443    char fn[256];
06444 
06445    time(&nowt);
06446 
06447    ast_localtime(&t, &tm, NULL);
06448    ast_localtime(&nowt, &now, NULL);
06449    daydiff = now.tm_yday - tm.tm_yday;
06450    if ((daydiff < 0) || (daydiff > 6)) {
06451       /* Day of month and month */
06452       if (!res)
06453          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
06454       if (!res)
06455          res = wait_file(chan, ints, "digits/pt-de", lang);
06456       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06457       if (!res)
06458          res = wait_file(chan, ints, fn, lang);
06459    
06460    } else if (daydiff) {
06461       /* Just what day of the week */
06462       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06463       if (!res)
06464          res = wait_file(chan, ints, fn, lang);
06465    }  /* Otherwise, it was today */
06466    if (tm.tm_hour > 1)
06467       snprintf(fn, sizeof(fn), "digits/pt-as");
06468    else
06469       snprintf(fn, sizeof(fn), "digits/pt-a");
06470    if (!res)
06471       res = wait_file(chan, ints, fn, lang);
06472    if (!res)
06473       res = ast_say_time(chan, t, ints, lang);
06474    return res;
06475 }
06476 
06477 /* Hebrew syntax */
06478 int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06479 {
06480    int res = 0;
06481    time_t nowt;
06482    int daydiff;
06483    struct tm tm;
06484    struct tm now;
06485    char fn[256];
06486 
06487    time(&nowt);
06488 
06489    ast_localtime(&t, &tm, NULL);
06490    ast_localtime(&nowt, &now, NULL);
06491    daydiff = now.tm_yday - tm.tm_yday;
06492    if ((daydiff < 0) || (daydiff > 6)) {
06493       /* Day of month and month */
06494       if (!res) {
06495          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06496          res = ast_streamfile(chan, fn, lang);
06497          if (!res)
06498             res = ast_waitstream(chan, ints);
06499       }
06500       if (!res) {
06501          res = ast_say_number(chan, tm.tm_mday, ints, lang, "f");
06502       }
06503    } else if (daydiff) {
06504       /* Just what day of the week */
06505       if (!res) {
06506          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06507          res = ast_streamfile(chan, fn, lang);
06508          if (!res) {
06509             res = ast_waitstream(chan, ints);
06510          }
06511       }
06512    }                    /* Otherwise, it was today */
06513    if (!res) {
06514       res = ast_say_time(chan, t, ints, lang);
06515    }
06516    return res;
06517 }
06518 
06519 /*********************************** GREEK SUPPORT ***************************************/
06520 
06521 
06522 /*
06523  * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
06524  */
06525 static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
06526    int tmp;
06527    int left;
06528    int res;
06529    char fn[256] = "";
06530 
06531    /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
06532    if (num < 5) {
06533       snprintf(fn, sizeof(fn), "digits/female-%d", num);
06534       res = wait_file(chan, ints, fn, lang);
06535    } else if (num < 13) {
06536       res = ast_say_number(chan, num, ints, lang, (char *) NULL);
06537    } else if (num <100 ) { 
06538       tmp = (num/10) * 10;
06539       left = num - tmp;
06540       snprintf(fn, sizeof(fn), "digits/%d", tmp);
06541       res = ast_streamfile(chan, fn, lang);
06542       if (!res)
06543          res = ast_waitstream(chan, ints);
06544       if (left)
06545          gr_say_number_female(left, chan, ints, lang);
06546          
06547    } else {
06548       return -1;
06549    }
06550    return res;
06551 }
06552 
06553 
06554 
06555 /*
06556  *    A list of the files that you need to create
06557  ->   digits/xilia = "xilia"
06558  ->   digits/myrio = "ekatomyrio"
06559  ->   digits/thousands = "xiliades"
06560  ->   digits/millions = "ektatomyria"
06561  ->   digits/[1..12]   :: A pronunciation of th digits form 1 to 12 e.g. "tria"
06562  ->   digits/[10..100]  :: A pronunciation of the tens from 10 to 90 
06563                                               e,g 80 = "ogdonta" 
06564                    Here we must note that we use digits/tens/100 to utter "ekato"
06565                    and digits/hundred-100 to utter "ekaton"
06566  ->   digits/hundred-[100...1000] :: A pronunciation of  hundreds from 100 to 1000 e.g 400 = 
06567                                                        "terakosia". Here again we use hundreds/1000 for "xilia" 
06568                    and digits/thousnds for "xiliades"
06569 */
06570 
06571 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
06572 {
06573    int res = 0;
06574    char fn[256] = "";
06575    int i=0;
06576 
06577  
06578    if (!num) {
06579       snprintf(fn, sizeof(fn), "digits/0");
06580       res = ast_streamfile(chan, fn, chan->language);
06581       if (!res)
06582          return  ast_waitstream(chan, ints);
06583    }
06584 
06585    while (!res && num ) {
06586       i++;
06587       if (num < 13) {
06588          snprintf(fn, sizeof(fn), "digits/%d", num);
06589          num = 0;
06590       } else if (num <= 100) {
06591          /* 13 < num <= 100  */
06592          snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
06593          num -= ((num / 10) * 10); 
06594       } else if (num < 200) {
06595          /* 100 < num < 200 */
06596          snprintf(fn, sizeof(fn), "digits/hundred-100");
06597          num -= ((num / 100) * 100);
06598       } else if (num < 1000) {
06599          /* 200 < num < 1000 */
06600          snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
06601          num -= ((num / 100) * 100);
06602       } else if (num < 2000){
06603          snprintf(fn, sizeof(fn), "digits/xilia");
06604          num -= ((num / 1000) * 1000);
06605       } else {
06606          /* num >  1000 */ 
06607          if (num < 1000000) {
06608             res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
06609             if (res)
06610                return res;
06611             num = num % 1000;
06612             snprintf(fn, sizeof(fn), "digits/thousands");
06613          }  else {
06614             if (num < 1000000000) { /* 1,000,000,000 */
06615                res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
06616                if (res)
06617                   return res;
06618                num = num % 1000000;
06619                snprintf(fn, sizeof(fn), "digits/millions");
06620             } else {
06621                ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
06622                res = -1;
06623             }
06624          }
06625       } 
06626       if (!res) {
06627          if (!ast_streamfile(chan, fn, language)) {
06628             if ((audiofd > -1) && (ctrlfd > -1))
06629                res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
06630             else
06631                res = ast_waitstream(chan, ints);
06632          }
06633          ast_stopstream(chan);
06634       }
06635    }
06636    return res;
06637 }
06638 
06639 
06640 /*
06641  * The format is  weekday - day - month -year
06642  * 
06643  * A list of the files that you need to create
06644  * digits/day-[1..7]  : "Deytera .. Paraskeyh"
06645  * digits/months/1..12 : "Ianouariou .. Dekembriou"  
06646                                        Attention the months are in 
06647             "gekinh klhsh"
06648  */
06649 
06650 
06651 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06652 {
06653    struct tm tm;
06654    
06655    char fn[256];
06656    int res = 0;
06657    
06658 
06659    ast_localtime(&t,&tm,NULL);
06660    /* W E E K - D A Y */
06661    if (!res) {
06662       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06663       res = ast_streamfile(chan, fn, lang);
06664       if (!res)
06665          res = ast_waitstream(chan, ints);
06666    }
06667    /* D A Y */
06668    if (!res) {
06669       gr_say_number_female(tm.tm_mday, chan, ints, lang);
06670    }
06671    /* M O N T H */
06672    if (!res) {
06673       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06674       res = ast_streamfile(chan, fn, lang);
06675       if (!res)
06676          res = ast_waitstream(chan, ints);
06677    }
06678    /* Y E A R */
06679    if (!res)
06680       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
06681    return res; 
06682 }
06683 
06684 
06685  
06686 /* A list of the files that you need to create
06687  * digits/female/1..4 : "Mia, dyo , treis, tesseris "
06688  * digits/kai : "KAI"
06689  * didgits : "h wra"
06690  * digits/p-m : "meta meshmbrias" 
06691  * digits/a-m : "pro meshmbrias"
06692  */
06693 
06694 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06695 {
06696 
06697    struct tm tm;
06698    int res = 0;
06699    int hour, pm=0;
06700 
06701    ast_localtime(&t, &tm, NULL);
06702    hour = tm.tm_hour;
06703 
06704    if (!hour)
06705       hour = 12;
06706    else if (hour == 12)
06707       pm = 1;
06708    else if (hour > 12) {
06709       hour -= 12;
06710       pm = 1;
06711    }
06712  
06713    res = gr_say_number_female(hour, chan, ints, lang);
06714    if (tm.tm_min) {
06715       if (!res)
06716          res = ast_streamfile(chan, "digits/kai", lang);
06717       if (!res)
06718          res = ast_waitstream(chan, ints);
06719       if (!res)
06720          res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
06721    } else {
06722       if (!res)
06723          res = ast_streamfile(chan, "digits/hwra", lang);
06724       if (!res)
06725          res = ast_waitstream(chan, ints);
06726    }
06727    if (pm) {
06728       if (!res)
06729          res = ast_streamfile(chan, "digits/p-m", lang);
06730    } else {
06731       if (!res)
06732          res = ast_streamfile(chan, "digits/a-m", lang);
06733    }
06734    if (!res)
06735       res = ast_waitstream(chan, ints);
06736    return res;
06737 }
06738 
06739 
06740 
06741 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
06742 {
06743    struct tm tm;
06744    char fn[256];
06745    int res = 0;
06746 
06747    ast_localtime(&t, &tm, NULL);
06748 
06749    
06750    /* W E E K - D A Y */
06751    if (!res) {
06752       snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
06753       res = ast_streamfile(chan, fn, lang);
06754       if (!res)
06755          res = ast_waitstream(chan, ints);
06756    }
06757    /* D A Y */
06758    if (!res) {
06759       gr_say_number_female(tm.tm_mday, chan, ints, lang);
06760    }
06761    /* M O N T H */
06762    if (!res) {
06763       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
06764       res = ast_streamfile(chan, fn, lang);
06765       if (!res)
06766          res = ast_waitstream(chan, ints);
06767    }
06768 
06769    res = ast_say_time_gr(chan, t, ints, lang);
06770    return res;
06771 }
06772 
06773 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)
06774 {
06775    
06776    struct tm tm;
06777    int res=0, offset, sndoffset;
06778    char sndfile[256], nextmsg[256];
06779 
06780    if (!format)
06781       format = "AdBY 'digits/at' IMp";
06782 
06783    ast_localtime(&time,&tm,timezone);
06784    
06785    for (offset=0 ; format[offset] != '\0' ; offset++) {
06786       ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
06787       switch (format[offset]) {
06788          /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
06789       case '\'':
06790          /* Literal name of a sound file */
06791          for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
06792             sndfile[sndoffset] = format[offset];
06793          }
06794          sndfile[sndoffset] = '\0';
06795          res = wait_file(chan,ints,sndfile,lang);
06796          break;
06797       case 'A':
06798       case 'a':
06799          /* Sunday - Saturday */
06800          snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
06801          res = wait_file(chan,ints,nextmsg,lang);
06802          break;
06803       case 'B':
06804       case 'b':
06805       case 'h':
06806          /* January - December */
06807          snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
06808          res = wait_file(chan,ints,nextmsg,lang);
06809          break;
06810       case 'd':
06811       case 'e':
06812          /* first - thirtyfirst */
06813          gr_say_number_female(tm.tm_mday, chan, ints, lang);
06814          break;
06815       case 'Y':
06816          /* Year */
06817          
06818          ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
06819          break;
06820       case 'I':
06821       case 'l':
06822          /* 12-Hour */
06823          if (tm.tm_hour == 0)
06824             gr_say_number_female(12, chan, ints, lang);
06825          else if (tm.tm_hour > 12)
06826             gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
06827          else
06828             gr_say_number_female(tm.tm_hour, chan, ints, lang);
06829          break;
06830       case 'H':
06831       case 'k':
06832          /* 24-Hour */
06833          gr_say_number_female(tm.tm_hour, chan, ints, lang);
06834          break;
06835       case 'M':
06836          /* Minute */
06837          if (tm.tm_min) {
06838             if (!res)
06839                res = ast_streamfile(chan, "digits/kai", lang);
06840             if (!res)
06841                res = ast_waitstream(chan, ints);
06842             if (!res)
06843                res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
06844          } else {
06845             if (!res)
06846                res = ast_streamfile(chan, "digits/oclock", lang);
06847             if (!res)
06848                res = ast_waitstream(chan, ints);
06849          }
06850          break;
06851       case 'P':
06852       case 'p':
06853          /* AM/PM */
06854          if (tm.tm_hour > 11)
06855             snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
06856          else
06857             snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
06858          res = wait_file(chan,ints,nextmsg,lang);
06859          break;
06860       case 'Q':
06861          /* Shorthand for "Today", "Yesterday", or ABdY */
06862             /* XXX As emphasized elsewhere, this should the native way in your
06863              * language to say the date, with changes in what you say, depending
06864              * upon how recent the date is. XXX */
06865          {
06866             struct timeval now;
06867             struct tm tmnow;
06868             time_t beg_today, tt;
06869             
06870             gettimeofday(&now,NULL);
06871             tt = now.tv_sec;
06872             ast_localtime(&tt,&tmnow,timezone);
06873             /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
06874             /* In any case, it saves not having to do ast_mktime() */
06875             beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
06876             if (beg_today < time) {
06877                /* Today */
06878                res = wait_file(chan,ints, "digits/today",lang);
06879             } else if (beg_today - 86400 < time) {
06880                /* Yesterday */
06881                res = wait_file(chan,ints, "digits/yesterday",lang);
06882             } else {
06883                res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
06884             }
06885          }
06886          break;
06887       case 'q':
06888          /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
06889             /* XXX As emphasized elsewhere, this should the native way in your
06890              * language to say the date, with changes in what you say, depending
06891              * upon how recent the date is. XXX */
06892          {
06893             struct timeval now;
06894             struct tm tmnow;
06895             time_t beg_today, tt;
06896             
06897             gettimeofday(&now,NULL);
06898             tt = now.tv_sec;
06899             ast_localtime(&tt,&tmnow,timezone);
06900             /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
06901             /* In any case, it saves not having to do ast_mktime() */
06902             beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
06903             if (beg_today < time) {
06904                /* Today */
06905             } else if ((beg_today - 86400) < time) {
06906                /* Yesterday */
06907                res = wait_file(chan,ints, "digits/yesterday",lang);
06908             } else if (beg_today - 86400 * 6 < time) {
06909                /* Within the last week */
06910                res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
06911             } else {
06912                res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
06913             }
06914          }
06915          break;
06916       case 'R':
06917          res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
06918          break;
06919       case 'S':
06920          /* Seconds */
06921          snprintf(nextmsg,sizeof(nextmsg), "digits/kai");
06922          res = wait_file(chan,ints,nextmsg,lang);
06923          if (!res)
06924             res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
06925          if (!res)
06926             snprintf(nextmsg,sizeof(nextmsg), "digits/seconds");
06927          res = wait_file(chan,ints,nextmsg,lang);
06928          break;
06929       case 'T':
06930          res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
06931          break;
06932       case ' ':
06933       case '   ':
06934          /* Just ignore spaces and tabs */
06935          break;
06936       default:
06937          /* Unknown character */
06938          ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
06939       }
06940       /* Jump out on DTMF */
06941       if (res) {
06942          break;
06943       }
06944    }
06945    return res;
06946 }
06947 
06948 
06949 
06950 
06951 /*********************************** Georgian Support ***************************************/
06952 
06953 
06954 /*
06955    Convert a number into a semi-localized string. Only for Georgian.
06956    res must be of at least 256 bytes, preallocated.
06957    The output corresponds to Georgian spoken numbers, so
06958    it may be either converted to real words by applying a direct conversion
06959    table, or played just by substituting the entities with played files.
06960 
06961    Output may consist of the following tokens (separated by spaces):
06962    0, minus.
06963    1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
06964    10-19.
06965    20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
06966    100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
06967    1000, 1000_. (atasi, atas).
06968    1000000, 1000000_. (milioni, milion).
06969    1000000000, 1000000000_. (miliardi, miliard).
06970 
06971    To be able to play the sounds, each of the above tokens needs
06972    a corresponding sound file. (e.g. 200_.gsm).
06973 */
06974 static char* ast_translate_number_ka(int num, char* res, int res_len)
06975 {
06976    char buf[256];
06977    int digit = 0;
06978    int remainder = 0;
06979 
06980 
06981    if (num < 0) {
06982       strncat(res, "minus ", res_len - strlen(res) - 1);
06983       if ( num > INT_MIN ) {
06984          num = -num;
06985       } else {
06986          num = 0;
06987       }
06988    }
06989 
06990 
06991    /* directly read the numbers */
06992    if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
06993       snprintf(buf, sizeof(buf), "%d", num);
06994       strncat(res, buf, res_len - strlen(res) - 1);
06995       return res;
06996    }
06997 
06998 
06999    if (num < 40) {  /* ocda... */
07000       strncat(res, "20_ ", res_len - strlen(res) - 1);
07001       return ast_translate_number_ka(num - 20, res, res_len);
07002    }
07003 
07004    if (num < 60) {  /* ormocda... */
07005       strncat(res, "40_ ", res_len - strlen(res) - 1);
07006       return ast_translate_number_ka(num - 40, res, res_len);
07007    }
07008 
07009    if (num < 80) {  /* samocda... */
07010       strncat(res, "60_ ", res_len - strlen(res) - 1);
07011       return ast_translate_number_ka(num - 60, res, res_len);
07012    }
07013 
07014    if (num < 100) {  /* otxmocda... */
07015       strncat(res, "80_ ", res_len - strlen(res) - 1);
07016       return ast_translate_number_ka(num - 80, res, res_len);
07017    }
07018 
07019 
07020    if (num < 1000) {  /*  as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
07021       remainder = num % 100;
07022       digit = (num - remainder) / 100;
07023 
07024       if (remainder == 0) {
07025          snprintf(buf, sizeof(buf), "%d", num);
07026          strncat(res, buf, res_len - strlen(res) - 1);
07027          return res;
07028       } else {
07029          snprintf(buf, sizeof(buf), "%d_ ", digit*100);
07030          strncat(res, buf, res_len - strlen(res) - 1);
07031          return ast_translate_number_ka(remainder, res, res_len);
07032       }
07033    }
07034 
07035 
07036    if (num == 1000) {
07037       strncat(res, "1000", res_len - strlen(res) - 1);
07038       return res;
07039    }
07040 
07041 
07042    if (num < 1000000) {
07043       remainder = num % 1000;
07044       digit = (num - remainder) / 1000;
07045 
07046       if (remainder == 0) {
07047          ast_translate_number_ka(digit, res, res_len);
07048          strncat(res, " 1000", res_len - strlen(res) - 1);
07049          return res;
07050       }
07051 
07052       if (digit == 1) {
07053          strncat(res, "1000_ ", res_len - strlen(res) - 1);
07054          return ast_translate_number_ka(remainder, res, res_len);
07055       }
07056 
07057       ast_translate_number_ka(digit, res, res_len);
07058       strncat(res, " 1000_ ", res_len - strlen(res) - 1);
07059       return ast_translate_number_ka(remainder, res, res_len);
07060 
07061    }
07062 
07063 
07064    if (num == 1000000) {
07065       strncat(res, "1 1000000", res_len - strlen(res) - 1);
07066       return res;
07067    }
07068 
07069 
07070    if (num < 1000000000) {
07071       remainder = num % 1000000;
07072       digit = (num - remainder) / 1000000;
07073 
07074       if (remainder == 0) {
07075          ast_translate_number_ka(digit, res, res_len);
07076          strncat(res, " 1000000", res_len - strlen(res) - 1);
07077          return res;
07078       }
07079 
07080       ast_translate_number_ka(digit, res, res_len);
07081       strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
07082       return ast_translate_number_ka(remainder, res, res_len);
07083 
07084    }
07085 
07086 
07087    if (num == 1000000000) {
07088       strncat(res, "1 1000000000", res_len - strlen(res) - 1);
07089       return res;
07090    }
07091 
07092 
07093    if (num > 1000000000) {
07094       remainder = num % 1000000000;
07095       digit = (num - remainder) / 1000000000;
07096 
07097       if (remainder == 0) {
07098          ast_translate_number_ka(digit, res, res_len);
07099          strncat(res, " 1000000000", res_len - strlen(res) - 1);
07100          return res;
07101       }
07102 
07103       ast_translate_number_ka(digit, res, res_len);
07104       strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
07105       return ast_translate_number_ka(remainder, res, res_len);
07106 
07107    }
07108 
07109    return res;
07110 
07111 }
07112 
07113 
07114 
07115 /*! \brief  ast_say_number_full_ka: Georgian syntax */
07116 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)
07117 {
07118    int res = 0;
07119    char fn[512] = "";
07120    char* s = 0;
07121    const char* remainder = fn;
07122 
07123    if (!num)
07124       return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
07125 
07126 
07127    ast_translate_number_ka(num, fn, 512);
07128 
07129 
07130 
07131    while (res == 0 && (s = strstr(remainder, " "))) {
07132       size_t len = s - remainder;
07133       char* new_string = malloc(len + 1 + strlen("digits/"));
07134 
07135       sprintf(new_string, "digits/");
07136       strncat(new_string, remainder, len);  /* we can't sprintf() it, it's not null-terminated. */
07137 /*       new_string[len + strlen("digits/")] = '\0'; */
07138 
07139       if (!ast_streamfile(chan, new_string, language)) {
07140          if ((audiofd  > -1) && (ctrlfd > -1))
07141             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
07142          else
07143             res = ast_waitstream(chan, ints);
07144       }
07145       ast_stopstream(chan);
07146 
07147       free(new_string);
07148 
07149       remainder = s + 1;  /* position just after the found space char. */
07150       while (*remainder == ' ')  /* skip multiple spaces */
07151          remainder++;
07152    }
07153 
07154 
07155    /* the last chunk. */
07156    if (res == 0 && *remainder) {
07157 
07158       char* new_string = malloc(strlen(remainder) + 1 + strlen("digits/"));
07159       sprintf(new_string, "digits/%s", remainder);
07160 
07161       if (!ast_streamfile(chan, new_string, language)) {
07162          if ((audiofd  > -1) && (ctrlfd > -1))
07163             res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
07164          else
07165             res = ast_waitstream(chan, ints);
07166       }
07167       ast_stopstream(chan);
07168 
07169       free(new_string);
07170 
07171    }
07172 
07173 
07174    return res;
07175 
07176 }
07177 
07178 
07179 
07180 /*
07181 Georgian support for date/time requires the following files (*.gsm):
07182 
07183 mon-1, mon-2, ... (ianvari, tebervali, ...)
07184 day-1, day-2, ... (orshabati, samshabati, ...)
07185 saati_da
07186 tsuti
07187 tslis
07188 */
07189 
07190 
07191 
07192 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
07193 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07194 {
07195    struct tm tm;
07196    char fn[256];
07197    int res = 0;
07198    ast_localtime(&t,&tm,NULL);
07199 
07200    if (!res)
07201       res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
07202 
07203    if (!res) {
07204       snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
07205       res = ast_streamfile(chan, fn, lang);
07206       if (!res)
07207          res = ast_waitstream(chan, ints);
07208    }
07209 
07210    if (!res) {
07211       res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
07212 /*       if (!res)
07213          res = ast_waitstream(chan, ints);
07214 */
07215    }
07216 
07217    if (!res) {
07218       snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07219       res = ast_streamfile(chan, fn, lang);
07220       if (!res)
07221          res = ast_waitstream(chan, ints);
07222    }
07223    return res;
07224 
07225 }
07226 
07227 
07228 
07229 
07230 
07231 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
07232 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07233 {
07234    struct tm tm;
07235    int res = 0;
07236 
07237    ast_localtime(&t, &tm, NULL);
07238 
07239    res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
07240    if (!res) {
07241       res = ast_streamfile(chan, "digits/saati_da", lang);
07242       if (!res)
07243          res = ast_waitstream(chan, ints);
07244    }
07245 
07246    if (tm.tm_min) {
07247       if (!res) {
07248          res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
07249 
07250          if (!res) {
07251             res = ast_streamfile(chan, "digits/tsuti", lang);
07252             if (!res)
07253                res = ast_waitstream(chan, ints);
07254          }
07255       }
07256    }
07257    return res;
07258 }
07259 
07260 
07261 
07262 /* Georgian syntax. Say date, then say time. */
07263 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07264 {
07265    struct tm tm;
07266    int res = 0;
07267 
07268    ast_localtime(&t, &tm, NULL);
07269    res = ast_say_date(chan, t, ints, lang);
07270    if (!res)
07271       ast_say_time(chan, t, ints, lang);
07272    return res;
07273 
07274 }
07275 
07276 
07277 
07278 
07279 /* Georgian syntax */
07280 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
07281 {
07282    int res=0;
07283    time_t nowt;
07284    int daydiff;
07285    struct tm tm;
07286    struct tm now;
07287    char fn[256];
07288 
07289    time(&nowt);
07290 
07291    ast_localtime(&t, &tm, NULL);
07292    ast_localtime(&nowt, &now, NULL);
07293    daydiff = now.tm_yday - tm.tm_yday;
07294    if ((daydiff < 0) || (daydiff > 6)) {
07295       /* Day of month and month */
07296       if (!res)
07297          res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
07298       if (!res) {
07299          snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
07300          res = ast_streamfile(chan, fn, lang);
07301          if (!res)
07302             res = ast_waitstream(chan, ints);
07303       }
07304 
07305    } else if (daydiff) {
07306       /* Just what day of the week */
07307       if (!res) {
07308          snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
07309          res = ast_streamfile(chan, fn, lang);
07310          if (!res)
07311             res = ast_waitstream(chan, ints);
07312       }
07313    } /* Otherwise, it was today */
07314    if (!res)
07315       res = ast_say_time(chan, t, ints, lang);
07316 
07317    return res;
07318 }
07319 
07320 /* In English, we use the plural for everything but one. For example:
07321  *  1 degree
07322  *  2 degrees
07323  *  5 degrees
07324  * The filename for the plural form is generated by appending "s". Note that
07325  * purpose is to generate a unique filename, not to implement irregular 
07326  * declensions. Thus:
07327  *  1 man
07328  *  2 mans (the "mans" soundfile will of course say "men")
07329  */
07330 static const char *counted_noun_ending_en(int num)
07331 {
07332    if (num == 1 || num == -1) {
07333       return "";
07334    } else {
07335       return "s";
07336    }
07337 }
07338 
07339 /* Counting of objects in slavic languages such as Russian and Ukrainian the
07340  * rules are more complicated. There are two plural forms used in counting.
07341  * They are the genative singular which we represent with the suffix "x1" and 
07342  * the genative plural which we represent with the suffix "x2". The base names
07343  * of the soundfiles remain in English. For example:
07344  *  1 degree (soudfile says "gradus")
07345  *  2 degreex1 (soundfile says "gradusa")
07346  *  5 degreex2 (soundfile says "gradusov")
07347  */
07348 static const char *counted_noun_ending_slavic(int num)
07349 {
07350       if (num < 0) {
07351        num *= -1;
07352    }
07353    num %= 100;       /* never pay attention to more than two digits */
07354    if (num >= 20) {     /* for numbers 20 and above, pay attention to only last digit */
07355        num %= 10;
07356    }
07357    if (num == 1) {         /* singular */
07358        return "";
07359    }
07360    if (num > 0 && num < 5) {  /* 2--4 get genative singular */
07361        return "x1";
07362    } else {       /* 5--19 get genative plural */
07363        return "x2";
07364    }
07365 }
07366 
07367 int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
07368 {
07369    char *temp;
07370    int temp_len;
07371    const char *ending;
07372    if (!strncasecmp(chan->language, "ru", 2)) {        /* Russian */
07373       ending = counted_noun_ending_slavic(num);
07374    } else if (!strncasecmp(chan->language, "ua", 2)) { /* Ukrainian */
07375       ending = counted_noun_ending_slavic(num);
07376    } else if (!strncasecmp(chan->language, "pl", 2)) { /* Polish */
07377       ending = counted_noun_ending_slavic(num);
07378    } else {                                            /* English and default */
07379       ending = counted_noun_ending_en(num);
07380    }
07381    temp = alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
07382    snprintf(temp, temp_len, "%s%s", noun, ending);
07383    return ast_play_and_wait(chan, temp);
07384 }
07385 
07386 /*
07387  * In slavic languages such as Russian and Ukrainian the rules for declining
07388  * adjectives are simpler than those for nouns.  When counting we use only
07389  * the singular (to which we give no suffix) and the genative plural (which
07390  * we represent by adding an "x").  Oh, an in the singular gender matters
07391  * so we append the supplied gender suffix ("m", "f", "n").
07392  */
07393 static const char *counted_adjective_ending_ru(int num, const char gender[])
07394 {
07395    if (num < 0) {
07396        num *= -1;
07397    }
07398    num %= 100;    /* never pay attention to more than two digits */
07399    if (num >= 20) {  /* at 20 and beyond only the last digit matters */
07400        num %= 10;
07401    }
07402    if (num == 1) {
07403        return gender ? gender : "";
07404    } else {    /* all other numbers get the genative plural */
07405        return "x";
07406    }
07407 }
07408 
07409 int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
07410 {
07411    char *temp;
07412    int temp_len;
07413    const char *ending;
07414    if (!strncasecmp(chan->language, "ru", 2)) {           /* Russian */
07415       ending = counted_adjective_ending_ru(num, gender);
07416    } else if (!strncasecmp(chan->language, "ua", 2)) {    /* Ukrainian */
07417       ending = counted_adjective_ending_ru(num, gender);
07418    } else if (!strncasecmp(chan->language, "pl", 2)) {    /* Polish */
07419       ending = counted_adjective_ending_ru(num, gender);
07420    } else {                                               /* English and default */
07421       ending = "";
07422    }
07423    temp = alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
07424    snprintf(temp, temp_len, "%s%s", adjective, ending);
07425    return ast_play_and_wait(chan, temp);
07426 }
07427 
07428 
07429 
07430 /*
07431  * remap the 'say' functions to use those in this file
07432  */
07433 static void __attribute__((constructor)) __say_init(void)
07434 {
07435    ast_say_number_full = say_number_full;
07436    ast_say_enumeration_full = say_enumeration_full;
07437    ast_say_digit_str_full = say_digit_str_full;
07438    ast_say_character_str_full = say_character_str_full;
07439    ast_say_phonetic_str_full = say_phonetic_str_full;
07440    ast_say_datetime = say_datetime;
07441    ast_say_time = say_time;
07442    ast_say_date = say_date;
07443    ast_say_datetime_from_now = say_datetime_from_now;
07444    ast_say_date_with_format = say_date_with_format;
07445 }

Generated on Sat Aug 6 00:39:32 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7