Mon Mar 12 2012 21:42:49

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)
static int aelsub_exec (struct ast_channel *chan, const char *vdata)
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 __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Asterisk Extension Language Compiler" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static int aeldebug = 0
static char * aelsub = "AELSub"
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 84 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().

#define DEBUG_MACROS   (1 << 2)

Definition at line 83 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().

#define DEBUG_READ   (1 << 0)

Definition at line 81 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().

#define DEBUG_TOKENS   (1 << 1)

Definition at line 82 of file pbx_ael.c.

Referenced by handle_cli_ael_set_debug().


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 303 of file pbx_ael.c.

{
static void __unreg_module ( void  ) [static]

Definition at line 303 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 );
}
static int aelsub_exec ( struct ast_channel chan,
const char *  vdata 
) [static]

Definition at line 135 of file pbx_ael.c.

References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_STANDARD_RAW_ARGS, ast_strdupa, name, pbx_exec(), and pbx_findapp().

Referenced by load_module().

{
   char buf[256], *data = ast_strdupa(vdata);
   struct ast_app *gosub = pbx_findapp("Gosub");
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(name);
      AST_APP_ARG(args);
   );

   if (gosub) {
      AST_STANDARD_RAW_ARGS(args, data);
      snprintf(buf, sizeof(buf), "%s,~~s~~,1(%s)", args.name, args.args);
      return pbx_exec(chan, gosub, buf);
   }
   return -1;
}
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(), buf2, ael_extension::cidmatch, context, ael_extension::context, destroy_extensions(), exten, fix_gotos_in_extensions(), gen_prios(), pval::hints, ael_extension::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, pval::type, ael_priority::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 2485 of file ast_expr2f.c.

void ast_expr_register_extra_error_info ( char *  errmsg)

Definition at line 2479 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, pval::next, argapp::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 241 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 207 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.

{
   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.

{
   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 155 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 284 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 __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Asterisk Extension Language Compiler" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 303 of file pbx_ael.c.

int aeldebug = 0 [static]

Definition at line 128 of file pbx_ael.c.

char* aelsub = "AELSub" [static]

Definition at line 133 of file pbx_ael.c.

Definition at line 303 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 260 of file pbx_ael.c.

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

Definition at line 86 of file pbx_ael.c.

char* registrar = "pbx_ael" [static]

Definition at line 87 of file pbx_ael.c.