ivrdemo.c
/*
* Asterisk -- An open source telephony toolkit.*
* Copyright (C) 1999 - 2005,Digium,Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site,mailing lists and IRC
* channels for your use.
*
* This program is free software,distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief IVR Demo application
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
/*** MODULEINFO
<defaultenabled>no</defaultenabled>
***/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <mxml.h>
#define XML_FILE_PATH "./menu.xml"
//define for menu attr
#define TITLE "title"
#define FLAGS "flags"
//define for option attr
#define OPTION "option"
#define ACTION "action"
typedef enum {False = 0,True = 1} BOOL;
typedef enum {
AST_ACTION_UPONE,/*!< adata is unused */
AST_ACTION_EXIT,/*!< adata is the return value for ast_ivr_menu_run if channel was not hungup */
AST_ACTION_CALLBACK,/*!< adata is an ast_ivr_callback */
AST_ACTION_PLAYBACK,/*!< adata is file to play */
AST_ACTION_BACKGROUND,/*!< adata is file to play */
AST_ACTION_PLAYLIST,/*!< adata is list of files,separated by ; to play */
AST_ACTION_MENU,/*!< adata is a pointer to an ast_ivr_menu */
AST_ACTION_REPEAT,/*!< adata is max # of repeats,cast to a pointer */
AST_ACTION_RESTART,/*!< adata is like repeat,but resets repeats to 0 */
AST_ACTION_TRANSFER,/*!< adata is a string with exten\verbatim[@context]\endverbatim */
AST_ACTION_WAITOPTION,/*!< adata is a timeout,or 0 for defaults */
AST_ACTION_NOOP,/*!< adata is unused */
AST_ACTION_BACKLIST,/*!< adata is list of files separated by ; allows interruption */
} ast_ivr_action;
//define for the last option attr flag
typedef enum {
STR_ATTR,
SUBMENU_ATTR,
ULONG_ATTR,
FUN_ATTR
} option_last_attr_flag;
struct ast_ivr_option {
char *option;
ast_ivr_action action;
void *adata;
};
typedef struct ast_ivr_option ast_ivr_option_t ;
struct ast_ivr_menu {
char *title; /*!< Title of menu */
unsigned int flags; /*!< Flags */
struct ast_ivr_option *options; /*!< All options */
};
typedef struct ast_ivr_menu ast_ivr_menu_t ;
/*************************************************************************************************
* use to parse : <mainmenu falgs=0 options="mainmenu" title="IVR Demo Main Menu">
* <submenu1 falgs=0 options="submenu1" title="IVR Demo Sub Menu1">
*/
typedef struct ivr_menu_des_s {
const char *title_str ; // title str
const char *flags_str ; // flag str
//const char *options_str ; // options str unused
} ivr_menu_des_t ;
typedef struct _value_string {
int value;
const char *string;
} value_string;
typedef int (*ivr_demo_func)(const char *chan,void *data);
typedef struct _string_2_fun {
const char *string;
ivr_demo_func fun;
} string_2_fun;
typedef struct _string_2_menu {
const char *string;
ast_ivr_menu_t * menu_ptr;
} string_2_menu;
//static BOOL load_xml_file(const char *filename);
static inline int strlen_zero(const char *s);
static BOOL load_xml_file(const char *filename,const char *menuname,ast_ivr_menu_t *ivr_menu) ;
static BOOL parse_menu_attrs(mxml_node_t *node,ivr_menu_des_t *menu_des);
static BOOL parse_option_attrs(mxml_node_t *node,int num_attrs,ast_ivr_option_t *option);
static ast_ivr_action str_2_action(const char *str);
static int ivr_demo_function(const char *chan,void *data);
static ivr_demo_func string_2_fun_pointer( const char * str);
static ast_ivr_menu_t* string_2_menu_pointer(const char * str);
static BOOL parse_last_option_attr(mxml_node_t *node,ast_ivr_option_t *option);
static ast_ivr_option_t *init_ivr_option(int *argc);
static ast_ivr_option_t *add_ivr_option(ast_ivr_option_t *option_ptr,int *argc,ast_ivr_option_t option);
//this table use to change string to ast_ivr_action
static const value_string option_vals[] = {
{ AST_ACTION_UPONE,"AST_ACTION_UPONE" },
{ AST_ACTION_EXIT,"AST_ACTION_EXIT" },
{ AST_ACTION_CALLBACK,"AST_ACTION_CALLBACK" },
{ AST_ACTION_PLAYBACK,"AST_ACTION_PLAYBACK" },
{ AST_ACTION_BACKGROUND,"AST_ACTION_BACKGROUND" },
{ AST_ACTION_PLAYLIST,"AST_ACTION_PLAYLIST" },
{ AST_ACTION_MENU,"AST_ACTION_MENU" },
{ AST_ACTION_REPEAT,"AST_ACTION_REPEAT" },
{ AST_ACTION_RESTART,"AST_ACTION_RESTART" },
{ AST_ACTION_TRANSFER,"AST_ACTION_TRANSFER" },
{ AST_ACTION_WAITOPTION,"AST_ACTION_WAITOPTION" },
{ AST_ACTION_NOOP,"AST_ACTION_NOOP" },
{ AST_ACTION_BACKLIST,"AST_ACTION_BACKLIST" }
};
static value_string option_last_attr_vals[] = {
{ STR_ATTR, "str" },
{ SUBMENU_ATTR,"submenu" },
{ ULONG_ATTR, "ulong" },
{ FUN_ATTR, "fun" }
};
//the ivr_menu
static ast_ivr_menu_t ivr_menu ;
static ast_ivr_menu_t ivr_submenu ;
//this table use to change string to function pointer
static string_2_fun string_fun_vals[] = {
{ "ivr_demo_func", ivr_demo_function }
};
//this table use to change string to menu pointer
static string_2_menu string_menu_vals[] = {
{ "ivr_submenu", &ivr_submenu}
};
static int ivr_demo_function(const char *chan,void *data)
{
fprintf(stderr,"Open the file %s error!\n",(char *)data);
return 1;
}
/*! \brief returns non-zero if the string is not defined,or has zero length */
static inline int strlen_zero(const char *s)
{
return (!s || (*s == '\0'));
}
/***********************************************************************************************
filename : the xml file name (including path)
menuname : such as :
<mainmenu flags=0 title="IVR Demo Main Menu"> ---------> the menuname is mainmenu
.......
</mainmenu>
<submenu1 flags=0 title="IVR Demo Sub Menu1"> ---------> the menuname is submenu1
.......
</submenu1>
ivr_menu_ptr: the pointer of the ast_ivr_menu_t
*/
static BOOL load_xml_file(const char *filename,ast_ivr_menu_t *ivr_menu_ptr)
{
FILE *fp = NULL;
mxml_node_t *tree;
mxml_node_t *menu;
mxml_node_t *mainmenu;
mxml_node_t *option;
ivr_menu_des_t menu_des ;
int option_num ;
ast_ivr_option_t *ivr_option ;
fp = fopen(filename,"r");
if( fp == NULL )
{
fprintf(stderr,filename);
return False;
}
fprintf(stderr,"##########Here load_xml_file!############\n");
tree = mxmlLoadFile(NULL,fp,MXML_TEXT_CALLBACK);
if( tree != False )
{
if ((menu = mxmlFindElement(tree,tree,"menu",NULL,
MXML_DESCEND)) == NULL)
{
fprintf(stderr,"Unable to find first <menu> element in XML tree!\n");
return False;
}
if ((mainmenu = mxmlFindElement(menu,menu,menuname,"Unable to find first <%s> element in XML tree!\n",menuname);
return False;
}
if( !parse_menu_attrs(mainmenu,&menu_des) )
{
fprintf(stderr,"Parse menu attrs error !\n");
return False;
}
//fprintf(stderr,"The menu_des.title_str is %s \n",menu_des.title_str);
//fprintf(stderr,"The menu_des.flags_str is %s \n",menu_des.flags_str);
ivr_option = init_ivr_option(&option_num);
if( !ivr_option )
{
fprintf(stderr,"init_ivr_option error!\n");
return False;
}
for(option=mxmlFindElement(mainmenu,mainmenu,"option",MXML_DESCEND_FIRST);
option;
option=mxmlFindElement(option,MXML_NO_DESCEND))
{
ast_ivr_option_t ast_option ;
ast_option.option = NULL ; //init
ast_option.action = -1 ;
ast_option.adata = NULL ;
int num_attrs = option->value.element.num_attrs ;
//fprintf(stderr,"The num_attrs is %d\n",num_attrs);
if( !parse_option_attrs(option,num_attrs,&ast_option) )
{
fprintf(stderr,"parse_option_attrs error!\n");
return False ;
}
//option_num += 1 ;
ivr_option = add_ivr_option(ivr_option,&option_num,ast_option);
if( !ivr_option )
{
fprintf(stderr,"add_ivr_option error!\n");
return False;
}
}
ivr_menu_ptr->title = (char *)menu_des.title_str;
ivr_menu_ptr->flags = atoi( menu_des.flags_str );
ivr_menu_ptr->options = ivr_option ;
}
fclose(fp);
return True;
}
/**********************************************************************
such as :
<mainmenu flags=0 title="IVR Demo Main Menu">
.......
</mainmenu>
to parse the "flags=0 " and "title="IVR Demo Main Menu""
*/
static BOOL parse_menu_attrs(mxml_node_t *node,ivr_menu_des_t *menu_des)
{
assert( node != NULL );
const char *tmp = NULL ;
tmp = mxmlElementGetAttr(node,TITLE) ;
if( strlen_zero(tmp) )
{
return False;
}
menu_des->title_str = tmp ;
tmp = mxmlElementGetAttr(node,FLAGS) ;
if( strlen_zero(tmp) )
{
return False;
}
menu_des->flags_str = tmp ;
return True;
}
/**************************************************************************************
such as :
<option option="s" action=AST_ACTION_BACKGROUND str="demo-abouttotry"></option>
<option option="s" action=AST_ACTION_WAITOPTION></option>
to parse all the attrs
*/
static BOOL parse_option_attrs(mxml_node_t *node,ast_ivr_option_t *option)
{
//fprintf(stderr,"The num_attrs is %d \n",num_attrs);
const char *tmp = NULL;
ast_ivr_action flags ;
tmp = mxmlElementGetAttr(node,OPTION) ;
if( strlen_zero(tmp) )
{
return False;
}
option->option = (char *)tmp ;
tmp = mxmlElementGetAttr(node,ACTION) ;
if( strlen_zero(tmp) )
{
return False;
}
flags = str_2_action(tmp);
//fprintf(stderr,"The flags is %d\n",flags);
if( flags == -1 )
{
return False;
}
option->action = flags;
if( num_attrs == 3 ) //some options have 3 attrs
{
if( !parse_last_option_attr(node,option) )
{
fprintf(stderr,"parse_last_option_attr error!\n");
return False;
}
}
return True;
}
//parse the last option attr
static BOOL parse_last_option_attr(mxml_node_t *node,ast_ivr_option_t *option)
{
const char *tmp = NULL;
BOOL found = False ;
int i ;
int num = sizeof(option_last_attr_vals) / sizeof(option_last_attr_vals[0]);
//fprintf(stderr,"The num is %d\n",num);
for(i=0;i<num;i++)
{
tmp = mxmlElementGetAttr(node,option_last_attr_vals[i].string) ;
if( !strlen_zero(tmp) )
{
//fprintf(stderr,"The tmp is %s \n",tmp);
//fprintf(stderr,"The (option_last_attr_flag)i is %d \n",i);
switch((option_last_attr_flag)i)
{
case STR_ATTR:
option->adata = (void *)tmp ;
found = True;
break;
case SUBMENU_ATTR:
{
ast_ivr_menu_t* menu = string_2_menu_pointer(tmp);
if( !menu )
{
found = False ;
}
option->adata = (void *)menu;
found = True;
}
break;
case ULONG_ATTR:
option->adata = (void *)atoi(tmp);
found = True;
break;
case FUN_ATTR:
{
ivr_demo_func fun = string_2_fun_pointer(tmp);
if( !fun )
{
found = False ;
}
option->adata = (void *)fun;
found = True;
}
break;
default:
found = False ;
break;
}
if( found )
{
return True ;
}
}
}
return False;
}
// change string to function pointer
static ivr_demo_func string_2_fun_pointer( const char * str)
{
int i ;
int num = sizeof(string_fun_vals) / sizeof(string_fun_vals[0]);
//fprintf(stderr,num);
for(i=0;i<num;i++)
{
if( !strcmp(str,string_fun_vals[i].string) )
{
return string_fun_vals[i].fun ;
}
}
return (ivr_demo_func)NULL;
}
//change string to menu pointer
static ast_ivr_menu_t* string_2_menu_pointer(const char * str)
{
int i ;
int num = sizeof(string_menu_vals) / sizeof(string_menu_vals[0]);
//fprintf(stderr,num);
for(i=0;i<num;i++)
{
if( !strcmp(str,string_menu_vals[i].string) )
{
return string_menu_vals[i].menu_ptr ;
}
}
return (ast_ivr_menu_t*)NULL;
}
//change string to ast_ivr_action
static ast_ivr_action str_2_action(const char *str)
{
int i ;
int action_num = sizeof(option_vals) / sizeof(option_vals[0]);
//fprintf(stderr,"The action_num is %d\n",action_num);
for(i=0;i<action_num;i++)
{
//fprintf(stderr,"The str is %s\n",str);
//fprintf(stderr,"The option_vals[i].string is %s\n",option_vals[i].string);
if( !strcmp(str,option_vals[i].string) )
{
return (ast_ivr_action)i;
}
}
return -1;
}
static ast_ivr_option_t *init_ivr_option(int *argc)
{
ast_ivr_option_t *ivr_option = NULL ;
*argc = 0 ;
ivr_option = calloc(1,sizeof(ast_ivr_option_t));
if( !ivr_option )
{
fprintf(stderr,"No Merrory!\n");
return (ast_ivr_option_t *)NULL ;
}
return ivr_option ;
}
static ast_ivr_option_t *add_ivr_option(ast_ivr_option_t *option_ptr,ast_ivr_option_t option)
{
/*
Grow the array; "*argc" currently contains the number of string
pointers,*not* counting the NULL pointer at the end,so we have
to add 1 in order to get the new size of the array,including the
new pointer and the terminating NULL pointer.
*/
option_ptr = realloc( option_ptr,((*argc)+1) * sizeof(ast_ivr_option_t ) );
if( !option_ptr )
{
fprintf(stderr,"No Merrory!\n");
return (ast_ivr_option_t *)NULL ;
}
option_ptr[*argc] = option ;
(*argc)++ ; //increase the num
return option_ptr;
}
int main( void )
{
if( load_xml_file(XML_FILE_PATH,"mainmenu",&ivr_menu) )
{
int i ;
//ivr_menu_ptr->title = (char *)menu_des.title_str;
//ivr_menu_ptr->flags = atoi( menu_des.flags_str );
fprintf(stderr,"The ivr_menu.title is %s\n",ivr_menu.title);
fprintf(stderr,"The ivr_menu.flags is %d\n",ivr_menu.flags);
for(i=0;i<10;i++)
{
//fprintf(stderr,"The ivr_menu.options[%d] is %s\n",i,ivr_menu.options[i]);
fprintf(stderr,"The ivr_menu.options[%d] :\n",i);
fprintf(stderr,"\tThe option %s \n",(char *)ivr_menu.options[i].option);
fprintf(stderr,"\tThe action %d \n",(ast_ivr_action)ivr_menu.options[i].action);
//fprintf(stderr,"\tThe adata %s \n",(char *)ivr_menu.options[i].adata);
}
}
if( load_xml_file(XML_FILE_PATH,"submenu1",&ivr_submenu) )
{
int i ;
//ivr_menu_ptr->title = (char *)menu_des.title_str;
//ivr_menu_ptr->flags = atoi( menu_des.flags_str );
fprintf(stderr,ivr_submenu.title);
fprintf(stderr,ivr_submenu.flags);
for(i=0;i<8;i++)
{
//fprintf(stderr,(char *)ivr_submenu.options[i].option);
fprintf(stderr,(ast_ivr_action)ivr_submenu.options[i].action);
//fprintf(stderr,(char *)ivr_menu.options[i].adata);
}
}
return 0 ;
}
***********************************************************
Makefile
CC = gcc -Wall -O2
LIBS =-L/usr/local/lib -lmxml -lpthread
INCLUDE = -I /usr/local/include
TARGET = ivrdemo
all:$(TARGET)
ivrdemo: ivrdemo.o
$(CC) -o ivrdemo ivrdemo.o $(LIBS)
.c.o:
$(CC) -c $(INCLUDE) $< $(LIBS)
clean:
@rm *.o
@rm ivrdemo
***********************************************************************'
menu.xml
<?xml version="1.0"?> <menu> <mainmenu flags=0 title="IVR Demo Main Menu"> <option option="s" action=AST_ACTION_BACKGROUND str="demo-congrats"></option> <option option="g" action=AST_ACTION_BACKGROUND str="demo-instruct"></option> <option option="g" action=AST_ACTION_WAITOPTION></option> <option option="1" action=AST_ACTION_PLAYBACK str="digits/1"></option> <option option="1" action=AST_ACTION_RESTART></option> <option option="2" action=AST_ACTION_MENU submenu="ivr_submenu"></option> <option option="2" action=AST_ACTION_RESTART></option> <option option="i" action=AST_ACTION_PLAYBACK str="invalid"></option> <option option="i" action=AST_ACTION_REPEAT ulong=2></option> <option option="#" action=AST_ACTION_EXIT></option> </mainmenu> <submenu1 flags=0 title="IVR Demo Sub Menu1"> <option option="s" action=AST_ACTION_BACKGROUND str="demo-abouttotry"></option> <option option="s" action=AST_ACTION_WAITOPTION></option> <option option="1" action=AST_ACTION_PLAYBACK str="digits/1"></option> <option option="1" action=AST_ACTION_RESTART></option> <option option="2" action=AST_ACTION_PLAYLIST str="digits/2;digits/3"></option> <option option="3" action=AST_ACTION_CALLBACK fun="ivr_demo_func"></option> <option option="*" action=AST_ACTION_REPEAT></option> <option option="#" action=AST_ACTION_UPONE></option> </submenu1> </menu>