Skip to content
Snippets Groups Projects
say.c 55.4 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);
    			if (!res) 
    				res = ast_waitstream(chan, ints);
    			ast_stopstream(chan);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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;
    }
    
    
    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.
    
          en - English, Swedish, Norwegian
          es - Spanish
    
     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.
    
     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
    
    */
    
    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, 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, 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);
    
    
    /*--- 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, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
    	   return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
    	} else if (!strcasecmp(language, "fr") ) {	/* French syntax */
    	   return(ast_say_number_full_fr(chan, num, ints, language, audiofd, ctrlfd));
    	} else if (!strcasecmp(language, "de") ) {	/* German syntax */
    	   return(ast_say_number_full_de(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, "it") ) {	/* Italian syntax */
    	   return(ast_say_number_full_it(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, "es") ) {	/* Spanish syntax */
    	   return(ast_say_number_full_es(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));
    	}
    
    	/* 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, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
    	   return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
    	}
    	/* French */
    
    	if (!strcasecmp(language, "fr")) {		/* French syntax */
    
    	   return(ast_say_number_full_fr(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, -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, "pt")) {	/* Portuguese syntax */
    	   return(ast_say_number_full_pt(chan, num, ints, language, options, -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, "es")) {	/* Spanish syntax */
    	   return(ast_say_number_full_es(chan, num, ints, language, -1, -1));
    	}
    
    	/* Default to english */
    
    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;
    			} else
    			if (num < 20) {
    				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);
    			} 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_fr: French syntax */
    static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int playh = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char fn[256] = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!num) 
    
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
               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 < 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, 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, 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_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");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				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);
                                           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;
                                                   }
                                           }
                                           if (num && num < 100)
                                                   playa++;
                                   }
                           }
    			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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			
    	}
    	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, 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_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;
    					} else {
                                                           ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
                                                           res = -1;
                                                   }
                                           }
                                           if (num && num < 100)
                                                   playa++;
                                   }
                           }
    
    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);
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_stopstream(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	return res;
    }
    
    
    /*------------ Portuguese ----------------------*/
    /* ast_say_number_full_pt: Portuguese syntax */
    
    /* 	Extra sounds needed: */
    
    /* 	For feminin all sound files end with F */
    /*	100E for 100+ something */
    
    /*	1000000S for plural */
    /*	pt-e for 'and' */
    
    539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
    static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
    {
    	int res = 0;
    	int playh = 0;
    	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 ) {
    
    			if (num < 20) {
                                     if ((num == 1 || num == 2) && (mf < 0))
                                         snprintf(fn, sizeof(fn), "digits/%dF", num);
                                     else
                                         snprintf(fn, sizeof(fn), "digits/%d", num);
                                     num = 0;
                             } else
                             if (num < 100) {
                                     snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
                                     if (num % 10)
                                         playh = 1;
                                     num = num % 10;
                             } else
                             if (num < 1000) {
                                     if (num == 100)
                                         snprintf(fn, sizeof(fn), "digits/100");
                                     else if (num < 200)
                                         snprintf(fn, sizeof(fn), "digits/100E");
                                     else {
                                         if (mf < 0 && num > 199)
                                             snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
                                         else
                                             snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
                                         if (num % 100)
                                             playh = 1;
                                     }
                                     num = num % 100;
                             } else
                             if (num < 1000000) {
                                     if (num > 1999) {
                                        res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
                                        if (res)
                                             return res;
                                     }
                                     snprintf(fn, sizeof(fn), "digits/1000");
                                     if ((num % 1000) && ((num % 1000) < 100  || !(num % 100)))
                                         playh = 1;
                                     num = num % 1000;
                             } else
                             if (num < 1000000000) {
                                    res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
                                    if (res)
                                         return res;
     
                                     if (num < 2000000)
                                         snprintf(fn, sizeof(fn), "digits/1000000");
                                     else
                                         snprintf(fn, sizeof(fn), "digits/1000000S");
     
                                     if ((num % 1000000) &&
                                       // no thousands
                                       ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
                                       // no hundreds and below
                                       (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
                                             playh = 1;
                                     num = num % 1000000;
                             }
    			if (!res && playh) {
                                    res = wait_file(chan, ints, "digits/pt-e", language);
                                    ast_stopstream(chan);
                                    playh = 0;
                            }
     			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] = "";
    
            if (!num)
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    
    		/*
    		Italian support
    
    		Like english, numbers till 20 are a single 'word', and other
     		compound, but with exceptions.
    		For example 21 is not twenty-one, but is a single word in it.
    		Idem for 28 (ie when a the 2nd part of a compund number
    		starts with a wovel)
    
    		There're exceptions also for hundred, thounsand 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.
    		
    		Same apply for thousand:
    		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);
                                    num = 0;
                            } else
                            if (num == 51) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 58) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 61) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 68) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 71) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 78) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 81) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 88) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 91) {
                                    snprintf(fn, sizeof(fn), "digits/%d", num);
                                    num = 0;
                            } else
                            if (num == 98) {
                                    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);
                            } else {
                                    if (num < 1000){
                                            if ((num / 100) > 1) {
                                                    snprintf(fn, sizeof(fn), "digits/%d", (num/100));
                                                    playh++;
                                            }
                                            else {
                                                    snprintf(fn, sizeof(fn), "digits/hundred");
                                            }
                                            num -= ((num / 100) * 100);
                                    } else {
                                            if (num < 1000000) { /* 1,000,000 */
                                                    if ((num/1000) > 1)
                                                            res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
                                                    if (res)
                                                            return res;
                                                    tempnum = num;
                                                    num = num % 1000;
                                                    if ((tempnum / 1000) < 2)
                                                            snprintf(fn, sizeof(fn), "digits/thousand");
                                                    else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
                                                            snprintf(fn, sizeof(fn), "digits/thousands");
                                            } else {
                                                    if (num < 1000000000) { /* 1,000,000,000 */
                                                            if ((num / 1000000) > 1)
                                                                    res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
                                                            if (res)
                                                                    return res;
                                                            tempnum = num;
                                                            num = num % 1000000;
                                                            if ((tempnum / 1000000) < 2)
                                                                    snprintf(fn, sizeof(fn), "digits/million");
                                                            else
                                                                    snprintf(fn, sizeof(fn), "digits/millions");
                                                    } 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_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)
    {
    	int res = 0;
    	int playa = 0;
    	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);
    
                            }
    			
    	}
    	return res;
    }
    
    /*--- ast_say_number_full_nl: dutch syntax */
    /* New files: ???
     */
    static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
    {
    	int res = 0;
    	int playh = 0;
    	int units = 0;
    	char fn[256] = "";
    	if (!num) 
    		return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
    	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 < 100) {
    		     units = num % 10;
    		     if (units > 0) {
    			res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
                            if (res)
                                return res;
                            num = num - units;
                            snprintf(fn, sizeof(fn), "digits/nl-en");
                         } else {
                            snprintf(fn, sizeof(fn), "digits/%d", num - units);
                            num = 0;
                         }
                     } 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);
    
                  }
    			
    	}
    	return res;
    }
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char fn[256];
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!res) {
    
    		snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_streamfile(chan, fn, lang);
    		if (!res)
    			res = ast_waitstream(chan, ints);
    	}
    	if (!res) {
    
    		snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_streamfile(chan, fn, lang);
    		if (!res)
    			res = ast_waitstream(chan, ints);
    	}
    	if (!res)
    
    		res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
    		/* Should portuguese add a gender here?  Defaults to masculin */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (!res)
    		res = ast_waitstream(chan, ints);
    	if (!res)
    
    		res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    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;
    }
    
    int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
    {
    	struct tm tm;
    	int res=0, offset, sndoffset;
    	char sndfile[256], nextmsg[256];
    
    	ast_localtime(&time,&tm,timezone);
    
    	for (offset=0 ; format[offset] != '\0' ; offset++) {
    		ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
    		switch (format[offset]) {
    			/* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
    			case '\'':