From 2a7d309debe319606d73748a8583585683728a5f Mon Sep 17 00:00:00 2001
From: "Kevin P. Fleming" <kpfleming@digium.com>
Date: Mon, 16 May 2005 00:35:38 +0000
Subject: [PATCH] add upgraded expression parser (bug #2058)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5691 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 Makefile             |  47 ++-
 ast_expr2.fl         | 167 ++++++++
 ast_expr2.y          | 901 +++++++++++++++++++++++++++++++++++++++++++
 doc/README.variables | 207 +++++++++-
 vercomp.sh           | 163 ++++++++
 5 files changed, 1476 insertions(+), 9 deletions(-)
 create mode 100755 ast_expr2.fl
 create mode 100755 ast_expr2.y
 create mode 100755 vercomp.sh

diff --git a/Makefile b/Makefile
index 80f9fdc587..6cd52af186 100755
--- a/Makefile
+++ b/Makefile
@@ -258,10 +258,21 @@ ifeq (${OSARCH},SunOS)
 LIBS+=-lpthread -ldl -lnsl -lsocket -lresolv -L$(CROSS_COMPILE_TARGET)/usr/local/ssl/lib
 endif
 LIBS+=-lssl
+
+FLEXVER_GT_2_5_31=$(shell ./vercomp.sh flex \>= 2.5.31)
+BISONVER=$(shell bison --version | grep \^bison | egrep -o '[0-9]+\.[-0-9.]+[a-z]?' )
+BISONVERGE_85=$(shell ./vercomp.sh bison \>= 1.85 )
+
+ifeq (${FLEXVER_GT_2_5_31},true)
+FLEXOBJS=ast_expr2.o ast_expr2f.o
+else
+FLEXOBJS=ast_expr.o
+endif
+
 OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
 	translate.o file.o say.o pbx.o cli.o md5.o term.o \
 	ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
-	cdr.o tdd.o acl.o rtp.o manager.o asterisk.o ast_expr.o \
+	cdr.o tdd.o acl.o rtp.o manager.o asterisk.o ${FLEXOBJS}  \
 	dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
 	astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
 	utils.o config_old.o plc.o jitterbuf.o dnsmgr.o
@@ -333,14 +344,46 @@ _version:
 .version: _version
 
 .y.c:
-	bison $< --name-prefix=ast_yy -o $@
+	@if (($(BISONVERGE_85) = false)); then \
+		echo ================================================================================= ;\
+		echo NOTE: you may have trouble if you do not have bison-1.85 or higher installed! ;\
+		echo NOTE: you can pick up a copy at: http://ftp.gnu.org/ or its mirrors ;\
+		echo NOTE: You Have: $(BISONVER) ;\
+		echo ================================================================================; \
+	else \
+		echo EXCELLENT-- You have Bison version $(BISONVER), this should work just fine...;\
+	fi
+	bison -v -d --name-prefix=ast_yy $< -o $@
 
 ast_expr.o: ast_expr.c
+	@echo NOTE:
+	@echo NOTE:
+	@echo NOTE: Using older version of ast_expr. To use the newer version,
+	@echo NOTE: Upgrade to flex 2.5.31 or higher, which can be found at http://
+	@echo NOTE:  http://sourceforge.net/project/showfiles.php?group_id=72099
+	@echo NOTE:
+	@echo NOTE:
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) ast_expr.c
+
+ast_expr2.o: ast_expr2.c
+
+ast_expr2f.o: ast_expr2f.c
+ast_expr2f.c: ast_expr2.fl
+	flex ast_expr2.fl
 
 cli.o: cli.c build.h
 
 asterisk.o: asterisk.c build.h
 
+testexpr2 :
+	flex ast_expr2.fl
+	bison -v -d --name-prefix=ast_yy -o ast_expr2.c ast_expr2.y
+	gcc -g -c -DSTANDALONE ast_expr2f.c
+	gcc -g -c -DSTANDALONE ast_expr2.c
+	gcc -g -o testexpr2 ast_expr2f.o ast_expr2.o
+	rm ast_expr2.c ast_expr2.o ast_expr2f.o ast_expr2f.c
+
+
 manpage: asterisk.8.gz
 
 asterisk.8.gz: asterisk.sgml
diff --git a/ast_expr2.fl b/ast_expr2.fl
new file mode 100755
index 0000000000..fc56994ee2
--- /dev/null
+++ b/ast_expr2.fl
@@ -0,0 +1,167 @@
+%{
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+#include <asterisk/ast_expr.h>
+#include <asterisk/logger.h>
+
+enum valtype {
+	AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+	enum valtype type;
+	union {
+		char *s;
+		quad_t i;
+	} u;
+} ;
+
+#include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
+
+#define SET_COLUMNS yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);yylloc_param->last_column = yylloc_param->last_column + yyleng - 1; yylloc_param->first_line = yylloc_param->last_line = 1
+#define SET_STRING yylval_param->val = (struct val *)calloc(sizeof(struct val),1); yylval_param->val->type = AST_EXPR_string; yylval_param->val->u.s = strdup(yytext);
+
+struct parse_io
+{
+	char *string;
+	struct val *val;
+	yyscan_t scanner;
+};
+ 
+
+%}
+
+%option prefix="ast_yy"
+%option batch
+%option outfile="ast_expr2f.c"
+%option reentrant
+%option bison-bridge
+%option bison-locations
+%option noyywrap
+
+%%
+
+\|	{ SET_COLUMNS; SET_STRING; return TOK_OR;}
+\&	{ SET_COLUMNS; SET_STRING; return TOK_AND;}
+\=	{ SET_COLUMNS; SET_STRING; return TOK_EQ;}
+\>	{ SET_COLUMNS; SET_STRING; return TOK_GT;}
+\<	{ SET_COLUMNS; SET_STRING; return TOK_LT;}
+\>\=	{ SET_COLUMNS; SET_STRING; return TOK_GE;}
+\<\=	{ SET_COLUMNS; SET_STRING; return TOK_LE;}
+\!\=	{ SET_COLUMNS; SET_STRING; return TOK_NE;}
+\+	{ SET_COLUMNS; SET_STRING; return TOK_PLUS;}
+\-	{ SET_COLUMNS; SET_STRING; return TOK_MINUS;}
+\*	{ SET_COLUMNS; SET_STRING; return TOK_MULT;}
+\/	{ SET_COLUMNS; SET_STRING; return TOK_DIV;}
+\%	{ SET_COLUMNS; SET_STRING; return TOK_MOD;}
+\:	{ SET_COLUMNS; SET_STRING; return TOK_COLON;}
+\(	{ SET_COLUMNS; SET_STRING; return TOK_LP;}
+\)	{ SET_COLUMNS; SET_STRING; return TOK_RP;}
+
+[ 	\r]		{}
+\"[^"]*\"   {SET_COLUMNS; SET_STRING; return TOKEN;}
+
+[\n]	{/* what to do with eol */}
+[0-9]+		{   SET_COLUMNS; 
+				yylval_param->val = (struct val *)calloc(sizeof(struct val),1);
+				yylval_param->val->type = AST_EXPR_integer; 
+				yylval_param->val->u.i = atoi(yytext); 
+				return TOKEN;}
+[a-zA-Z0-9,.?';{}\\_^%$#@!]+	{SET_COLUMNS; SET_STRING; return TOKEN;}
+
+%%
+
+/* I'm putting the interface routine to the whole parse here in the flexer input file
+   mainly because of all the flexer initialization that has to be done. Shouldn't matter
+   where it is, as long as it's somewhere. I didn't want to define a prototype for the
+   ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
+	UGH! that would be inappropriate. */
+
+int ast_yyparse( void *); /* need to/should define this prototype for the call to yyparse */
+char *ast_expr(char *arg); /* and this prototype for the following func */
+int		ast_yyerror(const char *,YYLTYPE *, struct parse_io *); /* likewise */
+
+char *ast_expr (char *arg)
+{
+	struct parse_io *io;
+	char *pirouni;
+	
+	io = (struct parse_io *)calloc(sizeof(struct parse_io),1);
+	io->string = arg;  /* to pass to the error routine */
+	
+	ast_yylex_init(&io->scanner);
+	
+	ast_yy_scan_string(arg,io->scanner);
+	
+	ast_yyparse ((void *)io);
+
+	ast_yylex_destroy(io->scanner);
+	
+
+	if (io->val==NULL) {
+		pirouni=strdup("0");
+		return(pirouni);
+	} else {
+		if (io->val->type == AST_EXPR_integer) {
+			pirouni=malloc(256);
+			sprintf (pirouni,"%lld", (long long)io->val->u.i);
+		}
+		else {
+			pirouni=strdup(io->val->u.s);
+		}
+		free(io->val);
+	}
+	free(io);
+	return(pirouni);
+}
+
+int ast_yyerror (const char *s,  yyltype *loc, struct parse_io *parseio )
+{	
+	struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
+	char spacebuf[8000]; /* best safe than sorry */
+	char spacebuf2[8000]; /* best safe than sorry */
+	int i=0;
+	spacebuf[0] = 0;
+	
+#ifdef WHEN_LOC_MEANS_SOMETHING
+	if( loc->first_column > 7990 ) /* if things get out of whack, why crash? */
+		loc->first_column = 7990;
+	if( loc->last_column > 7990 )
+		loc->last_column = 7990;
+	for(i=0;i<loc->first_column;i++) spacebuf[i] = ' ';
+	for(   ;i<loc->last_column;i++) spacebuf[i] = '^';
+	spacebuf[i] = 0;
+#endif
+	for(i=0;i< (int)(yytext - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);i++) spacebuf2[i] = ' ';  /* uh... assuming yyg is defined, then I can use the yycolumn macro,
+													which is the same thing as... get this:
+													yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
+													I was tempted to just use yy_buf_pos in the STATE, but..., well:
+														a. the yy_buf_pos is the current position in the buffer, which
+															may not relate to the entire string/buffer because of the
+															buffering.
+														b. but, analysis of the situation is that when you use the
+															yy_scan_string func, it creates a single buffer the size of
+															string, so the two would be the same... 
+													so, in the end, the yycolumn macro is available, shorter, therefore easier. */
+	spacebuf2[i++]='^';
+	spacebuf2[i]= 0;
+
+#ifdef STANDALONE
+	/* easier to read in the standalone version */
+	printf("ast_yyerror(): syntax error: %s; Input:\n%s\n%s\n",  
+			s, parseio->string,spacebuf2);
+#else
+	ast_log(LOG_WARNING,"ast_yyerror(): syntax error: %s; Input:\n%s\n%s\n",  
+			s, parseio->string,spacebuf2);
+	ast_log(LOG_WARNING,"If you have questions, please refer to doc/README.variables2 in the asterisk source.\n");
+#endif
+	return(0);
+}
diff --git a/ast_expr2.y b/ast_expr2.y
new file mode 100755
index 0000000000..be78d4645f
--- /dev/null
+++ b/ast_expr2.y
@@ -0,0 +1,901 @@
+%{
+/* Written by Pace Willisson (pace@blitz.com) 
+ * and placed in the public domain.
+ *
+ * Largely rewritten by J.T. Conklin (jtc@wimsey.com)
+ *
+ * And then overhauled twice by Steve Murphy (murf@e-tools.com)
+ * to add double-quoted strings, allow mult. spaces, improve
+ * error messages, and then to fold in a flex scanner for the 
+ * yylex operation.
+ *
+ * $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+#include <asterisk/ast_expr.h>
+#include <asterisk/logger.h>
+
+#ifdef LONG_LONG_MIN
+#define QUAD_MIN LONG_LONG_MIN
+#endif
+#ifdef LONG_LONG_MAX
+#define QUAD_MAX LONG_LONG_MAX
+#endif
+
+#  if ! defined(QUAD_MIN)
+#   define QUAD_MIN     (-0x7fffffffffffffffL-1)
+#  endif
+#  if ! defined(QUAD_MAX)
+#   define QUAD_MAX     (0x7fffffffffffffffL)
+#  endif
+
+#define YYPARSE_PARAM parseio
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+
+/* #define ast_log fprintf
+#define LOG_WARNING stderr */
+  
+enum valtype {
+	AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+	enum valtype type;
+	union {
+		char *s;
+		quad_t i;
+	} u;
+} ;
+
+typedef void *yyscan_t;
+
+struct parse_io
+{
+	char *string;
+	struct val *val;
+	yyscan_t scanner;
+};
+ 
+static int		chk_div __P((quad_t, quad_t));
+static int		chk_minus __P((quad_t, quad_t, quad_t));
+static int		chk_plus __P((quad_t, quad_t, quad_t));
+static int		chk_times __P((quad_t, quad_t, quad_t));
+static void		free_value __P((struct val *));
+static int		is_zero_or_null __P((struct val *));
+static int		isstring __P((struct val *));
+static struct val	*make_integer __P((quad_t));
+static struct val	*make_str __P((const char *));
+static struct val	*op_and __P((struct val *, struct val *));
+static struct val	*op_colon __P((struct val *, struct val *));
+static struct val	*op_eqtilde __P((struct val *, struct val *));
+static struct val	*op_div __P((struct val *, struct val *));
+static struct val	*op_eq __P((struct val *, struct val *));
+static struct val	*op_ge __P((struct val *, struct val *));
+static struct val	*op_gt __P((struct val *, struct val *));
+static struct val	*op_le __P((struct val *, struct val *));
+static struct val	*op_lt __P((struct val *, struct val *));
+static struct val	*op_minus __P((struct val *, struct val *));
+static struct val	*op_negate __P((struct val *));
+static struct val	*op_compl __P((struct val *));
+static struct val	*op_ne __P((struct val *, struct val *));
+static struct val	*op_or __P((struct val *, struct val *));
+static struct val	*op_plus __P((struct val *, struct val *));
+static struct val	*op_rem __P((struct val *, struct val *));
+static struct val	*op_times __P((struct val *, struct val *));
+static quad_t		to_integer __P((struct val *));
+static void		to_string __P((struct val *));
+
+/* uh, if I want to predeclare yylex with a YYLTYPE, I have to predeclare the yyltype... sigh */
+typedef struct yyltype
+{
+  int first_line;
+  int first_column;
+
+  int last_line;
+  int last_column;
+} yyltype;
+
+# define YYLTYPE yyltype
+# define YYLTYPE_IS_TRIVIAL 1
+
+/* we will get warning about no prototype for yylex! But we can't
+   define it here, we have no definition yet for YYSTYPE. */
+
+int		ast_yyerror(const char *,YYLTYPE *, struct parse_io *);
+ 
+/* I wanted to add args to the yyerror routine, so I could print out
+   some useful info about the error. Not as easy as it looks, but it
+   is possible. */
+#define ast_yyerror(x) ast_yyerror(x,&yyloc,parseio)
+
+%}
+ 
+%pure-parser
+%locations
+/* %debug  for when you are having big problems */
+
+/* %name-prefix="ast_yy" */
+
+%union
+{
+	struct val *val;
+}
+
+/* IN_ANOTHER_LIFE
+%{
+static int		ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
+%}
+*/
+
+%left <val> TOK_OR
+%left <val> TOK_AND
+%left <val> TOK_EQ TOK_GT TOK_LT TOK_GE TOK_LE TOK_NE
+%left <val> TOK_PLUS TOK_MINUS
+%left <val> TOK_MULT TOK_DIV TOK_MOD
+%left <val> TOK_COMPL TOK_EQTILDE
+%left UMINUS
+%left <val> TOK_COLON
+%left <val> TOK_RP TOK_LP
+
+%token <val> TOKEN
+%type <val> start expr
+
+%%
+
+start: expr { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+              ((struct parse_io *)parseio)->val->type = $$->type;
+              ((struct parse_io *)parseio)->val->u.s = $$->u.s; }
+	;
+
+expr:	TOKEN   { $$= $1;}
+	| TOK_LP expr TOK_RP { $$ = $2; 
+	                       @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						   @$.first_line=0; @$.last_line=0;}
+	| expr TOK_OR expr { $$ = op_or ($1, $3);
+                         @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						 @$.first_line=0; @$.last_line=0;}
+	| expr TOK_AND expr { $$ = op_and ($1, $3); 
+	                      @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+                          @$.first_line=0; @$.last_line=0;}
+	| expr TOK_EQ expr { $$ = op_eq ($1, $3); 
+	                     @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						 @$.first_line=0; @$.last_line=0;}
+	| expr TOK_GT expr { $$ = op_gt ($1, $3);
+                         @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						 @$.first_line=0; @$.last_line=0;}
+	| expr TOK_LT expr { $$ = op_lt ($1, $3); 
+	                     @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						 @$.first_line=0; @$.last_line=0;}
+	| expr TOK_GE expr  { $$ = op_ge ($1, $3); 
+	                      @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						  @$.first_line=0; @$.last_line=0;}
+	| expr TOK_LE expr  { $$ = op_le ($1, $3); 
+	                      @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						  @$.first_line=0; @$.last_line=0;}
+	| expr TOK_NE expr  { $$ = op_ne ($1, $3); 
+	                      @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						  @$.first_line=0; @$.last_line=0;}
+	| expr TOK_PLUS expr { $$ = op_plus ($1, $3); 
+	                       @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						   @$.first_line=0; @$.last_line=0;}
+	| expr TOK_MINUS expr { $$ = op_minus ($1, $3); 
+	                        @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+							@$.first_line=0; @$.last_line=0;}
+	| TOK_MINUS expr %prec UMINUS { $$ = op_negate ($2); 
+	                        @$.first_column = @1.first_column; @$.last_column = @2.last_column; 
+							@$.first_line=0; @$.last_line=0;}
+	| TOK_COMPL expr %prec UMINUS { $$ = op_compl ($2); 
+	                        @$.first_column = @1.first_column; @$.last_column = @2.last_column; 
+							@$.first_line=0; @$.last_line=0;}
+	| expr TOK_MULT expr { $$ = op_times ($1, $3); 
+	                       @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						   @$.first_line=0; @$.last_line=0;}
+	| expr TOK_DIV expr { $$ = op_div ($1, $3); 
+	                      @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						  @$.first_line=0; @$.last_line=0;}
+	| expr TOK_MOD expr { $$ = op_rem ($1, $3); 
+	                      @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+						  @$.first_line=0; @$.last_line=0;}
+	| expr TOK_COLON expr { $$ = op_colon ($1, $3); 
+	                        @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+							@$.first_line=0; @$.last_line=0;}
+	| expr TOK_EQTILDE expr { $$ = op_eqtilde ($1, $3); 
+	                        @$.first_column = @1.first_column; @$.last_column = @3.last_column; 
+							@$.first_line=0; @$.last_line=0;}
+	;
+
+%%
+
+static struct val *
+make_integer (quad_t i)
+{
+	struct val *vp;
+
+	vp = (struct val *) malloc (sizeof (*vp));
+	if (vp == NULL) {
+		ast_log(LOG_WARNING, "malloc() failed\n");
+		return(NULL);
+	}
+
+	vp->type = AST_EXPR_integer;
+	vp->u.i  = i;
+	return vp; 
+}
+
+static struct val *
+make_str (const char *s)
+{
+	struct val *vp;
+	size_t i;
+	int isint;
+
+	vp = (struct val *) malloc (sizeof (*vp));
+	if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
+		ast_log(LOG_WARNING,"malloc() failed\n");
+		return(NULL);
+	}
+
+	for(i = 1, isint = isdigit(s[0]) || s[0] == '-';
+	    isint && i < strlen(s);
+	    i++)
+	{
+		if(!isdigit(s[i]))
+			 isint = 0;
+	}
+
+	if (isint)
+		vp->type = AST_EXPR_numeric_string;
+	else	
+		vp->type = AST_EXPR_string;
+
+	return vp;
+}
+
+
+static void
+free_value (struct val *vp)
+{	
+	if (vp==NULL) {
+		return;
+	}
+	if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+		free (vp->u.s);	
+}
+
+
+static quad_t
+to_integer (struct val *vp)
+{
+	quad_t i;
+
+	if (vp == NULL) {
+		ast_log(LOG_WARNING,"vp==NULL in to_integer()\n");
+		return(0);
+	}
+
+	if (vp->type == AST_EXPR_integer)
+		return 1;
+
+	if (vp->type == AST_EXPR_string)
+		return 0;
+
+	/* vp->type == AST_EXPR_numeric_string, make it numeric */
+	errno = 0;
+	i  = strtoq(vp->u.s, (char**)NULL, 10);
+	if (errno != 0) {
+		free(vp->u.s);
+		ast_log(LOG_WARNING,"overflow\n");
+		return(0);
+	}
+	free (vp->u.s);
+	vp->u.i = i;
+	vp->type = AST_EXPR_integer;
+	return 1;
+}
+
+static void
+strip_quotes(struct val *vp)
+{
+	if (vp->type != AST_EXPR_string && vp->type != AST_EXPR_numeric_string)
+		return;
+	
+	if( vp->u.s[0] == '"' && vp->u.s[strlen(vp->u.s)-1] == '"' )
+	{
+		char *f, *t;
+		f = vp->u.s;
+		t = vp->u.s;
+		
+		while( *f )
+		{
+			if( *f  && *f != '"' )
+				*t++ = *f++;
+			else
+				f++;
+		}
+		*t = *f;
+	}
+}
+
+static void
+to_string (struct val *vp)
+{
+	char *tmp;
+
+	if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+		return;
+
+	tmp = malloc ((size_t)25);
+	if (tmp == NULL) {
+		ast_log(LOG_WARNING,"malloc() failed\n");
+		return;
+	}
+
+	sprintf (tmp, "%lld", (long long)vp->u.i);
+	vp->type = AST_EXPR_string;
+	vp->u.s  = tmp;
+}
+
+
+static int
+isstring (struct val *vp)
+{
+	/* only TRUE if this string is not a valid integer */
+	return (vp->type == AST_EXPR_string);
+}
+
+
+static int
+is_zero_or_null (struct val *vp)
+{
+	if (vp->type == AST_EXPR_integer) {
+		return (vp->u.i == 0);
+	} else {
+		return (*vp->u.s == 0 || (to_integer (vp) && vp->u.i == 0));
+	}
+	/* NOTREACHED */
+}
+
+#ifdef STANDALONE
+
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+	printf("LOG: lev:%d file:%s  line:%d func: %s  fmt:%s\n",
+		   level, file, line, function, fmt);
+	fflush(stdout);
+}
+
+int main(int argc,char **argv) {
+	char *s;
+
+	s=ast_expr(argv[1]);
+
+	printf("=====%s======\n",s);
+}
+
+#endif
+
+#undef ast_yyerror
+#define ast_yyerror(x) ast_yyerror(x, YYLTYPE *yylloc, struct parse_io *parseio)
+
+/* I put the ast_yyerror func in the flex input file,
+   because it refers to the buffer state. Best to
+   let it access the BUFFER stuff there and not trying
+   define all the structs, macros etc. in this file! */
+
+
+static struct val *
+op_or (struct val *a, struct val *b)
+{
+	if (is_zero_or_null (a)) {
+		free_value (a);
+		return (b);
+	} else {
+		free_value (b);
+		return (a);
+	}
+}
+		
+static struct val *
+op_and (struct val *a, struct val *b)
+{
+	if (is_zero_or_null (a) || is_zero_or_null (b)) {
+		free_value (a);
+		free_value (b);
+		return (make_integer ((quad_t)0));
+	} else {
+		free_value (b);
+		return (a);
+	}
+}
+
+static struct val *
+op_eq (struct val *a, struct val *b)
+{
+	struct val *r; 
+
+	if (isstring (a) || isstring (b)) {
+		to_string (a);
+		to_string (b);	
+		r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) == 0));
+	} else {
+		(void)to_integer(a);
+		(void)to_integer(b);
+		r = make_integer ((quad_t)(a->u.i == b->u.i));
+	}
+
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static struct val *
+op_gt (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (isstring (a) || isstring (b)) {
+		to_string (a);
+		to_string (b);
+		r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) > 0));
+	} else {
+		(void)to_integer(a);
+		(void)to_integer(b);
+		r = make_integer ((quad_t)(a->u.i > b->u.i));
+	}
+
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static struct val *
+op_lt (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (isstring (a) || isstring (b)) {
+		to_string (a);
+		to_string (b);
+		r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) < 0));
+	} else {
+		(void)to_integer(a);
+		(void)to_integer(b);
+		r = make_integer ((quad_t)(a->u.i < b->u.i));
+	}
+
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static struct val *
+op_ge (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (isstring (a) || isstring (b)) {
+		to_string (a);
+		to_string (b);
+		r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) >= 0));
+	} else {
+		(void)to_integer(a);
+		(void)to_integer(b);
+		r = make_integer ((quad_t)(a->u.i >= b->u.i));
+	}
+
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static struct val *
+op_le (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (isstring (a) || isstring (b)) {
+		to_string (a);
+		to_string (b);
+		r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) <= 0));
+	} else {
+		(void)to_integer(a);
+		(void)to_integer(b);
+		r = make_integer ((quad_t)(a->u.i <= b->u.i));
+	}
+
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static struct val *
+op_ne (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (isstring (a) || isstring (b)) {
+		to_string (a);
+		to_string (b);
+		r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) != 0));
+	} else {
+		(void)to_integer(a);
+		(void)to_integer(b);
+		r = make_integer ((quad_t)(a->u.i != b->u.i));
+	}
+
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static int
+chk_plus (quad_t a, quad_t b, quad_t r)
+{
+	/* sum of two positive numbers must be positive */
+	if (a > 0 && b > 0 && r <= 0)
+		return 1;
+	/* sum of two negative numbers must be negative */
+	if (a < 0 && b < 0 && r >= 0)
+		return 1;
+	/* all other cases are OK */
+	return 0;
+}
+
+static struct val *
+op_plus (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (!to_integer (a)) {
+		ast_log(LOG_WARNING,"non-numeric argument\n");
+		if (!to_integer (b)) {
+			free_value(a);
+			free_value(b);
+			return make_integer(0);
+		} else {
+			free_value(a);
+			return (b);
+		}
+	} else if (!to_integer(b)) {
+		free_value(b);
+		return (a);
+	}
+
+	r = make_integer (/*(quad_t)*/(a->u.i + b->u.i));
+	if (chk_plus (a->u.i, b->u.i, r->u.i)) {
+		ast_log(LOG_WARNING,"overflow\n");
+	}
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static int
+chk_minus (quad_t a, quad_t b, quad_t r)
+{
+	/* special case subtraction of QUAD_MIN */
+	if (b == QUAD_MIN) {
+		if (a >= 0)
+			return 1;
+		else
+			return 0;
+	}
+	/* this is allowed for b != QUAD_MIN */
+	return chk_plus (a, -b, r);
+}
+
+static struct val *
+op_minus (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (!to_integer (a)) {
+		ast_log(LOG_WARNING, "non-numeric argument\n");
+		if (!to_integer (b)) {
+			free_value(a);
+			free_value(b);
+			return make_integer(0);
+		} else {
+			r = make_integer(0 - b->u.i);
+			free_value(a);
+			free_value(b);
+			return (r);
+		}
+	} else if (!to_integer(b)) {
+		ast_log(LOG_WARNING, "non-numeric argument\n");
+		free_value(b);
+		return (a);
+	}
+
+	r = make_integer (/*(quad_t)*/(a->u.i - b->u.i));
+	if (chk_minus (a->u.i, b->u.i, r->u.i)) {
+		ast_log(LOG_WARNING, "overflow\n");
+	}
+	free_value (a);
+	free_value (b);
+	return r;
+}
+
+static struct val *
+op_negate (struct val *a)
+{
+	struct val *r;
+
+	if (!to_integer (a) ) {
+		free_value(a);
+		ast_log(LOG_WARNING, "non-numeric argument\n");
+		return make_integer(0);
+	}
+
+	r = make_integer (/*(quad_t)*/(- a->u.i));
+	if (chk_minus (0, a->u.i, r->u.i)) {
+		ast_log(LOG_WARNING, "overflow\n");
+	}
+	free_value (a);
+	return r;
+}
+
+static struct val *
+op_compl (struct val *a)
+{
+	int v1 = 1;
+	struct val *r;
+	
+	if( !a )
+	{
+		v1 = 0;
+	}
+	else
+	{
+		switch( a->type )
+		{
+		case AST_EXPR_integer:
+			if( a->u.i == 0 )
+				v1 = 0;
+			break;
+			
+		case AST_EXPR_string:
+			if( a->u.s == 0 )
+				v1 = 0;
+			else
+			{
+				if( a->u.s[0] == 0 )
+					v1 = 0;
+				else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+					v1 = 0;
+			}
+			break;
+			
+		case AST_EXPR_numeric_string:
+			if( a->u.s == 0 )
+				v1 = 0;
+			else
+			{
+				if( a->u.s[0] == 0 )
+					v1 = 0;
+				else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+					v1 = 0;
+			}
+			break;
+		}
+	}
+	
+	r = make_integer (!v1);
+	free_value (a);
+	return r;
+}
+
+static int
+chk_times (quad_t a, quad_t b, quad_t r)
+{
+	/* special case: first operand is 0, no overflow possible */
+	if (a == 0)
+		return 0;
+	/* cerify that result of division matches second operand */
+	if (r / a != b)
+		return 1;
+	return 0;
+}
+
+static struct val *
+op_times (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (!to_integer (a) || !to_integer (b)) {
+		free_value(a);
+		free_value(b);
+		ast_log(LOG_WARNING, "non-numeric argument\n");
+		return(make_integer(0));
+	}
+
+	r = make_integer (/*(quad_t)*/(a->u.i * b->u.i));
+	if (chk_times (a->u.i, b->u.i, r->u.i)) {
+		ast_log(LOG_WARNING, "overflow\n");
+	}
+	free_value (a);
+	free_value (b);
+	return (r);
+}
+
+static int
+chk_div (quad_t a, quad_t b)
+{
+	/* div by zero has been taken care of before */
+	/* only QUAD_MIN / -1 causes overflow */
+	if (a == QUAD_MIN && b == -1)
+		return 1;
+	/* everything else is OK */
+	return 0;
+}
+
+static struct val *
+op_div (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (!to_integer (a)) {
+		free_value(a);
+		free_value(b);
+		ast_log(LOG_WARNING, "non-numeric argument\n");
+		return make_integer(0);
+	} else if (!to_integer (b)) {
+		free_value(a);
+		free_value(b);
+		ast_log(LOG_WARNING, "non-numeric argument\n");
+		return make_integer(INT_MAX);
+	}
+
+	if (b->u.i == 0) {
+		ast_log(LOG_WARNING, "division by zero\n");		
+		free_value(a);
+		free_value(b);
+		return make_integer(INT_MAX);
+	}
+
+	r = make_integer (/*(quad_t)*/(a->u.i / b->u.i));
+	if (chk_div (a->u.i, b->u.i)) {
+		ast_log(LOG_WARNING, "overflow\n");
+	}
+	free_value (a);
+	free_value (b);
+	return r;
+}
+	
+static struct val *
+op_rem (struct val *a, struct val *b)
+{
+	struct val *r;
+
+	if (!to_integer (a) || !to_integer (b)) {
+		ast_log(LOG_WARNING, "non-numeric argument\n");
+		free_value(a);
+		free_value(b);
+		return make_integer(0);
+	}
+
+	if (b->u.i == 0) {
+		ast_log(LOG_WARNING, "div by zero\n");
+		free_value(a);
+		return(b);
+	}
+
+	r = make_integer (/*(quad_t)*/(a->u.i % b->u.i));
+	/* chk_rem necessary ??? */
+	free_value (a);
+	free_value (b);
+	return r;
+}
+	
+
+static struct val *
+op_colon (struct val *a, struct val *b)
+{
+	regex_t rp;
+	regmatch_t rm[2];
+	char errbuf[256];
+	int eval;
+	struct val *v;
+
+	/* coerce to both arguments to strings */
+	to_string(a);
+	to_string(b);
+	/* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+	strip_quotes(a);
+	strip_quotes(b);
+	/* compile regular expression */
+	if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+		regerror (eval, &rp, errbuf, sizeof(errbuf));
+		ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+		free_value(a);
+		free_value(b);
+		return make_str("");		
+	}
+
+	/* compare string against pattern */
+	/* remember that patterns are anchored to the beginning of the line */
+	if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) {
+		if (rm[1].rm_so >= 0) {
+			*(a->u.s + rm[1].rm_eo) = '\0';
+			v = make_str (a->u.s + rm[1].rm_so);
+
+		} else {
+			v = make_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
+		}
+	} else {
+		if (rp.re_nsub == 0) {
+			v = make_integer ((quad_t)0);
+		} else {
+			v = make_str ("");
+		}
+	}
+
+	/* free arguments and pattern buffer */
+	free_value (a);
+	free_value (b);
+	regfree (&rp);
+
+	return v;
+}
+	
+
+static struct val *
+op_eqtilde (struct val *a, struct val *b)
+{
+	regex_t rp;
+	regmatch_t rm[2];
+	char errbuf[256];
+	int eval;
+	struct val *v;
+
+	/* coerce to both arguments to strings */
+	to_string(a);
+	to_string(b);
+	/* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+	strip_quotes(a);
+	strip_quotes(b);
+	/* compile regular expression */
+	if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+		regerror (eval, &rp, errbuf, sizeof(errbuf));
+		ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+		free_value(a);
+		free_value(b);
+		return make_str("");		
+	}
+
+	/* compare string against pattern */
+	/* remember that patterns are anchored to the beginning of the line */
+	if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 ) {
+		if (rm[1].rm_so >= 0) {
+			*(a->u.s + rm[1].rm_eo) = '\0';
+			v = make_str (a->u.s + rm[1].rm_so);
+
+		} else {
+			v = make_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so));
+		}
+	} else {
+		if (rp.re_nsub == 0) {
+			v = make_integer ((quad_t)0);
+		} else {
+			v = make_str ("");
+		}
+	}
+
+	/* free arguments and pattern buffer */
+	free_value (a);
+	free_value (b);
+	regfree (&rp);
+
+	return v;
+}
diff --git a/doc/README.variables b/doc/README.variables
index 05955bdbac..ff5313f31d 100755
--- a/doc/README.variables
+++ b/doc/README.variables
@@ -1,5 +1,6 @@
+----------------------------
 Asterisk dial plan variables 
----------------------------
+----------------------------
 
 There are two levels of parameter evaluation done in the Asterisk
 dial plan in extensions.conf.
@@ -12,6 +13,15 @@ Asterisk has user-defined variables and standard variables set
 by various modules in Asterisk. These standard variables are
 listed at the end of this document.
 
+NOTE: During the Asterisk build process, the versions of bison and
+flex available on your system are probed. If you have versions of
+flex greater than or equal to 2.5.31, it will use flex to build a 
+"pure" (re-entrant) tokenizer for expressions. If you use bison version 
+greater than 1.85, it will use a bison grammar to generate a pure (re-entrant) 
+parser for $[] expressions. 
+Notes specific to the flex parser are marked with "**" at the beginning
+of the line.
+
 ___________________________
 PARAMETER QUOTING: 
 ---------------------------
@@ -123,6 +133,10 @@ considered as an expression and it is evaluated. Evaluation works similar to
 evaluation. 
 Note: The arguments and operands of the expression MUST BE separated 
 by at least one space. 
+** Using the Flex generated tokenizer, this is no longer the case. Spaces
+** are only required where they would seperate tokens that would normally
+** be merged into a single token. Using the new tokenizer, spaces can be
+** used freely.
 
 
 For example, after the sequence: 
@@ -132,6 +146,11 @@ exten => 1,2,Set(koko=$[2 * ${lala}])
 
 the value of variable koko is "6".
 
+** Using the new Flex generated tokenizer, the expressions above are still
+** legal, but so are the following:
+** exten => 1,1,Set(lala=$[1+2])
+** exten => 1,2,Set(koko=$[2*    ${lala}])
+
 And, further:
 
 exten => 1,1,Set(lala=$[1+2]);
@@ -141,15 +160,19 @@ token "1+2" are not numbers, it will be evaluated as the string "1+2". Again,
 please do not forget, that this is a very simple parsing engine, and it
 uses a space (at least one), to separate "tokens".
 
+** Please note that spaces are not required to separate tokens if you have
+** Flex version 2.5.31 or higher on your system.
+ 
 and, further:
 
 exten => 1,1,Set,"lala=$[  1 +    2   ]";
 
 will parse as intended. Extra spaces are ignored.
 
-___________________________
-SPACES INSIDE VARIABLE
----------------------------
+
+______________________________
+SPACES INSIDE VARIABLE VALUES
+------------------------------
 If the variable being evaluated contains spaces, there can be problems.
 
 For these cases, double quotes around text that may contain spaces
@@ -173,7 +196,7 @@ DELOREAN MOTORS : Privacy Manager
 
 and will result in syntax errors, because token DELOREAN is immediately
 followed by token MOTORS and the expression parser will not know how to 
-evaluate this expression.
+evaluate this expression, because it does not match its grammar.
 
 _____________________
 OPERATORS
@@ -204,6 +227,14 @@ with equal precedence are grouped within { } symbols.
              Return the results of multiplication, integer division, or
              remainder of integer-valued arguments.
 
+**   - expr1
+**          Return the result of subtracting expr1 from 0.
+**
+**   ! expr1
+**          Return the result of a logical complement of expr1.
+**          In other words, if expr1 is null, 0, an empty string,
+**          or the string "0", return a 1. Otherwise, return a "0". (only with flex >= 2.5.31)
+
      expr1 : expr2
              The `:' operator matches expr1 against expr2, which must be a
              regular expression.  The regular expression is anchored to the
@@ -216,11 +247,70 @@ with equal precedence are grouped within { } symbols.
              the pattern contains a regular expression subexpression the null
              string is returned; otherwise 0.
 
+             Normally, the double quotes wrapping a string are left as part
+             of the string. This is disastrous to the : operator. Therefore,
+             before the regex match is made, beginning and ending double quote
+             characters are stripped from both the pattern and the string.
+
+**    expr1 =~ expr2
+**             Exactly the same as the ':' operator, except that the match is
+**             not anchored to the beginning of the string. Pardon any similarity
+**             to seemingly similar operators in other programming languages!
+**             (only if flex >= 2.5.31)
+
+
+
 Parentheses are used for grouping in the usual manner.
 
-The parser must be parsed with bison (bison is REQUIRED - yacc cannot 
-produce pure parsers, which are reentrant) 
+Operator precedence is applied as one would expect in any of the C
+or C derived languages.
+
+The parser must be generated with bison (bison is REQUIRED - yacc cannot 
+produce pure parsers, which are reentrant)  The same with flex, if flex
+is at 2.5.31 or greater; Re-entrant scanners were not available before that
+version.
+
+
+
+Examples
 
+** "One Thousand Five Hundred" =~ "(T[^ ]+)"
+**	returns: Thousand
+
+** "One Thousand Five Hundred" =~ "T[^ ]+"
+**	returns: 8
+
+ "One Thousand Five Hundred" : "T[^ ]+"
+	returns: 0
+
+ "8015551212" : "(...)"
+	returns: 801
+
+ "3075551212":"...(...)"
+	returns: 555
+
+** ! "One Thousand Five Hundred" =~ "T[^ ]+"
+**	returns: 0 (because it applies to the string, which is non-null, which it turns to "0",
+                    and then looks for the pattern in the "0", and doesn't find it)
+
+** !( "One Thousand Five Hundred" : "T[^ ]+" )
+**	returns: 1  (because the string doesn't start with a word starting with T, so the
+                     match evals to 0, and the ! operator inverts it to 1 ).
+
+ 2 + 8 / 2
+	returns 6. (because of operator precedence; the division is done first, then the addition).
+
+** 2+8/2
+**	returns 6. Spaces aren't necessary.
+
+**(2+8)/2
+**	returns 5, of course.
+
+Of course, all of the above examples use constants, but would work the same if any of the
+numeric or string constants were replaced with a variable reference ${CALLERIDNUM}, for
+instance.
+
+ 
 ___________________________
 CONDITIONALS
 ---------------------------
@@ -277,6 +367,26 @@ going to be somewhere between the last '^' on the second line, and the
 '^' on the third line. That's right, in the example above, there are two
 '&' chars, separated by a space, and this is a definite no-no!
 
+** WITH FLEX >= 2.5.31, this has changed slightly. The line showing the 
+** part of the expression that was successfully parsed has been dropped,
+** and the parse error is explained in a somewhat cryptic format in the log.
+** 
+** The same line in extensions.conf as above, will now generate an error 
+** message in /var/log/asterisk/messages that looks like this:
+**
+** Jul 15 21:27:49 WARNING[1251240752]: ast_yyerror(): syntax error: parse error, unexpected TOK_AND, expecting TOK_MINUS or TOK_LP or TOKEN; Input:
+** "3072312154"  = "3071234567" & & "Steves Extension" : "Privacy Manager" 
+**                                ^
+**
+** The log line tells you that a syntax error was encountered. It now
+** also tells you (in grand standard bison format) that it hit an "AND" (&)
+** token unexpectedly, and that was hoping for for a MINUS (-), LP (left parenthesis),
+** or a plain token (a string or number).
+** 
+** As before, the next line shows the evaluated expression, and the line after
+** that, the position of the parser in the expression when it became confused,
+** marked with the "^" character.
+
 
 ___________________________
 NULL STRINGS
@@ -306,6 +416,89 @@ whatever language you desire, be it Perl, C, C++, Cobol, RPG, Java,
 Snobol, PL/I, Scheme, Common Lisp, Shell scripts, Tcl, Forth, Modula,
 Pascal, APL, assembler, etc.
 
+----------------------------
+INCOMPATIBILITIES
+----------------------------
+
+The asterisk expression parser has undergone some evolution. It is hoped
+that the changes will be viewed as positive. 
+
+The "original" expression parser had a simple, hand-written scanner, and 
+a simple bison grammar. This was upgraded to a more involved bison grammar,
+and a hand-written scanner upgraded to allow extra spaces, and to generate
+better error diagnostics. This upgrade required bison 1.85, and a [art of the user
+community felt the pain of having to upgrade their bison version.
+
+The next upgrade included new bison and flex input files, and the makefile
+was upgraded to detect current version of both flex and bison, conditionally
+compiling and linking the new files if the versions of flex and bison would
+allow it.
+
+If you have not touched your extensions.conf files in a year or so, the
+above upgrades may cause you some heartburn in certain circumstances, as
+several changes have been made, and these will affect asterisk's behavior on 
+legacy extension.conf constructs.  The changes have been engineered
+to minimize these conflicts, but there are bound to be problems.
+
+The following list gives some (and most likely, not all) of areas
+of possible concern with "legacy" extension.conf files:
+
+1. Tokens separated by space(s).
+   Previously, tokens were separated by spaces. Thus, ' 1 + 1 ' would evaluate
+  to the value '2', but '1+1' would evaluate to the string '1+1'. If this
+  behavior was depended on, then the expression evaluation will break. '1+1'
+  will now evaluate to '2', and something is not going to work right.
+  To keep such strings from being evaluated, simply wrap them in double 
+  quotes: '  "1+1" '
+
+2. The colon operator. In versions previous to double quoting, the
+   colon operator takes the right hand string, and using it as a 
+   regex pattern, looks for it in the left hand string. It is given
+   an implicit ^ operator at the beginning, meaning the pattern 
+   will match only at the beginning of the left hand string. 
+     If the pattern or the matching string had double quotes around
+   them, these could get in the way of the pattern match. Now,
+   the wrapping double quotes are stripped from both the pattern 
+   and the left hand string before applying the pattern. This
+   was done because it recognized that the new way of
+   scanning the expression doesn't use spaces to separate tokens,
+   and the average regex expression is full of operators that 
+   the scanner will recognize as expression operators. Thus, unless
+   the pattern is wrapped in double quotes, there will be trouble.
+   For instance,      ${VAR1} : (Who|What*)+
+   may have have worked before, but unless you wrap the pattern
+   in double quotes now, look out for trouble! This is better:
+         "${VAR1}" : "(Who|What*)+"
+   and should work as previous.
+
+3. Variables and Double Quotes
+   Before these changes, if a variable's value contained one or more double
+   quotes, it was no reason for concern. It is now!
+
+4. LE, GE, NE operators removed. The code supported these operators,
+   but they were not documented. The symbolic operators, <=, >=, and !=
+   should be used instead.
+
+**5. flex 2.5.31 or greater should be used. Bison-1.875 or greater. In
+**   the case of flex, earlier versions do not generate 'pure', or 
+**   reentrant C scanners. In the case of bison-1.875, earlier versions
+**   didn't support the location tracking mechanism.
+
+**    http://ftp.gnu.org/gnu/bison/bison-1.875.tar.bz2
+**    http://prdownloads.sourceforge.net/lex/flex-2.5.31.tar.bz2?download
+**	or http://lex.sourceforge.net/
+
+**6.  Added the unary '-' operator. So you can 3+ -4 and get -1.
+
+**7.  Added the unary '!' operator, which is a logical complement.
+**    Basically, if the string or number is null, empty, or '0',
+**    a '1' is returned. Otherwise a '0' is returned.
+
+**8.  Added the '=~' operator, just in case someone is just looking for
+**    match anywhere in the string. The only diff with the ':' is that
+**    match doesn't have to be anchored to the beginning of the string.
+
+
 ---------------------------------------------------------
 Asterisk standard channel variables 
 ---------------------------------------------------------
diff --git a/vercomp.sh b/vercomp.sh
new file mode 100755
index 0000000000..8ab64c7149
--- /dev/null
+++ b/vercomp.sh
@@ -0,0 +1,163 @@
+#! /bin/bash
+
+### flex just outputs a single line:
+
+## flex version 2.5.4
+
+
+### but bison is a bit more wordy
+
+## bison (GNU Bison) 1.875c
+## Written by Robert Corbett and Richard Stallman.
+## 
+## Copyright (C) 2003 Free Software Foundation, Inc.
+## This is free software; see the source for copying conditions.  There is NO
+## warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+### based on this, the version number of the program:
+###   a. in the first line of output
+###   b. is the last "word" of that line
+
+program=$1
+comparefunc=$2
+argver=$3
+
+progver1=`$program --version | head -1`
+
+[[ $progver1 =~ '([^ ]+$)'  ]]
+
+progver=$BASH_REMATCH
+
+progver2=$progver
+numprogverlist=0
+
+while [[ $progver2 =~ '^([^.]+)\.(.*)' ]]; do
+	progver2=${BASH_REMATCH[2]}
+	progverlist[$numprogverlist]=${BASH_REMATCH[1]}
+	progverlist[$(( ${numprogverlist}+1 ))]=${BASH_REMATCH[2]}
+
+##	echo ${BASH_REMATCH[0]}
+##	echo ${BASH_REMATCH[1]}
+##	echo ${BASH_REMATCH[2]}
+  	(( numprogverlist=$(( $numprogverlist+1 )) ))
+
+done
+	(( numprogverlist=$(( $numprogverlist+1 )) ))
+  	
+##	echo number of elements = $numprogverlist
+##	echo element 0 = ${progverlist[0]}
+##	echo element 1 = ${progverlist[1]}
+##	echo element 2 = ${progverlist[2]}
+
+argver2=$argver
+numargverlist=0
+
+while [[ $argver2 =~ '^([^.]+)\.(.*)' ]]; do
+	argver2=${BASH_REMATCH[2]}
+	argverlist[$numargverlist]=${BASH_REMATCH[1]}
+	argverlist[$(( ${numargverlist}+1 ))]=${BASH_REMATCH[2]}
+
+##	echo ${BASH_REMATCH[0]}
+##	echo ${BASH_REMATCH[1]}
+##	echo ${BASH_REMATCH[2]}
+  	(( numargverlist=$(( $numargverlist+1 )) ))
+
+done
+	(( numargverlist=$(( $numargverlist+1 )) ))
+  	
+##	echo number of argver elements = $numargverlist
+##	echo element 0 = ${argverlist[0]}
+##	echo element 1 = ${argverlist[1]}
+##	echo element 2 = ${argverlist[2]}
+
+if (( $numprogverlist < $numargverlist )); then
+	for (( i=$numprogverlist ; $i < $numargverlist ; i=$i + 1 )) ; do
+##		echo setting progverlist "[" $i "]" to 0
+		(( progverlist[$i]='0' ))
+		(( numprogverlist=${numprogverlist}+1 ))
+	done
+elif (( $numargverlist < $numprogverlist )); then
+	for (( i=$numargverlist ; $i < $numprogverlist ; i=$i + 1 )) ; do
+##		echo setting argverlist "[" $i "]" to 0
+		(( argverlist[$i]='0' ))
+		(( numargverlist=${numargverlist}+1 ))
+	done
+fi
+
+## echo numarg=$numargverlist   numprog=$numprogverlist
+## echo arg0: ${argverlist[0]}
+## echo arg1: ${argverlist[1]}
+## echo arg2: ${argverlist[2]}
+## echo prog0: ${progverlist[0]}
+## echo prog1: ${progverlist[1]}
+## echo prog2: ${progverlist[2]}
+
+## the main comparison loop 
+
+for (( i=0 ; $i < $numargverlist ; i=$i + 1 )) ; do
+##	echo i= $i
+
+	if [[ ${progverlist[$i]} =~ '^[0-9]+$' &&  ${argverlist[$i]} =~ '^[0-9]+$' ]] ; then  ## nothing but numbers
+		if (( ${progverlist[$i]} != ${argverlist[$i]} )); then
+			if [[ ${progverlist[$i]}  -lt ${argverlist[$i]} ]]; then
+				if [[ $comparefunc == "=" ]]; then
+					echo "false"
+					exit 0;
+				elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
+					echo "true"
+					exit 0;
+				elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
+					echo "false"
+					exit 0;
+				fi
+			elif [[ ${progverlist[$i]} -gt ${argverlist[$i]} ]]; then
+				if [[ $comparefunc == "=" ]]; then
+					echo "false"
+					exit 0;
+				elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
+					echo "false"
+					exit 0;
+				elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
+					echo "true"
+					exit 0;
+				fi
+			fi
+		fi
+	else  ## something besides just numbers
+		if [[ ${progverlist[$i]} != ${argverlist[$i]} ]]; then
+			if [[ ${progverlist[$i]} < ${argverlist[$i]} ]]; then
+				if [[ $comparefunc == "=" ]]; then
+					echo "false"
+					exit 0;
+				elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
+					echo "true"
+					exit 0;
+				elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
+					echo "false"
+					exit 0;
+				fi
+			elif [[ ${progverlist[$i]} > ${argverlist[$i]} ]]; then
+				if [[ $comparefunc == "=" ]]; then
+					echo "false"
+					exit 0;
+				elif [[ $comparefunc == "<" || $comparefunc == "<=" ]]; then
+					echo "false"
+					exit 0;
+				elif [[ $comparefunc == ">" || $comparefunc == ">=" ]]; then
+					echo "true"
+					exit 0;
+				fi
+			fi
+		fi
+	fi
+done
+
+if [[ $comparefunc == "=" ]]; then
+	echo "true"
+elif [[ $comparefunc == "<=" || $comparefunc == ">=" ]]; then
+	echo "true"
+else
+	echo "false"
+fi
+
+exit 0;
-- 
GitLab