Thu Apr 28 2011 17:15:25

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