Thu Apr 28 2011 17:16:14

Asterisk developer's documentation


pbx_ael.c File Reference

Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2. More...

#include "asterisk.h"
#include <ctype.h>
#include <regex.h>
#include <sys/stat.h>
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/callerid.h"
#include "asterisk/hashtab.h"
#include "asterisk/ael_structs.h"
#include "asterisk/pval.h"
Include dependency graph for pbx_ael.c:

Go to the source code of this file.

Defines

#define DEBUG_CONTEXTS   (1 << 3)
#define DEBUG_MACROS   (1 << 2)
#define DEBUG_READ   (1 << 0)
#define DEBUG_TOKENS   (1 << 1)

Functions

static void __reg_module (void)
static void __unreg_module (void)
void add_extensions (struct ael_extension *exten)
int ast_compile_ael2 (struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root)
void ast_expr_clear_extra_error_info (void)
void ast_expr_register_extra_error_info (char *errmsg)
int check_app_args (pval *appcall, pval *arglist, struct argapp *app)
void check_pval (pval *item, struct argapp *apps, int in_globals)
void check_pval_item (pval *item, struct argapp *apps, int in_globals)
void check_switch_expr (pval *item, struct argapp *apps)
void destroy_extensions (struct ael_extension *exten)
void destroy_pval (pval *item)
void destroy_pval_item (pval *item)
struct pvalfind_context (char *name)
struct pvalfind_macro (char *name)
static char * handle_cli_ael_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_ael_set_debug (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
int is_empty (char *arg)
int is_float (char *arg)
int is_int (char *arg)
static int load_module (void)
struct ael_extensionnew_exten (void)
struct ael_prioritynew_prio (void)
static int pbx_load_module (void)
static int reload (void)
void set_priorities (struct ael_extension *exten)
static int unload_module (void)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Asterisk Extension Language Compiler" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static int aeldebug = 0
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_ael []
static char * config = "extensions.ael"
static char * registrar = "pbx_ael"

Detailed Description

Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.

Definition in file pbx_ael.c.


Define Documentation

#define DEBUG_CONTEXTS   (1 << 3)

Definition at line 63 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().

#define DEBUG_MACROS   (1 << 2)

Definition at line 62 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().

#define DEBUG_READ   (1 << 0)

Definition at line 60 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().

#define DEBUG_TOKENS   (1 << 1)

Definition at line 61 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 255 of file pbx_ael.c.

{
static void __unreg_module ( void  ) [static]

Definition at line 255 of file pbx_ael.c.

{
void add_extensions ( struct ael_extension exten)

Definition at line 4241 of file pval.c.

References AEL_APPCALL, AEL_CONTROL1, AEL_FOR_CONTROL, AEL_IF_CONTROL, AEL_IFTIME_CONTROL, AEL_LABEL, AEL_RAND_CONTROL, AEL_RETURN, ael_priority::app, ael_priority::appargs, ast_add_extension2(), ast_free_ptr, ast_log(), AST_MAX_EXTENSION, ael_extension::cidmatch, ael_extension::context, pval::else_statements, ael_priority::exten, ael_priority::goto_false, ael_priority::goto_true, ael_extension::hints, last, LOG_WARNING, ael_extension::name, ael_priority::next, ael_extension::next_exten, ael_priority::origin, pbx_substitute_variables_helper(), ael_extension::plist, PRIORITY_HINT, ael_priority::priority_num, PV_IFTIME, PV_SWITCH, pval::str, strdup, pval::type, ael_priority::type, pval::u1, and pval::u3.

{
   struct ael_priority *pr;
   char *label=0;
   char realext[AST_MAX_EXTENSION];
   if (!exten) {
      ast_log(LOG_WARNING, "This file is Empty!\n" );
      return;
   }
   do {
      struct ael_priority *last = 0;
      
      pbx_substitute_variables_helper(NULL, exten->name, realext, sizeof(realext) - 1);
      if (exten->hints) {
         if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, PRIORITY_HINT, NULL, exten->cidmatch, 
                          exten->hints, NULL, ast_free_ptr, registrar)) {
            ast_log(LOG_WARNING, "Unable to add step at priority 'hint' of extension '%s'\n",
                  exten->name);
         }
      }
      
      for (pr=exten->plist; pr; pr=pr->next) {
         char app[2000];
         char appargs[2000];

         /* before we can add the extension, we need to prep the app/appargs;
            the CONTROL types need to be done after the priority numbers are calculated.
         */
         if (pr->type == AEL_LABEL) /* don't try to put labels in the dialplan! */ {
            last = pr;
            continue;
         }
         
         if (pr->app)
            strcpy(app, pr->app);
         else
            app[0] = 0;
         if (pr->appargs )
            strcpy(appargs, pr->appargs);
         else
            appargs[0] = 0;
         switch( pr->type ) {
         case AEL_APPCALL:
            /* easy case. Everything is all set up */
            break;
            
         case AEL_CONTROL1: /* FOR loop, WHILE loop, BREAK, CONTINUE, IF, IFTIME */
            /* simple, unconditional goto. */
            strcpy(app,"Goto");
            if (pr->goto_true->origin && pr->goto_true->origin->type == PV_SWITCH ) {
               snprintf(appargs,sizeof(appargs),"%s,%d", pr->goto_true->exten->name, pr->goto_true->priority_num);
            } else if (pr->goto_true->origin && pr->goto_true->origin->type == PV_IFTIME && pr->goto_true->origin->u3.else_statements ) {
               snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num+1);
            } else
               snprintf(appargs,sizeof(appargs),"%d", pr->goto_true->priority_num);
            break;
            
         case AEL_FOR_CONTROL:  /* WHILE loop test, FOR loop test */
            strcpy(app,"GotoIf");
            snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
            break;
            
         case AEL_IF_CONTROL:
            strcpy(app,"GotoIf");
            if (pr->origin->u3.else_statements )
               snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num+1);
            else
               snprintf(appargs,sizeof(appargs),"%s?%d:%d", pr->appargs, pr->priority_num+1, pr->goto_false->priority_num);
            break;

         case AEL_RAND_CONTROL:
            strcpy(app,"Random");
            snprintf(appargs,sizeof(appargs),"%s:%d", pr->appargs, pr->goto_true->priority_num+1);
            break;

         case AEL_IFTIME_CONTROL:
            strcpy(app,"GotoIfTime");
            snprintf(appargs,sizeof(appargs),"%s?%d", pr->appargs, pr->priority_num+2);
            break;

         case AEL_RETURN:
            strcpy(app,"Return");
            appargs[0] = 0;
            break;
            
         default:
            break;
         }
         if (last && last->type == AEL_LABEL ) {
            label = last->origin->u1.str;
         }
         else
            label = 0;
         
         if (ast_add_extension2(exten->context, 0 /*no replace*/, realext, pr->priority_num, (label?label:NULL), exten->cidmatch, 
                          app, strdup(appargs), ast_free_ptr, registrar)) {
            ast_log(LOG_WARNING, "Unable to add step at priority '%d' of extension '%s'\n", pr->priority_num, 
                  exten->name);
         }
         last = pr;
      }
      exten = exten->next_exten;
   } while ( exten );
}
int ast_compile_ael2 ( struct ast_context **  local_contexts,
struct ast_hashtab local_table,
struct pval root 
)

Definition at line 4427 of file pval.c.

References add_extensions(), AEL_APPCALL, AEL_LABEL, ael_priority::app, ael_priority::appargs, pval::arglist, ast_compat_app_set, ast_context_add_ignorepat2(), ast_context_add_include2(), ast_context_add_switch2(), ast_context_find_or_create(), attach_exten(), buf, ael_extension::cidmatch, ael_extension::context, context, destroy_extensions(), exten, fix_gotos_in_extensions(), gen_prios(), ael_extension::hints, pval::hints, linkprio(), pval::list, pval::macro_statements, ael_extension::name, new_exten(), new_prio(), pval::next, ael_priority::origin, pbx_builtin_setvar(), ael_extension::plist_last, PV_CONTEXT, PV_ESWITCHES, PV_EXTENSION, PV_GLOBALS, PV_IGNOREPAT, PV_INCLUDES, PV_MACRO, PV_SWITCHES, pval::regexten, ael_extension::regexten, remove_spaces_before_equals(), ael_extension::return_needed, set_priorities(), pval::statements, pval::str, strdup, ael_priority::type, pval::type, pval::u1, pval::u2, pval::u3, pval::u4, and pval::val.

{
   pval *p,*p2;
   struct ast_context *context;
   char buf[2000];
   struct ael_extension *exten;
   struct ael_extension *exten_list = 0;

   for (p=root; p; p=p->next ) { /* do the globals first, so they'll be there
                            when we try to eval them */
      switch (p->type) {
      case PV_GLOBALS:
         /* just VARDEC elements */
         for (p2=p->u1.list; p2; p2=p2->next) {
            char buf2[2000];
            snprintf(buf2,sizeof(buf2),"%s=%s", p2->u1.str, p2->u2.val);
            pbx_builtin_setvar(NULL, buf2);
         }
         break;
      default:
         break;
      }
   }

   for (p=root; p; p=p->next ) {
      pval *lp;
      int argc;
      
      switch (p->type) {
      case PV_MACRO:
         
         context = ast_context_find_or_create(local_contexts, local_table, p->u1.str, registrar);
         
         exten = new_exten();
         exten->context = context;
         exten->name = strdup("~~s~~");
         argc = 1;
         for (lp=p->u2.arglist; lp; lp=lp->next) {
            /* for each arg, set up a "Set" command */
            struct ael_priority *np2 = new_prio();
            np2->type = AEL_APPCALL;
            if (!ast_compat_app_set) {
               np2->app = strdup("MSet");
            } else {
               np2->app = strdup("Set");
            }
            snprintf(buf,sizeof(buf),"LOCAL(%s)=${ARG%d}", lp->u1.str, argc++);
            remove_spaces_before_equals(buf);
            np2->appargs = strdup(buf);
            linkprio(exten, np2, NULL);
         }
         
         /* CONTAINS APPCALLS, CATCH, just like extensions... */
         if (gen_prios(exten, p->u1.str, p->u3.macro_statements, 0, context)) {
            return -1;
         }
         if (exten->return_needed) {  /* most likely, this will go away */
            struct ael_priority *np2 = new_prio();
            np2->type = AEL_APPCALL;
            np2->app = strdup("NoOp");
            snprintf(buf,sizeof(buf),"End of Macro %s-%s",p->u1.str, exten->name);
            np2->appargs = strdup(buf);
            linkprio(exten, np2, NULL);
            exten-> return_target = np2;
         }
         
         set_priorities(exten);
         attach_exten(&exten_list, exten);
         break;
         
      case PV_GLOBALS:
         /* already done */
         break;
         
      case PV_CONTEXT:
         context = ast_context_find_or_create(local_contexts, local_table, p->u1.str, registrar);
         
         /* contexts contain: ignorepat, includes, switches, eswitches, extensions,  */
         for (p2=p->u2.statements; p2; p2=p2->next) {
            pval *p3;
            char *s3;
            
            switch (p2->type) {
            case PV_EXTENSION:
               exten = new_exten();
               exten->name = strdup(p2->u1.str);
               exten->context = context;
               
               if( (s3=strchr(exten->name, '/') ) != 0 )
               {
                  *s3 = 0;
                  exten->cidmatch = s3+1;
               }
               
               if ( p2->u3.hints )
                  exten->hints = strdup(p2->u3.hints);
               exten->regexten = p2->u4.regexten;
               if (gen_prios(exten, p->u1.str, p2->u2.statements, 0, context)) {
                  return -1;
               }
               if (exten->return_needed) { /* returns don't generate a goto eoe (end of extension) any more, just a Return() app call) */
                  struct ael_priority *np2 = new_prio();
                  np2->type = AEL_APPCALL;
                  np2->app = strdup("NoOp");
                  snprintf(buf,sizeof(buf),"End of Extension %s", exten->name);
                  np2->appargs = strdup(buf);
                  linkprio(exten, np2, NULL);
                  exten-> return_target = np2;
               }
               /* is the last priority in the extension a label? Then add a trailing no-op */
               if ( exten->plist_last && exten->plist_last->type == AEL_LABEL ) {
                  struct ael_priority *np2 = new_prio();
                  np2->type = AEL_APPCALL;
                  np2->app = strdup("NoOp");
                  snprintf(buf,sizeof(buf),"A NoOp to follow a trailing label %s", exten->plist_last->origin->u1.str);
                  np2->appargs = strdup(buf);
                  linkprio(exten, np2, NULL);
               }

               set_priorities(exten);
               attach_exten(&exten_list, exten);
               break;
               
            case PV_IGNOREPAT:
               ast_context_add_ignorepat2(context, p2->u1.str, registrar);
               break;
               
            case PV_INCLUDES:
               for (p3 = p2->u1.list; p3 ;p3=p3->next) {
                  if ( p3->u2.arglist ) {
                     snprintf(buf,sizeof(buf), "%s,%s,%s,%s,%s", 
                            p3->u1.str,
                            p3->u2.arglist->u1.str,
                            p3->u2.arglist->next->u1.str,
                            p3->u2.arglist->next->next->u1.str,
                            p3->u2.arglist->next->next->next->u1.str);
                     ast_context_add_include2(context, buf, registrar);
                  } else
                     ast_context_add_include2(context, p3->u1.str, registrar);
               }
               break;
               
            case PV_SWITCHES:
               for (p3 = p2->u1.list; p3 ;p3=p3->next) {
                  char *c = strchr(p3->u1.str, '/');
                  if (c) {
                     *c = '\0';
                     c++;
                  } else
                     c = "";

                  ast_context_add_switch2(context, p3->u1.str, c, 0, registrar);
               }
               break;

            case PV_ESWITCHES:
               for (p3 = p2->u1.list; p3 ;p3=p3->next) {
                  char *c = strchr(p3->u1.str, '/');
                  if (c) {
                     *c = '\0';
                     c++;
                  } else
                     c = "";

                  ast_context_add_switch2(context, p3->u1.str, c, 1, registrar);
               }
               break;
            default:
               break;
            }
         }
         
         break;
         
      default:
         /* huh? what? */
         break;
         
      }
   }
   /* moved these from being done after a macro or extension were processed,
      to after all processing is done, for the sake of fixing gotos to labels inside cases... */
   /* I guess this would be considered 2nd pass of compiler now... */
   fix_gotos_in_extensions(exten_list); /* find and fix extension ref in gotos to labels that are in case statements */
   add_extensions(exten_list);   /* actually makes calls to create priorities in ast_contexts -- feeds dialplan to asterisk */
   destroy_extensions(exten_list);  /* all that remains is an empty husk, discard of it as is proper */
   
   return 0;
}
void ast_expr_clear_extra_error_info ( void  )

Definition at line 2456 of file ast_expr2f.c.

void ast_expr_register_extra_error_info ( char *  errmsg)

Definition at line 2450 of file ast_expr2f.c.

int check_app_args ( pval appcall,
pval arglist,
struct argapp app 
)

Definition at line 2127 of file pval.c.

References ast_log(), pval::endline, pval::filename, LOG_WARNING, pval::next, pval::startline, pval::str, and pval::u1.

{
#ifdef AAL_ARGCHECK
   struct argdesc *ad = app->args;
   pval *pa;
   int z;
   
   for (pa = arglist; pa; pa=pa->next) {
      if (!ad) {
         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Extra argument %s not in application call to %s !\n",
               arglist->filename, arglist->startline, arglist->endline, pa->u1.str, app->name);
         warns++;
         return 1;
      } else {
         /* find the first entry in the ad list that will match */
         do {
            if ( ad->dtype == ARGD_VARARG ) /* once we hit the VARARG, all bets are off. Discontinue the comparisons */
               break;
            
            z= option_matches( ad, pa, app);
            if (!z) {
               if ( !arglist )
                  arglist=appcall;
               
               if (ad->type == ARGD_REQUIRED) {
                  ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
                        arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
                  warns++;
                  return 1;
               }
            } else if (z && ad->dtype == ARGD_OPTIONSET) {
               option_matches_j( ad, pa, app);
            }
            ad = ad->next;
         } while (ad && !z);
      }
   }
   /* any app nodes left, that are not optional? */
   for ( ; ad; ad=ad->next) {
      if (ad->type == ARGD_REQUIRED && ad->dtype != ARGD_VARARG) {
         if ( !arglist ) 
            arglist=appcall;
         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: Required argument %s not in application call to %s !\n",
               arglist->filename, arglist->startline, arglist->endline, ad->dtype==ARGD_OPTIONSET?"options":ad->name, app->name);
         warns++;
         return 1;
      }
   }
   return 0;
#else
   return 0;
#endif
}
void check_pval ( pval item,
struct argapp apps,
int  in_globals 
)

Definition at line 2862 of file pval.c.

References check_pval_item(), and pval::next.

{
   pval *i;

   /* checks to do:
      1. Do goto's point to actual labels? 
      2. Do macro calls reference a macro?
      3. Does the number of macro args match the definition?
      4. Is a macro call missing its & at the front?
      5. Application calls-- we could check syntax for existing applications,
         but I need some some sort of universal description bnf for a general
        sort of method for checking arguments, in number, maybe even type, at least. 
        Don't want to hand code checks for hundreds of applications.
   */
   
   for (i=item; i; i=i->next) {
      check_pval_item(i,apps,in_globals);
   }
}
void check_pval_item ( pval item,
struct argapp apps,
int  in_globals 
)

Definition at line 2354 of file pval.c.

References pval::abstract, app, pval::arglist, ast_expr(), ast_expr_clear_extra_error_info(), ast_expr_register_extra_error_info(), ast_log(), check_abstract_reference(), check_app_args(), check_break(), check_continue(), check_day(), check_dow(), check_expr2_input(), check_goto(), check_includes(), check_label(), check_macro_returns(), check_month(), check_pval(), check_switch_expr(), check_timerange(), E_MATCH, pval::else_statements, pval::endcol, pval::endline, pval::filename, find_context(), find_macro(), find_pval_gotos(), pval::for_inc, pval::for_init, pval::for_statements, pval::for_test, free, pval::list, localized_pbx_load_module(), LOG_ERROR, LOG_WARNING, pval::macro_statements, argapp::next, pval::next, pbx_find_extension(), PV_APPLICATION_CALL, PV_BREAK, PV_CASE, PV_CATCH, PV_CONTEXT, PV_CONTINUE, PV_DEFAULT, PV_ESWITCHES, PV_EXTENSION, PV_FOR, PV_GLOBALS, PV_GOTO, PV_IF, PV_IFTIME, PV_IGNOREPAT, PV_INCLUDES, PV_LABEL, PV_LOCALVARDEC, PV_MACRO, PV_MACRO_CALL, PV_PATTERN, PV_RANDOM, PV_RETURN, PV_STATEMENTBLOCK, PV_SWITCH, PV_SWITCHES, PV_VARDEC, PV_WHILE, PV_WORD, pbx_find_info::stacklen, pval::startcol, pval::startline, pval::statements, pbx_find_info::status, STATUS_SUCCESS, pval::str, pval::type, pval::u1, pval::u2, pval::u3, pval::u4, and pval::val.

{
   pval *lp;
#ifdef AAL_ARGCHECK
   struct argapp *app, *found;
#endif
   struct pval *macro_def;
   struct pval *app_def;

   char errmsg[4096];
   char *strp;
   
   switch (item->type) {
   case PV_WORD:
      /* fields: item->u1.str == string associated with this (word).
                 item->u2.arglist  == pval list of 4 PV_WORD elements for time values (only in PV_INCLUDES) */
      break;
      
   case PV_MACRO:
      /* fields: item->u1.str     == name of macro
                 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
               item->u2.arglist->u1.str  == argument
               item->u2.arglist->next   == next arg

               item->u3.macro_statements == pval list of statements in macro body.
      */
      in_abstract_context = 0;
      current_context = item;
      current_extension = 0;

      check_macro_returns(item);
      
      for (lp=item->u2.arglist; lp; lp=lp->next) {
      
      }
      check_pval(item->u3.macro_statements, apps,in_globals);
      break;
         
   case PV_CONTEXT:
      /* fields: item->u1.str     == name of context
                 item->u2.statements == pval list of statements in context body
               item->u3.abstract == int 1 if an abstract keyword were present
      */
      current_context = item;
      current_extension = 0;
      if ( item->u3.abstract ) {
         in_abstract_context = 1;
         check_abstract_reference(item);
      } else
         in_abstract_context = 0;
      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_MACRO_CALL:
      /* fields: item->u1.str     == name of macro to call
                 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
               item->u2.arglist->u1.str  == argument
               item->u2.arglist->next   == next arg
      */
#ifdef STANDALONE
      /* if this is a standalone, we will need to make sure the 
         localized load of extensions.conf is done */
      if (!extensions_dot_conf_loaded) {
         localized_pbx_load_module();
         extensions_dot_conf_loaded++;
      }
#endif
      macro_def = find_macro(item->u1.str);
      if (!macro_def) {
#ifdef STANDALONE
         struct pbx_find_info pfiq = {.stacklen = 0 };
         struct pbx_find_info pfiq2 = {.stacklen = 0 };

         /* look for the macro in the extensions.conf world */
         pbx_find_extension(NULL, NULL, &pfiq, item->u1.str, "s", 1, NULL, NULL, E_MATCH);
         
         if (pfiq.status != STATUS_SUCCESS) {
            char namebuf2[256];
            snprintf(namebuf2, 256, "macro-%s", item->u1.str);
            
            /* look for the macro in the extensions.conf world */
            pbx_find_extension(NULL, NULL, &pfiq2, namebuf2, "s", 1, NULL, NULL, E_MATCH);
            
            if (pfiq2.status == STATUS_SUCCESS) {
               ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (macro-%s was found in the extensions.conf stuff, but we are using gosubs!)\n",
                     item->filename, item->startline, item->endline, item->u1.str, item->u1.str);
               warns++;
            } else {
               ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to non-existent %s! (Not even in the extensions.conf stuff!)\n",
                     item->filename, item->startline, item->endline, item->u1.str);
               warns++;
            }
         }
#else
         ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s cannot be found in the AEL code!\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
         
#endif
#ifdef THIS_IS_1DOT4
         char namebuf2[256];
         snprintf(namebuf2, 256, "macro-%s", item->u1.str);

         /* look for the macro in the extensions.conf world */
         pbx_find_extension(NULL, NULL, &pfiq, namebuf2, "s", 1, NULL, NULL, E_MATCH);
         
         if (pfiq.status != STATUS_SUCCESS) {
            ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: macro call to %s was not found in the AEL, nor the extensions.conf !\n",
                  item->filename, item->startline, item->endline, item->u1.str);
            warns++;
         }
         
#endif

      } else if (macro_def->type != PV_MACRO) {
         ast_log(LOG_ERROR,"Error: file %s, line %d-%d: macro call to %s references a context, not a macro!\n",
               item->filename, item->startline, item->endline, item->u1.str);
         errs++;
      } else {
         /* macro_def is a MACRO, so do the args match in number? */
         int hereargs = 0;
         int thereargs = 0;
         
         for (lp=item->u2.arglist; lp; lp=lp->next) {
            hereargs++;
         }
         for (lp=macro_def->u2.arglist; lp; lp=lp->next) {
            thereargs++;
         }
         if (hereargs != thereargs ) {
            ast_log(LOG_ERROR, "Error: file %s, line %d-%d: The macro call to %s has %d arguments, but the macro definition has %d arguments\n",
                  item->filename, item->startline, item->endline, item->u1.str, hereargs, thereargs);
            errs++;
         }
      }
      break;
         
   case PV_APPLICATION_CALL:
      /* fields: item->u1.str     == name of application to call
                 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
               item->u2.arglist->u1.str  == argument
               item->u2.arglist->next   == next arg
      */
      /* Need to check to see if the application is available! */
      app_def = find_context(item->u1.str);
      if (app_def && app_def->type == PV_MACRO) {
         ast_log(LOG_ERROR,"Error: file %s, line %d-%d: application call to %s references an existing macro, but had no & preceding it!\n",
               item->filename, item->startline, item->endline, item->u1.str);
         errs++;
      }
      if (strcasecmp(item->u1.str,"GotoIf") == 0
         || strcasecmp(item->u1.str,"GotoIfTime") == 0
         || strcasecmp(item->u1.str,"while") == 0
         || strcasecmp(item->u1.str,"endwhile") == 0
         || strcasecmp(item->u1.str,"random") == 0
         || strcasecmp(item->u1.str,"gosub") == 0
         || strcasecmp(item->u1.str,"gosubif") == 0
         || strcasecmp(item->u1.str,"continuewhile") == 0
         || strcasecmp(item->u1.str,"endwhile") == 0
         || strcasecmp(item->u1.str,"execif") == 0
         || strcasecmp(item->u1.str,"execiftime") == 0
         || strcasecmp(item->u1.str,"exitwhile") == 0
         || strcasecmp(item->u1.str,"goto") == 0
         || strcasecmp(item->u1.str,"macro") == 0
         || strcasecmp(item->u1.str,"macroexclusive") == 0
         || strcasecmp(item->u1.str,"macroif") == 0
         || strcasecmp(item->u1.str,"stackpop") == 0
         || strcasecmp(item->u1.str,"execIf") == 0 ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s affects flow of control, and needs to be re-written using AEL if, while, goto, etc. keywords instead!\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
      }
      if (strcasecmp(item->u1.str,"macroexit") == 0) {
            ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: I am converting the MacroExit call here to a return statement.\n",
                  item->filename, item->startline, item->endline);
            item->type = PV_RETURN;
            free(item->u1.str);
            item->u1.str = 0;
      }
      
#ifdef AAL_ARGCHECK
      found = 0;
      for (app=apps; app; app=app->next) {
         if (strcasecmp(app->name, item->u1.str) == 0) {
            found =app;
            break;
         }
      }
      if (!found) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: application call to %s not listed in applist database!\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
      } else
         check_app_args(item, item->u2.arglist, app);
#endif
      break;
      
   case PV_CASE:
      /* fields: item->u1.str     == value of case
                 item->u2.statements == pval list of statements under the case
      */
      /* Make sure sequence of statements under case is terminated with  goto, return, or break */
      /* find the last statement */
      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_PATTERN:
      /* fields: item->u1.str     == value of case
                 item->u2.statements == pval list of statements under the case
      */
      /* Make sure sequence of statements under case is terminated with  goto, return, or break */
      /* find the last statement */
      
      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_DEFAULT:
      /* fields: 
                 item->u2.statements == pval list of statements under the case
      */

      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_CATCH:
      /* fields: item->u1.str     == name of extension to catch
                 item->u2.statements == pval list of statements in context body
      */
      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_SWITCHES:
      /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
      */
      check_pval(item->u1.list, apps,in_globals);
      break;
         
   case PV_ESWITCHES:
      /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
      */
      check_pval(item->u1.list, apps,in_globals);
      break;
         
   case PV_INCLUDES:
      /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
      */
      check_pval(item->u1.list, apps,in_globals);
      check_includes(item);
      for (lp=item->u1.list; lp; lp=lp->next){
         char *incl_context = lp->u1.str;
         struct pval *that_context = find_context(incl_context);

         if ( lp->u2.arglist ) {
            check_timerange(lp->u2.arglist);
            check_dow(lp->u2.arglist->next);
            check_day(lp->u2.arglist->next->next);
            check_month(lp->u2.arglist->next->next->next);
         }
         
         if (that_context) {
            find_pval_gotos(that_context->u2.statements,0);
            
         }
      }
      break;
         
   case PV_STATEMENTBLOCK:
      /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
      */
      check_pval(item->u1.list, apps,in_globals);
      break;
         
   case PV_VARDEC:
      /* fields: item->u1.str     == variable name
                 item->u2.val     == variable value to assign
      */
      /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
      if( !in_globals ) { /* don't check stuff inside the globals context; no wrapping in $[ ] there... */
         snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
         ast_expr_register_extra_error_info(errmsg);
         ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
         ast_expr_clear_extra_error_info();
         if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
            ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
                  item->filename, item->startline, item->endline, item->u2.val);
            warns++;
         }
         check_expr2_input(item,item->u2.val);
      }
      break;
         
   case PV_LOCALVARDEC:
      /* fields: item->u1.str     == variable name
                 item->u2.val     == variable value to assign
      */
      /* the RHS of a vardec is encapsulated in a $[] expr. Is it legal? */
      snprintf(errmsg,sizeof(errmsg), "file %s, line %d, columns %d-%d, variable declaration expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.val);
      ast_expr_register_extra_error_info(errmsg);
      ast_expr(item->u2.val, expr_output, sizeof(expr_output),NULL);
      ast_expr_clear_extra_error_info();
      if ( strpbrk(item->u2.val,"~!-+<>=*/&^") && !strstr(item->u2.val,"${") ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
               item->filename, item->startline, item->endline, item->u2.val);
         warns++;
      }
      check_expr2_input(item,item->u2.val);
      break;
         
   case PV_GOTO:
      /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
                 item->u1.list->u1.str  == where the data on a PV_WORD will always be.
      */
      /* don't check goto's in abstract contexts */
      if ( in_abstract_context )
         break;
      
      check_goto(item);
      break;
         
   case PV_LABEL:
      /* fields: item->u1.str     == label name
      */
      if ( strspn(item->u1.str, "0123456789") == strlen(item->u1.str) ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: label '%s' is numeric, this is bad practice!\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
      }

      check_label(item);
      break;
         
   case PV_FOR:
      /* fields: item->u1.for_init     == a string containing the initalizer
                 item->u2.for_test     == a string containing the loop test
                 item->u3.for_inc      == a string containing the loop increment

               item->u4.for_statements == a pval list of statements in the for ()
      */
      snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, for test expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u2.for_test);
      ast_expr_register_extra_error_info(errmsg);

      strp = strchr(item->u1.for_init, '=');
      if (strp) {
         ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
      }
      ast_expr(item->u2.for_test, expr_output, sizeof(expr_output),NULL);
      strp = strchr(item->u3.for_inc, '=');
      if (strp) {
         ast_expr(strp+1, expr_output, sizeof(expr_output),NULL);
      }
      if ( strpbrk(item->u2.for_test,"~!-+<>=*/&^") && !strstr(item->u2.for_test,"${") ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
               item->filename, item->startline, item->endline, item->u2.for_test);
         warns++;
      }
      if ( strpbrk(item->u3.for_inc,"~!-+<>=*/&^") && !strstr(item->u3.for_inc,"${") ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
               item->filename, item->startline, item->endline, item->u3.for_inc);
         warns++;
      }
      check_expr2_input(item,item->u2.for_test);
      check_expr2_input(item,item->u3.for_inc);
      
      ast_expr_clear_extra_error_info();
      check_pval(item->u4.for_statements, apps,in_globals);
      break;
         
   case PV_WHILE:
      /* fields: item->u1.str        == the while conditional, as supplied by user

               item->u2.statements == a pval list of statements in the while ()
      */
      snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, while expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
      ast_expr_register_extra_error_info(errmsg);
      ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
      ast_expr_clear_extra_error_info();
      if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression %s has operators, but no variables. Interesting...\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
      }
      check_expr2_input(item,item->u1.str);
      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_BREAK:
      /* fields: none
      */
      check_break(item);
      break;
         
   case PV_RETURN:
      /* fields: none
      */
      break;
         
   case PV_CONTINUE:
      /* fields: none
      */
      check_continue(item);
      break;
         
   case PV_RANDOM:
      /* fields: item->u1.str        == the random number expression, as supplied by user

               item->u2.statements == a pval list of statements in the if ()
               item->u3.else_statements == a pval list of statements in the else
                                    (could be zero)
      */
      snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, random expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
      ast_expr_register_extra_error_info(errmsg);
      ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
      ast_expr_clear_extra_error_info();
      if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: random expression '%s' has operators, but no variables. Interesting...\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
      }
      check_expr2_input(item,item->u1.str);
      check_pval(item->u2.statements, apps,in_globals);
      if (item->u3.else_statements) {
         check_pval(item->u3.else_statements, apps,in_globals);
      }
      break;

   case PV_IFTIME:
      /* fields: item->u1.list        == the if time values, 4 of them, each in PV_WORD, linked list 

               item->u2.statements == a pval list of statements in the if ()
               item->u3.else_statements == a pval list of statements in the else
                                    (could be zero)
      */
      if ( item->u2.arglist ) {
         check_timerange(item->u1.list);
         check_dow(item->u1.list->next);
         check_day(item->u1.list->next->next);
         check_month(item->u1.list->next->next->next);
      }

      check_pval(item->u2.statements, apps,in_globals);
      if (item->u3.else_statements) {
         check_pval(item->u3.else_statements, apps,in_globals);
      }
      break;
         
   case PV_IF:
      /* fields: item->u1.str        == the if conditional, as supplied by user

               item->u2.statements == a pval list of statements in the if ()
               item->u3.else_statements == a pval list of statements in the else
                                    (could be zero)
      */
      snprintf(errmsg,sizeof(errmsg),"file %s, line %d, columns %d-%d, if expr '%s':", item->filename, item->startline, item->startcol, item->endcol, item->u1.str);
      ast_expr_register_extra_error_info(errmsg);
      ast_expr(item->u1.str, expr_output, sizeof(expr_output),NULL);
      ast_expr_clear_extra_error_info();
      if ( strpbrk(item->u1.str,"~!-+<>=*/&^") && !strstr(item->u1.str,"${") ) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: expression '%s' has operators, but no variables. Interesting...\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
      }
      check_expr2_input(item,item->u1.str);
      check_pval(item->u2.statements, apps,in_globals);
      if (item->u3.else_statements) {
         check_pval(item->u3.else_statements, apps,in_globals);
      }
      break;
         
   case PV_SWITCH:
      /* fields: item->u1.str        == the switch expression

               item->u2.statements == a pval list of statements in the switch, 
                                    (will be case statements, most likely!)
      */
      /* we can check the switch expression, see if it matches any of the app variables...
           if it does, then, are all the possible cases accounted for? */
      check_switch_expr(item, apps);
      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_EXTENSION:
      /* fields: item->u1.str        == the extension name, label, whatever it's called

               item->u2.statements == a pval list of statements in the extension
               item->u3.hints      == a char * hint argument
               item->u4.regexten   == an int boolean. non-zero says that regexten was specified
      */
      current_extension = item ;
      
      check_pval(item->u2.statements, apps,in_globals);
      break;
         
   case PV_IGNOREPAT:
      /* fields: item->u1.str        == the ignorepat data
      */
      break;
         
   case PV_GLOBALS:
      /* fields: item->u1.statements     == pval list of statements, usually vardecs
      */
      in_abstract_context = 0;
      check_pval(item->u1.statements, apps, 1);
      break;
   default:
      break;
   }
}
void check_switch_expr ( pval item,
struct argapp apps 
)

Definition at line 2181 of file pval.c.

References ast_log(), ast_strdupa, calloc, pval::endcol, pval::endline, pval::filename, LOG_WARNING, pval::next, argapp::next, PV_APPLICATION_CALL, PV_CASE, PV_DEFAULT, PV_PATTERN, PV_STATEMENTBLOCK, pval::startcol, pval::startline, pval::statements, pval::str, strdup, pval::type, pval::u1, and pval::u2.

{
#ifdef AAL_ARGCHECK
   /* get and clean the variable name */
   char *buff1, *p;
   struct argapp *a,*a2;
   struct appsetvar *v,*v2;
   struct argchoice *c;
   pval *t;
   
   p = item->u1.str;
   while (p && *p && (*p == ' ' || *p == '\t' || *p == '$' || *p == '{' ) )
      p++;
   
   buff1 = ast_strdupa(p);

   while (strlen(buff1) > 0 && ( buff1[strlen(buff1)-1] == '}' || buff1[strlen(buff1)-1] == ' ' || buff1[strlen(buff1)-1] == '\t'))
      buff1[strlen(buff1)-1] = 0;
   /* buff1 now contains the variable name */
   v = 0;
   for (a=apps; a; a=a->next) {
      for (v=a->setvars;v;v=v->next) {
         if (strcmp(v->name,buff1) == 0) {
            break;
         }
      }
      if ( v )
         break;
   }
   if (v && v->vals) {
      /* we have a match, to a variable that has a set of determined values */
      int def= 0;
      int pat = 0;
      int f1 = 0;
      
      /* first of all, does this switch have a default case ? */
      for (t=item->u2.statements; t; t=t->next) {
         if (t->type == PV_DEFAULT) {
            def =1;
            break;
         }
         if (t->type == PV_PATTERN) {
            pat++;
         }
      }
      if (def || pat) /* nothing to check. All cases accounted for! */
         return;
      for (c=v->vals; c; c=c->next) {
         f1 = 0;
         for (t=item->u2.statements; t; t=t->next) {
            if (t->type == PV_CASE || t->type == PV_PATTERN) {
               if (!strcmp(t->u1.str,c->name)) {
                  f1 = 1;
                  break;
               }
            }
         }
         if (!f1) {
            ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: switch with expression(%s) does not handle the case of %s !\n",
                  item->filename, item->startline, item->endline, item->u1.str, c->name);
            warns++;
         }
      }
      /* next, is there an app call in the current exten, that would set this var? */
      f1 = 0;
      t = current_extension->u2.statements;
      if ( t && t->type == PV_STATEMENTBLOCK )
         t = t->u1.statements;
      for (; t && t != item; t=t->next) {
         if (t->type == PV_APPLICATION_CALL) {
            /* find the application that matches the u1.str */
            for (a2=apps; a2; a2=a2->next) {
               if (strcasecmp(a2->name, t->u1.str)==0) {
                  for (v2=a2->setvars; v2; v2=v2->next) {
                     if (strcmp(v2->name, buff1) == 0) {
                        /* found an app that sets the var */
                        f1 = 1;
                        break;
                     }
                  }
               }
               if (f1)
                  break;
            }
         }
         if (f1)
            break;
      }
            
      /* see if it sets the var */
      if (!f1) {
         ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: Couldn't find an application call in this extension that sets the  expression (%s) value!\n",
               item->filename, item->startline, item->endline, item->u1.str);
         warns++;
      }
   }
#else
   pval *t,*tl=0,*p2;
   int def= 0;
   
   /* first of all, does this switch have a default case ? */
   for (t=item->u2.statements; t; t=t->next) {
      if (t->type == PV_DEFAULT) {
         def =1;
         break;
      }
      tl = t;
   }
   if (def) /* nothing to check. All cases accounted for! */
      return;
   /* if no default, warn and insert a default case at the end */
   p2 = tl->next = calloc(1, sizeof(struct pval));
   
   p2->type = PV_DEFAULT;
   p2->startline = tl->startline;
   p2->endline = tl->endline;
   p2->startcol = tl->startcol;
   p2->endcol = tl->endcol;
   p2->filename = strdup(tl->filename);
   ast_log(LOG_WARNING,"Warning: file %s, line %d-%d: A default case was automatically added to the switch.\n",
         p2->filename, p2->startline, p2->endline);
   warns++;
   
#endif
}
void destroy_extensions ( struct ael_extension exten)

Definition at line 2975 of file pval.c.

References ael_priority::app, ael_priority::appargs, free, ael_priority::goto_false, ael_priority::goto_true, ael_extension::hints, ael_extension::loop_break, ael_extension::loop_continue, ael_extension::name, ael_priority::next, ael_extension::next_exten, ael_priority::origin, ael_extension::plist, and ael_extension::plist_last.

{
   struct ael_extension *ne, *nen;
   for (ne=exten; ne; ne=nen) {
      struct ael_priority *pe, *pen;
      
      if (ne->name)
         free(ne->name);
      
      /* cidmatch fields are allocated with name, and freed when
         the name field is freed. Don't do a free for this field,
         unless you LIKE to see a crash! */

      if (ne->hints)
         free(ne->hints);
      
      for (pe=ne->plist; pe; pe=pen) {
         pen = pe->next;
         if (pe->app)
            free(pe->app);
         pe->app = 0;
         if (pe->appargs)
            free(pe->appargs);
         pe->appargs = 0;
         pe->origin = 0;
         pe->goto_true = 0;
         pe->goto_false = 0;
         free(pe);
      }
      nen = ne->next_exten;
      ne->next_exten = 0;
      ne->plist =0;
      ne->plist_last = 0;
      ne->next_exten = 0;
      ne->loop_break = 0;
      ne->loop_continue = 0;
      free(ne);
   }
}
void destroy_pval ( pval item)

Definition at line 4890 of file pval.c.

References destroy_pval_item(), and pval::next.

{
   pval *i,*nxt;
   
   for (i=item; i; i=nxt) {
      nxt = i->next;
      
      destroy_pval_item(i);
   }
}
void destroy_pval_item ( pval item)

Definition at line 4622 of file pval.c.

References pval::arglist, ast_log(), destroy_pval(), pval::else_statements, pval::filename, pval::for_inc, pval::for_init, pval::for_statements, pval::for_test, free, pval::hints, pval::list, LOG_WARNING, pval::macro_statements, PV_APPLICATION_CALL, PV_BREAK, PV_CASE, PV_CATCH, PV_CONTEXT, PV_CONTINUE, PV_DEFAULT, PV_ESWITCHES, PV_EXTENSION, PV_FOR, PV_GLOBALS, PV_GOTO, PV_IF, PV_IFTIME, PV_IGNOREPAT, PV_INCLUDES, PV_LABEL, PV_LOCALVARDEC, PV_MACRO, PV_MACRO_CALL, PV_PATTERN, PV_RANDOM, PV_RETURN, PV_STATEMENTBLOCK, PV_SWITCH, PV_SWITCHES, PV_VARDEC, PV_WHILE, PV_WORD, pval::statements, pval::str, pval::type, pval::u1, pval::u2, pval::u3, pval::u4, and pval::val.

{
   if (item == NULL) {
      ast_log(LOG_WARNING, "null item\n");
      return;
   }

   if (item->filename)
      free(item->filename);
   
   switch (item->type) {
   case PV_WORD:
      /* fields: item->u1.str == string associated with this (word). */
      if (item->u1.str )
         free(item->u1.str);
      if ( item->u2.arglist )
         destroy_pval(item->u2.arglist);
      break;
      
   case PV_MACRO:
      /* fields: item->u1.str     == name of macro
                 item->u2.arglist == pval list of PV_WORD arguments of macro, as given by user
               item->u2.arglist->u1.str  == argument
               item->u2.arglist->next   == next arg

               item->u3.macro_statements == pval list of statements in macro body.
      */
      destroy_pval(item->u2.arglist);
      if (item->u1.str )
         free(item->u1.str);
      destroy_pval(item->u3.macro_statements);
      break;
         
   case PV_CONTEXT:
      /* fields: item->u1.str     == name of context
                 item->u2.statements == pval list of statements in context body
               item->u3.abstract == int 1 if an abstract keyword were present
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.statements);
      break;
         
   case PV_MACRO_CALL:
      /* fields: item->u1.str     == name of macro to call
                 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
               item->u2.arglist->u1.str  == argument
               item->u2.arglist->next   == next arg
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.arglist);
      break;
         
   case PV_APPLICATION_CALL:
      /* fields: item->u1.str     == name of application to call
                 item->u2.arglist == pval list of PV_WORD arguments of macro call, as given by user
               item->u2.arglist->u1.str  == argument
               item->u2.arglist->next   == next arg
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.arglist);
      break;
         
   case PV_CASE:
      /* fields: item->u1.str     == value of case
                 item->u2.statements == pval list of statements under the case
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.statements);
      break;
         
   case PV_PATTERN:
      /* fields: item->u1.str     == value of case
                 item->u2.statements == pval list of statements under the case
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.statements);
      break;
         
   case PV_DEFAULT:
      /* fields: 
                 item->u2.statements == pval list of statements under the case
      */
      destroy_pval(item->u2.statements);
      break;
         
   case PV_CATCH:
      /* fields: item->u1.str     == name of extension to catch
                 item->u2.statements == pval list of statements in context body
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.statements);
      break;
         
   case PV_SWITCHES:
      /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
      */
      destroy_pval(item->u1.list);
      break;
         
   case PV_ESWITCHES:
      /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
      */
      destroy_pval(item->u1.list);
      break;
         
   case PV_INCLUDES:
      /* fields: item->u1.list     == pval list of PV_WORD elements, one per entry in the list
                 item->u2.arglist  == pval list of 4 PV_WORD elements for time values
      */
      destroy_pval(item->u1.list);
      break;
         
   case PV_STATEMENTBLOCK:
      /* fields: item->u1.list     == pval list of statements in block, one per entry in the list
      */
      destroy_pval(item->u1.list);
      break;
         
   case PV_LOCALVARDEC:
   case PV_VARDEC:
      /* fields: item->u1.str     == variable name
                 item->u2.val     == variable value to assign
      */
      if (item->u1.str)
         free(item->u1.str);
      if (item->u2.val)
         free(item->u2.val);
      break;
         
   case PV_GOTO:
      /* fields: item->u1.list     == pval list of PV_WORD target names, up to 3, in order as given by user.
                 item->u1.list->u1.str  == where the data on a PV_WORD will always be.
      */
      
      destroy_pval(item->u1.list);
      break;
         
   case PV_LABEL:
      /* fields: item->u1.str     == label name
      */
      if (item->u1.str)
         free(item->u1.str);
      break;
         
   case PV_FOR:
      /* fields: item->u1.for_init     == a string containing the initalizer
                 item->u2.for_test     == a string containing the loop test
                 item->u3.for_inc      == a string containing the loop increment

               item->u4.for_statements == a pval list of statements in the for ()
      */
      if (item->u1.for_init)
         free(item->u1.for_init);
      if (item->u2.for_test)
         free(item->u2.for_test);
      if (item->u3.for_inc)
         free(item->u3.for_inc);
      destroy_pval(item->u4.for_statements);
      break;
         
   case PV_WHILE:
      /* fields: item->u1.str        == the while conditional, as supplied by user

               item->u2.statements == a pval list of statements in the while ()
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.statements);
      break;
         
   case PV_BREAK:
      /* fields: none
      */
      break;
         
   case PV_RETURN:
      /* fields: none
      */
      break;
         
   case PV_CONTINUE:
      /* fields: none
      */
      break;
         
   case PV_IFTIME:
      /* fields: item->u1.list        == the 4 time values, in PV_WORD structs, linked list

               item->u2.statements == a pval list of statements in the if ()
               item->u3.else_statements == a pval list of statements in the else
                                    (could be zero)
      */
      destroy_pval(item->u1.list);
      destroy_pval(item->u2.statements);
      if (item->u3.else_statements) {
         destroy_pval(item->u3.else_statements);
      }
      break;
         
   case PV_RANDOM:
      /* fields: item->u1.str        == the random percentage, as supplied by user

               item->u2.statements == a pval list of statements in the true part ()
               item->u3.else_statements == a pval list of statements in the else
                                    (could be zero)
      fall thru to If */
   case PV_IF:
      /* fields: item->u1.str        == the if conditional, as supplied by user

               item->u2.statements == a pval list of statements in the if ()
               item->u3.else_statements == a pval list of statements in the else
                                    (could be zero)
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.statements);
      if (item->u3.else_statements) {
         destroy_pval(item->u3.else_statements);
      }
      break;
         
   case PV_SWITCH:
      /* fields: item->u1.str        == the switch expression

               item->u2.statements == a pval list of statements in the switch, 
                                    (will be case statements, most likely!)
      */
      if (item->u1.str)
         free(item->u1.str);
      destroy_pval(item->u2.statements);
      break;
         
   case PV_EXTENSION:
      /* fields: item->u1.str        == the extension name, label, whatever it's called

               item->u2.statements == a pval list of statements in the extension
               item->u3.hints      == a char * hint argument
               item->u4.regexten   == an int boolean. non-zero says that regexten was specified
      */
      if (item->u1.str)
         free(item->u1.str);
      if (item->u3.hints)
         free(item->u3.hints);
      destroy_pval(item->u2.statements);
      break;
         
   case PV_IGNOREPAT:
      /* fields: item->u1.str        == the ignorepat data
      */
      if (item->u1.str)
         free(item->u1.str);
      break;
         
   case PV_GLOBALS:
      /* fields: item->u1.statements     == pval list of statements, usually vardecs
      */
      destroy_pval(item->u1.statements);
      break;
   }
   free(item);
}
struct pval* find_context ( char *  name) [read]

Definition at line 1950 of file pval.c.

References match_pval(), and name.

{
   return_on_context_match = 1;
   count_labels = 0;
   match_context = name;
   match_exten = "*";  /* don't really need to set these, shouldn't be reached */
   match_label = "*";
   return match_pval(current_db);
}
struct pval* find_macro ( char *  name) [read]

Definition at line 1940 of file pval.c.

References match_pval(), and name.

{
   return_on_context_match = 1;
   count_labels = 0;
   match_context = name;
   match_exten = "*";  /* don't really need to set these, shouldn't be reached */
   match_label = "*";
   return match_pval(current_db);
}
static char* handle_cli_ael_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 199 of file pbx_ael.c.

References ast_cli_args::argc, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, pbx_load_module(), and ast_cli_entry::usage.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "ael reload";
      e->usage =
         "Usage: ael reload\n"
         "       Reloads AEL configuration.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != 2)
      return CLI_SHOWUSAGE;

   return (pbx_load_module() ? CLI_FAILURE : CLI_SUCCESS);
}
static char* handle_cli_ael_set_debug ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 165 of file pbx_ael.c.

References ast_cli_args::argc, ast_cli_entry::args, ast_cli_args::argv, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, DEBUG_CONTEXTS, DEBUG_MACROS, DEBUG_READ, DEBUG_TOKENS, and ast_cli_entry::usage.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "ael set debug {read|tokens|macros|contexts|off}";
      e->usage =
         "Usage: ael set debug {read|tokens|macros|contexts|off}\n"
         "       Enable AEL read, token, macro, or context debugging,\n"
         "       or disable all AEL debugging messages.  Note: this\n"
         "       currently does nothing.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != e->args)
      return CLI_SHOWUSAGE;

   if (!strcasecmp(a->argv[3], "read"))
      aeldebug |= DEBUG_READ;
   else if (!strcasecmp(a->argv[3], "tokens"))
      aeldebug |= DEBUG_TOKENS;
   else if (!strcasecmp(a->argv[3], "macros"))
      aeldebug |= DEBUG_MACROS;
   else if (!strcasecmp(a->argv[3], "contexts"))
      aeldebug |= DEBUG_CONTEXTS;
   else if (!strcasecmp(a->argv[3], "off"))
      aeldebug = 0;
   else
      return CLI_SHOWUSAGE;

   return CLI_SUCCESS;
}
int is_empty ( char *  arg)

Definition at line 1978 of file pval.c.

{
   if (!arg)
      return 1;
   if (*arg == 0)
      return 1;
   while (*arg) {
      if (*arg != ' ' && *arg != '\t')
         return 0;
      arg++;
   }
   return 1;
}
int is_float ( char *  arg)

Definition at line 1960 of file pval.c.

References s.

{
   char *s;
   for (s=arg; *s; s++) {
      if (*s != '.' && (*s < '0' || *s > '9'))
         return 0;
   }
   return 1;
}
int is_int ( char *  arg)

Definition at line 1969 of file pval.c.

References s.

{
   char *s;
   for (s=arg; *s; s++) {
      if (*s < '0' || *s > '9')
         return 0;
   }
   return 1;
}
static int load_module ( void  ) [static]
struct ael_extension* new_exten ( void  ) [read]

Definition at line 2927 of file pval.c.

References calloc.

{
   struct ael_extension *x = (struct ael_extension *)calloc(sizeof(struct ael_extension),1);
   return x;
}
struct ael_priority* new_prio ( void  ) [read]

Definition at line 2921 of file pval.c.

References calloc.

{
   struct ael_priority *x = (struct ael_priority *)calloc(sizeof(struct ael_priority),1);
   return x;
}
static int pbx_load_module ( void  ) [static]

Definition at line 113 of file pbx_ael.c.

References ael2_parse(), ael2_semantic_check(), ast_compile_ael2(), ast_config_AST_CONFIG_DIR, ast_context_verify_includes(), ast_hashtab_compare_contexts(), ast_hashtab_create(), ast_hashtab_hash_contexts(), ast_hashtab_newsize_java(), ast_hashtab_resize_java(), ast_log(), ast_merge_contexts_and_delete(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_walk_contexts(), destroy_pval(), errs, local_contexts, local_table, LOG_ERROR, LOG_NOTICE, and R_OK.

Referenced by handle_cli_ael_reload(), load_module(), and reload().

{
   int errs=0, sem_err=0, sem_warn=0, sem_note=0;
   char *rfilename;
   struct ast_context *local_contexts=NULL, *con;
   struct ast_hashtab *local_table=NULL;
   
   struct pval *parse_tree;

   ast_log(LOG_NOTICE, "Starting AEL load process.\n");
   if (config[0] == '/')
      rfilename = (char *)config;
   else {
      rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
      sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
   }
   if (access(rfilename,R_OK) != 0) {
      ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
      return AST_MODULE_LOAD_DECLINE;
   }
   
   parse_tree = ael2_parse(rfilename, &errs);
   ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
   ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
   if (errs == 0 && sem_err == 0) {
      ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
      local_table = ast_hashtab_create(11, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
      if (ast_compile_ael2(&local_contexts, local_table, parse_tree)) {
         ast_log(LOG_ERROR, "AEL compile failed! Aborting.\n");
         destroy_pval(parse_tree); /* free up the memory */
         return AST_MODULE_LOAD_DECLINE;
      }
      ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
      
      ast_merge_contexts_and_delete(&local_contexts, local_table, registrar);
      local_table = NULL; /* it's the dialplan global now */
      local_contexts = NULL;
      ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
      for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
         ast_context_verify_includes(con);
      ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
   } else {
      ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err);
      destroy_pval(parse_tree); /* free up the memory */
      return AST_MODULE_LOAD_DECLINE;
   }
   destroy_pval(parse_tree); /* free up the memory */
   
   return AST_MODULE_LOAD_SUCCESS;
}
static int reload ( void  ) [static]

Definition at line 236 of file pbx_ael.c.

References pbx_load_module().

{
   return pbx_load_module();
}
void set_priorities ( struct ael_extension exten)

Definition at line 4215 of file pval.c.

References ael_extension::is_switch, ael_priority::next, ael_extension::next_exten, ael_priority::origin, ael_extension::plist, ael_priority::priority_num, PV_LABEL, ael_extension::regexten, and pval::type.

{
   int i;
   struct ael_priority *pr;
   do {
      if (exten->is_switch)
         i = 10;
      else if (exten->regexten)
         i=2;
      else
         i=1;
      
      for (pr=exten->plist; pr; pr=pr->next) {
         pr->priority_num = i;
         
         if (!pr->origin || (pr->origin && pr->origin->type != PV_LABEL) ) /* Labels don't show up in the dialplan,
                                      but we want them to point to the right
                                      priority, which would be the next line
                                      after the label; */
            i++;
      }
      
      exten = exten->next_exten;
   } while ( exten );
}
static int unload_module ( void  ) [static]

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Asterisk Extension Language Compiler" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 255 of file pbx_ael.c.

int aeldebug = 0 [static]

Definition at line 107 of file pbx_ael.c.

Definition at line 255 of file pbx_ael.c.

struct ast_cli_entry cli_ael[] [static]
Initial value:
 {
   AST_CLI_DEFINE(handle_cli_ael_reload,    "Reload AEL configuration"),
   AST_CLI_DEFINE(handle_cli_ael_set_debug, "Enable AEL debugging flags")
}

Definition at line 218 of file pbx_ael.c.

char* config = "extensions.ael" [static]

Definition at line 65 of file pbx_ael.c.

char* registrar = "pbx_ael" [static]

Definition at line 66 of file pbx_ael.c.