Skip to content
Snippets Groups Projects
say.c 72.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
     * Say numbers and dates (maybe words one day too)
     * 
    
    Mark Spencer's avatar
    Mark Spencer committed
     * Copyright (C) 1999, Mark Spencer
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * Mark Spencer <markster@linux-support.net>
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    
    #include <sys/types.h>
    
    #include <string.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <time.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/file.h>
    #include <asterisk/channel.h>
    #include <asterisk/logger.h>
    #include <asterisk/say.h>
    
    #include <asterisk/lock.h>
    #include <asterisk/localtime.h>
    #include "asterisk.h"
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <stdio.h>
    
    
    
    /* Forward declaration */
    static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* XXX Merge with full version? XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char fn[256] = "";
    	int num = 0;
    	int res = 0;
    	while(fn2[num] && !res) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		switch (fn2[num]) {
    			case ('*'):
    				snprintf(fn, sizeof(fn), "digits/star");
    				break;
    			case ('#'):
    				snprintf(fn, sizeof(fn), "digits/pound");
    				break;
    			default:
    
    				if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */
    
    					snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
    				}
    
    		if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
    			res = ast_streamfile(chan, fn, lang);
    
    				res = ast_waitstream(chan, ints);
    			ast_stopstream(chan);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		num++;
    	}
    	return res;
    }
    
    
    int ast_say_character_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
    {
    	/* XXX Merge with full version? XXX */
    	char fn[256] = "";
    	char ltr;
    	int num = 0;
    	int res = 0;
    	while(fn2[num] && !res) {
    		fn[0] = '\0';
    		switch (fn2[num]) {
    			case ('*'):
    				snprintf(fn, sizeof(fn), "digits/star");
    				break;
    			case ('#'):
    				snprintf(fn, sizeof(fn), "digits/pound");
    				break;
     			case ('0'):
     			case ('1'):
     			case ('2'):
     			case ('3'):
     			case ('4'):
     			case ('5'):
     			case ('6'):
     			case ('7'):
     			case ('8'):
     			case ('9'):
      				snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
     				break;
    			case ('!'):
    				strncpy(fn, "letters/exclaimation-point", sizeof(fn));
    				break;    	
     			case ('@'):
     				strncpy(fn, "letters/at", sizeof(fn));
     				break;
     			case ('$'):
     				strncpy(fn, "letters/dollar", sizeof(fn));
     				break;
     			case ('-'):
     				strncpy(fn, "letters/dash", sizeof(fn));
     				break;
     			case ('.'):
     				strncpy(fn, "letters/dot", sizeof(fn));
     				break;
     			case ('='):
     				strncpy(fn, "letters/equals", sizeof(fn));
     				break;
     			case ('+'):
     				strncpy(fn, "letters/plus", sizeof(fn));
     				break;
     			case ('/'):
     				strncpy(fn, "letters/slash", sizeof(fn));
     				break;
     			case (' '):
     				strncpy(fn, "letters/space", sizeof(fn));
     				break;
     			default:
     				ltr = fn2[num];
     				if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';		/* file names are all lower-case */
     				snprintf(fn, sizeof(fn), "letters/%c", ltr);
      		}
    		if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
    			res = ast_streamfile(chan, fn, lang);
    			if (!res) 
    				res = ast_waitstream(chan, ints);
    		}	ast_stopstream(chan);
    		num++;
    	}
    	return res;
    }
    
    int ast_say_phonetic_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
    {
    	/* XXX Merge with full version? XXX */
    	char fn[256] = "";
    	char ltr;
    	int num = 0;
    	int res = 0;
    	int temp;
    	int play;
    	char hex[3];
    /*	while(fn2[num] && !res) { */
    	while(fn2[num]) {
    		play=1;
    		switch (fn2[num]) {
    			case ('*'):
    				snprintf(fn, sizeof(fn), "digits/star");
    				break;
    			case ('#'):
    				snprintf(fn, sizeof(fn), "digits/pound");
    				break;
    			case ('0'):
    			case ('1'):
    			case ('2'):
    			case ('3'):
    			case ('4'):
    			case ('5'):
    			case ('6'):
    			case ('7'):
    			case ('8'):
    				snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
    				break;
    			case ('!'):
    				strncpy(fn, "exclaimation-point", sizeof(fn));
    				break;    	
    			case ('@'):
    				strncpy(fn, "at", sizeof(fn));
    				break;
    			case ('$'):
    				strncpy(fn, "dollar", sizeof(fn));
    				break;	
    			case ('-'):
    				strncpy(fn, "dash", sizeof(fn));
    				break;
    			case ('.'):
    				strncpy(fn, "dot", sizeof(fn));
    				break;
    			case ('='):
    				strncpy(fn, "equals", sizeof(fn));
    				break;
    			case ('+'):
    				strncpy(fn, "plus", sizeof(fn));
    				break;
    			case ('/'):
    				strncpy(fn, "slash", sizeof(fn));
    				break;
    			case (' '):
    				strncpy(fn, "space", sizeof(fn));
    				break;
    			case ('%'):
    				play=0;
    				/* check if we have 2 chars after the % */
    				if (strlen(fn2)>num+2)
    				{
    				    hex[0]=fn2[num+1];
    				    hex[1]=fn2[num+2];
    				    hex[2]='\0';
    				    if (sscanf(hex,"%x", &temp))
    				    { /* Hex to char convertion successfull */
    				        fn2[num+2]=temp;
    				        num++;
    				        if (temp==37)
    				        { /* If it is a percent, play it now */
    				    	    strncpy(fn, "percent", sizeof(fn));
    					    	num++;
    					    	play=1;
    						}
    						/* check for invalid characters */
    						if ((temp<32) || (temp>126))
    						{
    						    num++;
    						}
    				    }
    				}
    				else
    				    num++;
    				break;
    			default:	/* '9' falls through to here, too */
    				ltr = tolower(fn2[num]);
    				snprintf(fn, sizeof(fn), "phonetic/%c_p", ltr);
    		}
    		if (play)
    		{
    		    res = ast_streamfile(chan, fn, lang);
    		    if (!res) 
    			res = ast_waitstream(chan, ints);
    		    ast_stopstream(chan);
    		}
    		num++;
    	}
    	return res;
    }
    
    
    int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
    {
    	char fn[256] = "";
    	int num = 0;
    	int res = 0;
    	while(fn2[num] && !res) {
    		snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
    		res = ast_streamfile(chan, fn, lang);
    		if (!res) 
    			res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    		ast_stopstream(chan);
    		num++;
    	}
    	return res;
    }
    
    
    int ast_say_character_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
    {
    	char fn[256] = "";
    	char ltr;
    	int num = 0;
    	int res = 0;
    	while(fn2[num] && !res) {
    		switch (fn2[num]) {
    			case ('*'):
    				snprintf(fn, sizeof(fn), "digits/star");
    				break;
    			case ('#'):
    				snprintf(fn, sizeof(fn), "digits/pound");
    				break;
    			case ('0'):
    			case ('1'):
    			case ('2'):
    			case ('3'):
    			case ('4'):
    			case ('5'):
    			case ('6'):
    			case ('7'):
    			case ('8'):
    			case ('9'):
    				snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
    				break;
    			case ('!'):
    				strncpy(fn, "exclaimation-point", sizeof(fn));
    				break;    	
    			case ('@'):
    				strncpy(fn, "at", sizeof(fn));
    				break;
    			case ('$'):
    				strncpy(fn, "dollar", sizeof(fn));
    				break;
    			case ('-'):
    				strncpy(fn, "dash", sizeof(fn));
    				break;
    			case ('.'):
    				strncpy(fn, "dot", sizeof(fn));
    				break;
    			case ('='):
    				strncpy(fn, "equals", sizeof(fn));
    				break;
    			case ('+'):
    				strncpy(fn, "plus", sizeof(fn));
    				break;
    			case ('/'):
    				strncpy(fn, "slash", sizeof(fn));
    				break;
    			case (' '):
    				strncpy(fn, "space", sizeof(fn));
    				break;
    			default:
    				ltr = fn2[num];
    				if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';		/* file names are all lower-case */
    				snprintf(fn, sizeof(fn), "letters/%c", ltr);
    		}
    		/* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
    		res = ast_streamfile(chan, fn, lang);
    		if (!res) 
    			res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    		ast_stopstream(chan);
    		num++;
    	}
    	return res;
    }
    
    int ast_say_phonetic_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
    {
    	char fn[256] = "";
    	char ltr;
    	int num = 0;
    	int res = 0;
    	while(fn2[num] && !res) {
    		switch (fn2[num]) {
    			case ('*'):
    				snprintf(fn, sizeof(fn), "digits/star");
    				break;
    			case ('#'):
    				snprintf(fn, sizeof(fn), "digits/pound");
    				break;
    			case ('0'):
    			case ('1'):
    			case ('2'):
    			case ('3'):
    			case ('4'):
    			case ('5'):
    			case ('6'):
    			case ('7'):
    			case ('8'):
    				snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
    				break;
    			case ('!'):
    				strncpy(fn, "exclaimation-point", sizeof(fn));
    				break;    	
    			case ('@'):
    				strncpy(fn, "at", sizeof(fn));
    				break;
    			case ('$'):
    				strncpy(fn, "dollar", sizeof(fn));
    				break;
    			case ('-'):
    				strncpy(fn, "dash", sizeof(fn));
    				break;
    			case ('.'):
    				strncpy(fn, "dot", sizeof(fn));
    				break;
    			case ('='):
    				strncpy(fn, "equals", sizeof(fn));
    				break;
    			case ('+'):
    				strncpy(fn, "plus", sizeof(fn));
    				break;
    			case ('/'):
    				strncpy(fn, "slash", sizeof(fn));
    				break;
    			case (' '):
    				strncpy(fn, "space", sizeof(fn));
    				break;
    			default:	/* '9' falls here... */
    				ltr = fn2[num];
    				if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';		/* file names are all lower-case */
    				snprintf(fn, sizeof(fn), "phonetic/%c", ltr);
    			}
    		/* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
    		res = ast_streamfile(chan, fn, lang);
    		if (!res) 
    			res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    		ast_stopstream(chan);
    		num++;
    	}
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* XXX Should I be merged with say_digits_full XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char fn2[256];
    	snprintf(fn2, sizeof(fn2), "%d", num);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ast_say_digit_str(chan, fn2, ints, lang);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    
    int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
    {
    	char fn2[256];
    	snprintf(fn2, sizeof(fn2), "%d", num);
    	return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
    }
    
    
    /* Forward declarations */
    /* Syntaxes supported, not really language codes.
    
     Gender:
     For Portuguese, we're using m & f options to saynumber() to indicate if the gender is masculine or feminine.
     For Danish, we're using c & n options to saynumber() to indicate if the gender is commune or neutrum.
     This still needs to be implemented for French, Spanish & German.
    
     
     Date/Time functions currently have less languages supported than saynumber().
    
    
     Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
    
     See contrib/i18n.testsuite.conf for some examples of the different syntaxes
    
     Portuguese sound files needed for Time/Date functions:
     pt-ah
     pt-ao
     pt-de
     pt-e
     pt-ora
     pt-meianoite
     pt-meiodia
     pt-sss
    
    
    /* Forward declarations of language specific variants of ast_say_number_full */
    
    static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
    static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
    
    static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
    
    static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
    
    static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
    
    static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
    
    static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
    
    static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
    
    static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
    
    
    /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
    
    static int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
    
    static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
    static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
    static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
    static int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
    static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
    
    static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang) 
    {
    	int res;
    	if ((res = ast_streamfile(chan, file, lang)))
    		ast_log(LOG_WARNING, "Unable to play message %s\n", file);
    	if (!res)
    		res = ast_waitstream(chan, ints);
    	return res;
    }
    
    
    /*--- ast_say_number_full: call language-specific functions */
    /* Called from AGI */
    
    int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
    
    {
    	char *options=(char *) NULL; 	/* While waiting for a general hack for agi */
    
    
    	if (!strcasecmp(language,"en") ) {	/* English syntax */
    
    	   return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
    	} else if (!strcasecmp(language, "da") ) {	/* Danish syntax */
    	   return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
    
    	} else if (!strcasecmp(language, "de") ) {	/* German syntax */
    	   return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
    
    	} else if (!strcasecmp(language, "es") ) {	/* Spanish syntax */
    	   return(ast_say_number_full_es(chan, num, ints, language, audiofd, ctrlfd));
    
    	} else if (!strcasecmp(language, "fr") ) {	/* French syntax */
    	   return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
    	} else if (!strcasecmp(language, "it") ) {	/* Italian syntax */
    	   return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
    
    	} else if (!strcasecmp(language, "nl") ) {	/* Dutch syntax */
    
    	   return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
    
    	} else if (!strcasecmp(language, "pt") ) {	/* Portuguese syntax */
    	   return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
    	} else if (!strcasecmp(language, "se") ) {	/* Swedish syntax */
    	   return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
    
    	}
    
    	/* Default to english */
    	return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
    }
    
    /*--- ast_say_number: call language-specific functions without file descriptors */
    int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
    {
    
    	if (!strcasecmp(language,"en") ) {	/* English syntax */
    
    	   return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
    
    	}else if (!strcasecmp(language, "da")) {	/* Danish syntax */
    
    	   return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
    
    	} else if (!strcasecmp(language, "de")) {	/* German syntax */
    
    	   return(ast_say_number_full_de(chan, num, ints, language, options, -1, -1));
    	} else if (!strcasecmp(language, "es")) {	/* Spanish syntax */
    	   return(ast_say_number_full_es(chan, num, ints, language, -1, -1));
    	} else if (!strcasecmp(language, "fr")) {	/* French syntax */
    	   return(ast_say_number_full_fr(chan, num, ints, language, options, -1, -1));
    
    	} else if (!strcasecmp(language, "it")) {	/* Italian syntax */
    	   return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
    
    	} else if (!strcasecmp(language, "nl")) {	/* Dutch syntax */
    
    	   return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
    
    	} else if (!strcasecmp(language, "pt")) {	/* Portuguese syntax */
    	   return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
    	} else if (!strcasecmp(language, "se")) {	/* Swedish syntax */
    	   return(ast_say_number_full_se(chan, num, ints, language, options, -1, -1));
    
    Mark Spencer's avatar
    Mark Spencer committed
            return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
    
    }
    
    /*--- ast_say_number_full_en: English syntax */
    /* This is the default syntax, if no other syntax defined in this file is used */
    static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
    
    {
    	int res = 0;
    	int playh = 0;
    	char fn[256] = "";
    	if (!num) 
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    
    			if (playh) {
    				snprintf(fn, sizeof(fn), "digits/hundred");
    				playh = 0;
    
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    
    				snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
    				num -= ((num / 10) * 10);
    			} else {
    				if (num < 1000){
    					snprintf(fn, sizeof(fn), "digits/%d", (num/100));
    					playh++;
    					num -= ((num / 100) * 100);
    				} else {
    
    					if (num < 1000000) { /* 1,000,000 */
    
    						res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
    
    						if (res)
    							return res;
    						num = num % 1000;
    						snprintf(fn, sizeof(fn), "digits/thousand");
    					} else {
    
    						if (num < 1000000000) {	/* 1,000,000,000 */
    
    							res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
    
    							if (res)
    								return res;
    							num = num % 1000000;
    							snprintf(fn, sizeof(fn), "digits/million");
    						} else {
    							ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
    							res = -1;
    						}
    					}
    				}
    			}
    
    			if (!res) {
    				if(!ast_streamfile(chan, fn, language)) {
    					if (audiofd && ctrlfd)
    						res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    					else
    						res = ast_waitstream(chan, ints);
    				}
    				ast_stopstream(chan);
    
    /*--- ast_say_number_full_da: Danish syntax */
    /* New files:
     In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and" 
     */
    static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
    {
    	int res = 0;
    	int playh = 0;
    	int playa = 0;
    	int cn = 1;		/* +1 = Commune; -1 = Neutrum */
    	char fn[256] = "";
    	if (!num) 
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    
    	if (options && !strncasecmp(options, "n",1)) cn = -1;
    
    	while(!res && (num || playh || playa )) {
    
    		/* The grammar for Danish numbers is the same as for English except
    		* for the following:
    
    		* - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
    
    		* - numbers 20 through 99 are said in reverse order, i.e. 21 is
    		*   "one-and twenty" and 68 is "eight-and sixty".
    		* - "million" is different in singular and plural form
    		* - numbers > 1000 with zero as the third digit from last have an
    		*   "and" before the last two digits, i.e. 2034 is "two thousand and
    		*   four-and thirty" and 1000012 is "one million and twelve".
    		*/
    		if (playh) {
    			snprintf(fn, sizeof(fn), "digits/hundred");
    			playh = 0;
    		} else if (playa) {
    			snprintf(fn, sizeof(fn), "digits/and");
    			playa = 0;
    		} else if (num == 1 && cn == -1) {
    			snprintf(fn, sizeof(fn), "digits/1N");
    			num = 0;
    		} else if (num < 20) {
    			snprintf(fn, sizeof(fn), "digits/%d", num);
    			num = 0;
    		} else if (num < 100) {
    			int ones = num % 10;
    			if (ones) {
    				snprintf(fn, sizeof(fn), "digits/%d-and", ones);
    				num -= ones;
    			} else {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				num = 0;
    
    			}
    		} else {
    			if (num < 1000) {
    				int hundreds = num / 100;
    				if (hundreds == 1)
    					snprintf(fn, sizeof(fn), "digits/1N");
    				else
    					snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
    
    				playh++;
    				num -= 100 * hundreds;
    				if (num)
    					playa++;
    
    			} else {
    				if (num < 1000000) {
    					res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
    					if (res)
    						return res;
    					num = num % 1000;
    					snprintf(fn, sizeof(fn), "digits/thousand");
    				} else {
    					if (num < 1000000000) {
    						int millions = num / 1000000;
    						res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
    						if (res)
    							return res;
    						if (millions == 1)
    							snprintf(fn, sizeof(fn), "digits/million");
    						else
    							snprintf(fn, sizeof(fn), "digits/millions");
    						num = num % 1000000;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else {
    
    						ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
    						res = -1;
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    		}
    		if (!res) {
    			if(!ast_streamfile(chan, fn, language)) {
    				if (audiofd && ctrlfd) 
    					res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    				else  
    					res = ast_waitstream(chan, ints);
    			}
    			ast_stopstream(chan);
    		}
    
    	}
    	return res;
    }
    
    /*--- ast_say_number_full_de: German syntax */
    /* New files:
     In addition to English, the following sounds are required: "millions", "and" and "1-and" through "9-and" 
     */
    
    static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
    
    {
    	int res = 0;
    	int playh = 0;
    	int playa = 0;
    	char fn[256] = "";
    	if (!num) 
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    
    	while(!res && (num || playh || playa )) {
    
    		/* The grammar for German numbers is the same as for English except
    		* for the following:
    		* - numbers 20 through 99 are said in reverse order, i.e. 21 is
    		*   "one-and twenty" and 68 is "eight-and sixty".
    		* - "million" is different in singular and plural form
    		* - numbers > 1000 with zero as the third digit from last have an
    		*   "and" before the last two digits, i.e. 2034 is "two thousand and
    		*   four-and thirty" and 1000012 is "one million and twelve".
    		*/
    		if (playh) {
    			snprintf(fn, sizeof(fn), "digits/hundred");
    			playh = 0;
    		} else if (playa) {
    			snprintf(fn, sizeof(fn), "digits/and");
    			playa = 0;
    		} else if (num < 20) {
    			snprintf(fn, sizeof(fn), "digits/%d", num);
    			num = 0;
    		} else if (num < 100) {
    			int ones = num % 10;
    			if (ones) {
    				snprintf(fn, sizeof(fn), "digits/%d-and", ones);
    				num -= ones;
    			} else {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    			}
    		} else {
    			if (num < 1000) {
    				int hundreds = num / 100;
    				if (hundreds == 1)
    					snprintf(fn, sizeof(fn), "digits/1N");
    				else
    					snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
    				playh++;
    				num -= 100 * hundreds;
    				if (num)
    					playa++;
    			} else {
    				if (num < 1000000) {
    
    					res = ast_say_number_full_de(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
    
    					if (res)
    						return res;
    					num = num % 1000;
    					snprintf(fn, sizeof(fn), "digits/thousand");
    				} else {
    					if (num < 1000000000) {
    						int millions = num / 1000000;
    
    						res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
    
    						if (res)
    							return res;
    						if (millions == 1)
    							snprintf(fn, sizeof(fn), "digits/million");
    						else
    							snprintf(fn, sizeof(fn), "digits/millions");
    						num = num % 1000000;
    
    						ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
    						res = -1;
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    		}
    		if (!res) {
    			if(!ast_streamfile(chan, fn, language)) {
    				if (audiofd && ctrlfd) 
    					res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    				else  
    					res = ast_waitstream(chan, ints);
    			}
    			ast_stopstream(chan);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	return res;
    }
    
    /*--- ast_say_number_full_es: spanish syntax */
    /* New files:
     Requires a few new audios:
       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 
     */
    static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
    
    	char fn[256] = "";
    	if (!num) 
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    
    	while (!res && num) {
    		if (playa) {
    			snprintf(fn, sizeof(fn), "digits/y");
    			playa = 0;
    		} else if (num < 31) {
    			snprintf(fn, sizeof(fn), "digits/%d", num);
    			num = 0;
    		} else if (num < 100) {
    			snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
    			num -= ((num/10)*10);
    			if (num)
    				playa++;
    		} else if (num == 100) {
    			snprintf(fn, sizeof(fn), "digits/cien");
    			num = 0;
    		} else {
    			if (num < 1000) {
    				snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
    				num -= ((num/100)*100);
    			} else {
    				if (num < 1000000) {
    					res = ast_say_number_full_es(chan, num / 1000, ints, language, audiofd, ctrlfd);
    					if (res)
    						return res;
    					num = num % 1000;
    					snprintf(fn, sizeof(fn), "digits/mil");
    				} else {
    					if (num < 2147483640) {
    						res = ast_say_number_full_es(chan, num / 1000000, ints, language, audiofd, ctrlfd);
    						if (res)
    							return res;
    						if ((num/1000000) == 1) {
    							snprintf(fn, sizeof(fn), "digits/millon");
    						} else {
    							snprintf(fn, sizeof(fn), "digits/millones");
    						}
    						num = num % 1000000;
    					} else {
    						ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
    						res = -1;
    					}
    				}
    			}
    		}
    
    		if (!res) {
    			if(!ast_streamfile(chan, fn, language)) {
    				if (audiofd && ctrlfd)
    					res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    				else
    					res = ast_waitstream(chan, ints);
    			}
    			ast_stopstream(chan);
    
    /*--- ast_say_number_full_fr: French syntax */
    
    /* 	Extra sounds needed: */
    /* 	1F: feminin 'une' */
    /* 	et: 'and' */
    static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
    
    	int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
    
    	char fn[256] = "";
    	if (!num) 
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    
    	
    	if (options && !strncasecmp(options, "f",1))
    		mf = -1;
    
    
    	while(!res && (num || playh || playa)) {
    		if (playh) {
    			snprintf(fn, sizeof(fn), "digits/hundred");
    			playh = 0;
    		} else if (playa) {
    			snprintf(fn, sizeof(fn), "digits/et");
    			playa = 0;
    
    		} else if (num == 1) {
    			if (mf < 0)
    				snprintf(fn, sizeof(fn), "digits/%dF", num);
    			else
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    			num = 0;
    
    		} else if (num < 21) {
    			snprintf(fn, sizeof(fn), "digits/%d", num);
    			num = 0;
    		} else if (num < 70) {
    			snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
    			if ((num % 10) == 1) playa++;
    			num = num % 10;
    		} else if (num < 80) {
    			snprintf(fn, sizeof(fn), "digits/60");
    			if ((num % 10) == 1) playa++;
    			num = num - 60;
    		} else if (num < 100) {
    			snprintf(fn, sizeof(fn), "digits/80");
    			num = num - 80;
    		} else if (num < 200) {
    			snprintf(fn, sizeof(fn), "digits/hundred");
    			num = num - 100;
    		} else if (num < 1000) {
    			snprintf(fn, sizeof(fn), "digits/%d", (num/100));
    			playh++;
    			num = num % 100;
    		} else if (num < 2000) {
    			snprintf(fn, sizeof(fn), "digits/thousand");
    			num = num - 1000;
    		} else if (num < 1000000) {
    
    			res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
    
    			if (res)
    				return res;
    			snprintf(fn, sizeof(fn), "digits/thousand");
    			num = num % 1000;
    		} else	if (num < 1000000000) {
    
    			res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
    
    			if (res)
    				return res;
    			snprintf(fn, sizeof(fn), "digits/million");
    			num = num % 1000000;
    		} else {
    			ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
    			res = -1;
    		}
    		if (!res) {
    			if(!ast_streamfile(chan, fn, language)) {
    				if (audiofd && ctrlfd)
    					res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
    				else
    					res = ast_waitstream(chan, ints);
    			}
    			ast_stopstream(chan);
    		}
    	}
    	return res;
    }
    
    /*--- ast_say_number_full_it:  Italian */
    
    static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
    {
    
    	int res = 0;
    	int playh = 0;
    	int tempnum = 0;
    	char fn[256] = "";
    
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    
    		/*
    		Italian support
    
    
    		Like english, numbers up to 20 are a single 'word', and others
    
    		For example 21 is not twenty-one, but there is a single word in 'it'.
    
    		Idem for 28 (ie when a the 2nd part of a compund number
    
    		There are exceptions also for hundred, thousand and million.
    
    		In english 100 = one hundred, 200 is two hundred.
    		In italian 100 = cento , like to say hundred (without one),
    		200 and more are like english.
    		
    
    		1000 is one thousand in en, 2000 is two thousand.
    		In it we have 1000 = mille , 2000 = 2 mila 
    
    		For million(s) we use the plural, if more than one
    		Also, one million is abbreviated in it, like on-million,
    		or 'un milione', not 'uno milione'.
    		So the right file is provided.
    		*/
    
    
    		while(!res && (num || playh)) {
    			if (playh) {
    				snprintf(fn, sizeof(fn), "digits/hundred");
    				playh = 0;
    			} else if (num < 20) {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    			} else if (num == 21) {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    			} else if (num == 28) {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    			} else if (num == 31) {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    			} else if (num == 38) {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    			} else if (num == 41) {
    				snprintf(fn, sizeof(fn), "digits/%d", num);
    				num = 0;
    			} else if (num == 48) {
    				snprintf(fn, sizeof(fn), "digits/%d", num);