aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cvsignore1
-rw-r--r--Makefile.am50
-rw-r--r--configure.in36
-rw-r--r--dftest.c150
-rw-r--r--epan/Makefile.am17
-rw-r--r--epan/configure.in8
-rw-r--r--epan/dfilter-grammar.y1191
-rw-r--r--epan/dfilter-int.h148
-rw-r--r--epan/dfilter-scanner.l366
-rw-r--r--epan/dfilter.c1086
-rw-r--r--epan/dfilter.h70
-rw-r--r--epan/dfilter/.cvsignore8
-rw-r--r--epan/dfilter/Makefile.am81
-rw-r--r--epan/dfilter/cppmagic.h14
-rw-r--r--epan/dfilter/dfilter-int.h52
-rw-r--r--epan/dfilter/dfilter.c352
-rw-r--r--epan/dfilter/dfilter.h65
-rw-r--r--epan/dfilter/dfvm.c395
-rw-r--r--epan/dfilter/dfvm.h77
-rw-r--r--epan/dfilter/gencode.c299
-rw-r--r--epan/dfilter/gencode.h7
-rw-r--r--epan/dfilter/glib-util.c47
-rw-r--r--epan/dfilter/glib-util.h4
-rw-r--r--epan/dfilter/grammar.lemon185
-rw-r--r--epan/dfilter/scanner.l157
-rw-r--r--epan/dfilter/semcheck.c472
-rw-r--r--epan/dfilter/semcheck.h10
-rw-r--r--epan/dfilter/sttype-pointer.c28
-rw-r--r--epan/dfilter/sttype-range.c174
-rw-r--r--epan/dfilter/sttype-range.h20
-rw-r--r--epan/dfilter/sttype-string.c29
-rw-r--r--epan/dfilter/sttype-test.c144
-rw-r--r--epan/dfilter/sttype-test.h30
-rw-r--r--epan/dfilter/syntax-tree.c169
-rw-r--r--epan/dfilter/syntax-tree.h95
-rw-r--r--epan/epan.c11
-rw-r--r--epan/epan.h7
-rw-r--r--epan/exceptions.h1
-rw-r--r--epan/ftypes/.cvsignore4
-rw-r--r--epan/ftypes/Makefile.am52
-rw-r--r--epan/ftypes/ftype-bytes.c386
-rw-r--r--epan/ftypes/ftype-double.c133
-rw-r--r--epan/ftypes/ftype-integer.c417
-rw-r--r--epan/ftypes/ftype-ipv4.c114
-rw-r--r--epan/ftypes/ftype-none.c20
-rw-r--r--epan/ftypes/ftype-string.c198
-rw-r--r--epan/ftypes/ftype-time.c78
-rw-r--r--epan/ftypes/ftype-tvbuff.c84
-rw-r--r--epan/ftypes/ftypes-int.h60
-rw-r--r--epan/ftypes/ftypes.c410
-rw-r--r--epan/ftypes/ftypes.h197
-rw-r--r--epan/gdebug.h38
-rw-r--r--epan/ipv4.h4
-rw-r--r--epan/proto.c532
-rw-r--r--epan/proto.h76
-rw-r--r--file.c29
-rw-r--r--file.h13
-rw-r--r--gtk/color_dlg.c10
-rw-r--r--gtk/colors.c14
-rw-r--r--gtk/colors.h6
-rw-r--r--gtk/dfilter_expr_dlg.c94
-rw-r--r--gtk/file_dlg.c8
-rw-r--r--gtk/find_dlg.c8
-rw-r--r--gtk/help_dlg.c4
-rw-r--r--gtk/main.c190
-rw-r--r--gtk/packet_win.c3
-rw-r--r--packet-x11.c22
-rw-r--r--tethereal.c10
-rw-r--r--tools/.cvsignore3
-rw-r--r--tools/Makefile.am1
-rw-r--r--tools/lemon/.cvsignore8
-rw-r--r--tools/lemon/Makefile.am44
-rw-r--r--tools/lemon/README15
-rw-r--r--tools/lemon/lemon.c4116
-rw-r--r--tools/lemon/lemon.html861
-rw-r--r--tools/lemon/lemonflex-head.inc34
-rw-r--r--tools/lemon/lemonflex-tail.inc59
-rw-r--r--tools/lemon/lempar.c598
78 files changed, 11523 insertions, 3486 deletions
diff --git a/.cvsignore b/.cvsignore
index e1e8406fcc..e51bac3845 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -14,6 +14,7 @@ configure
dfilter-grammar.c
dfilter-grammar.h
dfilter-scanner.c
+dftest
editcap
editcap.1
ethereal
diff --git a/Makefile.am b/Makefile.am
index 0446014462..0b01838d98 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
# Makefile.am
# Automake file for Ethereal
#
-# $Id: Makefile.am,v 1.280 2001/01/28 04:43:24 guy Exp $
+# $Id: Makefile.am,v 1.281 2001/02/01 20:21:13 gram Exp $
#
# Ethereal - Network traffic analyzer
# By Gerald Combs <gerald@zing.org>
@@ -61,11 +61,11 @@ ACLOCAL_AMFLAGS = `./aclocal-flags`
# automake will arrange that the Makefile define it as the union of all
# the "man{section}_MANS" variables.
#
-bin_PROGRAMS = @ethereal_bin@ @editcap_bin@ @tethereal_bin@
+bin_PROGRAMS = @ethereal_bin@ @editcap_bin@ @tethereal_bin@ @dftest_bin@ @randpkt_bin@
man1_MANS = @ethereal_man@ @editcap_man@ @tethereal_man@
man_MANS =
-EXTRA_PROGRAMS = ethereal ethereal_static tethereal tethereal_static editcap
+EXTRA_PROGRAMS = ethereal ethereal_static tethereal tethereal_static editcap dftest
sysconf_DATA = manuf
@@ -388,7 +388,12 @@ ethereal_optional_objects = @SNPRINTF_O@ @STRERROR_O@ \
# Additional libs that I know how to build. These will be
# linked into the ethereal executable.
-ethereal_additional_libs = wiretap/libwiretap.a gtk/libui.a epan/libethereal.a
+ethereal_additional_libs = \
+ wiretap/libwiretap.a \
+ gtk/libui.a \
+ epan/libethereal.a \
+ epan/ftypes/libftypes.a \
+ epan/dfilter/libdfilter.a
# This is the automake dependency variable for the executable
ethereal_DEPENDENCIES = \
@@ -435,7 +440,11 @@ tethereal_static_SOURCES = \
# Additional libs that I know how to build. These will be
# linked into the tethereal executable.
-tethereal_additional_libs = wiretap/libwiretap.a epan/libethereal.a
+tethereal_additional_libs = \
+ wiretap/libwiretap.a \
+ epan/libethereal.a \
+ epan/ftypes/libftypes.a \
+ epan/dfilter/libdfilter.a
# This is the automake dependency variable for the executable
tethereal_DEPENDENCIES = \
@@ -516,6 +525,35 @@ randpkt.o: randpkt.c
randpkt: randpkt.o wiretap/libwiretap.a
$(LINK) -o randpkt randpkt.o wiretap/libwiretap.a `glib-config --libs` -lz
+
+dftest_SOURCES = \
+ $(DISSECTOR_SOURCES) \
+ $(ETHEREAL_COMMON_SOURCES) \
+ dftest.c
+
+dftest_additional_libs = \
+ epan/libethereal.a \
+ epan/ftypes/libftypes.a \
+ epan/dfilter/libdfilter.a
+
+dftest_DEPENDENCIES = \
+ $(ethereal_optional_objects) \
+ $(dftest_additional_libs) \
+ plugins/gryphon/gryphon.la \
+ plugins/mgcp/mgcp.la
+
+# This automake variable adds to the link-line for the executable
+dftest_LDADD = \
+ $(ethereal_optional_objects) \
+ $(dftest_additional_libs) \
+ @SNMP_LIBS@ \
+ "-dlopen" self \
+ "-dlopen" plugins/gryphon/gryphon.la @GLIB_LIBS@ -lm \
+ "-dlopen" plugins/mgcp/mgcp.la @GLIB_LIBS@ -lm \
+ @PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@
+
+dftest_LDFLAGS = -export-dynamic
+
DISTCLEANFILES = \
register.c \
rdps \
@@ -608,7 +646,7 @@ endif
#dist-hook:
# @rm -f $(distdir)/register.c
-SUBDIRS = wiretap epan plugins packaging @ethereal_SUBDIRS@
+SUBDIRS = tools wiretap epan plugins packaging @ethereal_SUBDIRS@
ethereal.1: ethereal doc/ethereal.pod.template
(cd doc ; \
diff --git a/configure.in b/configure.in
index 64a5f4ad4d..e07cbb3028 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-# $Id: configure.in,v 1.112 2001/01/18 09:54:09 guy Exp $
+# $Id: configure.in,v 1.113 2001/02/01 20:21:13 gram Exp $
dnl
dnl Process this file with autoconf 2.13 or later to produce a
dnl configure script; 2.12 doesn't generate a "configure" script that
@@ -237,6 +237,31 @@ AC_SUBST(editcap_bin)
AC_SUBST(editcap_man)
+# Enable/disable dftest
+
+AC_ARG_ENABLE(dftest,
+[ --enable-dftest build dftest. [default=no]],,enable_dftest=no)
+
+if test "x$enable_dftest" = "xyes" ; then
+ dftest_bin="dftest"
+else
+ dftest_bin=""
+fi
+AC_SUBST(dftest_bin)
+
+
+# Enable/disable randpkt
+
+AC_ARG_ENABLE(randpkt,
+[ --enable-randpkt build randpkt. [default=no]],,enable_randpkt=no)
+
+if test "x$enable_randpkt" = "xyes" ; then
+ randpkt_bin="randpkt"
+else
+ randpkt_bin=""
+fi
+AC_SUBST(randpkt_bin)
+
dnl Checks for "gethostbyname()" - and "-lnsl", if we need it to get
dnl "gethostbyname()".
@@ -478,7 +503,6 @@ AC_CONFIG_SUBDIRS(epan wiretap)
AC_OUTPUT(
Makefile
doc/Makefile
- epan/Makefile
gtk/Makefile
packaging/Makefile
packaging/rpm/Makefile
@@ -488,7 +512,10 @@ AC_OUTPUT(
packaging/svr4/pkginfo
plugins/Makefile
plugins/gryphon/Makefile
- plugins/mgcp/Makefile)
+ plugins/mgcp/Makefile
+ tools/Makefile
+ tools/lemon/Makefile
+ ,)
# Pretty messages
@@ -512,6 +539,9 @@ echo "The Ethereal package has been configured with the following options."
echo " Build ethereal : $enable_ethereal"
echo " Build tethereal : $enable_tethereal"
echo " Build editcap : $enable_editcap"
+echo " Build randpkt : $enable_randpkt"
+echo " Build dftest : $enable_dftest"
+echo ""
echo " Install setuid : $setuid_message"
echo " Use pcap library : $enable_pcap"
echo " Use zlib library : $enable_zlib"
diff --git a/dftest.c b/dftest.c
new file mode 100644
index 0000000000..95d85f59ef
--- /dev/null
+++ b/dftest.c
@@ -0,0 +1,150 @@
+/* dftest.c.c
+ *
+ * $Id: dftest.c,v 1.1 2001/02/01 20:21:13 gram Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Shows display filter byte-code, for debugging dfilter routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <locale.h>
+#include <string.h>
+#include <errno.h>
+
+#if 0
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <signal.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+#endif
+
+#ifdef NEED_STRERROR_H
+#include "strerror.h"
+#endif
+
+#include <glib.h>
+#include <epan.h>
+
+#if 0
+#include "globals.h"
+#include "packet.h"
+#include "file.h"
+#include "column.h"
+#include "print.h"
+#include "resolv.h"
+#include "conversation.h"
+#endif
+#include "timestamp.h"
+#include "plugins.h"
+#include "prefs.h"
+#include "util.h"
+#include "epan/dfilter/dfilter.h"
+
+packet_info pi;
+ts_type timestamp_type = RELATIVE;
+
+int
+main(int argc, char **argv)
+{
+ char *text;
+ char *gpf_path, *pf_path;
+ int gpf_open_errno, pf_open_errno;
+ e_prefs *prefs;
+ dfilter_t *df;
+
+ /* register all dissectors; we must do this before checking for the
+ "-g" flag, as the "-g" flag dumps a list of fields registered
+ by the dissectors, and we must do it before we read the preferences,
+ in case any dissectors register preferences. */
+ epan_init(PLUGIN_DIR);
+
+ /* now register the preferences for any non-dissector modules.
+ we must do that before we read the preferences as well. */
+ prefs_register_modules();
+
+ /* set the c-language locale to the native environment. */
+ setlocale(LC_ALL, "");
+
+ prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
+ if (gpf_path != NULL) {
+ fprintf(stderr, "can't open global preferences file \"%s\": %s.\n",
+ pf_path, strerror(gpf_open_errno));
+ }
+ if (pf_path != NULL) {
+ fprintf(stderr, "can't open your preferences file \"%s\": %s.\n",
+ pf_path, strerror(pf_open_errno));
+ }
+
+ /* notify all registered modules that have had any of their preferences
+ changed either from one of the preferences file or from the command
+ line that its preferences have changed. */
+ prefs_apply_all();
+
+ /* Check for filter on command line */
+ if (argc <= 1) {
+ fprintf(stderr, "Usage: dftest filter\n");
+ exit(1);
+ }
+
+ /* Get filter text */
+ text = get_args_as_string(argc, argv, 1);
+
+ printf("Filter: \"%s\"\n", text);
+
+ /* Compile it */
+ if (!dfilter_compile(text, &df)) {
+ fprintf(stderr, "dftest: %s\n", dfilter_error_msg);
+ epan_cleanup();
+ exit(2);
+ }
+ printf("dfilter ptr = 0x%08x\n", (unsigned int) df);
+
+ printf("\n\n");
+
+ dfilter_dump(df);
+
+ epan_cleanup();
+ exit(0);
+}
diff --git a/epan/Makefile.am b/epan/Makefile.am
index b7b716ac91..e3448e772d 100644
--- a/epan/Makefile.am
+++ b/epan/Makefile.am
@@ -2,7 +2,7 @@
# Automake file for the EPAN library
# (Ethereal Protocol ANalyzer Library)
#
-# $Id: Makefile.am,v 1.15 2001/01/17 06:13:39 guy Exp $
+# $Id: Makefile.am,v 1.16 2001/02/01 20:21:15 gram Exp $
#
# Ethereal - Network traffic analyzer
# By Gerald Combs <gerald@zing.org>
@@ -23,9 +23,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-# Any POSIX-compatible YACC should honor the -p flag
-YFLAGS=-d -p dfilter_
+SUBDIRS = ftypes dfilter
# EPAN will eventually be a shared library. While I move source code around,
# however, it is an archive library.
@@ -41,11 +39,6 @@ libethereal_a_SOURCES = \
bitswap.h \
conversation.c \
conversation.h \
- dfilter-int.h \
- dfilter-grammar.y \
- dfilter-scanner.l \
- dfilter.c \
- dfilter.h \
epan.c \
epan.h \
except.c \
@@ -53,6 +46,7 @@ libethereal_a_SOURCES = \
exceptions.h \
filesystem.c \
filesystem.h \
+ gdebug.h \
ipv4.c \
ipv4.h \
packet.c \
@@ -78,9 +72,6 @@ EXTRA_libethereal_a_SOURCES = \
EXTRA_DIST = \
config.h.win32 \
- dfilter-grammar.c \
- dfilter-grammar.h \
- dfilter-scanner.c \
Makefile.nmake \
tvbtest.c
@@ -94,8 +85,6 @@ CLEANFILES = \
libethereal_a_LIBADD = @INET_ATON_O@ @INET_PTON_O@ @INET_NTOP_O@
libethereal_a_DEPENDENCIES = @INET_ATON_O@ @INET_PTON_O@ @INET_NTOP_O@
-dfilter-scanner.c : dfilter-scanner.l
- $(LEX) -Pdfilter_ -odfilter-scanner.c $(srcdir)/dfilter-scanner.l
tvbtest: tvbtest.o tvbuff.o except.o strutil.o
$(LINK) -o tvbtest tvbtest.o tvbuff.o except.o strutil.o `glib-config --libs`
diff --git a/epan/configure.in b/epan/configure.in
index b16734acce..51f84b489b 100644
--- a/epan/configure.in
+++ b/epan/configure.in
@@ -1,4 +1,4 @@
-# $Id: configure.in,v 1.3 2001/01/12 04:18:49 gram Exp $
+# $Id: configure.in,v 1.4 2001/02/01 20:21:15 gram Exp $
dnl
dnl Process this file with autoconf 2.13 or later to produce a
dnl configure script; 2.12 doesn't generate a "configure" script that
@@ -143,4 +143,8 @@ fi
AC_SUBST(INET_NTOP_C)
AC_SUBST(INET_NTOP_O)
-AC_OUTPUT(Makefile)
+AC_OUTPUT(
+ Makefile
+ dfilter/Makefile
+ ftypes/Makefile
+)
diff --git a/epan/dfilter-grammar.y b/epan/dfilter-grammar.y
deleted file mode 100644
index 757798c8ec..0000000000
--- a/epan/dfilter-grammar.y
+++ /dev/null
@@ -1,1191 +0,0 @@
-%{
-/* dfilter-grammar.y
- * Parser for display filters
- *
- * $Id: dfilter-grammar.y,v 1.2 2000/12/22 12:05:36 gram Exp $
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Gerald Combs
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
-#ifdef NEED_SNPRINTF_H
-# ifdef HAVE_STDARG_H
-# include <stdarg.h>
-# else
-# include <varargs.h>
-# endif
-# include "snprintf.h"
-#endif
-
-#ifndef __GLIB_H__
-#include <glib.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-#include <math.h>
-
-#ifndef _STDLIB_H
-#include <stdlib.h>
-#endif
-
-#ifndef __PROTO_H__
-#include "proto.h"
-#endif
-
-#ifndef __PACKET_H__
-#include "packet.h"
-#endif
-
-#ifndef __DFILTER_H__
-#include "dfilter.h"
-#endif
-
-#include "dfilter-int.h"
-
-#ifndef __RESOLV_H__
-#include "resolv.h"
-#endif
-
-static GNode* dfilter_mknode_join(GNode *n1, enum node_type ntype, int operand, GNode *n2);
-static GNode* dfilter_mknode_unary(int operand, GNode *n2);
-static GNode* dfilter_mknode_numeric_variable(gint id);
-static GNode* dfilter_mknode_numeric_value(guint32 val);
-static GNode* dfilter_mknode_floating_variable(gint id);
-static GNode* dfilter_mknode_floating_value(double val);
-static GNode* dfilter_mknode_ether_value(gchar*);
-static GNode* dfilter_mknode_ether_variable(gint id);
-static GNode* dfilter_mknode_ipxnet_value(guint32);
-static GNode* dfilter_mknode_ipxnet_variable(gint id);
-static GNode* dfilter_mknode_ipv4_value(char *host, int nmask_bits);
-static GNode* dfilter_mknode_ipv4_variable(gint id);
-static GNode* dfilter_mknode_ipv6_value(char *host);
-static GNode* dfilter_mknode_ipv6_variable(gint id);
-static GNode* dfilter_mknode_existence(gint id);
-static GNode* dfilter_mknode_bytes_value(GByteArray *barray);
-static GNode* dfilter_mknode_bytes_variable(gint id, gint offset, guint length, gboolean to_the_end);
-static GNode* dfilter_mknode_string_value(char *s);
-static GNode* dfilter_mknode_string_variable(gint id);
-
-static guint32 string_to_guint32(char *s, gboolean *success);
-static double string_to_double(char *s, gboolean *success);
-static int ether_str_to_guint8_array(const char *s, guint8 *mac);
-static guint dfilter_get_bytes_variable_offset(GNode *gnode);
-static guint dfilter_get_bytes_value_length(GNode* gnode);
-static void dfilter_set_bytes_variable_length(GNode *gnode, guint length);
-static guint dfilter_get_bytes_variable_length(GNode *gnode);
-static gint dfilter_get_bytes_variable_field_registered_length(GNode *gnode);
-static char* dfilter_get_variable_abbrev(GNode *gnode);
-static int check_bytes_variable_sanity(GNode *gnode);
-
-/* This is the dfilter we're currently processing. It's how
- * dfilter_compile communicates with us.
- */
-dfilter *global_df = NULL;
-
-%}
-
-%union {
- gint operand; /* logical, relation, alternation */
- struct {
- gint id;
- gint type; /* using macros defined below, in this yacc grammar */
- } variable;
- GNode* node;
- gchar* string;
- struct {
- gint offset;
- guint length;
- } byte_range;
-}
-
-%type <node> statement expression relation
-%type <node> numeric_value numeric_variable
-%type <node> floating_value floating_variable
-%type <node> ether_value ether_variable
-%type <node> ipxnet_value ipxnet_variable
-%type <node> ipv4_value ipv4_variable
-%type <node> ipv6_value ipv6_variable
-%type <node> string_value string_variable
-%type <node> variable_name
-%type <node> bytes_value bytes_variable
-
-%type <operand> numeric_relation
-%type <operand> equality_relation
-%type <operand> bytes_relation
-
-%type <variable> any_variable_type
-
-%token <variable> T_FT_UINT8
-%token <variable> T_FT_UINT16
-%token <variable> T_FT_UINT24
-%token <variable> T_FT_UINT32
-%token <variable> T_FT_INT8
-%token <variable> T_FT_INT16
-%token <variable> T_FT_INT24
-%token <variable> T_FT_INT32
-%token <variable> T_FT_ETHER
-%token <variable> T_FT_IPv4
-%token <variable> T_FT_IPv6
-%token <variable> T_FT_NONE
-%token <variable> T_FT_BYTES
-%token <variable> T_FT_BOOLEAN
-%token <variable> T_FT_STRING
-%token <variable> T_FT_IPXNET
-%token <variable> T_FT_DOUBLE
-
-%token <string> T_VAL_UNQUOTED_STRING
-%token <string> T_VAL_QUOTED_STRING
-%token <string> T_VAL_BYTE_STRING
-%token <byte_range> T_VAL_BYTE_RANGE
-
-%token <operand> TOK_AND TOK_OR TOK_NOT TOK_XOR
-%token <operand> TOK_EQ TOK_NE TOK_GT TOK_GE TOK_LT TOK_LE
-
-%expect 4
-%left TOK_AND
-%left TOK_OR TOK_XOR
-%nonassoc TOK_NOT
-
-%%
-
-statement: expression
- {
- global_df->dftree = $1;
- }
- | /* NULL */ { if (global_df != NULL) global_df->dftree = NULL; }
- ;
-
-expression: '(' expression ')' { $$ = $2; }
- | expression TOK_AND expression { $$ = dfilter_mknode_join($1, logical, $2, $3); }
- | expression TOK_OR expression { $$ = dfilter_mknode_join($1, logical, $2, $3); }
- | expression TOK_XOR expression { $$ = dfilter_mknode_join($1, logical, $2, $3); }
- | TOK_NOT expression { $$ = dfilter_mknode_unary(TOK_NOT, $2); }
- | relation { $$ = $1; }
- | variable_name { $$ = $1; }
- | expression error { YYABORT; }
- ;
-
-relation: numeric_variable numeric_relation numeric_value
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | numeric_variable numeric_relation numeric_variable
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
- | floating_variable numeric_relation floating_value
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | floating_variable numeric_relation floating_variable
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
- | ether_variable equality_relation ether_value
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | ether_variable equality_relation ether_variable
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
- | ipxnet_variable equality_relation ipxnet_value
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | ipxnet_variable equality_relation ipxnet_variable
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
- | string_variable equality_relation string_value
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | string_variable equality_relation string_variable
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
-
- | ipv4_variable numeric_relation ipv4_value
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | ipv4_variable numeric_relation ipv4_variable
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
- | ipv6_variable equality_relation ipv6_value
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | ipv6_variable equality_relation ipv6_variable
- {
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
- | bytes_variable bytes_relation bytes_value
- {
- int a_len, b_len;
-
- a_len = dfilter_get_bytes_variable_length($1);
- b_len = dfilter_get_bytes_value_length($3);
-
- if (a_len == 0) {
- dfilter_set_bytes_variable_length($1, b_len);
- a_len = b_len;
- }
-
- if (!check_bytes_variable_sanity($1)) {
- YYERROR;
- }
-
- if (a_len != b_len) {
- dfilter_fail("Field \"%s\" has %u byte%s being compared, but %u byte%s "
- "%s supplied.",
- dfilter_get_variable_abbrev($1),
- a_len, plurality(a_len, "", "s"),
- b_len, plurality(b_len, "", "s"),
- plurality(b_len, "was", "were"));
- YYERROR;
- }
-
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
- | bytes_variable bytes_relation bytes_variable
- {
- int a_len, b_len;
-
- a_len = dfilter_get_bytes_variable_length($1);
- b_len = dfilter_get_bytes_variable_length($3);
-
- if (!check_bytes_variable_sanity($1)) {
- YYERROR;
- }
-
- if (!check_bytes_variable_sanity($3)) {
- YYERROR;
- }
-
- if (a_len != b_len) {
- dfilter_fail("Fields \"%s\" and \"%s\" are being compared with "
- "disparate lengths of %u byte%s and %u byte%s.",
- dfilter_get_variable_abbrev($1),
- dfilter_get_variable_abbrev($3),
- a_len, plurality(a_len, "", "s"),
- b_len, plurality(b_len, "", "s"));
- YYERROR;
- }
-
- $$ = dfilter_mknode_join($1, relation, $2, $3);
- }
-
- ;
-
-
-numeric_value: T_VAL_UNQUOTED_STRING
- {
- gboolean success;
- $$ = dfilter_mknode_numeric_value(string_to_guint32($1, &success));
- g_free($1);
- if (!success) {
- YYERROR;
- }
- }
- ;
-
-ether_value: T_VAL_BYTE_STRING
- {
- $$ = dfilter_mknode_ether_value($1);
- g_free($1);
- if ($$ == NULL) {
- YYERROR;
- }
- }
- ;
-
-string_value: T_VAL_UNQUOTED_STRING
- {
- $$ = dfilter_mknode_string_value($1);
- g_free($1);
- if ($$ == NULL) {
- YYERROR;
- }
- }
- | T_VAL_QUOTED_STRING
- {
- $$ = dfilter_mknode_string_value($1);
- g_free($1);
- if ($$ == NULL) {
- YYERROR;
- }
- }
- ;
-
-ipxnet_value: T_VAL_UNQUOTED_STRING
- {
- gboolean success;
- $$ = dfilter_mknode_ipxnet_value(string_to_guint32($1, &success));
- g_free($1);
- if (!success) {
- YYERROR;
- }
- }
- ;
-
-floating_value: T_VAL_UNQUOTED_STRING
- {
- gboolean success;
- $$ = dfilter_mknode_floating_value(string_to_double($1, &success));
- g_free($1);
- if (!success) {
- YYERROR;
- }
- }
-
- | T_VAL_BYTE_STRING
- {
- /* e.g., 0.0, 0.1, 0.01 ... */
- gboolean success;
- $$ = dfilter_mknode_floating_value(string_to_double($1, &success));
- g_free($1);
- if (!success) {
- YYERROR;
- }
- }
- ;
-
-ipv4_value: T_VAL_UNQUOTED_STRING
- {
- $$ = dfilter_mknode_ipv4_value($1, 32);
- g_free($1);
- if ($$ == NULL) {
- YYERROR;
- }
- }
-
- | T_VAL_BYTE_STRING
- {
- $$ = dfilter_mknode_ipv4_value($1, 32);
- g_free($1);
- if ($$ == NULL) {
- YYERROR;
- }
- }
-
- | T_VAL_UNQUOTED_STRING '/' T_VAL_UNQUOTED_STRING
- {
- gboolean success;
- guint32 nmask_bits;
-
- nmask_bits = string_to_guint32($3, &success);
- if (!success) {
- g_free($1);
- g_free($3);
- YYERROR;
- }
-
- if (nmask_bits > 32) {
- dfilter_fail("The number of netmask bits in \"%s/%s\" should "
- "be between 0 and 32.", $1, $3);
- g_free($1);
- g_free($3);
- YYERROR;
- }
-
- $$ = dfilter_mknode_ipv4_value($1, nmask_bits);
- g_free($1);
- g_free($3);
- if ($$ == NULL) {
- YYERROR;
- }
- }
-
- | T_VAL_BYTE_STRING '/' T_VAL_UNQUOTED_STRING
- {
- gboolean success;
- guint32 nmask_bits;
-
- nmask_bits = string_to_guint32($3, &success);
- if (!success) {
- g_free($1);
- g_free($3);
- YYERROR;
- }
-
- if (nmask_bits > 32) {
- dfilter_fail("The number of netmask bits in \"%s/%s\" should "
- "be between 0 and 32.", $1, $3);
- g_free($1);
- g_free($3);
- YYERROR;
- }
- $$ = dfilter_mknode_ipv4_value($1, nmask_bits);
- g_free($1);
- g_free($3);
- if ($$ == NULL) {
- YYERROR;
- }
- }
- ;
-
-ipv6_value: T_VAL_UNQUOTED_STRING
- {
- $$ = dfilter_mknode_ipv6_value($1);
- g_free($1);
- if ($$ == NULL) {
- YYERROR;
- }
- }
-
- | T_VAL_BYTE_STRING
- {
- $$ = dfilter_mknode_ipv6_value($1);
- g_free($1);
- if ($$ == NULL) {
- YYERROR;
- }
- }
- ;
-
-bytes_value: T_VAL_BYTE_STRING
- {
- GByteArray *barray;
-
- /* the next function appends to list_of_byte_arrays for me */
- barray = byte_str_to_guint8_array($1);
- $$ = dfilter_mknode_bytes_value(barray);
- g_free($1);
- }
-
- | T_VAL_UNQUOTED_STRING
- {
- gboolean success;
- guint32 val32;
- guint8 val8;
- GByteArray *barray;
-
- val32 = string_to_guint32($1, &success);
- if (!success) {
- g_free($1);
- YYERROR;
- }
- if (val32 > 0xff) {
- dfilter_fail("The value \"%s\" cannot be stored in a single-byte byte-string. "
- "Use the multi-byte \"xx:yy\" representation.", $1);
- g_free($1);
- YYERROR;
- }
- val8 = (guint8) val32;
- barray = g_byte_array_new();
- global_df->list_of_byte_arrays = g_slist_append(global_df->list_of_byte_arrays, barray);
- g_byte_array_append(barray, &val8, 1);
-
- $$ = dfilter_mknode_bytes_value(barray);
- g_free($1);
- }
- ;
-
-numeric_variable: T_FT_UINT8 { $$ = dfilter_mknode_numeric_variable($1.id); }
- | T_FT_UINT16 { $$ = dfilter_mknode_numeric_variable($1.id); }
- | T_FT_UINT24 { $$ = dfilter_mknode_numeric_variable($1.id); }
- | T_FT_UINT32 { $$ = dfilter_mknode_numeric_variable($1.id); }
- | T_FT_INT8 { $$ = dfilter_mknode_numeric_variable($1.id); }
- | T_FT_INT16 { $$ = dfilter_mknode_numeric_variable($1.id); }
- | T_FT_INT24 { $$ = dfilter_mknode_numeric_variable($1.id); }
- | T_FT_INT32 { $$ = dfilter_mknode_numeric_variable($1.id); }
- ;
-
-ether_variable: T_FT_ETHER { $$ = dfilter_mknode_ether_variable($1.id); }
- ;
-
-floating_variable: T_FT_DOUBLE { $$ = dfilter_mknode_floating_variable($1.id); }
- ;
-
-ipxnet_variable: T_FT_IPXNET { $$ = dfilter_mknode_ipxnet_variable($1.id); }
- ;
-
-ipv4_variable: T_FT_IPv4 { $$ = dfilter_mknode_ipv4_variable($1.id); }
- ;
-
-ipv6_variable: T_FT_IPv6 { $$ = dfilter_mknode_ipv6_variable($1.id); }
- ;
-
-string_variable: T_FT_STRING { $$ = dfilter_mknode_string_variable($1.id); }
- ;
-
-bytes_variable: T_FT_BYTES T_VAL_BYTE_RANGE
- {
- $$ = dfilter_mknode_bytes_variable($1.id, $2.offset, $2.length, FALSE);
- }
-
- | T_FT_BYTES
- {
- $$ = dfilter_mknode_bytes_variable($1.id, 0, 0, TRUE);
- }
-
- ;
-
-variable_name: any_variable_type
- {
- GNode *variable;
- GNode *value;
- if ($1.type == T_FT_BOOLEAN) {
- /* Make "variable == TRUE" for BOOLEAN variable */
- variable = dfilter_mknode_numeric_variable($1.id);
- value = dfilter_mknode_numeric_value(TRUE);
- $$ = dfilter_mknode_join(variable, relation, TOK_EQ, value);
- }
- else {
- $$ = dfilter_mknode_existence($1.id);
- }
- }
- ;
-
-any_variable_type: T_FT_UINT8 { $$ = $1; }
- | T_FT_UINT16 { $$ = $1; }
- | T_FT_UINT24 { $$ = $1; }
- | T_FT_UINT32 { $$ = $1; }
- | T_FT_INT8 { $$ = $1; }
- | T_FT_INT16 { $$ = $1; }
- | T_FT_INT24 { $$ = $1; }
- | T_FT_INT32 { $$ = $1; }
- | T_FT_DOUBLE { $$ = $1; }
- | T_FT_ETHER { $$ = $1; }
- | T_FT_IPv4 { $$ = $1; }
- | T_FT_IPv6 { $$ = $1; }
- | T_FT_IPXNET { $$ = $1; }
- | T_FT_NONE { $$ = $1; }
- | T_FT_BYTES { $$ = $1; }
- | T_FT_BOOLEAN { $$ = $1; }
- | T_FT_STRING { $$ = $1; }
- ;
-
-numeric_relation: TOK_EQ { $$ = TOK_EQ; }
- | TOK_NE { $$ = TOK_NE; }
- | TOK_GT { $$ = TOK_GT; }
- | TOK_GE { $$ = TOK_GE; }
- | TOK_LT { $$ = TOK_LT; }
- | TOK_LE { $$ = TOK_LE; }
- ;
-
-equality_relation: TOK_EQ { $$ = TOK_EQ; }
- | TOK_NE { $$ = TOK_NE; }
- ;
-
-bytes_relation: TOK_EQ { $$ = TOK_EQ; }
- | TOK_NE { $$ = TOK_NE; }
- | TOK_GT { $$ = TOK_GT; }
- | TOK_LT { $$ = TOK_LT; }
- ;
-
-%%
-
-static GNode*
-dfilter_mknode_join(GNode *n1, enum node_type ntype, int operand, GNode *n2)
-{
- dfilter_node *node_root;
- GNode *gnode_root;
-
- node_root = g_mem_chunk_alloc(global_df->node_memchunk);
- node_root->ntype = ntype;
- node_root->elem_size = 0;
- node_root->fill_array_variable_func = NULL;
- node_root->fill_array_value_func = NULL;
- node_root->check_relation_func = NULL;
- if (ntype == relation) {
- node_root->value.relation = operand;
- }
- else if (ntype == logical) {
- node_root->value.logical = operand;
- }
- else {
- g_assert_not_reached();
- }
-
- gnode_root = g_node_new(node_root);
- g_node_append(gnode_root, n1);
- g_node_append(gnode_root, n2);
-
- return gnode_root;
-}
-
-static GNode*
-dfilter_mknode_unary(int operand, GNode *n2)
-{
- dfilter_node *node_root;
- GNode *gnode_root;
-
- node_root = g_mem_chunk_alloc(global_df->node_memchunk);
- node_root->ntype = logical;
- node_root->value.logical = operand;
- node_root->elem_size = 0;
- node_root->fill_array_variable_func = NULL;
- node_root->fill_array_value_func = NULL;
- node_root->check_relation_func = NULL;
-
- gnode_root = g_node_new(node_root);
- g_node_append(gnode_root, n2);
-
- return gnode_root;
-}
-
-
-static GNode*
-dfilter_mknode_numeric_variable(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = sizeof(guint32);
- node->fill_array_variable_func = fill_array_numeric_variable;
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_numeric;
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_ether_variable(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = sizeof(guint8) * 6;
- node->fill_array_variable_func = fill_array_ether_variable;
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_ether;
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_floating_variable(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = sizeof(double);
- node->fill_array_variable_func = fill_array_floating_variable;
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_floating;
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_ipxnet_variable(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = sizeof(guint8) * 4;
- node->fill_array_variable_func = fill_array_numeric_variable; /* cheating ! */
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_numeric; /* cheating ! */
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_ipv4_variable(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = sizeof(ipv4_addr);
- node->fill_array_variable_func = fill_array_ipv4_variable;
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_ipv4;
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_ipv6_variable(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = 16;
- node->fill_array_variable_func = fill_array_ipv6_variable;
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_ipv6;
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_string_variable(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = sizeof(char*);
- node->fill_array_variable_func = fill_array_string_variable;
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_string;
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_bytes_variable(gint id, gint offset, guint length, gboolean to_the_end)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = variable;
- node->elem_size = sizeof(GByteArray*);
- node->fill_array_variable_func = fill_array_bytes_variable;
- node->fill_array_value_func = NULL;
- node->check_relation_func = check_relation_bytes;
- node->value.variable = id;
- node->offset = offset;
- node->length = length;
- node->to_the_end = to_the_end;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-/* Gets length of variable represented by node from proto_register */
-static gint
-dfilter_get_bytes_variable_field_registered_length(GNode *gnode)
-{
- dfilter_node *node = gnode->data;
-
- /* Is this really a bytes_variable? */
- g_assert(node->fill_array_variable_func == fill_array_bytes_variable);
-
- return proto_registrar_get_length(node->value.variable);
-}
-
-/* Sets the length of a bytes_variable node */
-static void
-dfilter_set_bytes_variable_length(GNode *gnode, guint length)
-{
- dfilter_node *node = gnode->data;
-
- /* Is this really a bytes_variable? */
- g_assert(node->fill_array_variable_func == fill_array_bytes_variable);
-
- node->length = length;
-}
-
-/* Gets the length of a bytes_variable node */
-static guint
-dfilter_get_bytes_variable_length(GNode *gnode)
-{
- dfilter_node *node = gnode->data;
-
- /* Is this really a bytes_variable? */
- g_assert(node->fill_array_variable_func == fill_array_bytes_variable);
-
- return node->length;
-}
-
-/* Gets the offset of a bytes_variable node */
-static guint
-dfilter_get_bytes_variable_offset(GNode *gnode)
-{
- dfilter_node *node = gnode->data;
-
- /* Is this really a bytes_variable? */
- g_assert(node->fill_array_variable_func == fill_array_bytes_variable);
-
- return node->offset;
-}
-
-static char*
-dfilter_get_variable_abbrev(GNode *gnode)
-{
- dfilter_node *node = gnode->data;
-
- return proto_registrar_get_abbrev(node->value.variable);
-}
-
-static GNode*
-dfilter_mknode_numeric_value(guint32 val)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = numeric;
- node->elem_size = sizeof(guint32);
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_numeric_value;
- node->check_relation_func = check_relation_numeric;
- node->value.numeric = val;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_floating_value(double val)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = floating;
- node->elem_size = sizeof(double);
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_floating_value;
- node->check_relation_func = check_relation_floating;
- node->value.floating = val;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-/* Returns NULL on bad parse of ETHER value */
-static GNode*
-dfilter_mknode_ether_value(gchar *byte_string)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = ether;
- node->elem_size = sizeof(guint8) * 6;
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_ether_value;
- node->check_relation_func = check_relation_ether;
-
- if (!ether_str_to_guint8_array(byte_string, &node->value.ether[0])) {
- /* Rather than free the mem_chunk allocation, let it
- * stay. It will be cleaned up when "dfilter_compile()"
- * calls "dfilter_destroy()". */
- dfilter_fail("\"%s\" is not a valid hardware address.",
- byte_string);
- return NULL;
- }
-
- gnode = g_node_new(node);
- return gnode;
-}
-
-static GNode*
-dfilter_mknode_ipxnet_value(guint32 ipx_net_val)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = ipxnet;
- node->elem_size = sizeof(guint8) * 4;
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_numeric_value; /* cheating ! */
- node->check_relation_func = check_relation_numeric; /* cheating ! */
- node->value.numeric = ipx_net_val;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-/* Returns NULL on bad parse of IP value */
-static GNode*
-dfilter_mknode_ipv4_value(char *host, int nmask_bits)
-{
- dfilter_node *node;
- GNode *gnode;
- guint32 addr;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = numeric;
- node->elem_size = sizeof(ipv4_addr);
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_ipv4_value;
- node->check_relation_func = check_relation_ipv4;
- if (!get_host_ipaddr(host, &addr)) {
- /* Rather than free the mem_chunk allocation, let it
- * stay. It will be cleaned up when "dfilter_compile()"
- * calls "dfilter_destroy()". */
- dfilter_fail("\"%s\" isn't a valid host name or IP address.",
- host);
- return NULL;
- }
- ipv4_addr_set_host_order_addr(&node->value.ipv4, addr);
- ipv4_addr_set_netmask_bits(&node->value.ipv4, nmask_bits);
-
- gnode = g_node_new(node);
- return gnode;
-}
-
-/* Returns NULL on bad parse of IPv6 value */
-static GNode*
-dfilter_mknode_ipv6_value(char *host)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = ipv6;
- node->elem_size = 16;
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_ipv6_value;
- node->check_relation_func = check_relation_ipv6;
-
- if (!get_host_ipaddr6(host, (struct e_in6_addr*)&node->value.ipv6[0])) {
- /* Rather than free the mem_chunk allocation, let it
- * stay. It will be cleaned up when "dfilter_compile()"
- * calls "dfilter_destroy()". */
- dfilter_fail("\"%s\" isn't a valid IPv6 address.",
- host);
- return NULL;
- }
-
- gnode = g_node_new(node);
- return gnode;
-}
-
-
-static GNode*
-dfilter_mknode_string_value(char *s)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = string;
- node->elem_size = sizeof(char*);
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_string_value;
- node->check_relation_func = check_relation_string;
- node->value.string = g_strdup(s);
- global_df->list_of_strings = g_slist_append(global_df->list_of_strings,
- node->value.string);
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-
-static GNode*
-dfilter_mknode_bytes_value(GByteArray *barray)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = bytes;
- node->elem_size = sizeof(GByteArray*);
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = fill_array_bytes_value;
- node->check_relation_func = check_relation_bytes;
- node->value.bytes = barray;
- node->offset = G_MAXINT;
- node->length = barray->len;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-/* Given a node representing a bytes_value, returns
- * the length of the byte array */
-static guint
-dfilter_get_bytes_value_length(GNode* gnode)
-{
- dfilter_node *node = gnode->data;
-
- g_assert(node->ntype == bytes);
- return node->length;
-}
-
-static guint32
-string_to_guint32(char *s, gboolean *success)
-{
- char *endptr;
- guint32 val;
-
- val = strtoul(s, &endptr, 0);
- *success = TRUE;
- if (endptr == s || *endptr != '\0') {
- /* This isn't a valid number. */
- dfilter_fail("\"%s\" is not a valid number.", s);
- *success = FALSE;
- }
- if (errno == ERANGE) {
- *success = FALSE;
- if (val == ULONG_MAX) {
- dfilter_fail("\"%s\" causes an integer overflow.", s);
- }
- else {
- dfilter_fail("\"%s\" is not an integer.", s);
- }
- }
-
- return (guint32)val;
-}
-
-static double
-string_to_double(char *s, gboolean *success)
-{
- char *endptr = NULL;
- double retval;
-
- retval = strtod(s, &endptr);
- *success = TRUE;
-
- if (endptr == s) {
- dfilter_fail("\"%s\" is not a valid floating-point number.", s);
- *success = FALSE;
- }
-
- if (errno == ERANGE) {
- *success = FALSE;
- if (retval == 0) {
- dfilter_fail("\"%s\" causes a floating-point underflow.", s);
- }
- else if (retval == HUGE_VAL) {
- dfilter_fail("\"%s\" causes a floating-point overflow.", s);
- }
- else {
- dfilter_fail("\"%s\" is not a valid floating-point.", s);
- }
- }
- return retval;
-}
-
-static GNode*
-dfilter_mknode_existence(gint id)
-{
- dfilter_node *node;
- GNode *gnode;
-
- node = g_mem_chunk_alloc(global_df->node_memchunk);
- node->ntype = existence;
- node->elem_size = sizeof(guint32);
- node->fill_array_variable_func = NULL;
- node->fill_array_value_func = NULL;
- node->check_relation_func = NULL;
- node->value.variable = id;
- gnode = g_node_new(node);
-
- return gnode;
-}
-
-
-/* converts a string representing an ether HW address
- * to a guint8 array.
- *
- * Returns 0 on failure, 1 on success.
- */
-static int
-ether_str_to_guint8_array(const char *s, guint8 *mac)
-{
- char ether_str[18]; /* 2+1+2+1+2+1+2+1+2+1+2 + 1 */
- char *p, *str;
- int i = 0;
-
- if (strlen(s) > 17) {
- return 0;
- }
- strcpy(ether_str, s); /* local copy of string */
- str = ether_str;
- while ((p = strtok(str, "-:."))) {
- /* catch short strings with too many hex bytes: 0.0.0.0.0.0.0 */
- if (i > 5) {
- return 0;
- }
- mac[i] = (guint8) strtoul(p, NULL, 16);
- i++;
- /* subsequent calls to strtok() require NULL as arg 1 */
- str = NULL;
- }
- if (i != 6)
- return 0; /* failed to read 6 hex pairs */
- else
- return 1; /* read exactly 6 hex pairs */
-}
-
-
-static int
-check_bytes_variable_sanity(GNode *gnode)
-{
- int a_off, a_len, reg_len, t_off;
-
- a_off = dfilter_get_bytes_variable_offset(gnode);
- a_len = dfilter_get_bytes_variable_length(gnode);
- reg_len = dfilter_get_bytes_variable_field_registered_length(gnode);
-
- if (reg_len > 0) {
- t_off = a_off >= 0 ? a_off : reg_len + a_off;
- if (t_off + a_len > reg_len) {
- dfilter_fail("The \"%s\" field is only %u byte%s wide, but "
- "%u byte%s %s supplied.",
- dfilter_get_variable_abbrev(gnode),
- reg_len, plurality(reg_len, "", "s"),
- a_len, plurality(a_len, "", "s"),
- plurality(a_len, "was", "were"));
- return 0;
- }
- }
- return 1;
-}
diff --git a/epan/dfilter-int.h b/epan/dfilter-int.h
deleted file mode 100644
index c50462440d..0000000000
--- a/epan/dfilter-int.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/* dfilter-int.h
- * Definitions for routines common to multiple modules in the display
- * filter code, but not used outside that code.
- *
- * $Id: dfilter-int.h,v 1.2 2000/12/22 12:05:36 gram Exp $
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Gerald Combs
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __DFILTER_INT_H__
-#define __DFILTER_INT_H__
-
-#ifndef __IPV4_H__
-#include "ipv4.h"
-#endif
-
-/* in dfilter-scanner.l */
-GByteArray *byte_str_to_guint8_array(const char *s);
-void dfilter_scanner_text(char*);
-void dfilter_scanner_cleanup(void);
-
-/* in dfilter-grammar.y */
-extern dfilter *global_df;
-
-/* Here we provide interfaces to make our scanner act and look like lex */
-int dfilter_lex(void);
-void dfilter_error(char *s);
-
-/* Report an error during compilation of a filter; this is called by code
- * other than parser code, so all it does is record that an error occurred,
- * so that even if the filter is nominally syntactically valid, we still
- * fail.
- */
-#if __GNUC__ == 2
-void dfilter_fail(char *fmt, ...)
- __attribute__((format (printf, 1, 2)));
-#else
-void dfilter_fail(char *fmt, ...);
-#endif
-
-/* functions that dfilter-grammar.y needs during parsing*/
-gboolean check_relation_numeric(gint operand, GArray *a, GArray *b);
-gboolean check_relation_floating(gint operand, GArray *a, GArray *b);
-gboolean check_relation_ether(gint operand, GArray *a, GArray *b);
-gboolean check_relation_ipv4(gint operand, GArray *a, GArray *b);
-gboolean check_relation_ipv6(gint operand, GArray *a, GArray *b);
-gboolean check_relation_bytes(gint operand, GArray *a, GArray *b);
-gboolean check_relation_string(gint operand, GArray *a, GArray *b);
-
-void fill_array_numeric_variable(field_info*, GArray*, const guint8*);
-void fill_array_floating_variable(field_info*, GArray*, const guint8*);
-void fill_array_ether_variable(field_info*, GArray*, const guint8*);
-void fill_array_ipv4_variable(field_info*, GArray*, const guint8*);
-void fill_array_ipv6_variable(field_info*, GArray*, const guint8*);
-void fill_array_bytes_variable(field_info*, GArray*, const guint8*);
-void fill_array_string_variable(field_info*, GArray*, const guint8*);
-
-gboolean fill_array_numeric_value(GNode *gnode, gpointer data);
-gboolean fill_array_floating_value(GNode *gnode, gpointer data);
-gboolean fill_array_ether_value(GNode *gnode, gpointer data);
-gboolean fill_array_ipv4_value(GNode *gnode, gpointer data);
-gboolean fill_array_ipv6_value(GNode *gnode, gpointer data);
-gboolean fill_array_bytes_value(GNode *gnode, gpointer data);
-gboolean fill_array_string_value(GNode *gnode, gpointer data);
-
-#ifdef WIN32
-#define boolean truth_value
-#endif
-
-enum node_type {
- relation, /* eq, ne, gt, ge, lt, le */
- logical, /* and, or, not, xor */
- variable, /* protocol or header field id */
- existence, /* existence of a variable (protocol or hf) */
- alternation, /* &, | */
- boolean, /* true, false */
- numeric, /* uint8, uint16, or uint32 value */
- floating, /* double */
- abs_time,
- string,
- ether,
- bytes,
- ipv4,
- ipv6,
- ipxnet
-};
-
-typedef gboolean(*CheckRelationFunc) (gint operand, GArray *a, GArray *b);
-typedef void(*FillArrayFunc) (field_info*, GArray*, const guint8*);
-
-/* This struct is the parse tree node created by this grammary and used
- * directly in the display filter routines to filter packets.
- */
-typedef struct dfilter_node {
- enum node_type ntype; /* from dfilter-grammar.h */
- int elem_size; /* computed at dfilter parse time rather than
- when finding elements for each packet. Saves time
- in get_values_from_ptree() */
- CheckRelationFunc check_relation_func;
- FillArrayFunc fill_array_variable_func;
- GNodeTraverseFunc fill_array_value_func;
-
- /* copied from proto.h */
- union {
- gint relation; /* if type == relation (eq, ne, gt, ge, lt, le) */
- gint logical; /* if type == logical (and, or, not, xor) */
- gint variable; /* if type == variable (protocol or header field abbrev) */
- gint alternation; /* if type == alternation (& or |) */
-
- guint32 numeric;
- double floating;
- struct timeval abs_time; /* the whole struct, not a pointer */
- gchar *string;
- guint8 ether[6];
- ipv4_addr ipv4; /* the whole struct, not a pointer */
- guint8 ipv6[16];
- GByteArray *bytes;
- } value;
-
- /* used for byte-ranges */
- gint offset;
- guint length;
-
- /* used to indicate range should go to end of sequence */
- gboolean to_the_end;
-} dfilter_node;
-
-/* lookup an abbreviation in our token hash, returing the ID # */
-int dfilter_lookup_token(char *abbrev);
-
-#endif /* ! __DFILTER_INT_H__ */
diff --git a/epan/dfilter-scanner.l b/epan/dfilter-scanner.l
deleted file mode 100644
index b78ddcb2c4..0000000000
--- a/epan/dfilter-scanner.l
+++ /dev/null
@@ -1,366 +0,0 @@
-%{
-
-/* dfilter-scanner.l
- * Scanner for display filters
- *
- * $Id: dfilter-scanner.l,v 1.1 2000/09/27 04:54:48 gram Exp $
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Gerald Combs
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#ifdef HAVE_IO_H
-#include <io.h> /* for isatty() on win32 */
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#ifndef _STDIO_H
-#include <stdio.h>
-#endif
-
-#ifndef _STRING_H
-#include <string.h>
-#endif
-
-#ifndef __G_LIB_H__
-#include <glib.h>
-#endif
-
-#ifndef __PROTO_H__
-#include "proto.h"
-#endif
-
-#ifndef __DFILTER_H__
-#include "dfilter.h"
-#endif
-
-#include "dfilter-int.h"
-
-#include "dfilter-grammar.h"
-
-/* Flex has a few routines which help us get the scanner to read
- * from a string rather than from a file. POSIX lex only provides
- * for reading from a file; any method of reading from a string
- * is inherently non-portable. Besides reading from a string,
- * we have to worry about resetting the scanner after a bad
- * parse; this too is non-portable. Combine the reset with
- * a string input, and you have major non-portability. I'll provide
- * the routines for flex here. If you really want to modify the
- * scanner and use a non-flex lex implementation, you may
- * add more ifdef's below.
- */
-#ifdef FLEX_SCANNER
-
-/* Flex has built-in support for using a string as an input source
- * instead of using a file. Nice!
- */
-YY_BUFFER_STATE string_input_buffer;
-
-/* We don't need yyunput, so use this macro to get it out of the
- * generated C file, avoiding a compiler warning about its lack of use */
-#define YY_NO_UNPUT 1
-
-#else
-
-static char *in_buffer;
-#undef getc
-#define getc(fp) (*in_buffer == 0 ? EOF : *in_buffer++)
-
-#endif
-
-
-%}
-
-whitespace [\t ]
-hex [A-Fa-f0-9]{1,2}
-hexsep [-:\.]
-minus [-]
-plus [+]
-
-%%
-
-[\t\n ]+ /* ignore whitespace */
-
-
-and|\&\& { dfilter_lval.operand = TOK_AND; return TOK_AND; }
-or|\|\| { dfilter_lval.operand = TOK_OR; return TOK_OR; }
-not|\! { dfilter_lval.operand = TOK_NOT; return TOK_NOT; }
-xor|\^\^ { dfilter_lval.operand = TOK_XOR; return TOK_XOR; }
-eq|\=\= { dfilter_lval.operand = TOK_EQ; return TOK_EQ; }
-ne|\!\= { dfilter_lval.operand = TOK_NE; return TOK_NE; }
-gt|\> { dfilter_lval.operand = TOK_GT; return TOK_GT; }
-ge|\>\= { dfilter_lval.operand = TOK_GE; return TOK_GE; }
-lt|\< { dfilter_lval.operand = TOK_LT; return TOK_LT; }
-le|\<\= { dfilter_lval.operand = TOK_LE; return TOK_LE; }
-
-\[{whitespace}*-?[0-9]+{whitespace}*:{whitespace}*[0-9]+{whitespace}*\] { /* range [ x : y ] */
-
- char *byterange_string = g_strdup(yytext);
- char *s = byterange_string + 1; /* I don't want the first '[' */
- char *p;
-
- /* Get the offset from the string */
- if ((p = strtok(s, ":"))) {
- dfilter_lval.byte_range.offset = strtol(p, NULL, 10);
- }
- else {
- g_free(byterange_string);
- return 0;
- }
-
- /* Get the Length from the string */
- if ((p = strtok(NULL, "]"))) {
- dfilter_lval.byte_range.length = strtoul(p, NULL, 10);
- }
- else {
- g_free(byterange_string);
- return 0;
- }
- g_free(byterange_string);
- return T_VAL_BYTE_RANGE;
-}
-
-\[{whitespace}*-?[0-9]+{whitespace}*\] { /* range [ x ] */
-
- char *byterange_string = g_strdup(yytext);
- char *s = byterange_string + 1; /* I don't want the first '[' */
- char *p;
-
- /* Get the offset from the string */
- if ((p = strtok(s, "]"))) {
- dfilter_lval.byte_range.offset = strtol(p, NULL, 10);
- }
- else {
- g_free(byterange_string);
- return 0;
- }
-
- dfilter_lval.byte_range.length = 0;
- g_free(byterange_string);
- return T_VAL_BYTE_RANGE;
-}
-
-{hex}({hexsep}{hex})+ { /* byte string, any length */
- dfilter_lval.string = g_strdup(yytext);
- return T_VAL_BYTE_STRING;
-}
-
-
-0[xX][A-Fa-f0-9]+ { /* hex values */
- dfilter_lval.string = g_strdup(yytext);
- return T_VAL_UNQUOTED_STRING;
-}
-
-[A-Za-z0-9\:][A-Za-z0-9\.\_\-\:]+ {
- /* looks like a protocol, field name, or hostname */
-
- int retval = 0;
- enum ftenum ftype;
- dfilter_lval.variable.id = dfilter_lookup_token(yytext);
- if (dfilter_lval.variable.id < 0) {
- dfilter_lval.string = g_strdup(yytext);
- return T_VAL_UNQUOTED_STRING;
- }
-
- ftype = proto_registrar_get_ftype(dfilter_lval.variable.id);
- switch (ftype) {
- case FT_NONE:
- retval = T_FT_NONE;
- break;
- case FT_BOOLEAN:
- retval = T_FT_BOOLEAN;
- break;
- case FT_UINT8:
- retval = T_FT_UINT8;
- break;
- case FT_UINT16:
- retval = T_FT_UINT16;
- break;
- case FT_UINT24:
- retval = T_FT_UINT24;
- break;
- case FT_UINT32:
- retval = T_FT_UINT32;
- break;
- case FT_INT8:
- retval = T_FT_INT8;
- break;
- case FT_INT16:
- retval = T_FT_INT16;
- break;
- case FT_INT24:
- retval = T_FT_INT24;
- break;
- case FT_INT32:
- retval = T_FT_INT32;
- break;
- case FT_DOUBLE:
- retval = T_FT_DOUBLE;
- break;
- case FT_ABSOLUTE_TIME:
- dfilter_fail("Sorry, you can't filter on field \"%s\", as we don't yet support filtering on time-of-day values.",
- yytext);
- retval = 0;
- break;
- case FT_RELATIVE_TIME:
- dfilter_fail("Sorry, you can't filter on field \"%s\", as we don't yet support filtering on time-delta values.",
- yytext);
- retval = 0;
- break;
- case FT_STRING:
- case FT_STRINGZ:
- case FT_UINT_STRING:
- retval = T_FT_STRING;
- break;
- case FT_ETHER:
- retval = T_FT_ETHER;
- break;
- case FT_BYTES:
- retval = T_FT_BYTES;
- break;
- case FT_IPv4:
- retval = T_FT_IPv4;
- break;
- case FT_IPv6:
- retval = T_FT_IPv6;
- break;
- case FT_IPXNET:
- retval = T_FT_IPXNET;
- break;
- default:
- printf("ftype for %s is %d\n", yytext, ftype);
- g_assert_not_reached();
- retval = 0;
- break;
- }
- dfilter_lval.variable.type = retval;
- return retval;
-}
-
-({plus}|{minus})?[0-9]+ { /* decimal and octal integers */
- dfilter_lval.string = g_strdup(yytext);
- return T_VAL_UNQUOTED_STRING;
-}
-
-({plus}|{minus})?([0-9]+|[0-9]+\.[0-9]+|\.[0-9]+)([eE]({plus}|{minus})?[0-9]+)? {
- /* I'm trying to capture all floating points here, and
- * am using the strtod manpage as the description of
- * valid formats */
- dfilter_lval.string = g_strdup(yytext);
- return T_VAL_UNQUOTED_STRING;
-}
-
-[0-9\:\.]+ {
- dfilter_lval.string = g_strdup(yytext);
- return T_VAL_UNQUOTED_STRING;
-}
-
-\"[^"]+\" {
- int length;
-
- /* Don't copy the first quote. */
- dfilter_lval.string = g_strdup(&yytext[1]);
-
- /* Chop of the final quote mark. */
- length = strlen(dfilter_lval.string);
- g_assert(length > 0);
- dfilter_lval.string[length - 1] = 0;
-
- return T_VAL_QUOTED_STRING;
-}
-
-. return yytext[0];
-%%
-
-/* Resets scanner and assigns the char* argument
- * as the text to scan
- */
-void
-dfilter_scanner_text(char *text)
-{
-#ifdef FLEX_SCANNER
- string_input_buffer = yy_scan_string(text);
-#else
- in_buffer = text;
-#endif
-}
-
-void
-dfilter_scanner_cleanup(void)
-{
-#ifdef FLEX_SCANNER
- yy_delete_buffer(string_input_buffer);
-#else
- /* There is no standard way to reset a lex scanner.
- * This is necessary after a failed parse on a syntactically
- * incorrect display filter. You have to reset the scanner
- * so that yy_lex() doesn't start scanning from the middle
- * of the previous input string.
- */
-#endif
-}
-
-/* Flex has an option '%option noyywrap' so that I don't have to
- * provide this yywrap function, but in order to maintain portability,
- * I'll just use this yywrap() function.
- */
-int
-yywrap()
-{
- return 1; /* stop at EOF, instead of looking for next file */
-}
-
-/* converts a string representing a byte array
- * to a guint8 array.
- *
- * Returns a non-null GByteArray pointer on success, NULL on failure.
- */
-GByteArray*
-byte_str_to_guint8_array(const char *s)
-{
- GByteArray *barray;
- guint8 val;
- char *byte_str;
- char *p, *str;
-
- barray = g_byte_array_new();
- /* XXX - don't use global_df, but pass in pointer to GSList* */
- global_df->list_of_byte_arrays = g_slist_append(global_df->list_of_byte_arrays, barray);
-
- /* Local copy of string, since strtok will munge it */
- byte_str = g_strdup(s);
- str = byte_str;
- while ((p = strtok(str, "-:."))) {
- val = (guint8) strtoul(p, NULL, 16);
- g_byte_array_append(barray, &val, 1);
-
- /* subsequent calls to strtok() require NULL as arg 1 */
- str = NULL;
- }
-
- g_free(byte_str);
- return barray;
-}
diff --git a/epan/dfilter.c b/epan/dfilter.c
deleted file mode 100644
index 378c109d9b..0000000000
--- a/epan/dfilter.c
+++ /dev/null
@@ -1,1086 +0,0 @@
-/* dfilter.c
- * Routines for display filters
- *
- * $Id: dfilter.c,v 1.3 2000/12/22 12:05:36 gram Exp $
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Gerald Combs
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <glib.h>
-
-#ifdef NEED_SNPRINTF_H
-# include "snprintf.h"
-#endif
-
-#include "proto.h"
-#include "dfilter.h"
-#include "dfilter-int.h"
-#include "dfilter-grammar.h"
-
-int dfilter_parse(void); /* yacc entry-point */
-
-#define DFILTER_LEX_ABBREV_OFFSET 2000
-
-/* Balanced tree of abbreviations and IDs */
-GTree *dfilter_tokens = NULL;
-
-/* Comparision function for tree insertion. A wrapper around strcmp() */
-static int g_strcmp(gconstpointer a, gconstpointer b);
-
-/* Silly global variables used to pass parameter to check_relation_bytes() */
-int bytes_offset = 0;
-int bytes_length = 0;
-gboolean bytes_to_the_end = FALSE;
-
-YYSTYPE yylval;
-
-/* Global error message space for dfilter_compile errors */
-gchar dfilter_error_msg_buf[1024];
-gchar *dfilter_error_msg; /* NULL when no error resulted */
-
-static gboolean dfilter_apply_node(GNode *gnode, proto_tree *ptree, const guint8 *pd);
-static gboolean check_relation(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8 *pd);
-static gboolean check_logical(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8 *pd);
-static GArray* get_values_from_ptree(dfilter_node *dnode, proto_tree *ptree, const guint8 *pd);
-static GArray* get_values_from_dfilter(dfilter_node *dnode, GNode *gnode);
-static gboolean check_existence_in_ptree(dfilter_node *dnode, proto_tree *ptree);
-static void clear_byte_array(gpointer data, gpointer user_data);
-
-/* this is not so pretty. I need my own g_array "function" (macro) to
- * retreive the pointer to the data stored in an array cell. I need this
- * for type ether.. GArray makes it easy for me to store 6 bytes inside an array
- * cell, but hard to retrieve it.
- */
-#define g_array_index_ptr(a,s,i) (((guint8*) (a)->data) + (i*s))
-
-extern int hf_text_only; /* in proto.c */
-
-void
-dfilter_init(void)
-{
- int i, num_symbols, symbol;
- char *s;
-
- dfilter_tokens = g_tree_new(g_strcmp);
-
- /* Add the header field and protocol abbrevs to the symbol table */
- num_symbols = proto_registrar_n();
-
- for (i=0; i < num_symbols; i++) {
- if (i == hf_text_only) {
- continue;
- }
- s = proto_registrar_get_abbrev(i);
- g_assert(s); /* Not Null */
- g_assert(s[0] != 0); /* Not empty string */
- /* Make sure we don't have duplicate abbreviation */
- if (g_tree_lookup(dfilter_tokens, s)) {
- g_message("Already have abbreviation \"%s\"", s);
- g_assert(0);
- }
- symbol = DFILTER_LEX_ABBREV_OFFSET + i;
- g_tree_insert(dfilter_tokens, s, GINT_TO_POINTER(symbol));
- }
-}
-
-void
-dfilter_cleanup(void)
-{
- if (dfilter_tokens)
- g_tree_destroy(dfilter_tokens);
-}
-
-/* Compiles the textual representation of the display filter into a tree
- * of operations to perform. Can be called multiple times, compiling a new
- * display filter each time, without having to clear any memory used, since
- * dfilter_compile will take care of that automatically.
- *
- * Returns 0 on success, non-zero on failure.
- *
- * On success, sets the "dfilter *" pointed to by its second argument
- * either to a null pointer (if the filter is a null filter, as
- * generated by an all-blank string) or to a pointer to a newly-allocated
- * dfilter structure (if the filter isn't null).
- *
- * On failure, "dfilter_error_msg" points to an appropriate error message.
- * This error message is a global string, so another invocation of
- * dfilter_compile will clear it. If the caller needs is stored, he
- * needs to g_strdup it himself.
- */
-int
-dfilter_compile(gchar *dfilter_text, dfilter **dfp)
-{
- dfilter *df;
- int retval;
-
- g_assert(dfilter_text != NULL);
-
- df = dfilter_new();
-
- /* tell the scanner to use the filter string as input */
- dfilter_scanner_text(dfilter_text);
-
- /* Assign global variable so dfilter_parse knows which dfilter we're
- * talking about. Reset the global error message.
- */
- global_df = df;
- dfilter_error_msg = NULL;
-
- /* The magic happens right here. */
- retval = dfilter_parse();
-
- /* clean up lex */
- dfilter_scanner_cleanup();
-
- /* Errors not found by the parser may not cause the parse to
- * fail; if "dfilter_error_msg" is set, it means somebody
- * else called "dfilter_fail()", e.g. the lexical analyzer,
- * so treat that as a parse error. */
- if (dfilter_error_msg != NULL)
- retval = 1;
-
- if (retval != 0) {
- if (dfilter_error_msg == NULL) {
- snprintf(dfilter_error_msg_buf, sizeof(dfilter_error_msg_buf),
- "Unable to parse filter string \"%s\".",
- dfilter_text);
- dfilter_error_msg = &dfilter_error_msg_buf[0];
- }
- }
-
- /* Set global_df to NULL just to be tidy. */
- global_df = NULL;
-
- if (retval == 0) {
- /* Success. Check if the filter is empty; if so, discard
- * it and set "*dfp" to NULL, otherwise set "*dfp" to
- * point to the filter. */
- if (df->dftree == NULL) {
- /* The filter is empty. */
- dfilter_destroy(df);
- df = NULL;
- }
- *dfp = df;
- } else {
- /* Failure. Destroy the filter. */
- dfilter_destroy(df);
- df = NULL;
- }
- return retval;
-}
-
-/* Allocates new dfilter, initializes values, and returns pointer to dfilter */
-dfilter*
-dfilter_new(void)
-{
- dfilter *df;
-
- df = g_malloc(sizeof(dfilter));
-
- df->dftree = NULL;
- df->node_memchunk = g_mem_chunk_new("df->node_memchunk",
- sizeof(dfilter_node), 20 * sizeof(dfilter_node), G_ALLOC_ONLY);
- df->list_of_byte_arrays = NULL;
- df->list_of_strings = NULL;
-
- return df;
-}
-
-static void
-free_string(gpointer data, gpointer user_data)
-{
- char *string = data;
- if (string)
- g_free(string);
-}
-
-/* Frees all memory used by dfilter, and frees dfilter itself */
-void
-dfilter_destroy(dfilter *df)
-{
- if (!df)
- return;
-
- if (df->dftree != NULL)
- g_node_destroy(df->dftree);
-
- /* clear the memory that the tree was using for nodes */
- if (df->node_memchunk)
- g_mem_chunk_reset(df->node_memchunk);
-
- /* clear the memory that the tree was using for byte arrays */
- if (df->list_of_byte_arrays) {
- g_slist_foreach(df->list_of_byte_arrays, clear_byte_array, NULL);
- g_slist_free(df->list_of_byte_arrays);
- }
-
- /* clear the allocated strings */
- if (df->list_of_strings) {
- g_slist_foreach(df->list_of_strings, free_string, NULL);
- g_slist_free(df->list_of_strings);
- }
-
- df->dftree = NULL;
- df->list_of_byte_arrays = NULL;
- df->list_of_strings = NULL;
-
- /* Git rid of memchunk */
- if (df->node_memchunk)
- g_mem_chunk_destroy(df->node_memchunk);
-
- g_free(df);
-}
-
-
-static void
-clear_byte_array(gpointer data, gpointer user_data)
-{
- GByteArray *barray = data;
- if (barray)
- g_byte_array_free(barray, TRUE);
-}
-
-/* Called when the yacc grammar finds a parsing error */
-void
-dfilter_error(char *s)
-{
-}
-
-/* Called when an error other than a parsing error occurs. */
-void
-dfilter_fail(char *format, ...)
-{
- va_list ap;
-
- /* If we've already reported one error, don't overwrite it with this
- * one. */
- if (dfilter_error_msg != NULL)
- return;
-
- va_start(ap, format);
- vsnprintf(dfilter_error_msg_buf, sizeof dfilter_error_msg_buf, format, ap);
- dfilter_error_msg = dfilter_error_msg_buf;
- va_end(ap);
-}
-
-/* lookup an abbreviation in our token tree, returing the ID #
- * If the abbreviation doesn't exit, returns -1 */
-int dfilter_lookup_token(char *abbrev)
-{
- int value;
-
- g_assert(abbrev != NULL);
- value = GPOINTER_TO_INT(g_tree_lookup(dfilter_tokens, abbrev));
-
- if (value < DFILTER_LEX_ABBREV_OFFSET) {
- return -1;
- }
- return value - DFILTER_LEX_ABBREV_OFFSET;
-}
-
-static int
-g_strcmp(gconstpointer a, gconstpointer b)
-{
- return strcmp((const char*)a, (const char*)b);
-}
-
-
-gboolean
-dfilter_apply(dfilter *dfcode, proto_tree *ptree, const guint8* pd, guint pd_len)
-{
- gboolean retval;
- if (dfcode == NULL)
- return FALSE;
- retval = dfilter_apply_node(dfcode->dftree, ptree, pd);
- return retval;
-}
-
-static gboolean
-dfilter_apply_node(GNode *gnode, proto_tree *ptree, const guint8* pd)
-{
- GNode *gnode_a, *gnode_b;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
-
- /* We'll get 2 NULLs if we don't have children */
- gnode_a = g_node_nth_child(gnode, 0);
- gnode_b = g_node_nth_child(gnode, 1);
-
- switch(dnode->ntype) {
- case variable:
- /* We'll never see this case because if the parser finds the name of
- * a variable, it will cause it to be an 'existence' operation.
- */
- g_assert_not_reached();
-
- case logical:
- g_assert(gnode_a);
- return check_logical(dnode->value.logical, gnode_a, gnode_b, ptree, pd);
-
- case relation:
- g_assert(gnode_a && gnode_b);
- return check_relation(dnode->value.relation, gnode_a, gnode_b, ptree, pd);
-
- case alternation:
- g_assert_not_reached();
- /* not coded yet */
-
- case numeric:
- case floating:
- case ipv4:
- case ipv6:
- case boolean:
- case ether:
- case string:
- case abs_time:
- case bytes:
- case ipxnet:
- /* the only time we'll see these at this point is if the display filter
- * is really wacky. (like simply "192.168.1.1"). The parser as it stands
- * now let these by. Just return TRUE */
- g_assert(!gnode_a && !gnode_b);
- return TRUE;
-
- case existence: /* checking the existence of a protocol or hf*/
- g_assert(!gnode_a && !gnode_b);
- return check_existence_in_ptree(dnode, ptree);
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
-static gboolean
-check_logical(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8 *pd)
-{
- gboolean val_a = dfilter_apply_node(a, ptree, pd);
- gboolean val_b;
-
- switch(operand) {
- case TOK_AND:
- g_assert(b);
- return (val_a && dfilter_apply_node(b, ptree, pd));
- case TOK_OR:
- g_assert(b);
- return (val_a || dfilter_apply_node(b, ptree, pd));
- case TOK_XOR:
- g_assert(b);
- val_b = dfilter_apply_node(b, ptree, pd);
- return ( ( val_a || val_b ) && ! ( val_a && val_b ) );
- case TOK_NOT:
- return (!val_a);
- default:
- g_assert_not_reached();
- }
- g_assert_not_reached();
- return FALSE;
-}
-
-
-static void
-free_array_of_byte_arrays(GArray *array)
-{
- int i, len;
- GByteArray *ba_ptr;
-
- len = array->len;
-
- for (i = 0; i < len ; i++) {
- ba_ptr = g_array_index(array, GByteArray*, i);
- g_byte_array_free(ba_ptr, TRUE);
- }
-}
-
-/* this is inefficient. I get arrays for both a and b that represent all the values present. That is,
- * if a is bootp.option, e.g., i'll get an array showing all the bootp.option values in the protocol
- * tree. Then I'll get an array for b, which more than likely is a single int, and then I'll compare
- * them all. It makes my coding easier in the beginning, but I should change this to make it run
- * faster.
- */
-static gboolean
-check_relation(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8* pd)
-{
- dfilter_node *node_a = (dfilter_node*) (a->data);
- dfilter_node *node_b = (dfilter_node*) (b->data);
- GArray *vals_a, *vals_b;
- gboolean retval;
-
-
- if (node_a->ntype == variable)
- vals_a = get_values_from_ptree(node_a, ptree, pd);
- else
- vals_a = get_values_from_dfilter(node_a, a);
-
- if (node_b->ntype == variable)
- vals_b = get_values_from_ptree(node_b, ptree, pd);
- else
- vals_b = get_values_from_dfilter(node_b, b);
-
- retval = node_a->check_relation_func(operand, vals_a, vals_b);
-
- /* Free GByteArrays alloated by fill_array_bytes_variable() */
- if (node_a->fill_array_variable_func == fill_array_bytes_variable) {
- free_array_of_byte_arrays(vals_a);
- }
-
- if (node_b->fill_array_variable_func == fill_array_bytes_variable) {
- free_array_of_byte_arrays(vals_b);
- }
-
- g_array_free(vals_a, FALSE);
- g_array_free(vals_b, FALSE);
-
- return retval;
-}
-
-
-static gboolean
-check_existence_in_ptree(dfilter_node *dnode, proto_tree *ptree)
-{
- int target;
-
- target = dnode->value.variable;
- return proto_check_for_protocol_or_field(ptree, target);
-}
-
-static GArray*
-get_values_from_ptree(dfilter_node *dnode, proto_tree *ptree, const guint8 *pd)
-{
- GArray *result_array;
- GPtrArray *finfo_array;
- int i, len;
- field_info *finfo;
-
- /* Prepare the array for results */
- g_assert(dnode->elem_size > 0);
- result_array = g_array_new(FALSE, FALSE, dnode->elem_size);
-
- /* Set bytes_offset, bytes_length, and bytes_to_the_end
- * for this dnode
- */
-
- bytes_offset = dnode->offset;
- bytes_length = dnode->length;
- bytes_to_the_end = dnode->to_the_end;
-
- /* Cull the finfos from the proto_tree */
- finfo_array = proto_get_finfo_ptr_array(ptree, dnode->value.variable);
- if (!finfo_array) {
- return result_array;
- }
-
- len = g_ptr_array_len(finfo_array);
-
- for (i = 0; i < len; i++) {
- finfo = g_ptr_array_index(finfo_array, i);
- dnode->fill_array_variable_func(finfo, result_array, pd);
- }
-
- g_ptr_array_free(finfo_array, FALSE);
-
- return result_array;
-}
-
-
-void
-fill_array_numeric_variable(field_info *finfo, GArray *array, const guint8 *pd)
-{
- g_array_append_val(array, finfo->value.numeric);
-}
-
-void
-fill_array_floating_variable(field_info *finfo, GArray *array, const guint8 *pd)
-{
- g_array_append_val(array, finfo->value.floating);
-}
-
-void
-fill_array_ether_variable(field_info *finfo, GArray *array, const guint8 *pd)
-{
- /* hmmm, yes, I *can* copy a pointer instead of memcpy() */
- g_array_append_val(array, finfo->value.ether);
-}
-
-void
-fill_array_ipv4_variable(field_info *finfo, GArray *array, const guint8 *pd)
-{
- /* hmmm, yes, I *can* copy a pointer instead of memcpy() */
- g_array_append_val(array, finfo->value.ipv4);
-}
-
-void
-fill_array_ipv6_variable(field_info *finfo, GArray *array, const guint8 *pd)
-{
- /* hmmm, yes, I *can* copy a pointer instead of memcpy() */
- g_array_append_val(array, finfo->value.ipv6);
-}
-
-void
-fill_array_string_variable(field_info *finfo, GArray *array, const guint8 *pd)
-{
- g_array_append_val(array, finfo->value.string);
-}
-
-void
-fill_array_bytes_variable(field_info *finfo, GArray *array, const guint8 *pd)
-{
- GByteArray *barray;
- guint read_start, pkt_end, read_len;
-
- if (bytes_offset < 0) {
- /* Handle negative byte offsets */
- bytes_offset = finfo->length + bytes_offset;
- if (bytes_offset < 0) {
- return;
- }
- }
-
- /* Check to make sure offset exists for this field */
- if (bytes_offset >= finfo->length) {
- return;
- }
-
- pkt_end = finfo->start + finfo->length;
- read_start = finfo->start + bytes_offset;
- if(bytes_to_the_end){
- read_len = pkt_end - read_start;;
- }
- else {
- read_len = bytes_length;
- }
- /* Check to make sure entire length requested is inside field */
- if (pkt_end < read_start + read_len) {
- return;
- }
-
- barray = g_byte_array_new();
- g_byte_array_append(barray, pd + read_start, read_len);
- g_array_append_val(array, barray);
-}
-
-static GArray*
-get_values_from_dfilter(dfilter_node *dnode, GNode *gnode)
-{
- GArray *array;
-
- g_assert(dnode->elem_size > 0);
- array = g_array_new(FALSE, FALSE, dnode->elem_size);
-
- g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1,
- dnode->fill_array_value_func, array);
- return array;
-}
-
-gboolean fill_array_numeric_value(GNode *gnode, gpointer data)
-{
- GArray *array = (GArray*)data;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
-
- g_array_append_val(array, dnode->value.numeric);
- return FALSE; /* FALSE = do not end traversal of GNode tree */
-}
-
-gboolean fill_array_floating_value(GNode *gnode, gpointer data)
-{
- GArray *array = (GArray*)data;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
-
- g_array_append_val(array, dnode->value.floating);
- return FALSE; /* FALSE = do not end traversal of GNode tree */
-}
-
-gboolean fill_array_ether_value(GNode *gnode, gpointer data)
-{
- GArray *array = (GArray*)data;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
-
- g_array_append_val(array, dnode->value.ether);
-
- return FALSE; /* FALSE = do not end traversal of GNode tree */
-}
-
-gboolean fill_array_ipv4_value(GNode *gnode, gpointer data)
-{
- GArray *array = (GArray*)data;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
-
- g_array_append_val(array, dnode->value.ipv4);
-
- return FALSE; /* FALSE = do not end traversal of GNode tree */
-}
-
-gboolean fill_array_ipv6_value(GNode *gnode, gpointer data)
-{
- GArray *array = (GArray*)data;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
-
- g_array_append_val(array, dnode->value.ipv6);
-
- return FALSE; /* FALSE = do not end traversal of GNode tree */
-}
-
-gboolean fill_array_bytes_value(GNode *gnode, gpointer data)
-{
- GArray *array = (GArray*)data;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
- GByteArray *barray = dnode->value.bytes;
-
- g_array_append_val(array, barray);
-
- return FALSE; /* FALSE = do not end traversal of GNode tree */
-}
-
-gboolean fill_array_string_value(GNode *gnode, gpointer data)
-{
- GArray *array = (GArray*)data;
- dfilter_node *dnode = (dfilter_node*) (gnode->data);
-
- g_array_append_val(array, dnode->value.string);
-
- return FALSE; /* FALSE = do not end traversal of GNode tree */
-}
-
-gboolean check_relation_numeric(gint operand, GArray *a, GArray *b)
-{
- int i, j, len_a, len_b;
- guint32 val_a;
-
- len_a = a->len;
- len_b = b->len;
-
-
- switch(operand) {
- case TOK_EQ:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, guint32, i);
- for (j = 0; j < len_b; j++) {
- if (val_a == g_array_index(b, guint32, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_NE:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, guint32, i);
- for (j = 0; j < len_b; j++) {
- if (val_a != g_array_index(b, guint32, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_GT:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, guint32, i);
- for (j = 0; j < len_b; j++) {
- if (val_a > g_array_index(b, guint32, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_GE:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, guint32, i);
- for (j = 0; j < len_b; j++) {
- if (val_a >= g_array_index(b, guint32, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_LT:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, guint32, i);
- for (j = 0; j < len_b; j++) {
- if (val_a < g_array_index(b, guint32, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_LE:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, guint32, i);
- for (j = 0; j < len_b; j++) {
- if (val_a <= g_array_index(b, guint32, j))
- return TRUE;
- }
- }
- return FALSE;
-
- default:
- g_assert_not_reached();
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
-gboolean check_relation_floating(gint operand, GArray *a, GArray *b)
-{
- int i, j, len_a, len_b;
- double val_a;
-
- len_a = a->len;
- len_b = b->len;
-
-
- switch(operand) {
- case TOK_EQ:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, double, i);
- for (j = 0; j < len_b; j++) {
- if (val_a == g_array_index(b, double, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_NE:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, double, i);
- for (j = 0; j < len_b; j++) {
- if (val_a != g_array_index(b, double, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_GT:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, double, i);
- for (j = 0; j < len_b; j++) {
- if (val_a > g_array_index(b, double, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_GE:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, double, i);
- for (j = 0; j < len_b; j++) {
- if (val_a >= g_array_index(b, double, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_LT:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, double, i);
- for (j = 0; j < len_b; j++) {
- if (val_a < g_array_index(b, double, j))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_LE:
- for(i = 0; i < len_a; i++) {
- val_a = g_array_index(a, double, i);
- for (j = 0; j < len_b; j++) {
- if (val_a <= g_array_index(b, double, j))
- return TRUE;
- }
- }
- return FALSE;
-
- default:
- g_assert_not_reached();
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
-gboolean check_relation_ipv4(gint operand, GArray *a, GArray *b)
-{
- int i, j, len_a, len_b;
- ipv4_addr *ptr_a, *ptr_b;
-
- len_a = a->len;
- len_b = b->len;
-
-
- switch(operand) {
- case TOK_EQ:
- for(i = 0; i < len_a; i++) {
- ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i);
- for (j = 0; j < len_b; j++) {
- ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j);
- if (ipv4_addr_eq(ptr_a, ptr_b))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_NE:
- for(i = 0; i < len_a; i++) {
- ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i);
- for (j = 0; j < len_b; j++) {
- ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j);
- if (ipv4_addr_ne(ptr_a, ptr_b))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_GT:
- for(i = 0; i < len_a; i++) {
- ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i);
- for (j = 0; j < len_b; j++) {
- ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j);
- if (ipv4_addr_gt(ptr_a, ptr_b))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_GE:
- for(i = 0; i < len_a; i++) {
- ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i);
- for (j = 0; j < len_b; j++) {
- ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j);
- if (ipv4_addr_ge(ptr_a, ptr_b))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_LT:
- for(i = 0; i < len_a; i++) {
- ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i);
- for (j = 0; j < len_b; j++) {
- ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j);
- if (ipv4_addr_lt(ptr_a, ptr_b))
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_LE:
- for(i = 0; i < len_a; i++) {
- ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i);
- for (j = 0; j < len_b; j++) {
- ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j);
- if (ipv4_addr_le(ptr_a, ptr_b))
- return TRUE;
- }
- }
- return FALSE;
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
-gboolean check_relation_ipv6(gint operand, GArray *a, GArray *b)
-{
- int i, j, len_a, len_b;
- guint8 *ptr_a, *ptr_b;
-
- len_a = a->len;
- len_b = b->len;
-
-
- switch(operand) {
- case TOK_EQ:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index_ptr(a, 16, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index_ptr(b, 16, j);
- if (memcmp(ptr_a, ptr_b, 16) == 0)
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_NE:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index_ptr(a, 16, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index_ptr(b, 16, j);
- if (memcmp(ptr_a, ptr_b, 16) != 0)
- return TRUE;
- }
- }
- return FALSE;
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
-gboolean check_relation_ether(gint operand, GArray *a, GArray *b)
-{
- int i, j, len_a, len_b;
- guint8 *ptr_a, *ptr_b;
-
- len_a = a->len;
- len_b = b->len;
-
-
- switch(operand) {
- case TOK_EQ:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index_ptr(a, 6, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index_ptr(b, 6, j);
- if (memcmp(ptr_a, ptr_b, 6) == 0)
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_NE:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index_ptr(a, 6, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index_ptr(b, 6, j);
- if (memcmp(ptr_a, ptr_b, 6) != 0)
- return TRUE;
- }
- }
- return FALSE;
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
-gboolean check_relation_bytes(gint operand, GArray *a, GArray *b)
-{
- int i, j, len_a, len_b;
- GByteArray *ptr_a,*ptr_b;
-
- len_a = a->len;
- len_b = b->len;
-
-
- switch(operand) {
- case TOK_EQ:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index(a, GByteArray*, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index(b, GByteArray*, j);
- if(ptr_a->len != ptr_b->len)
- return FALSE; if (memcmp(ptr_a->data, ptr_b->data, ptr_a->len) == 0)
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_NE:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index(a, GByteArray*, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index(b, GByteArray*, j);
- if(ptr_a->len != ptr_b->len)
- return TRUE;
- if (memcmp(ptr_a->data, ptr_b->data, ptr_a->len) != 0)
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_GT:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index(a, GByteArray*, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index(b, GByteArray*, j);
- if(ptr_a->len > ptr_b->len)
- return TRUE;
- if(ptr_a->len < ptr_b->len)
- return FALSE;
- if (memcmp(ptr_a->data, ptr_b->data, ptr_a->len) > 0)
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_LT:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index(a, GByteArray*, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index(b, GByteArray*, j);
- if(ptr_a->len < ptr_b->len)
- return TRUE;
- if(ptr_a->len > ptr_b->len)
- return FALSE;
- if (memcmp(ptr_a->data, ptr_b->data, ptr_a->len) < 0)
- return TRUE;
- }
- }
- return FALSE;
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
-gboolean check_relation_string(gint operand, GArray *a, GArray *b)
-{
- int i, j, len_a, len_b;
- char *ptr_a, *ptr_b;
-
- len_a = a->len;
- len_b = b->len;
-
-
- switch(operand) {
- case TOK_EQ:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index(a, char*, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index(b, char*, j);
- if (strcmp(ptr_a, ptr_b) == 0)
- return TRUE;
- }
- }
- return FALSE;
-
- case TOK_NE:
- for(i = 0; i < len_a; i++) {
- ptr_a = g_array_index(a, char*, i);
- for (j = 0; j < len_b; j++) {
- ptr_b = g_array_index(b, char*, j);
- if (strcmp(ptr_a, ptr_b) != 0)
- return TRUE;
- }
- }
- return FALSE;
- }
-
- g_assert_not_reached();
- return FALSE;
-}
-
diff --git a/epan/dfilter.h b/epan/dfilter.h
deleted file mode 100644
index 200430001e..0000000000
--- a/epan/dfilter.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* dfilter.h
- * Definitions for display filters
- *
- * $Id: dfilter.h,v 1.1 2000/09/27 04:54:49 gram Exp $
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Gerald Combs
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __DFILTER_H__
-#define __DFILTER_H__
-
-#include "proto.h"
-
-/* dfilter_error_msg is NULL if there was no error during dfilter_compile,
- * otherwise it points to a displayable error message. */
-extern gchar *dfilter_error_msg;
-extern gchar dfilter_error_msg_buf[1024];
-
-typedef struct {
-
- GNode *dftree;
-
- /* space for dfilter_nodes */
- GMemChunk *node_memchunk;
-
- /* list of byte arrays we allocate during parse. We can traverse this list
- * faster than the tree when we go back and free the byte arrays */
- GSList *list_of_byte_arrays;
-
- /* List of strings allocated during parse. */
- GSList *list_of_strings;
-
-} dfilter;
-
-/* Initialization of the symbol table. Called once during program startup */
-void dfilter_init(void);
-
-/* Free the memory used by the symbol table. Called at program shutdown */
-void dfilter_cleanup(void);
-
-/* Allocate and initialize new dfilter struct. Returns pointer to new dfilter */
-dfilter* dfilter_new(void);
-
-/* Frees all memory used by dfilter, and frees dfilter itself */
-void dfilter_destroy(dfilter *df);
-
-/* Compile display filter text */
-int dfilter_compile(gchar* dfilter_text, dfilter** dfp);
-
-/* Apply compiled dfilter to a proto_tree */
-gboolean dfilter_apply(dfilter *df, proto_tree *ptree, const guint8* pd, guint pd_len);
-
-#endif /* ! __DFILTER_H__ */
diff --git a/epan/dfilter/.cvsignore b/epan/dfilter/.cvsignore
new file mode 100644
index 0000000000..8881f6cef9
--- /dev/null
+++ b/epan/dfilter/.cvsignore
@@ -0,0 +1,8 @@
+.cvsignore
+.deps
+scanner.c
+Makefile
+grammar.h
+grammar.out
+Makefile.in
+grammar.c
diff --git a/epan/dfilter/Makefile.am b/epan/dfilter/Makefile.am
new file mode 100644
index 0000000000..3de048250d
--- /dev/null
+++ b/epan/dfilter/Makefile.am
@@ -0,0 +1,81 @@
+# Makefile.am
+# Automake file for the GTK interface routines for Ethereal
+#
+# $Id: Makefile.am,v 1.1 2001/02/01 20:21:18 gram Exp $
+#
+# Ethereal - Network traffic analyzer
+# By Gerald Combs <gerald@zing.org>
+# Copyright 2001 Gerald Combs
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# We produce an archive library. In the future, when libethereal is a
+# shared library, this will be linked into libethereal. While libethereal
+# is an archive library, any executable linking against libethereal will
+# also need to link against libftypes.
+noinst_LIBRARIES = libdfilter.a
+
+CLEANFILES = \
+ libdfilter.a \
+ *~
+
+INCLUDES = -I$(srcdir)/../.. -I$(srcdir)/$(LEMON)
+
+libdfilter_a_SOURCES = \
+ cppmagic.h \
+ dfilter.c \
+ dfilter.h \
+ dfilter-int.h \
+ dfvm.c \
+ dfvm.h \
+ gencode.c \
+ gencode.h \
+ glib-util.c \
+ glib-util.h \
+ grammar.c \
+ grammar.h \
+ scanner.c \
+ semcheck.c \
+ semcheck.h \
+ sttype-pointer.c \
+ sttype-range.c \
+ sttype-range.h \
+ sttype-string.c \
+ sttype-test.c \
+ sttype-test.h \
+ syntax-tree.c \
+ syntax-tree.h
+
+# Makefile.nmake
+EXTRA_DIST = \
+ grammar.lemon \
+ lemonflex-head.inc \
+ lemonflex-tail.inc \
+ lemon.c \
+ lempar.c \
+ scanner.l
+
+scanner.c : scanner.l
+ $(LEX) -Pdf_ -oscanner.c $(srcdir)/scanner.l
+
+scanner.o : scanner.c grammar.h
+
+LEMON=../../tools/lemon
+
+grammar.c grammar.h : grammar.lemon
+ $(LEMON)/lemon t=$(srcdir)/$(LEMON)/lempar.c $(srcdir)/grammar.lemon || \
+ (rm -f grammar.c grammar.h ; false)
+
diff --git a/epan/dfilter/cppmagic.h b/epan/dfilter/cppmagic.h
new file mode 100644
index 0000000000..92bef11eb1
--- /dev/null
+++ b/epan/dfilter/cppmagic.h
@@ -0,0 +1,14 @@
+/* $Id: cppmagic.h,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+/**************************************** CPP definitions ***************/
+
+/* CPP magic: Concatenate two strings or macros that resolve to strings.
+ * Use CONCAT(), not _CONCAT() */
+#define _CONCAT(a,b) a ## b
+#define CONCAT(a,b) _CONCAT(a,b)
+
+/* CPP magic: Surround a string or a macro that resolves to a string with
+ * double quotes. */
+#define _STRINGIFY(a) # a
+#define STRINGIFY(a) _STRINGIFY(a)
+
diff --git a/epan/dfilter/dfilter-int.h b/epan/dfilter/dfilter-int.h
new file mode 100644
index 0000000000..e3bd84325e
--- /dev/null
+++ b/epan/dfilter/dfilter-int.h
@@ -0,0 +1,52 @@
+/* dfilter-int.h
+ * Header information for use by multiple files in the dfilter submodule.
+ *
+ * $Id: dfilter-int.h,v 1.1 2001/02/01 20:21:18 gram Exp $
+ *
+ */
+
+#ifndef DFILTER_INT_H
+#define DFILTER_INT_H
+
+#include "dfilter.h"
+#include "syntax-tree.h"
+
+#include "proto.h"
+
+/* Passed back to user */
+struct _dfilter_t {
+ GPtrArray *insns;
+ int num_registers;
+ GList **registers;
+ gboolean *attempted_load;
+};
+
+typedef struct {
+ /* Syntax Tree stuff */
+ stnode_t *st_root;
+ gboolean syntax_error;
+ GPtrArray *insns;
+ GHashTable *loaded_fields;
+ int next_insn_id;
+ int next_register;
+} dfwork_t;
+
+/* Constructor/Destructor prototypes for Lemon Parser */
+void *DfilterAlloc(void* (*)());
+void DfilterFree(void*, void (*)());
+void Dfilter(void*, int, stnode_t*, dfwork_t*);
+
+/* Scanner's lval */
+extern stnode_t *df_lval;
+
+/* Given a field abbreviation, returns the proto ID, or -1 if
+ * it doesn't exist. */
+header_field_info*
+dfilter_lookup_token(char *abbrev);
+
+/* Set dfilter_error_msg_buf and dfilter_error_msg */
+void
+dfilter_fail(char *format, ...);
+
+
+#endif
diff --git a/epan/dfilter/dfilter.c b/epan/dfilter/dfilter.c
new file mode 100644
index 0000000000..a8d249cbf4
--- /dev/null
+++ b/epan/dfilter/dfilter.c
@@ -0,0 +1,352 @@
+/* dfilter.c
+ * Main entry point for dfilter routines
+ *
+ * $Id: dfilter.c,v 1.1 2001/02/01 20:21:18 gram Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef NEED_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include "dfilter-int.h"
+#include "syntax-tree.h"
+#include "gencode.h"
+#include "semcheck.h"
+#include "dfvm.h"
+
+
+/* Balanced tree of abbreviations and IDs */
+GTree *dfilter_tokens = NULL;
+
+#define DFILTER_TOKEN_ID_OFFSET 1
+
+/* Comparision function for tree insertion. A wrapper around strcmp() */
+static int g_strcmp(gconstpointer a, gconstpointer b);
+
+/* Global error message space for dfilter_compile errors */
+gchar dfilter_error_msg_buf[1024];
+gchar *dfilter_error_msg; /* NULL when no error resulted */
+
+/* In proto.c */
+extern int hf_text_only;
+
+/* From scanner.c */
+void df_scanner_text(const char *text);
+void df_scanner_file(FILE *fh);
+void df_scanner_cleanup(void);
+int df_lex(void);
+
+/* Holds the singular instance of our Lemon parser object */
+void* ParserObj = NULL;
+
+void
+dfilter_fail(char *format, ...)
+{
+ va_list args;
+
+ /* If we've already reported one error, don't overwite it */
+ if (dfilter_error_msg != NULL)
+ return;
+
+ va_start(args, format);
+
+ vsnprintf(dfilter_error_msg_buf, sizeof(dfilter_error_msg_buf),
+ format, args);
+ dfilter_error_msg = dfilter_error_msg_buf;
+ va_end(args);
+}
+
+
+/* Initialize the dfilter module */
+void
+dfilter_init(void)
+{
+ int id, num_symbols;
+ char *abbrev;
+ header_field_info *hfinfo, *same_name_hfinfo;
+
+ num_symbols = proto_registrar_n();
+
+ if (dfilter_tokens) {
+ /* XXX - needed? */
+ g_message("I expected hf_ids to be NULL\n");
+ g_tree_destroy(dfilter_tokens);
+
+ /* Make sure the hfinfo->same_name links are broken */
+ for (id = 0; id < num_symbols; id++) {
+ hfinfo = proto_registrar_get_nth(id);
+ hfinfo->same_name = NULL;
+ }
+ }
+ dfilter_tokens = g_tree_new(g_strcmp);
+
+ /* Populate the abbrev/ID GTree (header-field symbol table) */
+
+
+ for (id = 0; id < num_symbols; id++) {
+ if (id == hf_text_only) {
+ continue;
+ }
+ abbrev = proto_registrar_get_abbrev(id);
+ hfinfo = proto_registrar_get_nth(id);
+
+ g_assert(abbrev); /* Not Null */
+ g_assert(abbrev[0] != 0); /* Not empty string */
+
+ /* We allow multiple hfinfo's to be registered under the same
+ * abbreviation. This was done for X.25 */
+ same_name_hfinfo = g_tree_lookup(dfilter_tokens, abbrev);
+ if (same_name_hfinfo) {
+ /* Set the "same_name" pointer in the hfinfo, then
+ * allow the code after this if{} block to replace the
+ * old hfinfo with the new hfinfo in the GTree. Thus,
+ * we end up with a linked-list of same-named hfinfo's,
+ * with the root of the list being the hfinfo in the GTree */
+ hfinfo->same_name = same_name_hfinfo;
+
+ }
+ g_tree_insert(dfilter_tokens, abbrev, hfinfo);
+ }
+
+ if (ParserObj) {
+ g_message("I expected ParserObj to be NULL\n");
+ /* Free the Lemon Parser object */
+ DfilterFree(ParserObj, g_free);
+ }
+ /* Allocate an instance of our Lemon-based parser */
+ ParserObj = DfilterAlloc(g_malloc);
+
+ /* Initialize the syntax-tree sub-sub-system */
+ sttype_init();
+}
+
+/* Clean-up the dfilter module */
+void
+dfilter_cleanup(void)
+{
+ /* Free the abbrev/ID GTree */
+ if (dfilter_tokens) {
+ g_tree_destroy(dfilter_tokens);
+ dfilter_tokens = NULL;
+ }
+
+ /* Free the Lemon Parser object */
+ if (ParserObj) {
+ DfilterFree(ParserObj, g_free);
+ }
+
+ /* Clean up the syntax-tree sub-sub-system */
+ sttype_cleanup();
+}
+
+
+
+/* Lookup an abbreviation in our token tree, returing the ID #
+ * If the abbreviation doesn't exit, returns -1 */
+header_field_info*
+dfilter_lookup_token(char *abbrev)
+{
+ g_assert(abbrev != NULL);
+ return g_tree_lookup(dfilter_tokens, abbrev);
+}
+
+/* String comparison func for dfilter_token GTree */
+static int
+g_strcmp(gconstpointer a, gconstpointer b)
+{
+ return strcmp((const char*)a, (const char*)b);
+}
+
+static dfilter_t*
+dfilter_new(void)
+{
+ dfilter_t *df;
+
+ df = g_new(dfilter_t, 1);
+ df->insns = NULL;
+
+ return df;
+}
+
+/* Given a GPtrArray of instructions (dfvm_insn_t),
+ * free them. */
+static void
+free_insns(GPtrArray *insns)
+{
+ int i;
+ dfvm_insn_t *insn;
+
+ for (i = 0; i < insns->len; i++) {
+ insn = g_ptr_array_index(insns, i);
+ dfvm_insn_free(insn);
+ }
+}
+
+void
+dfilter_free(dfilter_t *df)
+{
+ if (df->insns) {
+ free_insns(df->insns);
+ }
+
+ g_free(df->registers);
+ g_free(df->attempted_load);
+ g_free(df);
+}
+
+
+static dfwork_t*
+dfwork_new(void)
+{
+ dfwork_t *dfw;
+
+ dfw = g_new(dfwork_t, 1);
+
+ dfw->st_root = NULL;
+ dfw->syntax_error = FALSE;
+ dfw->insns = NULL;
+ dfw->loaded_fields = NULL;
+ dfw->next_insn_id = 0;
+ dfw->next_register = 0;
+
+ return dfw;
+}
+
+static void
+dfwork_free(dfwork_t *dfw)
+{
+ if (dfw->st_root) {
+ stnode_free(dfw->st_root);
+ }
+
+ if (dfw->loaded_fields) {
+ g_hash_table_destroy(dfw->loaded_fields);
+ }
+
+ if (dfw->insns) {
+ free_insns(dfw->insns);
+ }
+
+ g_free(dfw);
+}
+
+
+gboolean
+dfilter_compile(gchar *text, dfilter_t **dfp)
+{
+ int token;
+ dfilter_t *dfilter;
+ dfwork_t *dfw;
+
+ dfilter_error_msg = NULL;
+
+ dfw = dfwork_new();
+
+ df_scanner_text(text);
+
+ while (1) {
+ df_lval = stnode_new(STTYPE_UNINITIALIZED, NULL);
+ token = df_lex();
+
+ /* Check for end-of-input */
+ if (token == 0) {
+ /* Tell the parser that we have reached the end of input */
+ Dfilter(ParserObj, 0, NULL, dfw);
+
+ /* Free the stnode_t that we just generated, since
+ * the parser doesn't know about it and won't free it
+ * for us. */
+ stnode_free(df_lval);
+ df_lval = NULL;
+ break;
+ }
+
+ /* Give the token to the parser */
+ Dfilter(ParserObj, token, df_lval, dfw);
+
+ if (dfw->syntax_error) {
+ break;
+ }
+ }
+
+ /* One last check for syntax error (after EOF) */
+ if (dfw->syntax_error) {
+ goto FAILURE;
+ }
+
+
+ /* Success, but was it an empty filter? If so, discard
+ * it and set *dfp to NULL */
+ if (dfw->st_root == NULL) {
+ *dfp = NULL;
+ }
+ else {
+
+ /* Check semantics and do necessary type conversion*/
+ if (!dfw_semcheck(dfw)) {
+ goto FAILURE;
+ }
+
+ /* Create bytecode */
+ dfw_gencode(dfw);
+
+ /* Tuck away the bytecode in the dfilter_t */
+ dfilter = dfilter_new();
+ dfilter->insns = dfw->insns;
+ dfw->insns = NULL;
+
+ /* Initialize run-time space */
+ dfilter->num_registers = dfw->next_register;
+ dfilter->registers = g_new0(GList*, dfilter->num_registers);
+ dfilter->attempted_load = g_new0(gboolean, dfilter->num_registers);
+
+ /* And give it to the user. */
+ *dfp = dfilter;
+ }
+ /* SUCCESS */
+ dfwork_free(dfw);
+
+ /* Reset flex */
+ df_scanner_cleanup();
+
+ return TRUE;
+
+FAILURE:
+ if (dfw) {
+ dfwork_free(dfw);
+ }
+ dfilter_fail("Unable to parse filter string \"%s\".", text);
+ *dfp = NULL;
+
+ /* Reset flex */
+ df_scanner_cleanup();
+ return FALSE;
+
+}
+
+
+gboolean
+dfilter_apply(dfilter_t *df, tvbuff_t *tvb, proto_tree *tree)
+{
+ return dfvm_apply(df, tvb, tree);
+}
+
+gboolean
+dfilter_apply_edt(dfilter_t *df, epan_dissect_t* edt)
+{
+ return dfvm_apply(df, edt->tvb, edt->tree);
+}
+
+
+void
+dfilter_dump(dfilter_t *df)
+{
+ dfvm_dump(stdout, df->insns);
+}
diff --git a/epan/dfilter/dfilter.h b/epan/dfilter/dfilter.h
new file mode 100644
index 0000000000..b9442f42c8
--- /dev/null
+++ b/epan/dfilter/dfilter.h
@@ -0,0 +1,65 @@
+/* dfilter.h
+ *
+ * $Id: dfilter.h,v 1.1 2001/02/01 20:21:18 gram Exp $
+ */
+
+#ifndef DFILTER_H
+#define DFILTER_H
+
+#include <glib.h>
+#include "epan.h"
+#include "proto.h"
+
+/* Passed back to user */
+typedef struct _dfilter_t dfilter_t;
+
+/* Module-level initialization */
+void
+dfilter_init(void);
+
+/* Module-level cleanup */
+void
+dfilter_cleanup(void);
+
+/* Compiles a string to a dfilter_t.
+ * On success, sets the dfilter* pointed to by dfp
+ * to either a NULL pointer (if the filter is a null
+ * filter, as generated by an all-blank string) or to
+ * a pointer to the newly-allocated dfilter_t
+ * structure.
+ *
+ * On failure, dfilter_error_msg points to an
+ * appropriate error message. This error message is
+ * a global string, so another invocation of
+ * dfilter_compile() will clear it. The dfilter*
+ * will be set to NULL after a failure.
+ *
+ * Returns TRUE on success, FALSE on failure.
+ */
+gboolean
+dfilter_compile(gchar *text, dfilter_t **dfp);
+
+/* Frees all memory used by dfilter, and frees
+ * the dfilter itself. */
+void
+dfilter_free(dfilter_t *df);
+
+
+/* dfilter_error_msg is NULL if there was no error during dfilter_compile,
+ * otherwise it points to a displayable error message. */
+extern gchar *dfilter_error_msg;
+
+/* Apply compiled dfilter */
+gboolean
+dfilter_apply_edt(dfilter_t *df, epan_dissect_t* edt);
+
+/* Apply compiled dfilter */
+gboolean
+dfilter_apply(dfilter_t *df, tvbuff_t *tvb, proto_tree *tree);
+
+
+/* Print bytecode of dfilter to stdout */
+void
+dfilter_dump(dfilter_t *df);
+
+#endif
diff --git a/epan/dfilter/dfvm.c b/epan/dfilter/dfvm.c
new file mode 100644
index 0000000000..bf8bfe8e17
--- /dev/null
+++ b/epan/dfilter/dfvm.c
@@ -0,0 +1,395 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dfvm.h"
+
+dfvm_insn_t*
+dfvm_insn_new(dfvm_opcode_t op)
+{
+ dfvm_insn_t *insn;
+
+ insn = g_new(dfvm_insn_t, 1);
+ insn->op = op;
+ insn->arg1 = NULL;
+ insn->arg2 = NULL;
+ insn->arg3 = NULL;
+ insn->arg4 = NULL;
+ return insn;
+}
+
+void
+dfvm_insn_free(dfvm_insn_t *insn)
+{
+ if (insn->arg1) {
+ dfvm_value_free(insn->arg1);
+ }
+ if (insn->arg2) {
+ dfvm_value_free(insn->arg2);
+ }
+ if (insn->arg3) {
+ dfvm_value_free(insn->arg3);
+ }
+ if (insn->arg4) {
+ dfvm_value_free(insn->arg4);
+ }
+ g_free(insn);
+}
+
+
+
+dfvm_value_t*
+dfvm_value_new(dfvm_value_type_t type)
+{
+ dfvm_value_t *v;
+
+ v = g_new(dfvm_value_t, 1);
+ v->type = type;
+ return v;
+}
+
+void
+dfvm_value_free(dfvm_value_t *v)
+{
+ switch (v->type) {
+ case FVALUE:
+ fvalue_free(v->value.fvalue);
+ break;
+ default:
+ /* nothing */
+ ;
+ }
+ g_free(v);
+}
+
+
+void
+dfvm_dump(FILE *f, GPtrArray *insns)
+{
+ int id, length;
+ dfvm_insn_t *insn;
+ dfvm_value_t *arg1;
+ dfvm_value_t *arg2;
+ dfvm_value_t *arg3;
+ dfvm_value_t *arg4;
+
+ length = insns->len;
+
+ for (id = 0; id < length; id++) {
+
+ insn = g_ptr_array_index(insns, id);
+ arg1 = insn->arg1;
+ arg2 = insn->arg2;
+ arg3 = insn->arg3;
+ arg4 = insn->arg4;
+
+ switch (insn->op) {
+ case CHECK_EXISTS:
+ fprintf(f, "%05d CHECK_EXISTS\t%s\n",
+ id, proto_registrar_get_abbrev(arg1->value.numeric));
+ break;
+
+ case READ_TREE:
+ fprintf(f, "%05d READ_TREE\t\t%s -> reg#%d\n",
+ id, proto_registrar_get_abbrev(arg1->value.numeric),
+ arg2->value.numeric);
+ break;
+
+ case PUT_FVALUE:
+ fprintf(f, "%05d PUT_FVALUE\t<%s> -> reg#%d\n",
+ id, fvalue_type_name(arg1->value.fvalue),
+ arg2->value.numeric);
+ break;
+
+ case MK_RANGE:
+ fprintf(f, "%05d MK_RANGE\t\treg#%d[%d:%d] -> reg#%d\n",
+ id,
+ arg1->value.numeric,
+ arg3->value.numeric,
+ arg4->value.numeric,
+ arg2->value.numeric);
+ break;
+
+ case ANY_EQ:
+ fprintf(f, "%05d ANY_EQ\t\treg#%d == reg#%d\n",
+ id, arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_NE:
+ fprintf(f, "%05d ANY_NE\t\treg#%d == reg#%d\n",
+ id, arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_GT:
+ fprintf(f, "%05d ANY_GT\t\treg#%d == reg#%d\n",
+ id, arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_GE:
+ fprintf(f, "%05d ANY_GE\t\treg#%d == reg#%d\n",
+ id, arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_LT:
+ fprintf(f, "%05d ANY_LT\t\treg#%d == reg#%d\n",
+ id, arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_LE:
+ fprintf(f, "%05d ANY_LE\t\treg#%d == reg#%d\n",
+ id, arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case NOT:
+ fprintf(f, "%05d NOT\n", id);
+ break;
+
+ case RETURN:
+ fprintf(f, "%05d RETURN\n", id);
+ break;
+
+ case IF_TRUE_GOTO:
+ fprintf(f, "%05d IF-TRUE-GOTO\t%d\n",
+ id, arg1->value.numeric);
+ break;
+
+ case IF_FALSE_GOTO:
+ fprintf(f, "%05d IF-FALSE-GOTO\t%d\n",
+ id, arg1->value.numeric);
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+}
+
+/* Reads a field from the proto_tree and loads the fvalues into a register,
+ * if that field has not already been read. */
+static gboolean
+read_tree(dfilter_t *df, proto_tree *tree, int field_id, int reg)
+{
+ GPtrArray *finfos;
+ field_info *finfo;
+ int i, len;
+ GList *fvalues = NULL;
+
+ /* Already loaded in this run of the dfilter? */
+ if (df->attempted_load[reg]) {
+ if (df->registers[reg]) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+ }
+
+ df->attempted_load[reg] = TRUE;
+
+ finfos = proto_get_finfo_ptr_array(tree, field_id);
+ if (!finfos) {
+ return FALSE;
+ }
+
+ len = finfos->len;
+ for (i = 0; i < len; i++) {
+ finfo = g_ptr_array_index(finfos, i);
+ fvalues = g_list_prepend(fvalues, finfo->value);
+ }
+ fvalues = g_list_reverse(fvalues);
+
+ df->registers[reg] = fvalues;
+ return TRUE;
+}
+
+
+static gboolean
+put_fvalue(dfilter_t *df, fvalue_t *fv, int reg)
+{
+ df->registers[reg] = g_list_append(NULL, fv);
+ return TRUE;
+}
+
+typedef gboolean (*FvalueCmpFunc)(fvalue_t*, fvalue_t*);
+
+static gboolean
+any_test(dfilter_t *df, FvalueCmpFunc cmp, int reg1, int reg2)
+{
+ GList *list_a, *list_b;
+
+ list_a = df->registers[reg1];
+
+ while (list_a) {
+ list_b = df->registers[reg2];
+ while (list_b) {
+ if (cmp(list_a->data, list_b->data)) {
+ return TRUE;
+ }
+ list_b = g_list_next(list_b);
+ }
+ list_a = g_list_next(list_a);
+ }
+ return FALSE;
+}
+
+
+/* Free the list nodes w/o freeing the memory that each
+ * list node points to. */
+static void
+free_register_overhead(dfilter_t* df)
+{
+ int i;
+
+ for (i = 0; i < df->num_registers; i++) {
+ if (df->registers[i]) {
+ g_list_free(df->registers[i]);
+ }
+ }
+}
+
+/* Takes the list of fvalue_t's in a register, uses fvalue_slice()
+ * to make a new list of fvalue_t's (which are ranges, or byte-slices),
+ * and puts the new list into a new register. */
+static void
+mk_range(dfilter_t *df, int from_reg, int to_reg, int start, int end)
+{
+ GList *from_list, *to_list;
+ fvalue_t *old_fv, *new_fv;
+
+ to_list = NULL;
+ from_list = df->registers[from_reg];
+
+ while (from_list) {
+ old_fv = from_list->data;
+ new_fv = fvalue_slice(old_fv, start, end);
+ /* Assert there because semcheck.c should have
+ * already caught the cases in which a slice
+ * cannot be made. */
+ g_assert(new_fv);
+ to_list = g_list_append(to_list, new_fv);
+
+ from_list = g_list_next(from_list);
+ }
+
+ df->registers[to_reg] = to_list;
+}
+
+
+
+gboolean
+dfvm_apply(dfilter_t *df, tvbuff_t *tvb, proto_tree *tree)
+{
+ int i, id, length;
+ gboolean accum = TRUE;
+ dfvm_insn_t *insn;
+ dfvm_value_t *arg1;
+ dfvm_value_t *arg2;
+ dfvm_value_t *arg3;
+ dfvm_value_t *arg4;
+
+ g_assert(tvb);
+ g_assert(tree);
+
+
+ /* Clear registers */
+ for (i = 0; i < df->num_registers; i++) {
+ df->registers[i] = NULL;
+ df->attempted_load[i] = FALSE;
+ }
+
+ length = df->insns->len;
+
+ for (id = 0; id < length; id++) {
+
+ AGAIN:
+ insn = g_ptr_array_index(df->insns, id);
+ arg1 = insn->arg1;
+ arg2 = insn->arg2;
+
+ switch (insn->op) {
+ case CHECK_EXISTS:
+ accum = proto_check_for_protocol_or_field(tree,
+ arg1->value.numeric);
+ break;
+
+ case READ_TREE:
+ accum = read_tree(df, tree,
+ arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case PUT_FVALUE:
+ accum = put_fvalue(df,
+ arg1->value.fvalue, arg2->value.numeric);
+ break;
+
+ case MK_RANGE:
+ arg3 = insn->arg3;
+ arg4 = insn->arg4;
+ mk_range(df,
+ arg1->value.numeric, arg2->value.numeric,
+ arg3->value.numeric, arg4->value.numeric);
+ break;
+
+ case ANY_EQ:
+ accum = any_test(df, fvalue_eq,
+ arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_NE:
+ accum = any_test(df, fvalue_ne,
+ arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_GT:
+ accum = any_test(df, fvalue_gt,
+ arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_GE:
+ accum = any_test(df, fvalue_ge,
+ arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_LT:
+ accum = any_test(df, fvalue_lt,
+ arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case ANY_LE:
+ accum = any_test(df, fvalue_le,
+ arg1->value.numeric, arg2->value.numeric);
+ break;
+
+ case NOT:
+ accum = !accum;
+ break;
+
+ case RETURN:
+ free_register_overhead(df);
+ return accum;
+
+ case IF_TRUE_GOTO:
+ if (accum) {
+ id = arg1->value.numeric;
+ goto AGAIN;
+ }
+ break;
+
+ case IF_FALSE_GOTO:
+ if (!accum) {
+ id = arg1->value.numeric;
+ goto AGAIN;
+ }
+ break;
+
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+
+ g_assert_not_reached();
+ return FALSE; /* to appease the compiler */
+}
diff --git a/epan/dfilter/dfvm.h b/epan/dfilter/dfvm.h
new file mode 100644
index 0000000000..d7803635da
--- /dev/null
+++ b/epan/dfilter/dfvm.h
@@ -0,0 +1,77 @@
+#ifndef DFVM_H
+#define DFVM_H
+
+#include <stdio.h>
+#include "proto.h"
+#include "dfilter-int.h"
+#include "syntax-tree.h"
+
+typedef enum {
+ EMPTY,
+ FVALUE,
+ FIELD_ID,
+ INSN_NUMBER,
+ REGISTER,
+ INTEGER
+} dfvm_value_type_t;
+
+typedef struct {
+ dfvm_value_type_t type;
+
+ union {
+ fvalue_t *fvalue;
+ guint32 numeric;
+ } value;
+
+} dfvm_value_t;
+
+
+typedef enum {
+
+ IF_TRUE_GOTO,
+ IF_FALSE_GOTO,
+ CHECK_EXISTS,
+ NOT,
+ RETURN,
+ READ_TREE,
+ PUT_FVALUE,
+ ANY_EQ,
+ ANY_NE,
+ ANY_GT,
+ ANY_GE,
+ ANY_LT,
+ ANY_LE,
+ MK_RANGE
+
+} dfvm_opcode_t;
+
+typedef struct {
+ int id;
+ int LHS;
+ dfvm_opcode_t op;
+ dfvm_value_t *arg1;
+ dfvm_value_t *arg2;
+ dfvm_value_t *arg3;
+ dfvm_value_t *arg4;
+} dfvm_insn_t;
+
+dfvm_insn_t*
+dfvm_insn_new(dfvm_opcode_t op);
+
+void
+dfvm_insn_free(dfvm_insn_t *insn);
+
+dfvm_value_t*
+dfvm_value_new(dfvm_value_type_t type);
+
+void
+dfvm_value_free(dfvm_value_t *v);
+
+void
+dfvm_dump(FILE *f, GPtrArray *insns);
+
+gboolean
+dfvm_apply(dfilter_t *df, tvbuff_t *tvb, proto_tree *tree);
+
+
+#endif
diff --git a/epan/dfilter/gencode.c b/epan/dfilter/gencode.c
new file mode 100644
index 0000000000..61c5546cb6
--- /dev/null
+++ b/epan/dfilter/gencode.c
@@ -0,0 +1,299 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dfilter-int.h"
+#include "dfvm.h"
+#include "syntax-tree.h"
+#include "sttype-range.h"
+#include "sttype-test.h"
+#include "ftypes/ftypes.h"
+#include "gdebug.h"
+
+static void
+gencode(dfwork_t *dfw, stnode_t *st_node);
+
+static void
+dfw_append_insn(dfwork_t *dfw, dfvm_insn_t *insn)
+{
+ insn->id = dfw->next_insn_id;
+ dfw->next_insn_id++;
+ g_ptr_array_add(dfw->insns, insn);
+}
+
+/* returns register number */
+static int
+dfw_append_read_tree(dfwork_t *dfw, int field_id)
+{
+ dfvm_insn_t *insn;
+ dfvm_value_t *val1, *val2;
+ int reg = -1;
+
+ /* Keep track of which registers
+ * were used for which field_id's so that we
+ * can re-use registers. */
+ reg = GPOINTER_TO_UINT(
+ g_hash_table_lookup(dfw->loaded_fields,
+ GUINT_TO_POINTER(field_id)));
+ if (reg) {
+ /* Reg's are stored in has as reg+1, so
+ * that the non-existence of a field_id in
+ * the hash, or 0, can be differentiated from
+ * a field_id being loaded into register #0. */
+ reg--;
+ }
+ else {
+ reg = dfw->next_register++;
+ g_hash_table_insert(dfw->loaded_fields,
+ GUINT_TO_POINTER(field_id),
+ GUINT_TO_POINTER(reg + 1));
+ }
+
+ insn = dfvm_insn_new(READ_TREE);
+ val1 = dfvm_value_new(FIELD_ID);
+ val1->value.numeric = field_id;
+ val2 = dfvm_value_new(REGISTER);
+ val2->value.numeric = reg;
+
+ insn->arg1 = val1;
+ insn->arg2 = val2;
+ dfw_append_insn(dfw, insn);
+
+ return reg;
+}
+
+/* returns register number */
+static int
+dfw_append_put_fvalue(dfwork_t *dfw, fvalue_t *fv)
+{
+ dfvm_insn_t *insn;
+ dfvm_value_t *val1, *val2;
+ int reg;
+
+ insn = dfvm_insn_new(PUT_FVALUE);
+ val1 = dfvm_value_new(FVALUE);
+ val1->value.fvalue = fv;
+ val2 = dfvm_value_new(REGISTER);
+ reg = dfw->next_register++;
+ val2->value.numeric = reg;
+ insn->arg1 = val1;
+ insn->arg2 = val2;
+ dfw_append_insn(dfw, insn);
+
+ return reg;
+}
+
+/* returns register number */
+static int
+dfw_append_mk_range(dfwork_t *dfw, stnode_t *node)
+{
+ int hf_reg, reg;
+ header_field_info *hfinfo;
+ dfvm_insn_t *insn;
+ dfvm_value_t *val;
+
+ hfinfo = sttype_range_hfinfo(node);
+ hf_reg = dfw_append_read_tree(dfw, hfinfo->id);
+
+ insn = dfvm_insn_new(MK_RANGE);
+
+ val = dfvm_value_new(REGISTER);
+ val->value.numeric = hf_reg;
+ insn->arg1 = val;
+
+ val = dfvm_value_new(REGISTER);
+ reg =dfw->next_register++;
+ val->value.numeric = reg;
+ insn->arg2 = val;
+
+ val = dfvm_value_new(INTEGER);
+ val->value.numeric = sttype_range_start(node);
+ insn->arg3 = val;
+
+ val = dfvm_value_new(INTEGER);
+ val->value.numeric = sttype_range_end(node);
+ insn->arg4 = val;
+
+ dfw_append_insn(dfw, insn);
+
+ return reg;
+}
+
+
+static void
+gen_relation(dfwork_t *dfw, dfvm_opcode_t op, stnode_t *st_arg1, stnode_t *st_arg2)
+{
+ sttype_id_t type1, type2;
+ dfvm_insn_t *insn;
+ dfvm_value_t *val1, *val2;
+ dfvm_value_t *jmp1 = NULL, *jmp2 = NULL;
+ int reg1 = -1, reg2 = -1;
+ header_field_info *hfinfo;
+
+ fvalue_t *junk = NULL;
+
+ type1 = stnode_type_id(st_arg1);
+ type2 = stnode_type_id(st_arg2);
+
+ if (type1 == STTYPE_FIELD) {
+ hfinfo = stnode_data(st_arg1);
+ reg1 = dfw_append_read_tree(dfw, hfinfo->id);
+
+ insn = dfvm_insn_new(IF_FALSE_GOTO);
+ jmp1 = dfvm_value_new(INSN_NUMBER);
+ insn->arg1 = jmp1;
+ dfw_append_insn(dfw, insn);
+ }
+ else if (type1 == STTYPE_FVALUE) {
+ reg1 = dfw_append_put_fvalue(dfw, stnode_data(st_arg1));
+ }
+ else if (type1 == STTYPE_RANGE) {
+ reg1 = dfw_append_mk_range(dfw, st_arg1);
+ }
+ else {
+ g_assert_not_reached();
+ }
+
+ if (type2 == STTYPE_FIELD) {
+ hfinfo = stnode_data(st_arg2);
+ reg2 = dfw_append_read_tree(dfw, hfinfo->id);
+
+ insn = dfvm_insn_new(IF_FALSE_GOTO);
+ jmp2 = dfvm_value_new(INSN_NUMBER);
+ insn->arg1 = jmp2;
+ dfw_append_insn(dfw, insn);
+ }
+ else if (type2 == STTYPE_FVALUE) {
+ reg2 = dfw_append_put_fvalue(dfw, stnode_data(st_arg2));
+ }
+ else {
+ g_assert_not_reached();
+ }
+
+ insn = dfvm_insn_new(op);
+ val1 = dfvm_value_new(REGISTER);
+ val1->value.numeric = reg1;
+ val2 = dfvm_value_new(REGISTER);
+ val2->value.numeric = reg2;
+ insn->arg1 = val1;
+ insn->arg2 = val2;
+ dfw_append_insn(dfw, insn);
+
+ if (jmp1) {
+ jmp1->value.numeric = dfw->next_insn_id;
+ }
+
+ if (jmp2) {
+ jmp2->value.numeric = dfw->next_insn_id;
+ }
+}
+
+
+static void
+gen_test(dfwork_t *dfw, stnode_t *st_node)
+{
+ test_op_t st_op;
+ stnode_t *st_arg1, *st_arg2;
+ dfvm_value_t *val1;
+ dfvm_insn_t *insn;
+
+ header_field_info *hfinfo;
+
+ sttype_test_get(st_node, &st_op, &st_arg1, &st_arg2);
+
+ switch (st_op) {
+ case TEST_OP_UNINITIALIZED:
+ g_assert_not_reached();
+ break;
+
+ case TEST_OP_EXISTS:
+ val1 = dfvm_value_new(FIELD_ID);
+ hfinfo = stnode_data(st_arg1);
+ val1->value.numeric = hfinfo->id;
+ insn = dfvm_insn_new(CHECK_EXISTS);
+ insn->arg1 = val1;
+ dfw_append_insn(dfw, insn);
+ break;
+
+ case TEST_OP_NOT:
+ gencode(dfw, st_arg1);
+ insn = dfvm_insn_new(NOT);
+ dfw_append_insn(dfw, insn);
+ break;
+
+ case TEST_OP_AND:
+ gencode(dfw, st_arg1);
+
+ insn = dfvm_insn_new(IF_FALSE_GOTO);
+ val1 = dfvm_value_new(INSN_NUMBER);
+ insn->arg1 = val1;
+ dfw_append_insn(dfw, insn);
+
+ gencode(dfw, st_arg2);
+ val1->value.numeric = dfw->next_insn_id;
+ break;
+
+ case TEST_OP_OR:
+ gencode(dfw, st_arg1);
+
+ insn = dfvm_insn_new(IF_TRUE_GOTO);
+ val1 = dfvm_value_new(INSN_NUMBER);
+ insn->arg1 = val1;
+ dfw_append_insn(dfw, insn);
+
+ gencode(dfw, st_arg2);
+ val1->value.numeric = dfw->next_insn_id;
+ break;
+
+ case TEST_OP_EQ:
+ gen_relation(dfw, ANY_EQ, st_arg1, st_arg2);
+ break;
+
+ case TEST_OP_NE:
+ gen_relation(dfw, ANY_NE, st_arg1, st_arg2);
+ break;
+
+ case TEST_OP_GT:
+ gen_relation(dfw, ANY_GT, st_arg1, st_arg2);
+ break;
+
+ case TEST_OP_GE:
+ gen_relation(dfw, ANY_GE, st_arg1, st_arg2);
+ break;
+
+ case TEST_OP_LT:
+ gen_relation(dfw, ANY_LT, st_arg1, st_arg2);
+ break;
+
+ case TEST_OP_LE:
+ gen_relation(dfw, ANY_LE, st_arg1, st_arg2);
+ break;
+ }
+}
+
+static void
+gencode(dfwork_t *dfw, stnode_t *st_node)
+{
+ const char *name;
+
+ name = stnode_type_name(st_node);
+
+ switch (stnode_type_id(st_node)) {
+ case STTYPE_TEST:
+ gen_test(dfw, st_node);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+void
+dfw_gencode(dfwork_t *dfw)
+{
+ dfw->insns = g_ptr_array_new();
+ dfw->loaded_fields = g_hash_table_new(g_direct_hash, g_direct_equal);
+ gencode(dfw, dfw->st_root);
+ dfw_append_insn(dfw, dfvm_insn_new(RETURN));
+}
+
diff --git a/epan/dfilter/gencode.h b/epan/dfilter/gencode.h
new file mode 100644
index 0000000000..db56b9082c
--- /dev/null
+++ b/epan/dfilter/gencode.h
@@ -0,0 +1,7 @@
+#ifndef GENCODE_H
+#define GENCODE_H
+
+void
+dfw_gencode(dfwork_t *dfw);
+
+#endif
diff --git a/epan/dfilter/glib-util.c b/epan/dfilter/glib-util.c
new file mode 100644
index 0000000000..9e63fab4f3
--- /dev/null
+++ b/epan/dfilter/glib-util.c
@@ -0,0 +1,47 @@
+/* $Id: glib-util.c,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+#include <string.h>
+#include <glib.h>
+
+
+#include <glib-util.h>
+
+char*
+g_substrdup(const char *s, int start, int len)
+{
+ int s_len, abs_start, abs_len;
+ char *newstring;
+
+
+ s_len = strlen(s);
+
+ if (start < 0) {
+ abs_start = s_len + start;
+ if (abs_start < 0) {
+ return NULL;
+ }
+ }
+ else {
+ abs_start = start;
+ }
+
+ if (len < 0) {
+ abs_len = s_len + len + 1 - abs_start;
+ if (abs_len < 0) {
+ return NULL;
+ }
+ }
+ else {
+ abs_len = len;
+ }
+
+
+ if (abs_start + abs_len > s_len) {
+ return NULL;
+ }
+
+ newstring = g_strndup(s + abs_start, abs_len + 1);
+ newstring[abs_len] = 0;
+
+ return newstring;
+}
diff --git a/epan/dfilter/glib-util.h b/epan/dfilter/glib-util.h
new file mode 100644
index 0000000000..90a81f7eed
--- /dev/null
+++ b/epan/dfilter/glib-util.h
@@ -0,0 +1,4 @@
+/* $Id: glib-util.h,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+char*
+g_substrdup(const char *s, int start, int len);
diff --git a/epan/dfilter/grammar.lemon b/epan/dfilter/grammar.lemon
new file mode 100644
index 0000000000..8072458ab7
--- /dev/null
+++ b/epan/dfilter/grammar.lemon
@@ -0,0 +1,185 @@
+/* $Id: grammar.lemon,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+%include {
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dfilter-int.h"
+#include "syntax-tree.h"
+#include "sttype-range.h"
+#include "sttype-test.h"
+
+/*extern char *df_text;*/
+
+/* End of C code */
+}
+
+/* Parser Information */
+%name Dfilter
+%token_prefix TOKEN_
+%extra_argument {dfwork_t *dfw}
+
+/* Terminal and Non-Terminal types and destructors */
+%token_type {stnode_t*}
+%token_destructor {stnode_free($$);}
+
+%type sentence {stnode_t*}
+%destructor sentence {stnode_free($$);}
+
+%type expr {stnode_t*}
+%destructor expr {stnode_free($$);}
+
+%type entity {stnode_t*}
+%destructor entity {stnode_free($$);}
+
+%type relation_test {stnode_t*}
+%destructor relation_test {stnode_free($$);}
+
+%type logical_test {stnode_t*}
+%destructor logical_test {stnode_free($$);}
+
+%type rel_op2 {test_op_t}
+
+%type range {stnode_t*}
+%destructor range {stnode_free($$);}
+
+/* This is called as soon as a syntax error happens. After that,
+any "error" symbols are shifted, if possible. */
+%syntax_error {
+
+ header_field_info *hfinfo;
+
+ if (!TOKEN) {
+ dfilter_fail("Unexpected end of filter string.");
+ return;
+ }
+
+ switch(stnode_type_id(TOKEN)) {
+ case STTYPE_UNINITIALIZED:
+ dfilter_fail("Syntax error.");
+ break;
+ case STTYPE_TEST:
+ dfilter_fail("Syntax error, TEST.");
+ break;
+ case STTYPE_STRING:
+ dfilter_fail("The string \"%s\" was unexpected in this context.",
+ stnode_data(TOKEN));
+ break;
+ case STTYPE_FIELD:
+ hfinfo = stnode_data(TOKEN);
+ dfilter_fail("Syntax error near \"%s\".", hfinfo->abbrev);
+ break;
+
+ /* These aren't handed to use as terminal tokens from
+ the scanner, so was can assert that we'll never
+ see them here. */
+ case STTYPE_NUM_TYPES:
+ case STTYPE_RANGE:
+ case STTYPE_FVALUE:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+/* When a parse fails, mark an error. This occurs after
+the above syntax_error code and after the parser fails to
+use error recovery, shifting an "error" symbol and successfully
+shifting 3 more symbols. */
+%parse_failure {
+ dfw->syntax_error = TRUE;
+}
+
+/* ----------------- The grammar -------------- */
+
+/* Associativity */
+%left TEST_AND.
+%left TEST_OR.
+%nonassoc TEST_EQ TEST_NE TEST_LT TEST_LE TEST_GT TEST_GE.
+%right TEST_NOT.
+
+/* Top-level targets */
+sentence ::= expr(X). { dfw->st_root = X; }
+sentence ::= . { dfw->st_root = NULL; }
+
+expr(X) ::= relation_test(R). { X = R; }
+expr(X) ::= logical_test(L). { X = L; }
+
+expr(X) ::= LPAREN expr(Y) RPAREN.
+{
+ X = Y;
+}
+
+
+/* Logical tests */
+logical_test(T) ::= expr(E) TEST_AND expr(F).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set2(T, TEST_OP_AND, E, F);
+}
+
+logical_test(T) ::= expr(E) TEST_OR expr(F).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set2(T, TEST_OP_OR, E, F);
+}
+
+logical_test(T) ::= TEST_NOT expr(E).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set1(T, TEST_OP_NOT, E);
+}
+
+logical_test(T) ::= FIELD(F).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set1(T, TEST_OP_EXISTS, F);
+}
+
+
+
+/* Entities, or things that can be compared/tested/checked */
+entity(E) ::= FIELD(F). { E = F; }
+entity(E) ::= STRING(S). { E = S; }
+entity(E) ::= range(R). { E = R; }
+
+range(R) ::= FIELD(F) LBRACKET STRING(X) COLON STRING(Y) RBRACKET.
+{
+ R = stnode_new(STTYPE_RANGE, NULL);
+ sttype_range_set(R, F, X, Y);
+}
+
+range(R) ::= FIELD(F) LBRACKET STRING(X) COLON RBRACKET.
+{
+ R = stnode_new(STTYPE_RANGE, NULL);
+ sttype_range_set(R, F, X, NULL);
+}
+
+range(R) ::= FIELD(F) LBRACKET COLON STRING(Y) RBRACKET.
+{
+ R = stnode_new(STTYPE_RANGE, NULL);
+ sttype_range_set(R, F, NULL, Y);
+}
+
+range(R) ::= FIELD(F) LBRACKET STRING(Y) RBRACKET.
+{
+ R = stnode_new(STTYPE_RANGE, NULL);
+ sttype_range_set1(R, F, Y);
+}
+
+/* Relational tests */
+relation_test(T) ::= entity(E) rel_op2(O) entity(F).
+{
+ T = stnode_new(STTYPE_TEST, NULL);
+ sttype_test_set2(T, O, E, F);
+}
+
+rel_op2(O) ::= TEST_EQ. { O = TEST_OP_EQ; }
+rel_op2(O) ::= TEST_NE. { O = TEST_OP_NE; }
+rel_op2(O) ::= TEST_GT. { O = TEST_OP_GT; }
+rel_op2(O) ::= TEST_GE. { O = TEST_OP_GE; }
+rel_op2(O) ::= TEST_LT. { O = TEST_OP_LT; }
+rel_op2(O) ::= TEST_LE. { O = TEST_OP_LE; }
+
+
+
diff --git a/epan/dfilter/scanner.l b/epan/dfilter/scanner.l
new file mode 100644
index 0000000000..a10c9fd77f
--- /dev/null
+++ b/epan/dfilter/scanner.l
@@ -0,0 +1,157 @@
+%{
+/* scanner.l
+ * Scanner for Ethereal's dfilter language
+ *
+ * $Id: scanner.l,v 1.1 2001/02/01 20:21:18 gram Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "glib-util.h"
+#include "dfilter-int.h"
+#include "syntax-tree.h"
+#include "grammar.h"
+
+#define LVAL df_lval
+#define LVAL_TYPE stnode_t*
+#define LVAL_INIT_VAL NULL
+#define MODNAME df
+
+#include <lemonflex-head.inc>
+
+/*#undef YY_NO_UNPUT*/
+
+int set_lval(int token, gpointer data);
+int simple(int token);
+
+%}
+
+%x RANGE
+
+BWCHARS [[:alnum:]\[\]\-_.+!@#%^&*=/:]
+INITVAR [_A-Za-z]
+VARCHARS [[:alnum:]_]
+
+
+%%
+
+[[:blank:]\n]+ /* ignore whitespace */
+
+
+
+"(" return simple(TOKEN_LPAREN);
+")" return simple(TOKEN_RPAREN);
+
+"==" return simple(TOKEN_TEST_EQ);
+"eq" return simple(TOKEN_TEST_EQ);
+"!=" return simple(TOKEN_TEST_NE);
+"ne" return simple(TOKEN_TEST_NE);
+">" return simple(TOKEN_TEST_GT);
+"gt" return simple(TOKEN_TEST_GT);
+">=" return simple(TOKEN_TEST_GE);
+"ge" return simple(TOKEN_TEST_GE);
+"<" return simple(TOKEN_TEST_LT);
+"lt" return simple(TOKEN_TEST_LT);
+"<=" return simple(TOKEN_TEST_LE);
+"le" return simple(TOKEN_TEST_LE);
+
+"!" return simple(TOKEN_TEST_NOT);
+"not" return simple(TOKEN_TEST_NOT);
+"&&" return simple(TOKEN_TEST_AND);
+"and" return simple(TOKEN_TEST_AND);
+"||" return simple(TOKEN_TEST_OR);
+"or" return simple(TOKEN_TEST_OR);
+
+
+"[" {
+ BEGIN(RANGE);
+ return simple(TOKEN_LBRACKET);
+}
+
+<RANGE>[+-]?[[:digit:]]+ {
+ return set_lval(TOKEN_STRING, g_strdup(yytext));
+}
+<RANGE>[+-]?0x[[:xdigit:]]+ {
+ return set_lval(TOKEN_STRING, g_strdup(yytext));
+}
+<RANGE>":" return simple(TOKEN_COLON);
+
+<RANGE>"]" {
+ BEGIN(INITIAL);
+ return simple(TOKEN_RBRACKET);
+}
+
+
+\"[^"]*\" {
+ return set_lval(TOKEN_STRING, g_substrdup(yytext, 1, -2));
+}
+
+
+
+[[:alnum:]_.:]+ {
+ /* Is it a field name? */
+ header_field_info *hfinfo;
+
+ hfinfo = dfilter_lookup_token(yytext);
+ if (hfinfo) {
+ /* Yes, it's a field name */
+ return set_lval(TOKEN_FIELD, hfinfo);
+ }
+ else {
+ /* No, so treat it as a string */
+ return set_lval(TOKEN_STRING, g_strdup(yytext));
+ }
+}
+
+
+
+%%
+
+int
+simple(int token)
+{
+ switch (token) {
+ case TOKEN_LPAREN:
+ case TOKEN_RPAREN:
+ case TOKEN_LBRACKET:
+ case TOKEN_RBRACKET:
+ case TOKEN_COLON:
+ case TOKEN_TEST_EQ:
+ case TOKEN_TEST_NE:
+ case TOKEN_TEST_GT:
+ case TOKEN_TEST_GE:
+ case TOKEN_TEST_LT:
+ case TOKEN_TEST_LE:
+ case TOKEN_TEST_NOT:
+ case TOKEN_TEST_AND:
+ case TOKEN_TEST_OR:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return token;
+}
+
+int
+set_lval(int token, gpointer data)
+{
+ sttype_id_t type_id = STTYPE_UNINITIALIZED;
+
+ switch (token) {
+ case TOKEN_STRING:
+ type_id = STTYPE_STRING;
+ break;
+ case TOKEN_FIELD:
+ type_id = STTYPE_FIELD;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ stnode_init(df_lval, type_id, data);
+ return token;
+}
+
+#include <lemonflex-tail.inc>
diff --git a/epan/dfilter/semcheck.c b/epan/dfilter/semcheck.c
new file mode 100644
index 0000000000..24b1b93f80
--- /dev/null
+++ b/epan/dfilter/semcheck.c
@@ -0,0 +1,472 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "dfilter-int.h"
+#include "syntax-tree.h"
+#include "sttype-range.h"
+#include "sttype-test.h"
+
+#include "exceptions.h"
+
+static void
+semcheck(dfwork_t *dfw, stnode_t *st_node);
+
+typedef gboolean (*FtypeCanFunc)(enum ftenum);
+
+/* Compares to ftenum_t's and decides if they're
+ * compatible or not (if they're the same basic type) */
+static gboolean
+compatible_ftypes(ftenum_t a, ftenum_t b)
+{
+ switch (a) {
+ case FT_NONE:
+ case FT_PROTOCOL:
+ case FT_DOUBLE:
+ case FT_ABSOLUTE_TIME:
+ case FT_RELATIVE_TIME:
+ case FT_IPv4:
+ case FT_IPv6:
+ case FT_IPXNET:
+ return a == b;
+
+ case FT_ETHER:
+ case FT_BYTES:
+ return (b == FT_ETHER || b == FT_BYTES);
+
+ case FT_BOOLEAN:
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ switch (b) {
+ case FT_BOOLEAN:
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_UINT_STRING:
+ switch (b) {
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_UINT_STRING:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+
+ case FT_NUM_TYPES:
+ g_assert_not_reached();
+ }
+
+ g_assert_not_reached();
+ return FALSE;
+}
+
+/* Creates a FT_UINT32 fvalue with a given value. */
+static fvalue_t*
+mk_uint32_fvalue(guint32 val)
+{
+ fvalue_t *fv;
+
+ fv = fvalue_new(FT_UINT32);
+ fvalue_set_integer(fv, val);
+
+ return fv;
+}
+
+
+/* Try to make an fvalue from a string using a value_string or true_false_string.
+ * This works only for ftypes that are integers. Returns the created fvalue_t*
+ * or NULL if impossible. */
+static fvalue_t*
+mk_fvalue_from_val_string(header_field_info *hfinfo, char *s)
+{
+ static true_false_string default_tf = { "True", "False" };
+ true_false_string *tf = &default_tf;
+ value_string *vals;
+
+ /* Early return? */
+ switch(hfinfo->type) {
+ case FT_NONE:
+ case FT_PROTOCOL:
+ case FT_DOUBLE:
+ case FT_ABSOLUTE_TIME:
+ case FT_RELATIVE_TIME:
+ case FT_IPv4:
+ case FT_IPv6:
+ case FT_IPXNET:
+ case FT_ETHER:
+ case FT_BYTES:
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_UINT_STRING:
+ return FALSE;
+
+ case FT_BOOLEAN:
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ break;
+
+ case FT_NUM_TYPES:
+ g_assert_not_reached();
+ }
+
+ /* Reset the dfilter error message, since *something* interesting
+ * will happen, and the error message will be more interesting than
+ * any error message I happen to have now. */
+ dfilter_error_msg = NULL;
+
+ /* TRUE/FALSE *always* exist for FT_BOOLEAN. */
+ if (hfinfo->type == FT_BOOLEAN) {
+ if (hfinfo->strings) {
+ tf = hfinfo->strings;
+ }
+
+ if (strcasecmp(s, tf->true_string) == 0) {
+ return mk_uint32_fvalue(TRUE);
+ }
+ else if (strcasecmp(s, tf->false_string) == 0) {
+ return mk_uint32_fvalue(FALSE);
+ }
+ else {
+ dfilter_fail("\"%s\" cannot be found among the possible values for %s.",
+ s, hfinfo->abbrev);
+ return NULL;
+ }
+ }
+
+ /* Do val_strings exist? */
+ if (!hfinfo->strings) {
+ dfilter_fail("%s cannot accept strings as values.",
+ hfinfo->abbrev);
+ return FALSE;
+ }
+
+ vals = hfinfo->strings;
+ while (vals->strptr != NULL) {
+ if (strcasecmp(s, vals->strptr) == 0) {
+ return mk_uint32_fvalue(vals->value);
+ }
+ vals++;
+ }
+ dfilter_fail("\"%s\" cannot be found among the possible values for %s.",
+ s, hfinfo->abbrev);
+ return FALSE;
+}
+
+
+static gboolean
+is_bytes_type(enum ftenum type)
+{
+ switch(type) {
+ case FT_ETHER:
+ case FT_BYTES:
+ case FT_IPv6:
+ return TRUE;
+
+ case FT_NONE:
+ case FT_PROTOCOL:
+ case FT_DOUBLE:
+ case FT_ABSOLUTE_TIME:
+ case FT_RELATIVE_TIME:
+ case FT_IPv4:
+ case FT_IPXNET:
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_UINT_STRING:
+ case FT_BOOLEAN:
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ return FALSE;
+
+ case FT_NUM_TYPES:
+ g_assert_not_reached();
+ }
+
+ g_assert_not_reached();
+ return FALSE;
+}
+
+/* This could really be split up... it's too big. */
+static void
+check_relation(dfwork_t *dfw, FtypeCanFunc can_func, stnode_t *st_node,
+ stnode_t *st_arg1, stnode_t *st_arg2)
+{
+ stnode_t *new_st;
+ sttype_id_t type1, type2;
+ header_field_info *hfinfo1, *hfinfo2;
+ ftenum_t ftype1, ftype2;
+ fvalue_t *fvalue;
+ char *s;
+
+ type1 = stnode_type_id(st_arg1);
+ type2 = stnode_type_id(st_arg2);
+ if (type1 == STTYPE_FIELD) {
+ hfinfo1 = stnode_data(st_arg1);
+ ftype1 = hfinfo1->type;
+
+ if (!can_func(ftype1)) {
+ dfilter_fail("%s (type=%s) cannot participate in specified comparison.",
+ hfinfo1->abbrev, ftype_pretty_name(ftype1));
+ THROW(TypeError);
+ }
+
+
+ if (type2 == STTYPE_FIELD) {
+ hfinfo2 = stnode_data(st_arg2);
+ ftype2 = hfinfo2->type;
+
+ if (!compatible_ftypes(ftype1, ftype2)) {
+ dfilter_fail("%s and %s are not of compatible types.",
+ hfinfo1->abbrev, hfinfo2->abbrev);
+ THROW(TypeError);
+ }
+ /* Do this check even though you'd think that if
+ * they're compatible, then can_func() would pass. */
+ if (!can_func(ftype2)) {
+ dfilter_fail("%s (type=%s) cannot participate in specified comparison.",
+ hfinfo2->abbrev, ftype_pretty_name(ftype2));
+ THROW(TypeError);
+ }
+ }
+ else if (type2 == STTYPE_STRING) {
+ s = stnode_data(st_arg2);
+ fvalue = fvalue_from_string(ftype1, s, dfilter_fail);
+ if (!fvalue) {
+ /* check value_string */
+ fvalue = mk_fvalue_from_val_string(hfinfo1, s);
+ if (!fvalue) {
+ THROW(TypeError);
+ }
+ }
+
+ new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ sttype_test_set2_args(st_node, st_arg1, new_st);
+ stnode_free(st_arg2);
+ }
+ else if (type2 == STTYPE_RANGE) {
+ if (!is_bytes_type(ftype1)) {
+ if (!ftype_can_slice(ftype1)) {
+ dfilter_fail("\"%s\" is a %s and cannot be converted into a sequence of bytes.",
+ hfinfo1->abbrev,
+ ftype_pretty_name(ftype1));
+ THROW(TypeError);
+ }
+
+ /* Convert entire field to bytes */
+ new_st = stnode_new(STTYPE_RANGE, NULL);
+
+ /* st_arg1 is freed in this step */
+ sttype_range_set(new_st, st_arg1, NULL, NULL);
+
+ sttype_test_set2_args(st_node, new_st, st_arg2);
+ }
+ }
+ else {
+ g_assert_not_reached();
+ }
+ }
+ else if (type1 == STTYPE_STRING) {
+
+ if (type2 == STTYPE_FIELD) {
+ hfinfo2 = stnode_data(st_arg2);
+ ftype2 = hfinfo2->type;
+
+ s = stnode_data(st_arg1);
+ fvalue = fvalue_from_string(ftype2, s, dfilter_fail);
+ if (!fvalue) {
+ /* check value_string */
+ fvalue = mk_fvalue_from_val_string(hfinfo2, s);
+ if (!fvalue) {
+ THROW(TypeError);
+ }
+ }
+
+ new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ sttype_test_set2_args(st_node, new_st, st_arg2);
+ stnode_free(st_arg1);
+ }
+ else if (type2 == STTYPE_STRING) {
+ /* Well now that's silly... */
+ dfilter_fail("Neither \"%s\" nor \"%s\" are field or protocol names.",
+ stnode_data(st_arg1),
+ stnode_data(st_arg2));
+ THROW(TypeError);
+ }
+ else if (type2 == STTYPE_RANGE) {
+ s = stnode_data(st_arg1);
+ fvalue = fvalue_from_string(FT_BYTES, s, dfilter_fail);
+ if (!fvalue) {
+ THROW(TypeError);
+ }
+ new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ sttype_test_set2_args(st_node, new_st, st_arg2);
+ stnode_free(st_arg1);
+ }
+ else {
+ g_assert_not_reached();
+ }
+ }
+ else if (type1 == STTYPE_RANGE) {
+ hfinfo1 = sttype_range_hfinfo(st_arg1);
+ ftype1 = hfinfo1->type;
+
+ if (!ftype_can_slice(ftype1)) {
+ dfilter_fail("\"%s\" is a %s and cannot be sliced into a sequence of bytes.",
+ hfinfo1->abbrev, ftype_pretty_name(ftype1));
+ THROW(TypeError);
+ }
+
+
+ if (type2 == STTYPE_FIELD) {
+ hfinfo2 = sttype_range_hfinfo(st_arg2);
+ ftype2 = hfinfo2->type;
+
+ if (!is_bytes_type(ftype2)) {
+ if (!ftype_can_slice(ftype2)) {
+ dfilter_fail("\"%s\" is a %s and cannot be converted into a sequence of bytes.",
+ hfinfo2->abbrev,
+ ftype_pretty_name(ftype2));
+ THROW(TypeError);
+ }
+
+ /* Convert entire field to bytes */
+ new_st = stnode_new(STTYPE_RANGE, NULL);
+
+ /* st_arg2 is freed in this step */
+ sttype_range_set(new_st, st_arg2, NULL, NULL);
+
+ sttype_test_set2_args(st_node, st_arg1, new_st);
+ }
+ }
+ else if (type2 == STTYPE_STRING) {
+ s = stnode_data(st_arg2);
+ fvalue = fvalue_from_string(FT_BYTES, s, dfilter_fail);
+ if (!fvalue) {
+ THROW(TypeError);
+ }
+ new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ sttype_test_set2_args(st_node, st_arg1, new_st);
+ stnode_free(st_arg2);
+ }
+ else if (type2 == STTYPE_RANGE) {
+ /* XXX - check lengths of both ranges */
+ }
+ else {
+ g_assert_not_reached();
+ }
+ }
+ else {
+ g_assert_not_reached();
+ }
+}
+
+static void
+check_test(dfwork_t *dfw, stnode_t *st_node)
+{
+ test_op_t st_op;
+ stnode_t *st_arg1, *st_arg2;
+
+ sttype_test_get(st_node, &st_op, &st_arg1, &st_arg2);
+
+ switch (st_op) {
+ case TEST_OP_UNINITIALIZED:
+ g_assert_not_reached();
+ break;
+
+ case TEST_OP_EXISTS:
+ /* nothing */
+ break;
+
+ case TEST_OP_NOT:
+ semcheck(dfw, st_arg1);
+ break;
+
+ case TEST_OP_AND:
+ case TEST_OP_OR:
+ semcheck(dfw, st_arg1);
+ semcheck(dfw, st_arg2);
+ break;
+
+ case TEST_OP_EQ:
+ check_relation(dfw, ftype_can_eq, st_node, st_arg1, st_arg2);
+ break;
+ case TEST_OP_NE:
+ check_relation(dfw, ftype_can_ne, st_node, st_arg1, st_arg2);
+ break;
+ case TEST_OP_GT:
+ check_relation(dfw, ftype_can_gt, st_node, st_arg1, st_arg2);
+ break;
+ case TEST_OP_GE:
+ check_relation(dfw, ftype_can_ge, st_node, st_arg1, st_arg2);
+ break;
+ case TEST_OP_LT:
+ check_relation(dfw, ftype_can_lt, st_node, st_arg1, st_arg2);
+ break;
+ case TEST_OP_LE:
+ check_relation(dfw, ftype_can_le, st_node, st_arg1, st_arg2);
+ break;
+ }
+}
+
+
+static void
+semcheck(dfwork_t *dfw, stnode_t *st_node)
+{
+ const char *name;
+
+ name = stnode_type_name(st_node);
+
+ switch (stnode_type_id(st_node)) {
+ case STTYPE_TEST:
+ check_test(dfw, st_node);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+gboolean
+dfw_semcheck(dfwork_t *dfw)
+{
+ TRY {
+ semcheck(dfw, dfw->st_root);
+ }
+ CATCH(TypeError) {
+ return FALSE;
+ }
+ ENDTRY;
+
+ return TRUE;
+}
diff --git a/epan/dfilter/semcheck.h b/epan/dfilter/semcheck.h
new file mode 100644
index 0000000000..54f795dfe4
--- /dev/null
+++ b/epan/dfilter/semcheck.h
@@ -0,0 +1,10 @@
+#ifndef SEMCHECK_H
+#define SEMCHECK_H
+
+
+
+gboolean
+dfw_semcheck(dfwork_t *dfw);
+
+
+#endif
diff --git a/epan/dfilter/sttype-pointer.c b/epan/dfilter/sttype-pointer.c
new file mode 100644
index 0000000000..64a49658f2
--- /dev/null
+++ b/epan/dfilter/sttype-pointer.c
@@ -0,0 +1,28 @@
+/* $Id: sttype-pointer.c,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ftypes/ftypes.h"
+#include "syntax-tree.h"
+
+void
+sttype_register_pointer(void)
+{
+ static sttype_t field_type = {
+ STTYPE_FIELD,
+ "FIELD",
+ NULL,
+ NULL,
+ };
+ static sttype_t fvalue_type = {
+ STTYPE_FVALUE,
+ "FVALUE",
+ NULL,
+ NULL,
+ };
+
+ sttype_register(&field_type);
+ sttype_register(&fvalue_type);
+}
diff --git a/epan/dfilter/sttype-range.c b/epan/dfilter/sttype-range.c
new file mode 100644
index 0000000000..12bada4cc4
--- /dev/null
+++ b/epan/dfilter/sttype-range.c
@@ -0,0 +1,174 @@
+/* $Id: sttype-range.c,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+/* The ideas in this code came from Ed Warnicke's original implementation
+ * of dranges for the old display filter code (Ethereal 0.8.15 and before).
+ * The code is different, but definitely inspired by his code.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include "proto.h"
+#include "sttype-range.h"
+
+typedef struct {
+ guint32 magic;
+ header_field_info *hfinfo;
+ gint start;
+ gint end;
+ char *start_error;
+ char *end_error;
+} range_t;
+
+#define RANGE_MAGIC 0xec0990ce
+
+static gpointer
+range_new(gpointer junk)
+{
+ range_t *range;
+
+ g_assert(junk == NULL);
+
+ range = g_new(range_t, 1);
+
+ range->magic = RANGE_MAGIC;
+ range->hfinfo = NULL;
+ range->start = 0;
+ range->end = -1;
+ range->start_error = NULL;
+ range->end_error = NULL;
+
+ return (gpointer) range;
+}
+
+static void
+range_free(gpointer value)
+{
+ range_t *range = value;
+ assert_magic(range, RANGE_MAGIC);
+
+ if (range->start_error)
+ g_free(range->start_error);
+ if (range->end_error)
+ g_free(range->end_error);
+
+ g_free(range);
+}
+
+static gint
+string_to_gint(char *s, gboolean *success)
+{
+ char *endptr;
+ gint val;
+
+ *success = TRUE;
+ val = strtol(s, &endptr, 0);
+
+ if (endptr == s || *endptr != '\0') {
+ *success = FALSE;
+ }
+ else if (errno == ERANGE) {
+ *success = FALSE;
+ }
+
+ return val;
+}
+
+static void
+range_set(stnode_t *node, stnode_t *field, char *start, char *end)
+{
+ range_t *range;
+ gboolean success;
+
+ range = stnode_data(node);
+ assert_magic(range, RANGE_MAGIC);
+
+ range->hfinfo = stnode_data(field);
+ stnode_free(field);
+
+ if (start) {
+ range->start = string_to_gint(start, &success);
+ if (!success) {
+ /* Save the error-causing string for later reporting */
+ range->start_error = g_strdup(start);
+ }
+ }
+ else {
+ range->start = 0;
+ }
+
+ if (end) {
+ range->end = string_to_gint(end, &success);
+
+ if (!success) {
+ /* Save the error-causing string for later reporting */
+ range->end_error = g_strdup(end);
+ }
+ }
+ else {
+ range->end = G_MAXINT;
+ }
+}
+
+void
+sttype_range_set(stnode_t *node, stnode_t *field, stnode_t *start, stnode_t *end)
+{
+ char *start_str, *end_str;
+
+ if (start) {
+ start_str = stnode_data(start);
+ }
+ else {
+ start_str = NULL;
+ }
+
+ if (end) {
+ end_str = stnode_data(end);
+ }
+ else {
+ end_str = NULL;
+ }
+
+ range_set(node, field, start_str, end_str);
+
+ if (start)
+ stnode_free(start);
+ if (end)
+ stnode_free(end);
+}
+
+void
+sttype_range_set1(stnode_t *node, stnode_t *field, stnode_t *offset)
+{
+ char *offset_str;
+
+ g_assert(offset);
+
+ offset_str = stnode_data(offset);
+ range_set(node, field, offset_str, "1");
+ stnode_free(offset);
+}
+
+
+STTYPE_ACCESSOR(header_field_info*, range, hfinfo, RANGE_MAGIC)
+STTYPE_ACCESSOR(gint, range, start, RANGE_MAGIC)
+STTYPE_ACCESSOR(gint, range, end, RANGE_MAGIC)
+STTYPE_ACCESSOR(char*, range, start_error, RANGE_MAGIC)
+STTYPE_ACCESSOR(char*, range, end_error, RANGE_MAGIC)
+
+
+void
+sttype_register_range(void)
+{
+ static sttype_t range_type = {
+ STTYPE_RANGE,
+ "RANGE",
+ range_new,
+ range_free,
+ };
+
+ sttype_register(&range_type);
+}
diff --git a/epan/dfilter/sttype-range.h b/epan/dfilter/sttype-range.h
new file mode 100644
index 0000000000..aff39c84b5
--- /dev/null
+++ b/epan/dfilter/sttype-range.h
@@ -0,0 +1,20 @@
+#ifndef STTYPE_RANGE_H
+#define STTYPE_RANGE_H
+
+#include "syntax-tree.h"
+
+STTYPE_ACCESSOR_PROTOTYPE(header_field_info*, range, hfinfo)
+STTYPE_ACCESSOR_PROTOTYPE(gint, range, start)
+STTYPE_ACCESSOR_PROTOTYPE(gint, range, end)
+STTYPE_ACCESSOR_PROTOTYPE(char*, range, start_error)
+STTYPE_ACCESSOR_PROTOTYPE(char*, range, end_error)
+
+/* Set a range, [x:y], [:y], [x:] */
+void
+sttype_range_set(stnode_t *node, stnode_t *field, stnode_t *start, stnode_t *end);
+
+/* Set a single-byte lookup, [x] */
+void
+sttype_range_set1(stnode_t *node, stnode_t *field, stnode_t *offset);
+
+#endif
diff --git a/epan/dfilter/sttype-string.c b/epan/dfilter/sttype-string.c
new file mode 100644
index 0000000000..c19797844d
--- /dev/null
+++ b/epan/dfilter/sttype-string.c
@@ -0,0 +1,29 @@
+/* $Id: sttype-string.c,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+#include "syntax-tree.h"
+
+static gpointer
+string_new(gpointer string)
+{
+ return (gpointer) g_strdup((char*) string);
+}
+
+static void
+string_free(gpointer value)
+{
+ g_free(value);
+}
+
+
+void
+sttype_register_string(void)
+{
+ static sttype_t string_type = {
+ STTYPE_STRING,
+ "STRING",
+ string_new,
+ string_free,
+ };
+
+ sttype_register(&string_type);
+}
diff --git a/epan/dfilter/sttype-test.c b/epan/dfilter/sttype-test.c
new file mode 100644
index 0000000000..90b07048e0
--- /dev/null
+++ b/epan/dfilter/sttype-test.c
@@ -0,0 +1,144 @@
+/* $Id: sttype-test.c,v 1.1 2001/02/01 20:21:18 gram Exp $ */
+
+#include "syntax-tree.h"
+#include "sttype-test.h"
+
+typedef struct {
+ guint32 magic;
+ test_op_t op;
+ stnode_t *val1;
+ stnode_t *val2;
+} test_t;
+
+#define TEST_MAGIC 0xab9009ba
+
+static gpointer
+test_new(gpointer junk)
+{
+ test_t *test;
+
+ g_assert(junk == NULL);
+
+ test = g_new(test_t, 1);
+
+ test->magic = TEST_MAGIC;
+ test->op = TEST_OP_UNINITIALIZED;
+ test->val1 = NULL;
+ test->val2 = NULL;
+
+ return (gpointer) test;
+}
+
+static void
+test_free(gpointer value)
+{
+ test_t *test = value;
+ assert_magic(test, TEST_MAGIC);
+
+ if (test->val1)
+ stnode_free(test->val1);
+ if (test->val2)
+ stnode_free(test->val2);
+
+ g_free(test);
+}
+
+static int
+num_operands(test_op_t op)
+{
+ switch(op) {
+ case TEST_OP_UNINITIALIZED:
+ g_assert_not_reached();
+ case TEST_OP_EXISTS:
+ return 1;
+ case TEST_OP_NOT:
+ return 1;
+ case TEST_OP_AND:
+ return 2;
+ case TEST_OP_OR:
+ return 2;
+ case TEST_OP_EQ:
+ return 2;
+ case TEST_OP_NE:
+ return 2;
+ case TEST_OP_GT:
+ return 2;
+ case TEST_OP_GE:
+ return 2;
+ case TEST_OP_LT:
+ return 2;
+ case TEST_OP_LE:
+ return 2;
+ }
+ g_assert_not_reached();
+ return -1;
+}
+
+
+void
+sttype_test_set1(stnode_t *node, test_op_t op, stnode_t *val1)
+{
+ test_t *test;
+
+ test = stnode_data(node);
+ assert_magic(test, TEST_MAGIC);
+
+ g_assert(num_operands(op) == 1);
+ test->op = op;
+ test->val1 = val1;
+}
+
+void
+sttype_test_set2(stnode_t *node, test_op_t op, stnode_t *val1, stnode_t *val2)
+{
+ test_t *test;
+
+ test = stnode_data(node);
+ assert_magic(test, TEST_MAGIC);
+
+ g_assert(num_operands(op) == 2);
+ test->op = op;
+ test->val1 = val1;
+ test->val2 = val2;
+}
+
+void
+sttype_test_set2_args(stnode_t *node, stnode_t *val1, stnode_t *val2)
+{
+ test_t *test;
+
+ test = stnode_data(node);
+ assert_magic(test, TEST_MAGIC);
+
+ if (num_operands(test->op) == 1) {
+ g_assert(val2 == NULL);
+ }
+ test->val1 = val1;
+ test->val2 = val2;
+}
+
+void
+sttype_test_get(stnode_t *node, test_op_t *p_op, stnode_t **p_val1, stnode_t **p_val2)
+{
+ test_t *test;
+
+ test = stnode_data(node);
+ assert_magic(test, TEST_MAGIC);
+
+ *p_op = test->op;
+ *p_val1 = test->val1;
+ *p_val2 = test->val2;
+}
+
+void
+sttype_register_test(void)
+{
+ static sttype_t test_type = {
+ STTYPE_TEST,
+ "TEST",
+ test_new,
+ test_free,
+ };
+
+ sttype_register(&test_type);
+}
diff --git a/epan/dfilter/sttype-test.h b/epan/dfilter/sttype-test.h
new file mode 100644
index 0000000000..69765df9e3
--- /dev/null
+++ b/epan/dfilter/sttype-test.h
@@ -0,0 +1,30 @@
+#ifndef STTYPE_TEST_H
+#define STTYPE_TEST_H
+
+typedef enum {
+ TEST_OP_UNINITIALIZED,
+ TEST_OP_EXISTS,
+ TEST_OP_NOT,
+ TEST_OP_AND,
+ TEST_OP_OR,
+ TEST_OP_EQ,
+ TEST_OP_NE,
+ TEST_OP_GT,
+ TEST_OP_GE,
+ TEST_OP_LT,
+ TEST_OP_LE
+} test_op_t;
+
+void
+sttype_test_set1(stnode_t *node, test_op_t op, stnode_t *val1);
+
+void
+sttype_test_set2(stnode_t *node, test_op_t op, stnode_t *val1, stnode_t *val2);
+
+void
+sttype_test_set2_args(stnode_t *node, stnode_t *val1, stnode_t *val2);
+
+void
+sttype_test_get(stnode_t *node, test_op_t *p_op, stnode_t **p_val1, stnode_t **p_val2);
+
+#endif
diff --git a/epan/dfilter/syntax-tree.c b/epan/dfilter/syntax-tree.c
new file mode 100644
index 0000000000..238f685d84
--- /dev/null
+++ b/epan/dfilter/syntax-tree.c
@@ -0,0 +1,169 @@
+/* syntax-tree.c
+ *
+ * $Id: syntax-tree.c,v 1.1 2001/02/01 20:21:18 gram Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syntax-tree.h"
+
+/* Keep track of sttype_t's via their sttype_id_t number */
+static sttype_t* type_list[STTYPE_NUM_TYPES];
+
+/* These are the sttype_t registration function prototypes. */
+void sttype_register_pointer(void);
+void sttype_register_range(void);
+void sttype_register_string(void);
+void sttype_register_test(void);
+
+
+#define STNODE_MAGIC 0xe9b00b9e
+
+
+void
+sttype_init(void)
+{
+ sttype_register_pointer();
+ sttype_register_range();
+ sttype_register_string();
+ sttype_register_test();
+}
+
+void
+sttype_cleanup(void)
+{
+ /* nothing to do */
+}
+
+
+void
+sttype_register(sttype_t *type)
+{
+ sttype_id_t type_id;
+
+ type_id = type->id;
+
+ /* Check input */
+ g_assert(type_id >= 0);
+ g_assert(type_id < STTYPE_NUM_TYPES);
+
+ /* Don't re-register. */
+ g_assert(type_list[type_id] == NULL);
+
+ type_list[type_id] = type;
+}
+
+static sttype_t*
+sttype_lookup(sttype_id_t type_id)
+{
+ sttype_t *result;
+
+ /* Check input */
+ g_assert(type_id >= 0);
+ g_assert(type_id < STTYPE_NUM_TYPES);
+
+ result = type_list[type_id];
+
+ /* Check output. */
+ g_assert(result != NULL);
+
+ return result;
+}
+
+
+stnode_t*
+stnode_new(sttype_id_t type_id, gpointer data)
+{
+ sttype_t *type;
+ stnode_t *node;
+
+ node = g_new(stnode_t, 1);
+ node->magic = STNODE_MAGIC;
+
+ if (type_id == STTYPE_UNINITIALIZED) {
+ node->type = NULL;
+ node->data = NULL;
+ }
+ else {
+ type = sttype_lookup(type_id);
+ g_assert(type);
+ node->type = type;
+ if (type->func_new) {
+ node->data = type->func_new(data);
+ }
+ else {
+ node->data = data;
+ }
+
+ }
+
+ return node;
+}
+
+void
+stnode_init(stnode_t *node, sttype_id_t type_id, gpointer data)
+{
+ sttype_t *type;
+
+ assert_magic(node, STNODE_MAGIC);
+ g_assert(!node->type);
+ g_assert(!node->data);
+
+ type = sttype_lookup(type_id);
+ g_assert(type);
+ node->type = type;
+ if (type->func_new) {
+ node->data = type->func_new(data);
+ }
+ else {
+ node->data = data;
+ }
+}
+
+void
+stnode_free(stnode_t *node)
+{
+ assert_magic(node, STNODE_MAGIC);
+ if (node->type) {
+ if (node->type->func_free) {
+ node->type->func_free(node->data);
+ }
+ }
+ else {
+ g_assert(!node->data);
+ }
+ g_free(node);
+}
+
+const char*
+stnode_type_name(stnode_t *node)
+{
+ assert_magic(node, STNODE_MAGIC);
+ if (node->type)
+ return node->type->name;
+ else
+ return "UNINITIALIZED";
+}
+
+sttype_id_t
+stnode_type_id(stnode_t *node)
+{
+ assert_magic(node, STNODE_MAGIC);
+ if (node->type)
+ return node->type->id;
+ else
+ return STTYPE_UNINITIALIZED;
+}
+
+gpointer
+stnode_data(stnode_t *node)
+{
+ assert_magic(node, STNODE_MAGIC);
+ if (node)
+ return node->data;
+ else
+ return NULL;
+}
diff --git a/epan/dfilter/syntax-tree.h b/epan/dfilter/syntax-tree.h
new file mode 100644
index 0000000000..3f60c09f1a
--- /dev/null
+++ b/epan/dfilter/syntax-tree.h
@@ -0,0 +1,95 @@
+/* syntax-tree.h
+ *
+ * $Id: syntax-tree.h,v 1.1 2001/02/01 20:21:18 gram Exp $
+ *
+ */
+#ifndef SYNTAX_TREE_H
+#define SYNTAX_TREE_H
+
+#include <glib.h>
+#include "cppmagic.h"
+
+typedef enum {
+ STTYPE_UNINITIALIZED,
+ STTYPE_TEST,
+ STTYPE_STRING,
+ STTYPE_FIELD,
+ STTYPE_FVALUE,
+ STTYPE_RANGE,
+ STTYPE_NUM_TYPES
+} sttype_id_t;
+
+typedef gpointer (*STTypeNewFunc)(gpointer);
+typedef void (*STTypeFreeFunc)(gpointer);
+
+
+/* Type information */
+typedef struct {
+ sttype_id_t id;
+ const char *name;
+ STTypeNewFunc func_new;
+ STTypeFreeFunc func_free;
+} sttype_t;
+
+/* Node (type instance) information */
+typedef struct {
+ guint32 magic;
+ sttype_t *type;
+ gpointer data;
+
+} stnode_t;
+
+void
+sttype_init(void);
+
+void
+sttype_cleanup(void);
+
+void
+sttype_register(sttype_t *type);
+
+stnode_t*
+stnode_new(sttype_id_t type_id, gpointer data);
+
+void
+stnode_init(stnode_t *node, sttype_id_t type_id, gpointer data);
+
+void
+stnode_free(stnode_t *node);
+
+const char*
+stnode_type_name(stnode_t *node);
+
+sttype_id_t
+stnode_type_id(stnode_t *node);
+
+gpointer
+stnode_data(stnode_t *node);
+
+#define assert_magic(obj, mnum) \
+ g_assert((obj)); \
+ if ((obj)->magic != (mnum)) { \
+ g_print("\nMagic num is 0x%08x, but should be 0x%08x", \
+ (obj)->magic, (mnum)); \
+ g_assert((obj)->magic == (mnum)); \
+ }
+
+
+
+
+#define STTYPE_ACCESSOR(ret,type,attr,magicnum) \
+ ret \
+ CONCAT(CONCAT(CONCAT(sttype_,type),_),attr) (stnode_t *node) \
+{\
+ CONCAT(type,_t) *value; \
+ value = stnode_data(node);\
+ assert_magic(value, magicnum); \
+ return value->attr; \
+}
+
+#define STTYPE_ACCESSOR_PROTOTYPE(ret,type,attr) \
+ ret \
+ CONCAT(CONCAT(CONCAT(sttype_,type),_),attr) (stnode_t *node);
+
+
+#endif
diff --git a/epan/epan.c b/epan/epan.c
index 74f89d9b15..7ebf350f12 100644
--- a/epan/epan.c
+++ b/epan/epan.c
@@ -1,6 +1,6 @@
/* epan.h
*
- * $Id: epan.c,v 1.5 2001/01/26 06:14:50 guy Exp $
+ * $Id: epan.c,v 1.6 2001/02/01 20:21:16 gram Exp $
*
* Ethereal Protocol Analyzer Library
*
@@ -14,7 +14,7 @@
#include <epan.h>
#include "conversation.h"
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include "except.h"
#include "packet.h"
#include "proto.h"
@@ -69,11 +69,6 @@ epan_conversation_init(void)
}
-struct epan_dissect {
-
- tvbuff_t *tvb;
- proto_tree *tree;
-};
epan_dissect_t*
epan_dissect_new(void* pseudo_header, const guint8* data, frame_data *fd, proto_tree *tree)
@@ -83,6 +78,8 @@ epan_dissect_new(void* pseudo_header, const guint8* data, frame_data *fd, proto_
edt = g_new(epan_dissect_t, 1);
/* XXX - init tree */
+ edt->tree = tree;
+
dissect_packet(&edt->tvb, pseudo_header, data, fd, tree);
return edt;
diff --git a/epan/epan.h b/epan/epan.h
index 3c6a921206..9f94865f2e 100644
--- a/epan/epan.h
+++ b/epan/epan.h
@@ -1,6 +1,6 @@
/* epan.h
*
- * $Id: epan.h,v 1.4 2000/10/16 23:17:39 guy Exp $
+ * $Id: epan.h,v 1.5 2001/02/01 20:21:16 gram Exp $
*
* Ethereal Protocol Analyzer Library
*
@@ -44,7 +44,10 @@ epan_free(epan_t*);
* as the structures that the epan_dissect_t contains might have pointers
* to addresses in your byte array.
*/
-typedef struct epan_dissect epan_dissect_t;
+typedef struct {
+ tvbuff_t *tvb;
+ proto_tree *tree;
+} epan_dissect_t;
epan_dissect_t*
epan_dissect_new(void* pseudo_header, const guint8* data, frame_data *fd, proto_tree *tree);
diff --git a/epan/exceptions.h b/epan/exceptions.h
index 19eda9fe2b..54932dd2f5 100644
--- a/epan/exceptions.h
+++ b/epan/exceptions.h
@@ -11,6 +11,7 @@
/* Ethereal's exceptions */
#define BoundsError 1 /* Index is out of range */
#define ReportedBoundsError 2 /* Index is beyond reported length (not cap_len) */
+#define TypeError 3 /* During dfilter parsing */
/* Usage:
*
diff --git a/epan/ftypes/.cvsignore b/epan/ftypes/.cvsignore
new file mode 100644
index 0000000000..601c4fbff4
--- /dev/null
+++ b/epan/ftypes/.cvsignore
@@ -0,0 +1,4 @@
+.cvsignore
+.deps
+Makefile
+Makefile.in
diff --git a/epan/ftypes/Makefile.am b/epan/ftypes/Makefile.am
new file mode 100644
index 0000000000..3af23be17c
--- /dev/null
+++ b/epan/ftypes/Makefile.am
@@ -0,0 +1,52 @@
+# Makefile.am
+# Automake file for the GTK interface routines for Ethereal
+#
+# $Id: Makefile.am,v 1.1 2001/02/01 20:21:19 gram Exp $
+#
+# Ethereal - Network traffic analyzer
+# By Gerald Combs <gerald@zing.org>
+# Copyright 1998 Gerald Combs
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# We produce an archive library. In the future, when libethereal is a
+# shared library, this will be linked into libethereal. While libethereal
+# is an archive library, any executable linking against libethereal will
+# also need to link against libftypes.
+noinst_LIBRARIES = libftypes.a
+
+CLEANFILES = \
+ libftypes.a \
+ *~
+
+INCLUDES = -I$(srcdir)/../..
+
+libftypes_a_SOURCES = \
+ ftypes.c \
+ ftypes.h \
+ ftypes-int.h \
+ ftype-bytes.c \
+ ftype-double.c \
+ ftype-integer.c \
+ ftype-ipv4.c \
+ ftype-none.c \
+ ftype-string.c \
+ ftype-time.c \
+ ftype-tvbuff.c
+
+#EXTRA_DIST = \
+# Makefile.nmake
+
diff --git a/epan/ftypes/ftype-bytes.c b/epan/ftypes/ftype-bytes.c
new file mode 100644
index 0000000000..726bbbd707
--- /dev/null
+++ b/epan/ftypes/ftype-bytes.c
@@ -0,0 +1,386 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+#include <string.h>
+#include <ctype.h>
+#include "resolv.h"
+
+#define ETHER_LEN 6
+#define IPv6_LEN 16
+
+static void
+ftype_from_tvbuff(field_info *fi, tvbuff_t *tvb, int start, int length,
+ gboolean little_endian)
+{
+ /* XXX */
+ g_assert_not_reached();
+}
+
+
+static void
+bytes_fvalue_new(fvalue_t *fv)
+{
+ fv->value.bytes = NULL;
+}
+
+void
+bytes_fvalue_free(fvalue_t *fv)
+{
+ if (fv->value.bytes) {
+ g_byte_array_free(fv->value.bytes, TRUE);
+ }
+}
+
+
+static void
+bytes_fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied)
+{
+ g_assert(already_copied);
+ fv->value.bytes = value;
+}
+
+static void
+common_fvalue_set(fvalue_t *fv, guint8* data, guint len)
+{
+ fv->value.bytes = g_byte_array_new();
+ g_byte_array_append(fv->value.bytes, data, len);
+}
+
+static void
+ether_fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied)
+{
+ g_assert(!already_copied);
+ common_fvalue_set(fv, value, ETHER_LEN);
+}
+
+static void
+ipv6_fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied)
+{
+ g_assert(!already_copied);
+ common_fvalue_set(fv, value, IPv6_LEN);
+}
+
+static gpointer
+value_get(fvalue_t *fv)
+{
+ return fv->value.bytes->data;
+}
+
+static gboolean
+is_byte_sep(guint8 c)
+{
+ return (c == '-' || c == ':' || c == '.');
+}
+
+static gboolean
+val_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ GByteArray *bytes;
+ guint8 val;
+ char *p, *q, *punct;
+ char two_digits[3];
+ char one_digit[2];
+ gboolean fail = FALSE;
+
+ bytes = g_byte_array_new();
+
+ p = s;
+ while (*p) {
+ q = p+1;
+ if (*q && isxdigit(*p) && isxdigit(*q)) {
+ two_digits[0] = *p;
+ two_digits[1] = *q;
+ two_digits[2] = '\0';
+
+ val = (guint8) strtoul(two_digits, NULL, 16);
+ g_byte_array_append(bytes, &val, 1);
+ punct = q + 1;
+ if (*punct) {
+ if (is_byte_sep(*punct)) {
+ p = punct + 1;
+ continue;
+ }
+ else {
+ fail = TRUE;
+ break;
+ }
+ }
+ else {
+ p = punct;
+ continue;
+ }
+ }
+ else if (*q && isxdigit(*p) && is_byte_sep(*q)) {
+ one_digit[0] = *p;
+ one_digit[1] = '\0';
+
+ val = (guint8) strtoul(one_digit, NULL, 16);
+ g_byte_array_append(bytes, &val, 1);
+ p = q + 1;
+ continue;
+ }
+ else if (!*q && isxdigit(*p)) {
+ one_digit[0] = *p;
+ one_digit[1] = '\0';
+
+ val = (guint8) strtoul(one_digit, NULL, 16);
+ g_byte_array_append(bytes, &val, 1);
+ p = q;
+ continue;
+ }
+ else {
+ fail = TRUE;
+ break;
+ }
+ }
+
+ if (fail) {
+ g_byte_array_free(bytes, TRUE);
+ return FALSE;
+ }
+
+ fv->value.bytes = bytes;
+
+
+ return TRUE;
+}
+
+static gboolean
+ether_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ guint8 *mac;
+
+ if (val_from_string(fv, s, log)) {
+ return TRUE;
+ }
+
+ mac = get_ether_addr(s);
+ if (!mac) {
+ return FALSE;
+ }
+
+ ether_fvalue_set(fv, mac, FALSE);
+ return TRUE;
+}
+
+static gboolean
+ipv6_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ guint8 buffer[16];
+
+ if (!get_host_ipaddr6(s, (struct e_in6_addr*)buffer)) {
+ return FALSE;
+ }
+
+ ipv6_fvalue_set(fv, buffer, FALSE);
+ return TRUE;
+}
+
+static guint
+len(fvalue_t *fv)
+{
+ return fv->value.bytes->len;
+}
+
+static void
+slice(fvalue_t *fv, GByteArray *bytes, guint offset, guint length)
+{
+ guint8* data;
+
+ data = fv->value.bytes->data + offset;
+
+ g_byte_array_append(bytes, data, length);
+}
+
+
+static gboolean
+cmp_eq(fvalue_t *fv_a, fvalue_t *fv_b)
+{
+ GByteArray *a = fv_a->value.bytes;
+ GByteArray *b = fv_b->value.bytes;
+
+ if (a->len != b->len) {
+ return FALSE;
+ }
+
+ return (memcmp(a->data, b->data, a->len) == 0);
+}
+
+
+static gboolean
+cmp_ne(fvalue_t *fv_a, fvalue_t *fv_b)
+{
+ GByteArray *a = fv_a->value.bytes;
+ GByteArray *b = fv_b->value.bytes;
+
+ if (a->len != b->len) {
+ return FALSE;
+ }
+
+ return (memcmp(a->data, b->data, a->len) != 0);
+}
+
+
+static gboolean
+cmp_gt(fvalue_t *fv_a, fvalue_t *fv_b)
+{
+ GByteArray *a = fv_a->value.bytes;
+ GByteArray *b = fv_b->value.bytes;
+
+ if (a->len > b->len) {
+ return TRUE;
+ }
+
+ if (a->len < b->len) {
+ return FALSE;
+ }
+
+ return (memcmp(a->data, b->data, a->len) > 0);
+}
+
+static gboolean
+cmp_ge(fvalue_t *fv_a, fvalue_t *fv_b)
+{
+ GByteArray *a = fv_a->value.bytes;
+ GByteArray *b = fv_b->value.bytes;
+
+ if (a->len > b->len) {
+ return TRUE;
+ }
+
+ if (a->len < b->len) {
+ return FALSE;
+ }
+
+ return (memcmp(a->data, b->data, a->len) >= 0);
+}
+
+static gboolean
+cmp_lt(fvalue_t *fv_a, fvalue_t *fv_b)
+{
+ GByteArray *a = fv_a->value.bytes;
+ GByteArray *b = fv_b->value.bytes;
+
+ if (a->len < b->len) {
+ return TRUE;
+ }
+
+ if (a->len > b->len) {
+ return FALSE;
+ }
+
+ return (memcmp(a->data, b->data, a->len) < 0);
+}
+
+static gboolean
+cmp_le(fvalue_t *fv_a, fvalue_t *fv_b)
+{
+ GByteArray *a = fv_a->value.bytes;
+ GByteArray *b = fv_b->value.bytes;
+
+ if (a->len < b->len) {
+ return TRUE;
+ }
+
+ if (a->len > b->len) {
+ return FALSE;
+ }
+
+ return (memcmp(a->data, b->data, a->len) <= 0);
+}
+
+void
+ftype_register_bytes(void)
+{
+
+ static ftype_t bytes_type = {
+ "FT_BYTES",
+ "sequence of bytes",
+ 0,
+ bytes_fvalue_new,
+ bytes_fvalue_free,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ bytes_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+
+ len,
+ slice,
+ };
+
+ static ftype_t ether_type = {
+ "FT_ETHER",
+ "Ethernet or other MAC address",
+ ETHER_LEN,
+ bytes_fvalue_new,
+ bytes_fvalue_free,
+ ftype_from_tvbuff,
+ ether_from_string,
+
+ ether_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+
+ len,
+ slice,
+ };
+
+ static ftype_t ipv6_type = {
+ "FT_IPv6",
+ "IPv6 address",
+ IPv6_LEN,
+ bytes_fvalue_new,
+ bytes_fvalue_free,
+ ftype_from_tvbuff,
+ ipv6_from_string,
+
+ ipv6_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+
+ len,
+ slice,
+ };
+
+ ftype_register(FT_BYTES, &bytes_type);
+ ftype_register(FT_ETHER, &ether_type);
+ ftype_register(FT_IPv6, &ipv6_type);
+}
diff --git a/epan/ftypes/ftype-double.c b/epan/ftypes/ftype-double.c
new file mode 100644
index 0000000000..146794a597
--- /dev/null
+++ b/epan/ftypes/ftype-double.c
@@ -0,0 +1,133 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+#include <stdlib.h>
+#include <math.h>
+#include <errno.h>
+
+static void
+ftype_from_tvbuff(field_info *fi, tvbuff_t *tvb, int start, int length,
+ gboolean little_endian)
+{
+ /* XXX */
+ g_assert_not_reached();
+}
+
+
+static void
+double_fvalue_new(fvalue_t *fv)
+{
+ fv->value.floating = 0.0;
+}
+
+static void
+double_fvalue_set_floating(fvalue_t *fv, gdouble value)
+{
+ fv->value.floating = value;
+}
+
+static double
+value_get_floating(fvalue_t *fv)
+{
+ return fv->value.floating;
+}
+
+static gboolean
+val_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ char *endptr = NULL;
+
+ fv->value.floating = strtod(s, &endptr);
+
+ if (endptr == s || *endptr != '\0') {
+ /* This isn't a valid number. */
+ log("\"%s\" is not a valid number.", s);
+ return FALSE;
+ }
+ if (errno == ERANGE) {
+ if (fv->value.floating == 0) {
+ log("\"%s\" causes floating-point underflow.", s);
+ }
+ else if (fv->value.floating == HUGE_VAL) {
+ log("\"%s\" causes floating-point overflow.", s);
+ }
+ else {
+ log("\"%s\" is not a valid floating-point number.", s);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+cmp_eq(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.floating == b->value.floating;
+}
+
+static gboolean
+cmp_ne(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.floating != b->value.floating;
+}
+
+static gboolean
+cmp_gt(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.floating > b->value.floating;
+}
+
+static gboolean
+cmp_ge(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.floating >= b->value.floating;
+}
+
+static gboolean
+cmp_lt(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.floating < b->value.floating;
+}
+
+static gboolean
+cmp_le(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.floating <= b->value.floating;
+}
+
+void
+ftype_register_double(void)
+{
+
+ static ftype_t double_type = {
+ "FT_DOUBLE",
+ "floating point",
+ 0,
+ double_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ NULL,
+ double_fvalue_set_floating,
+
+ NULL,
+ NULL,
+ value_get_floating,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+ };
+
+ ftype_register(FT_DOUBLE, &double_type);
+}
diff --git a/epan/ftypes/ftype-integer.c b/epan/ftypes/ftype-integer.c
new file mode 100644
index 0000000000..cef237c7ed
--- /dev/null
+++ b/epan/ftypes/ftype-integer.c
@@ -0,0 +1,417 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#ifdef NEED_SNPRINTF_H
+#include "snprintf.h"
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include "ftypes-int.h"
+#include "resolv.h"
+
+
+static void
+ftype_from_tvbuff(field_info *fi, tvbuff_t *tvb, int start, int length,
+ gboolean little_endian)
+{
+ /* XXX */
+ g_assert_not_reached();
+}
+
+
+static void
+int_fvalue_new(fvalue_t *fv)
+{
+ fv->value.integer = 0;
+}
+
+static void
+set_integer(fvalue_t *fv, guint32 value)
+{
+ fv->value.integer = value;
+}
+
+static guint32
+get_integer(fvalue_t *fv)
+{
+ return fv->value.integer;
+}
+
+static gboolean
+val_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ char *endptr;
+
+ fv->value.integer = strtoul(s, &endptr, 0);
+
+ if (endptr == s || *endptr != '\0') {
+ /* This isn't a valid number. */
+ log("\"%s\" is not a valid number.", s);
+ return FALSE;
+ }
+ if (errno == ERANGE) {
+ if (fv->value.integer == ULONG_MAX) {
+ log("\"%s\" causes an integer overflow.", s);
+ }
+ else {
+ log("\"%s\" is not an integer.", s);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ipxnet_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ guint32 val;
+ gboolean known;
+
+ if (val_from_string(fv, s, log)) {
+ return TRUE;
+ }
+
+ val = get_ipxnet_addr(s, &known);
+ if (known) {
+ fv->value.integer = val;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+cmp_eq(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.integer == b->value.integer;
+}
+
+static gboolean
+cmp_ne(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.integer != b->value.integer;
+}
+
+static gboolean
+u_cmp_gt(fvalue_t *a, fvalue_t *b)
+{
+ return (int)a->value.integer > (int)b->value.integer;
+}
+
+static gboolean
+u_cmp_ge(fvalue_t *a, fvalue_t *b)
+{
+ return (int)a->value.integer >= (int)b->value.integer;
+}
+
+static gboolean
+u_cmp_lt(fvalue_t *a, fvalue_t *b)
+{
+ return (int)a->value.integer < (int)b->value.integer;
+}
+
+static gboolean
+u_cmp_le(fvalue_t *a, fvalue_t *b)
+{
+ return (int)a->value.integer <= (int)b->value.integer;
+}
+
+static gboolean
+s_cmp_gt(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.integer > b->value.integer;
+}
+
+static gboolean
+s_cmp_ge(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.integer >= b->value.integer;
+}
+
+static gboolean
+s_cmp_lt(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.integer < b->value.integer;
+}
+
+static gboolean
+s_cmp_le(fvalue_t *a, fvalue_t *b)
+{
+ return a->value.integer <= b->value.integer;
+}
+
+/* BOOLEAN-specific */
+
+static void
+boolean_fvalue_new(fvalue_t *fv)
+{
+ fv->value.integer = TRUE;
+}
+
+
+
+
+
+void
+ftype_register_integers(void)
+{
+
+ static ftype_t uint8_type = {
+ "FT_UINT8",
+ "unsigned, 1 byte",
+ 1,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ u_cmp_gt,
+ u_cmp_ge,
+ u_cmp_lt,
+ u_cmp_le,
+ };
+ static ftype_t uint16_type = {
+ "FT_UINT16",
+ "unsigned, 2 bytes",
+ 2,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ u_cmp_gt,
+ u_cmp_ge,
+ u_cmp_lt,
+ u_cmp_le,
+ };
+ static ftype_t uint24_type = {
+ "FT_UINT24",
+ "unsigned, 3 bytes",
+ 3,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ u_cmp_gt,
+ u_cmp_ge,
+ u_cmp_lt,
+ u_cmp_le,
+ };
+ static ftype_t uint32_type = {
+ "FT_UINT32",
+ "unsigned, 4 bytes",
+ 4,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ u_cmp_gt,
+ u_cmp_ge,
+ u_cmp_lt,
+ u_cmp_le,
+ };
+ static ftype_t int8_type = {
+ "FT_INT8",
+ "signed, 1 byte",
+ 1,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ s_cmp_gt,
+ s_cmp_ge,
+ s_cmp_lt,
+ s_cmp_le,
+ };
+ static ftype_t int16_type = {
+ "FT_INT16",
+ "signed, 2 bytes",
+ 2,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ s_cmp_gt,
+ s_cmp_ge,
+ s_cmp_lt,
+ s_cmp_le,
+ };
+ static ftype_t int24_type = {
+ "FT_INT24",
+ "signed, 3 bytes",
+ 3,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ s_cmp_gt,
+ s_cmp_ge,
+ s_cmp_lt,
+ s_cmp_le,
+ };
+ static ftype_t int32_type = {
+ "FT_INT32",
+ "signed, 4 bytes",
+ 4,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ s_cmp_gt,
+ s_cmp_ge,
+ s_cmp_lt,
+ s_cmp_le,
+ };
+ static ftype_t boolean_type = {
+ "FT_BOOLEAN",
+ "Boolean",
+ 0,
+ boolean_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ u_cmp_gt,
+ u_cmp_ge,
+ u_cmp_lt,
+ u_cmp_le,
+ };
+
+ static ftype_t ipxnet_type = {
+ "FT_IPXNET",
+ "IPX network number",
+ 4,
+ int_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ ipxnet_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ NULL,
+ get_integer,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ u_cmp_gt,
+ u_cmp_ge,
+ u_cmp_lt,
+ u_cmp_le,
+ };
+
+
+ ftype_register(FT_UINT8, &uint8_type);
+ ftype_register(FT_UINT16, &uint16_type);
+ ftype_register(FT_UINT24, &uint24_type);
+ ftype_register(FT_UINT32, &uint32_type);
+ ftype_register(FT_INT8, &int8_type);
+ ftype_register(FT_INT16, &int16_type);
+ ftype_register(FT_INT24, &int24_type);
+ ftype_register(FT_INT32, &int32_type);
+ ftype_register(FT_BOOLEAN, &boolean_type);
+ ftype_register(FT_IPXNET, &ipxnet_type);
+}
diff --git a/epan/ftypes/ftype-ipv4.c b/epan/ftypes/ftype-ipv4.c
new file mode 100644
index 0000000000..eae5677a25
--- /dev/null
+++ b/epan/ftypes/ftype-ipv4.c
@@ -0,0 +1,114 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+#include "ipv4.h"
+#include "resolv.h"
+
+static void
+ftype_from_tvbuff(field_info *fi, tvbuff_t *tvb, int start, int length,
+ gboolean little_endian)
+{
+ /* XXX */
+ g_assert_not_reached();
+}
+
+
+
+static void
+set_integer(fvalue_t *fv, guint32 value)
+{
+ ipv4_addr_set_net_order_addr(&(fv->value.ipv4), value);
+ ipv4_addr_set_netmask_bits(&(fv->value.ipv4), 32);
+}
+
+static gpointer
+value_get(fvalue_t *fv)
+{
+ return &(fv->value.ipv4);
+}
+
+static gboolean
+val_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ guint32 addr;
+
+ if (!get_host_ipaddr(s, &addr)) {
+ log("\"%s\" is not a valid hostname or IPv4 address.", s);
+ return FALSE;
+ }
+ ipv4_addr_set_host_order_addr(&(fv->value.ipv4), addr);
+ /*ipv4_addr_set_netmask_bits(&node->value.ipv4, nmask_bits);*/
+ ipv4_addr_set_netmask_bits(&(fv->value.ipv4), 32);
+ return TRUE;
+}
+
+static gboolean
+cmp_eq(fvalue_t *a, fvalue_t *b)
+{
+ return ipv4_addr_eq(&a->value.ipv4, &b->value.ipv4);
+}
+
+static gboolean
+cmp_ne(fvalue_t *a, fvalue_t *b)
+{
+ return ipv4_addr_ne(&a->value.ipv4, &b->value.ipv4);
+}
+
+static gboolean
+cmp_gt(fvalue_t *a, fvalue_t *b)
+{
+ return ipv4_addr_gt(&a->value.ipv4, &b->value.ipv4);
+}
+
+static gboolean
+cmp_ge(fvalue_t *a, fvalue_t *b)
+{
+ return ipv4_addr_ge(&a->value.ipv4, &b->value.ipv4);
+}
+
+static gboolean
+cmp_lt(fvalue_t *a, fvalue_t *b)
+{
+ return ipv4_addr_lt(&a->value.ipv4, &b->value.ipv4);
+}
+
+static gboolean
+cmp_le(fvalue_t *a, fvalue_t *b)
+{
+ return ipv4_addr_le(&a->value.ipv4, &b->value.ipv4);
+}
+
+void
+ftype_register_ipv4(void)
+{
+
+ static ftype_t ipv4_type = {
+ "FT_IPv4",
+ "IPv4 address",
+ 4,
+ NULL,
+ NULL,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ NULL,
+ set_integer,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+ };
+
+ ftype_register(FT_IPv4, &ipv4_type);
+}
diff --git a/epan/ftypes/ftype-none.c b/epan/ftypes/ftype-none.c
new file mode 100644
index 0000000000..1bb64a3759
--- /dev/null
+++ b/epan/ftypes/ftype-none.c
@@ -0,0 +1,20 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+
+
+void
+ftype_register_none(void)
+{
+
+ static ftype_t none_type = {
+ "FT_NONE",
+ "label",
+ 0,
+ };
+
+ ftype_register(FT_NONE, &none_type);
+}
diff --git a/epan/ftypes/ftype-string.c b/epan/ftypes/ftype-string.c
new file mode 100644
index 0000000000..38ce87faa0
--- /dev/null
+++ b/epan/ftypes/ftype-string.c
@@ -0,0 +1,198 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+#include <string.h>
+
+static void
+ftype_from_tvbuff(field_info *fi, tvbuff_t *tvb, int start, int length,
+ gboolean little_endian)
+{
+ /* XXX */
+ g_assert_not_reached();
+}
+
+
+static void
+string_fvalue_new(fvalue_t *fv)
+{
+ fv->value.string = NULL;
+}
+
+static void
+string_fvalue_free(fvalue_t *fv)
+{
+ if (fv->value.string) {
+ g_free(fv->value.string);
+ }
+}
+
+static void
+string_fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied)
+{
+ if (already_copied) {
+ fv->value.string = value;
+ }
+ else {
+ fv->value.string = g_strdup(value);
+ }
+}
+
+static gpointer
+value_get(fvalue_t *fv)
+{
+ return fv->value.string;
+}
+
+static gboolean
+val_from_string(fvalue_t *fv, char *s, LogFunc log)
+{
+ fv->value.string = g_strdup(s);
+ return TRUE;
+}
+
+static guint
+len(fvalue_t *fv)
+{
+ return strlen(fv->value.string);
+}
+
+static void
+slice(fvalue_t *fv, GByteArray *bytes, guint offset, guint length)
+{
+ guint8* data;
+
+ data = fv->value.string + offset;
+
+ g_byte_array_append(bytes, data, length);
+}
+
+
+static gboolean
+cmp_eq(fvalue_t *a, fvalue_t *b)
+{
+ return (strcmp(a->value.string, b->value.string) == 0);
+}
+
+static gboolean
+cmp_ne(fvalue_t *a, fvalue_t *b)
+{
+ return (strcmp(a->value.string, b->value.string) != 0);
+}
+
+static gboolean
+cmp_gt(fvalue_t *a, fvalue_t *b)
+{
+ return (strcmp(a->value.string, b->value.string) > 0);
+}
+
+static gboolean
+cmp_ge(fvalue_t *a, fvalue_t *b)
+{
+ return (strcmp(a->value.string, b->value.string) >= 0);
+}
+
+static gboolean
+cmp_lt(fvalue_t *a, fvalue_t *b)
+{
+ return (strcmp(a->value.string, b->value.string) < 0);
+}
+
+static gboolean
+cmp_le(fvalue_t *a, fvalue_t *b)
+{
+ return (strcmp(a->value.string, b->value.string) <= 0);
+}
+
+void
+ftype_register_string(void)
+{
+
+ static ftype_t string_type = {
+ "FT_STRING",
+ "character string",
+ 0,
+ string_fvalue_new,
+ string_fvalue_free,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ string_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+
+ len,
+ slice,
+ };
+ static ftype_t stringz_type = {
+ "FT_STRINGZ",
+ "character string",
+ 0,
+ string_fvalue_new,
+ string_fvalue_free,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ string_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+
+ len,
+ slice,
+ };
+ static ftype_t uint_string_type = {
+ "FT_UINT_STRING",
+ "character string",
+ 0,
+ string_fvalue_new,
+ string_fvalue_free,
+ ftype_from_tvbuff,
+ val_from_string,
+
+ string_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ cmp_eq,
+ cmp_ne,
+ cmp_gt,
+ cmp_ge,
+ cmp_lt,
+ cmp_le,
+
+ len,
+ slice,
+ };
+
+ ftype_register(FT_STRING, &string_type);
+ ftype_register(FT_STRINGZ, &stringz_type);
+ ftype_register(FT_UINT_STRING, &uint_string_type);
+}
diff --git a/epan/ftypes/ftype-time.c b/epan/ftypes/ftype-time.c
new file mode 100644
index 0000000000..6eb2a3d4cb
--- /dev/null
+++ b/epan/ftypes/ftype-time.c
@@ -0,0 +1,78 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+
+static void
+ftype_from_tvbuff(field_info *fi, tvbuff_t *tvb, int start, int length,
+ gboolean little_endian)
+{
+ /* XXX */
+ g_assert_not_reached();
+}
+
+
+static void
+time_fvalue_new(fvalue_t *fv)
+{
+ fv->value.time.tv_sec = 0;
+ fv->value.time.tv_usec = 0;
+}
+
+static void
+time_fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied)
+{
+ g_assert(!already_copied);
+ memcpy(&(fv->value.time), value, sizeof(struct timeval));
+}
+
+static gpointer
+value_get(fvalue_t *fv)
+{
+ return &(fv->value.time);
+}
+
+void
+ftype_register_time(void)
+{
+
+ static ftype_t abstime_type = {
+ "FT_ABSOLUTE_TIME",
+ "date/time",
+ 0,
+ time_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ NULL,
+
+ time_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL
+ };
+ static ftype_t reltime_type = {
+ "FT_RELATIVE_TIME",
+ "time offset",
+ 0,
+ time_fvalue_new,
+ NULL,
+ ftype_from_tvbuff,
+ NULL,
+
+ time_fvalue_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL
+ };
+
+ ftype_register(FT_ABSOLUTE_TIME, &abstime_type);
+ ftype_register(FT_RELATIVE_TIME, &reltime_type);
+}
diff --git a/epan/ftypes/ftype-tvbuff.c b/epan/ftypes/ftype-tvbuff.c
new file mode 100644
index 0000000000..e9617ca607
--- /dev/null
+++ b/epan/ftypes/ftype-tvbuff.c
@@ -0,0 +1,84 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+#include "gdebug.h"
+
+static void
+value_new(fvalue_t *fv)
+{
+ fv->value.tvb = NULL;
+}
+
+
+static void
+value_set(fvalue_t *fv, gpointer value, gboolean already_copied)
+{
+ g_assert(already_copied);
+ fv->value.tvb = value;
+}
+
+static gpointer
+value_get(fvalue_t *fv)
+{
+ return fv->value.tvb;
+}
+
+static guint
+len(fvalue_t *fv)
+{
+ if (fv->value.tvb)
+ return tvb_length(fv->value.tvb);
+ else
+ return 0;
+}
+
+static void
+slice(fvalue_t *fv, GByteArray *bytes, guint offset, guint length)
+{
+ guint8* data;
+
+ if (fv->value.tvb) {
+ data = tvb_get_ptr(fv->value.tvb, offset, length);
+ g_byte_array_append(bytes, data, length);
+ }
+}
+
+void
+ftype_register_tvbuff(void)
+{
+
+ static ftype_t protocol_type = {
+ "FT_PROTOCOL",
+ "protocol",
+ 0,
+ value_new,
+ NULL,
+ NULL,
+ NULL,
+
+ value_set,
+ NULL,
+ NULL,
+
+ value_get,
+ NULL,
+ NULL,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ len,
+ slice,
+
+ };
+
+
+ ftype_register(FT_PROTOCOL, &protocol_type);
+}
diff --git a/epan/ftypes/ftypes-int.h b/epan/ftypes/ftypes-int.h
new file mode 100644
index 0000000000..4e6ce75bee
--- /dev/null
+++ b/epan/ftypes/ftypes-int.h
@@ -0,0 +1,60 @@
+#ifndef FTYPES_INT_H
+#define FTYPES_INT_H
+
+#include "packet.h"
+#include "ftypes.h"
+
+typedef void (*FtypeFromTvbuffFunc)(field_info*, tvbuff_t*, int, int, gboolean);
+typedef void (*FvalueNewFunc)(fvalue_t*);
+typedef void (*FvalueFreeFunc)(fvalue_t*);
+
+typedef gboolean (*FvalueFromString)(fvalue_t*, char*, LogFunc);
+
+typedef void (*FvalueSetFunc)(fvalue_t*, gpointer, gboolean);
+typedef void (*FvalueSetIntegerFunc)(fvalue_t*, guint32);
+typedef void (*FvalueSetFloatingFunc)(fvalue_t*, gdouble);
+
+typedef gpointer (*FvalueGetFunc)(fvalue_t*);
+typedef guint32 (*FvalueGetIntegerFunc)(fvalue_t*);
+typedef double (*FvalueGetFloatingFunc)(fvalue_t*);
+
+typedef gboolean (*FvalueCmp)(fvalue_t*, fvalue_t*);
+
+typedef guint (*FvalueLen)(fvalue_t*);
+typedef void (*FvalueSlice)(fvalue_t*, GByteArray *, guint, guint);
+
+struct _ftype_t {
+ const char *name;
+ const char *pretty_name;
+ int wire_size;
+ FvalueNewFunc new_value;
+ FvalueFreeFunc free_value;
+ FtypeFromTvbuffFunc from_tvbuff;
+ FvalueFromString val_from_string;
+
+ /* could be union */
+ FvalueSetFunc set_value;
+ FvalueSetIntegerFunc set_value_integer;
+ FvalueSetFloatingFunc set_value_floating;
+
+ /* could be union */
+ FvalueGetFunc get_value;
+ FvalueGetIntegerFunc get_value_integer;
+ FvalueGetFloatingFunc get_value_floating;
+
+ FvalueCmp cmp_eq;
+ FvalueCmp cmp_ne;
+ FvalueCmp cmp_gt;
+ FvalueCmp cmp_ge;
+ FvalueCmp cmp_lt;
+ FvalueCmp cmp_le;
+
+ FvalueLen len;
+ FvalueSlice slice;
+};
+
+
+void
+ftype_register(enum ftenum ftype, ftype_t *ft);
+
+#endif
diff --git a/epan/ftypes/ftypes.c b/epan/ftypes/ftypes.c
new file mode 100644
index 0000000000..7234008d53
--- /dev/null
+++ b/epan/ftypes/ftypes.c
@@ -0,0 +1,410 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ftypes-int.h>
+
+/* Keep track of ftype_t's via their ftenum number */
+static ftype_t* type_list[FT_NUM_TYPES];
+
+/* Space for quickly allocating/de-allocating fvalue_t's */
+static GMemChunk *gmc_fvalue = NULL;
+
+/* These are the ftype registration functions that need to be called.
+ * This list and the initialization function could be produced
+ * via a script, like the dissector registration, but there's so few
+ * that I don't mind doing it by hand for now. */
+void ftype_register_bytes(void);
+void ftype_register_double(void);
+void ftype_register_integers(void);
+void ftype_register_ipv4(void);
+void ftype_register_none(void);
+void ftype_register_string(void);
+void ftype_register_time(void);
+void ftype_register_tvbuff(void);
+
+/* Initialize the ftype module. */
+void
+ftypes_initialize(void)
+{
+ ftype_register_bytes();
+ ftype_register_double();
+ ftype_register_integers();
+ ftype_register_ipv4();
+ ftype_register_none();
+ ftype_register_string();
+ ftype_register_time();
+ ftype_register_tvbuff();
+
+ if (gmc_fvalue)
+ g_mem_chunk_destroy(gmc_fvalue);
+
+ gmc_fvalue = g_mem_chunk_new("gmc_fvalue", sizeof(fvalue_t),
+ 200 * sizeof(fvalue_t), G_ALLOC_AND_FREE);
+}
+
+void
+ftypes_cleanup(void)
+{
+ if (gmc_fvalue)
+ g_mem_chunk_destroy(gmc_fvalue);
+}
+
+
+
+/* Each ftype_t is registered via this function */
+void
+ftype_register(enum ftenum ftype, ftype_t *ft)
+{
+ /* Check input */
+ g_assert(ftype >= 0);
+ g_assert(ftype < FT_NUM_TYPES);
+
+ /* Don't re-register. */
+ g_assert(type_list[ftype] == NULL);
+
+ type_list[ftype] = ft;
+}
+
+/* Given an ftenum number, return an ftype_t* */
+static ftype_t*
+ftype_lookup(enum ftenum ftype)
+{
+ ftype_t* result;
+
+ /* Check input */
+ g_assert(ftype >= 0);
+ g_assert(ftype < FT_NUM_TYPES);
+
+ result = type_list[ftype];
+
+ /* Check output. */
+ g_assert(result != NULL);
+
+ return result;
+}
+
+
+/* Returns a string representing the name of the type. Useful
+ * for glossary production. */
+const char*
+ftype_name(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->name;
+}
+
+const char*
+ftype_pretty_name(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->pretty_name;
+}
+
+int
+ftype_length(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->wire_size;
+}
+
+gboolean
+ftype_can_slice(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->slice ? TRUE : FALSE;
+}
+
+gboolean
+ftype_can_eq(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->cmp_eq ? TRUE : FALSE;
+}
+
+gboolean
+ftype_can_ne(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->cmp_ne ? TRUE : FALSE;
+}
+
+gboolean
+ftype_can_gt(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->cmp_gt ? TRUE : FALSE;
+}
+
+gboolean
+ftype_can_ge(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->cmp_ge ? TRUE : FALSE;
+}
+
+gboolean
+ftype_can_lt(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->cmp_lt ? TRUE : FALSE;
+}
+
+gboolean
+ftype_can_le(enum ftenum ftype)
+{
+ ftype_t *ft;
+
+ ft = ftype_lookup(ftype);
+ return ft->cmp_le ? TRUE : FALSE;
+}
+
+/* ---------------------------------------------------------- */
+
+/* Allocate and initialize an fvalue_t, given an ftype */
+fvalue_t*
+fvalue_new(ftenum_t ftype)
+{
+ fvalue_t *fv;
+ ftype_t *ft;
+ FvalueNewFunc new_value;
+
+ fv = g_mem_chunk_alloc(gmc_fvalue);
+
+ ft = ftype_lookup(ftype);
+ fv->ftype = ft;
+
+ new_value = ft->new_value;
+ if (new_value) {
+ new_value(fv);
+ }
+
+ return fv;
+}
+
+/* Free all memory used by an fvalue_t */
+void
+fvalue_free(fvalue_t *fv)
+{
+ FvalueFreeFunc free_value;
+
+ free_value = fv->ftype->free_value;
+ if (free_value) {
+ free_value(fv);
+ }
+
+ g_mem_chunk_free(gmc_fvalue, fv);
+}
+
+
+
+fvalue_t*
+fvalue_from_string(ftenum_t ftype, char *s, LogFunc log)
+{
+ fvalue_t *fv;
+
+ fv = fvalue_new(ftype);
+ if (fv->ftype->val_from_string) {
+ if (fv->ftype->val_from_string(fv, s, log)) {
+ return fv;
+ }
+ }
+ else {
+ log("\"%s\" cannot be converted to %s.",
+ s, ftype_pretty_name(ftype));
+ }
+ fvalue_free(fv);
+ return NULL;
+}
+
+const char*
+fvalue_type_name(fvalue_t *fv)
+{
+ return fv->ftype->name;
+}
+
+
+guint
+fvalue_length(fvalue_t *fv)
+{
+ if (fv->ftype->len)
+ return fv->ftype->len(fv);
+ else
+ return fv->ftype->wire_size;
+}
+
+/* Returns a new FT_BYTES fvalue_t* if possible, otherwise NULL */
+fvalue_t*
+fvalue_slice(fvalue_t *fv, gint start, gint end)
+{
+ GByteArray *bytes;
+ guint data_length, abs_end;
+ guint offset=0, length=0;
+ fvalue_t *new_fv;
+
+ if (!fv->ftype->slice) {
+ return NULL;
+ }
+
+ data_length = fvalue_length(fv);
+ bytes = g_byte_array_new();
+
+ /* Find absolute start position (offset) */
+ if (start < 0) {
+ start = data_length + start;
+ if (start < 0) {
+ offset = 0;
+ }
+ else {
+ offset = start;
+ }
+ }
+ else {
+ offset = start;
+ }
+
+ /* Limit the offset value */
+ if (offset > data_length) {
+ offset = data_length;
+ }
+
+ /* Find absolute end position (abs_end) */
+ if (end < 0) {
+ end = data_length + end;
+ if (end < 0) {
+ abs_end = 0;
+ }
+ else {
+ abs_end = end;
+ }
+ }
+ else {
+ abs_end = end;
+ }
+
+ /* Limit the abs_end value */
+ if (abs_end > data_length) {
+ abs_end = data_length;
+ }
+
+ /* Does end position occur *after* start position? */
+ if (abs_end > offset) {
+ length = abs_end - offset;
+ fv->ftype->slice(fv, bytes, offset, length);
+ }
+
+ new_fv = fvalue_new(FT_BYTES);
+ fvalue_set(new_fv, bytes, TRUE);
+ return new_fv;
+}
+
+
+void
+fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied)
+{
+ g_assert(fv->ftype->set_value);
+ fv->ftype->set_value(fv, value, already_copied);
+}
+
+void
+fvalue_set_integer(fvalue_t *fv, guint32 value)
+{
+ g_assert(fv->ftype->set_value_integer);
+ fv->ftype->set_value_integer(fv, value);
+}
+
+void
+fvalue_set_floating(fvalue_t *fv, gdouble value)
+{
+ g_assert(fv->ftype->set_value_floating);
+ fv->ftype->set_value_floating(fv, value);
+}
+
+
+gpointer
+fvalue_get(fvalue_t *fv)
+{
+ g_assert(fv->ftype->get_value);
+ return fv->ftype->get_value(fv);
+}
+
+guint32
+fvalue_get_integer(fvalue_t *fv)
+{
+ g_assert(fv->ftype->get_value_integer);
+ return fv->ftype->get_value_integer(fv);
+}
+
+double
+fvalue_get_floating(fvalue_t *fv)
+{
+ g_assert(fv->ftype->get_value_floating);
+ return fv->ftype->get_value_floating(fv);
+}
+
+gboolean
+fvalue_eq(fvalue_t *a, fvalue_t *b)
+{
+ /* XXX - check compatibility of a and b */
+ g_assert(a->ftype->cmp_eq);
+ return a->ftype->cmp_eq(a, b);
+}
+
+gboolean
+fvalue_ne(fvalue_t *a, fvalue_t *b)
+{
+ /* XXX - check compatibility of a and b */
+ g_assert(a->ftype->cmp_ne);
+ return a->ftype->cmp_ne(a, b);
+}
+
+gboolean
+fvalue_gt(fvalue_t *a, fvalue_t *b)
+{
+ /* XXX - check compatibility of a and b */
+ g_assert(a->ftype->cmp_gt);
+ return a->ftype->cmp_gt(a, b);
+}
+
+gboolean
+fvalue_ge(fvalue_t *a, fvalue_t *b)
+{
+ /* XXX - check compatibility of a and b */
+ g_assert(a->ftype->cmp_ge);
+ return a->ftype->cmp_ge(a, b);
+}
+
+gboolean
+fvalue_lt(fvalue_t *a, fvalue_t *b)
+{
+ /* XXX - check compatibility of a and b */
+ g_assert(a->ftype->cmp_lt);
+ return a->ftype->cmp_lt(a, b);
+}
+
+gboolean
+fvalue_le(fvalue_t *a, fvalue_t *b)
+{
+ /* XXX - check compatibility of a and b */
+ g_assert(a->ftype->cmp_le);
+ return a->ftype->cmp_le(a, b);
+}
diff --git a/epan/ftypes/ftypes.h b/epan/ftypes/ftypes.h
new file mode 100644
index 0000000000..1431f35427
--- /dev/null
+++ b/epan/ftypes/ftypes.h
@@ -0,0 +1,197 @@
+/* ftypes.h
+ * Definitions for field types
+ *
+ * $Id: ftypes.h,v 1.1 2001/02/01 20:21:19 gram Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 2001 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef FTYPES_H
+#define FTYPES_H
+
+#include <glib.h>
+
+
+/* field types */
+enum ftenum {
+ FT_NONE, /* used for text labels with no value */
+ FT_PROTOCOL,
+ FT_BOOLEAN, /* TRUE and FALSE come from <glib.h> */
+ FT_UINT8,
+ FT_UINT16,
+ FT_UINT24, /* really a UINT32, but displayed as 3 hex-digits if FD_HEX*/
+ FT_UINT32,
+ FT_INT8,
+ FT_INT16,
+ FT_INT24,
+ FT_INT32,
+ FT_DOUBLE,
+ FT_ABSOLUTE_TIME,
+ FT_RELATIVE_TIME,
+ FT_STRING,
+ FT_STRINGZ, /* for use with proto_tree_add_item() */
+ FT_UINT_STRING, /* for use with proto_tree_add_item() */
+ FT_ETHER,
+ FT_BYTES,
+ FT_IPv4,
+ FT_IPv6,
+ FT_IPXNET,
+/* FT_TEXT_ONLY,*/ /* non-filterable, used when converting ethereal
+ from old-style proto_tree to new-style proto_tree */
+ FT_NUM_TYPES /* last item number plus one */
+};
+
+typedef enum ftenum ftenum_t;
+typedef struct _ftype_t ftype_t;
+
+/* Initialize the ftypes subsytem. Called once. */
+void
+ftypes_initialize(void);
+
+/* Cleanup the ftypes subsystem. Called once. */
+void
+ftypes_cleanup(void);
+
+
+/* ---------------- FTYPE ----------------- */
+
+/* Return a string representing the name of the type */
+const char*
+ftype_name(ftenum_t ftype);
+
+/* Return a string presenting a "pretty" representation of the
+ * name of the type. The pretty name means more to the user than
+ * that "FT_*" name. */
+const char*
+ftype_pretty_name(ftenum_t ftype);
+
+/* Returns length of field in packet, or 0 if not determinable/defined. */
+int
+ftype_length(ftenum_t ftype);
+
+gboolean
+ftype_can_slice(enum ftenum ftype);
+
+gboolean
+ftype_can_eq(enum ftenum ftype);
+
+gboolean
+ftype_can_ne(enum ftenum ftype);
+
+gboolean
+ftype_can_gt(enum ftenum ftype);
+
+gboolean
+ftype_can_ge(enum ftenum ftype);
+
+gboolean
+ftype_can_lt(enum ftenum ftype);
+
+gboolean
+ftype_can_le(enum ftenum ftype);
+
+/* ---------------- FVALUE ----------------- */
+
+#include "ipv4.h"
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_WINSOCK_H
+#include <winsock.h>
+#endif
+
+#include "tvbuff.h"
+
+typedef struct {
+ ftype_t *ftype;
+ union {
+ /* Put a few basic types in here */
+ gpointer pointer;
+ guint32 integer;
+ gdouble floating;
+ gchar *string;
+ GByteArray *bytes;
+ ipv4_addr ipv4;
+ guint8 ipv6[16];
+ struct timeval time;
+ tvbuff_t *tvb;
+ } value;
+} fvalue_t;
+
+fvalue_t*
+fvalue_new(ftenum_t ftype);
+
+void
+fvalue_free(fvalue_t *fv);
+
+typedef void (*LogFunc)(char*,...);
+
+fvalue_t*
+fvalue_from_string(ftenum_t ftype, char *s, LogFunc log);
+
+const char*
+fvalue_type_name(fvalue_t *fv);
+
+void
+fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied);
+
+void
+fvalue_set_integer(fvalue_t *fv, guint32 value);
+
+void
+fvalue_set_floating(fvalue_t *fv, gdouble value);
+
+gpointer
+fvalue_get(fvalue_t *fv);
+
+guint32
+fvalue_get_integer(fvalue_t *fv);
+
+double
+fvalue_get_floating(fvalue_t *fv);
+
+gboolean
+fvalue_eq(fvalue_t *a, fvalue_t *b);
+
+gboolean
+fvalue_ne(fvalue_t *a, fvalue_t *b);
+
+gboolean
+fvalue_gt(fvalue_t *a, fvalue_t *b);
+
+gboolean
+fvalue_ge(fvalue_t *a, fvalue_t *b);
+
+gboolean
+fvalue_lt(fvalue_t *a, fvalue_t *b);
+
+gboolean
+fvalue_le(fvalue_t *a, fvalue_t *b);
+
+guint
+fvalue_length(fvalue_t *fv);
+
+fvalue_t*
+fvalue_slice(fvalue_t *fv, gint start, gint end);
+
+#endif /* ftypes.h */
diff --git a/epan/gdebug.h b/epan/gdebug.h
new file mode 100644
index 0000000000..230931fbf8
--- /dev/null
+++ b/epan/gdebug.h
@@ -0,0 +1,38 @@
+/* $Id: gdebug.h,v 1.1 2001/02/01 20:21:16 gram Exp $ */
+
+#ifndef GDEBUG_H
+#define GDEBUG_H
+
+#ifdef __GNUC__
+
+/* The last "%s" in g_log() is for the empty-string arg that
+ * g_debug() always passes. */
+#define _g_debug(format, args...) \
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
+ "%s():%s +%d: " format "%s", \
+ G_GNUC_PRETTY_FUNCTION, __FILE__, __LINE__, ##args) ;
+
+/* Always pass a empty-string argument to _g_debug() so that g_debug will always
+ * have at least 2 arguments. If user passes 1 arg to g_debug() (i.e., only
+ * a format string), _g_debug() will still work. */
+#define g_debug(args...) \
+ _g_debug(args, "")
+
+
+#else
+
+#include <stdio.h>
+#include <stdarg.h>
+
+static void
+g_debug(const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+#endif /* __GNUC__ */
+
+#endif /* GDEBUG_H */
diff --git a/epan/ipv4.h b/epan/ipv4.h
index 8154465cfb..20728bd56d 100644
--- a/epan/ipv4.h
+++ b/epan/ipv4.h
@@ -5,7 +5,7 @@
*
* Gilbert Ramirez <gram@xiexie.org>
*
- * $Id: ipv4.h,v 1.1 2000/09/28 03:52:12 gram Exp $
+ * $Id: ipv4.h,v 1.2 2001/02/01 20:21:16 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -30,6 +30,8 @@
#ifndef __IPV4_H__
#define __IPV4_H__
+#include <glib.h>
+
typedef struct {
guint32 addr; /* stored in host order */
guint32 nmask; /* stored in host order */
diff --git a/epan/proto.c b/epan/proto.c
index fd45be0eae..29df3f68bc 100644
--- a/epan/proto.c
+++ b/epan/proto.c
@@ -1,7 +1,7 @@
/* proto.c
* Routines for protocol tree
*
- * $Id: proto.c,v 1.6 2001/02/01 07:34:30 guy Exp $
+ * $Id: proto.c,v 1.7 2001/02/01 20:21:16 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -60,7 +60,7 @@ static void fill_label_numeric_bitfield(field_info *fi, gchar *label_str);
static void fill_label_int(field_info *fi, gchar *label_str);
static void fill_label_enumerated_int(field_info *fi, gchar *label_str);
-static int hfinfo_bitwidth(header_field_info *hfinfo);
+int hfinfo_bitwidth(header_field_info *hfinfo);
static char* hfinfo_uint_vals_format(header_field_info *hfinfo);
static char* hfinfo_uint_format(header_field_info *hfinfo);
static char* hfinfo_int_vals_format(header_field_info *hfinfo);
@@ -166,7 +166,7 @@ proto_init(const char *plugin_dir)
{
static hf_register_info hf[] = {
{ &hf_text_only,
- { "Text", "text", FT_TEXT_ONLY, BASE_NONE, NULL, 0x0,
+ { "Text", "text", FT_NONE, BASE_NONE, NULL, 0x0,
"" }},
};
@@ -182,8 +182,7 @@ proto_init(const char *plugin_dir)
g_free(tree_is_expanded);
gmc_hfinfo = g_mem_chunk_new("gmc_hfinfo",
- sizeof(struct header_field_info), 50 * sizeof(struct
- header_field_info), G_ALLOC_ONLY);
+ sizeof(header_field_info), 50 * sizeof(header_field_info), G_ALLOC_ONLY);
gmc_field_info = g_mem_chunk_new("gmc_field_info",
sizeof(struct field_info), 200 * sizeof(struct field_info),
G_ALLOC_AND_FREE);
@@ -198,6 +197,9 @@ proto_init(const char *plugin_dir)
tree_is_expanded[0] = FALSE;
num_tree_types = 1;
+ /* Initialize the ftype subsystem */
+ ftypes_initialize();
+
/* Have each built-in dissector register its protocols, fields,
dissector tables, and dissectors to be called through a
handle, and do whatever one-time initialization it needs to
@@ -241,6 +243,9 @@ proto_cleanup(void)
g_mem_chunk_destroy(gmc_item_labels);
if (gpa_hfinfo)
g_ptr_array_free(gpa_hfinfo, FALSE);
+
+ /* Cleanup the ftype subsystem */
+ ftypes_cleanup();
}
/* frees the resources that the dissection a proto_tree uses */
@@ -265,23 +270,17 @@ proto_tree_free_node(GNode *node, gpointer data)
field_info *fi = (field_info*) (node->data);
if (fi != NULL) {
- if (fi->representation)
+ if (fi->representation) {
g_mem_chunk_free(gmc_item_labels, fi->representation);
- if (fi->hfinfo->type == FT_STRING)
- g_free(fi->value.string);
- else if (fi->hfinfo->type == FT_STRINGZ)
- g_free(fi->value.string);
- else if (fi->hfinfo->type == FT_UINT_STRING)
- g_free(fi->value.string);
- else if (fi->hfinfo->type == FT_BYTES)
- g_free(fi->value.bytes);
+ }
+ fvalue_free(fi->value);
free_field_info(fi);
}
return FALSE; /* FALSE = do not end traversal of GNode tree */
}
/* Finds a record in the hf_info_records array by id. */
-struct header_field_info*
+header_field_info*
proto_registrar_get_nth(int hfindex)
{
g_assert(hfindex >= 0 && hfindex < gpa_hfinfo->len);
@@ -458,6 +457,10 @@ proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
/* no value to set for FT_NONE */
break;
+ case FT_PROTOCOL:
+ proto_tree_set_protocol_tvb(new_fi, tvb);
+ break;
+
case FT_BYTES:
proto_tree_set_bytes_tvb(new_fi, tvb, start, length);
break;
@@ -542,7 +545,7 @@ proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
default:
g_error("new_fi->hfinfo->type %d (%s) not handled\n",
new_fi->hfinfo->type,
- proto_registrar_ftype_name(new_fi->hfinfo->type));
+ ftype_name(new_fi->hfinfo->type));
g_assert_not_reached();
break;
@@ -576,7 +579,7 @@ proto_tree_add_item_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb,
/* Add a FT_NONE to a proto_tree */
proto_item *
-proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
+proto_tree_add_none_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
gint length, const char *format, ...)
{
proto_item *pi;
@@ -596,10 +599,48 @@ proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gin
va_end(ap);
/* no value to set for FT_NONE */
+ return pi;
+}
+
+
+static void
+proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb)
+{
+ fvalue_set(fi->value, tvb, TRUE);
+}
+
+/* Add a FT_PROTOCOL to a proto_tree */
+proto_item *
+proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
+ gint length, const char *format, ...)
+{
+ proto_item *pi;
+ va_list ap;
+ header_field_info *hfinfo;
+ field_info *new_fi;
+
+ if (!tree)
+ return (NULL);
+ hfinfo = proto_registrar_get_nth(hfindex);
+ g_assert(hfinfo->type == FT_PROTOCOL);
+
+ pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi);
+
+ va_start(ap, format);
+ proto_tree_set_representation(pi, format, ap);
+ va_end(ap);
+
+ if (start == 0) {
+ proto_tree_set_protocol_tvb(new_fi, tvb);
+ }
+ else {
+ proto_tree_set_protocol_tvb(new_fi, NULL);
+ }
return pi;
}
+
/* Add a FT_BYTES to a proto_tree */
proto_item *
proto_tree_add_bytes(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
@@ -656,34 +697,23 @@ proto_tree_add_bytes_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint s
return pi;
}
-/* Set the FT_BYTES value */
static void
proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length)
{
- g_assert(start_ptr != NULL);
+ GByteArray *bytes;
+ bytes = g_byte_array_new();
if (length > 0) {
- /* This g_malloc'ed memory is freed in
- proto_tree_free_node() */
- fi->value.bytes = g_malloc(length);
- memcpy(fi->value.bytes, start_ptr, length);
- }
- else {
- fi->value.bytes = NULL;
+ g_byte_array_append(bytes, start_ptr, length);
}
+ fvalue_set(fi->value, bytes, TRUE);
}
+
static void
proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length)
{
- if (length > 0) {
- /* This g_malloc'ed memory is freed in
- proto_tree_free_node() */
- fi->value.bytes = tvb_memdup(tvb, offset, length);
- }
- else {
- fi->value.bytes = NULL;
- }
+ proto_tree_set_bytes(fi, tvb_get_ptr(tvb, offset, length), length);
}
/* Add a FT_*TIME to a proto_tree */
@@ -747,7 +777,7 @@ proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint st
static void
proto_tree_set_time(field_info *fi, struct timeval *value_ptr)
{
- memcpy(&fi->value.time, value_ptr, sizeof(struct timeval));
+ fvalue_set(fi->value, value_ptr, FALSE);
}
/* Add a FT_IPXNET to a proto_tree */
@@ -810,7 +840,7 @@ proto_tree_add_ipxnet_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint
static void
proto_tree_set_ipxnet(field_info *fi, guint32 value)
{
- fi->value.numeric = value;
+ fvalue_set_integer(fi->value, value);
}
/* Add a FT_IPv4 to a proto_tree */
@@ -873,8 +903,7 @@ proto_tree_add_ipv4_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint st
static void
proto_tree_set_ipv4(field_info *fi, guint32 value)
{
- ipv4_addr_set_net_order_addr(&(fi->value.ipv4), value);
- ipv4_addr_set_netmask_bits(&(fi->value.ipv4), 32);
+ fvalue_set_integer(fi->value, value);
}
/* Add a FT_IPv6 to a proto_tree */
@@ -937,13 +966,13 @@ proto_tree_add_ipv6_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint st
static void
proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr)
{
- memcpy(fi->value.ipv6, value_ptr, 16);
+ fvalue_set(fi->value, (gpointer) value_ptr, FALSE);
}
static void
proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start)
{
- tvb_memcpy(tvb, fi->value.ipv6, start, 16);
+ proto_tree_set_ipv6(fi, tvb_get_ptr(tvb, start, 16));
}
/* Add a FT_STRING to a proto_tree */
@@ -1006,17 +1035,19 @@ proto_tree_add_string_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint
static void
proto_tree_set_string(field_info *fi, const char* value)
{
- /* This g_strdup'ed memory is freed in proto_tree_free_node() */
- fi->value.string = g_strdup(value);
+ fvalue_set(fi->value, (gpointer) value, FALSE);
}
static void
proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length)
{
+ gchar *string;
+
/* This memory is freed in proto_tree_free_node() */
- fi->value.string = g_malloc(length + 1);
- tvb_memcpy(tvb, fi->value.string, start, length);
- fi->value.string[length] = '\0';
+ string = g_malloc(length + 1);
+ tvb_memcpy(tvb, string, start, length);
+ string[length] = '\0';
+ fvalue_set(fi->value, string, TRUE);
}
/* Add a FT_ETHER to a proto_tree */
@@ -1079,13 +1110,13 @@ proto_tree_add_ether_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint s
static void
proto_tree_set_ether(field_info *fi, const guint8* value)
{
- memcpy(fi->value.ether, value, 6);
+ fvalue_set(fi->value, (gpointer) value, FALSE);
}
static void
proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start)
{
- tvb_memcpy(tvb, fi->value.ether, start, 6);
+ proto_tree_set_ether(fi, tvb_get_ptr(tvb, start, 6));
}
/* Add a FT_BOOLEAN to a proto_tree */
@@ -1146,7 +1177,7 @@ proto_tree_add_boolean_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint
/* Set the FT_BOOLEAN value */
static void
-proto_tree_set_boolean(field_info *fi, guint32 value)
+proto_tree_set_boolean(field_info *fi, guint32 value)
{
proto_tree_set_uint(fi, value);
}
@@ -1211,7 +1242,7 @@ proto_tree_add_double_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint
static void
proto_tree_set_double(field_info *fi, double value)
{
- fi->value.floating = value;
+ fvalue_set_floating(fi->value, value);
}
/* Add any FT_UINT* to a proto_tree */
@@ -1283,19 +1314,22 @@ proto_tree_add_uint_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint st
static void
proto_tree_set_uint(field_info *fi, guint32 value)
{
- header_field_info *hfinfo;
+ header_field_info *hfinfo;
+ guint32 integer;
hfinfo = fi->hfinfo;
- fi->value.numeric = value;
+ integer = value;
+
if (hfinfo->bitmask) {
/* Mask out irrelevant portions */
- fi->value.numeric &= hfinfo->bitmask;
+ integer &= hfinfo->bitmask;
/* Shift bits */
if (hfinfo->bitshift > 0) {
- fi->value.numeric >>= hfinfo->bitshift;
+ integer >>= hfinfo->bitshift;
}
}
+ fvalue_set_integer(fi->value, integer);
}
/* Add any FT_INT* to a proto_tree */
@@ -1367,19 +1401,22 @@ proto_tree_add_int_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint sta
static void
proto_tree_set_int(field_info *fi, gint32 value)
{
- header_field_info *hfinfo;
+ header_field_info *hfinfo;
+ guint32 integer;
hfinfo = fi->hfinfo;
- fi->value.numeric = (guint32) value;
+ integer = (guint32) value;
+
if (hfinfo->bitmask) {
/* Mask out irrelevant portions */
- fi->value.numeric &= hfinfo->bitmask;
+ integer &= hfinfo->bitmask;
/* Shift bits */
if (hfinfo->bitshift > 0) {
- fi->value.numeric >>= hfinfo->bitshift;
+ integer >>= hfinfo->bitshift;
}
}
+ fvalue_set_integer(fi->value, integer);
}
@@ -1437,6 +1474,8 @@ alloc_field_info(int hfindex, tvbuff_t *tvb, gint start, gint length)
fi->visible = proto_tree_is_visible;
fi->representation = NULL;
+ fi->value = fvalue_new(fi->hfinfo->type);
+
return fi;
}
@@ -1508,7 +1547,7 @@ int
proto_register_protocol(char *name, char *short_name, char *filter_name)
{
protocol_t *protocol;
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
int proto_id;
/* Add this protocol to the list of known protocols; the list
@@ -1528,7 +1567,7 @@ proto_register_protocol(char *name, char *short_name, char *filter_name)
hfinfo = g_mem_chunk_alloc(gmc_hfinfo);
hfinfo->name = name;
hfinfo->abbrev = filter_name;
- hfinfo->type = FT_NONE;
+ hfinfo->type = FT_PROTOCOL;
hfinfo->strings = NULL;
hfinfo->bitmask = 0;
hfinfo->bitshift = 0;
@@ -1735,6 +1774,7 @@ proto_register_field_init(header_field_info *hfinfo, int parent)
}
hfinfo->parent = parent;
+ hfinfo->same_name = NULL;
/* if we always add and never delete, then id == len - 1 is correct */
g_ptr_array_add(gpa_hfinfo, hfinfo);
@@ -1770,11 +1810,16 @@ proto_register_subtree_array(gint **indices, int num_indices)
void
proto_item_fill_label(field_info *fi, gchar *label_str)
{
- struct header_field_info *hfinfo = fi->hfinfo;
+ header_field_info *hfinfo = fi->hfinfo;
+
+ guint8 *bytes;
+ guint32 integer;
+ ipv4_addr *ipv4;
guint32 n_addr; /* network-order IPv4 address */
switch(hfinfo->type) {
case FT_NONE:
+ case FT_PROTOCOL:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s", hfinfo->name);
break;
@@ -1784,10 +1829,11 @@ proto_item_fill_label(field_info *fi, gchar *label_str)
break;
case FT_BYTES:
- if (fi->value.bytes) {
+ bytes = fvalue_get(fi->value);
+ if (bytes) {
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s", hfinfo->name,
- bytes_to_str(fi->value.bytes, fi->length));
+ bytes_to_str(bytes, fi->length));
}
else {
snprintf(label_str, ITEM_LABEL_LENGTH,
@@ -1838,37 +1884,39 @@ proto_item_fill_label(field_info *fi, gchar *label_str)
case FT_DOUBLE:
snprintf(label_str, ITEM_LABEL_LENGTH,
- "%s: %g", hfinfo->name,
- fi->value.floating);
+ "%s: %g", hfinfo->name, fvalue_get_floating(fi->value));
break;
case FT_ABSOLUTE_TIME:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s", hfinfo->name,
- abs_time_to_str(&fi->value.time));
+ abs_time_to_str(fvalue_get(fi->value)));
break;
case FT_RELATIVE_TIME:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s seconds", hfinfo->name,
- rel_time_to_str(&fi->value.time));
+ rel_time_to_str(fvalue_get(fi->value)));
break;
case FT_IPXNET:
+ integer = fvalue_get_integer(fi->value);
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: 0x%08X (%s)", hfinfo->name,
- fi->value.numeric, get_ipxnet_name(fi->value.numeric));
+ integer, get_ipxnet_name(integer));
break;
case FT_ETHER:
+ bytes = fvalue_get(fi->value);
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s (%s)", hfinfo->name,
- ether_to_str(fi->value.ether),
- get_ether_name(fi->value.ether));
+ ether_to_str(bytes),
+ get_ether_name(bytes));
break;
case FT_IPv4:
- n_addr = ipv4_get_net_order_addr(&fi->value.ipv4);
+ ipv4 = fvalue_get(fi->value);
+ n_addr = ipv4_get_net_order_addr(ipv4);
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s (%s)", hfinfo->name,
get_hostname(n_addr),
@@ -1876,23 +1924,24 @@ proto_item_fill_label(field_info *fi, gchar *label_str)
break;
case FT_IPv6:
+ bytes = fvalue_get(fi->value);
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s (%s)", hfinfo->name,
- get_hostname6((struct e_in6_addr *)fi->value.ipv6),
- ip6_to_str((struct e_in6_addr*)fi->value.ipv6));
+ get_hostname6((struct e_in6_addr *)bytes),
+ ip6_to_str((struct e_in6_addr*)bytes));
break;
case FT_STRING:
case FT_STRINGZ:
case FT_UINT_STRING:
snprintf(label_str, ITEM_LABEL_LENGTH,
- "%s: %s", hfinfo->name, fi->value.string);
+ "%s: %s", hfinfo->name, (char*) fvalue_get(fi->value));
break;
default:
g_error("hfinfo->type %d (%s) not handled\n",
hfinfo->type,
- proto_registrar_ftype_name(hfinfo->type));
+ ftype_name(hfinfo->type));
g_assert_not_reached();
break;
}
@@ -1901,24 +1950,26 @@ proto_item_fill_label(field_info *fi, gchar *label_str)
static void
fill_label_boolean(field_info *fi, gchar *label_str)
{
- char *p = label_str;
- int bitfield_byte_length = 0, bitwidth;
- guint32 unshifted_value;
+ char *p = label_str;
+ int bitfield_byte_length = 0, bitwidth;
+ guint32 unshifted_value;
+ guint32 value;
- struct header_field_info *hfinfo = fi->hfinfo;
- struct true_false_string default_tf = { "True", "False" };
- struct true_false_string *tfstring = &default_tf;
+ header_field_info *hfinfo = fi->hfinfo;
+ static true_false_string default_tf = { "True", "False" };
+ true_false_string *tfstring = &default_tf;
if (hfinfo->strings) {
tfstring = (struct true_false_string*) hfinfo->strings;
}
+ value = fvalue_get_integer(fi->value);
if (hfinfo->bitmask) {
/* Figure out the bit width */
bitwidth = hfinfo_bitwidth(hfinfo);
/* Un-shift bits */
- unshifted_value = fi->value.numeric;
+ unshifted_value = value;
if (hfinfo->bitshift > 0) {
unshifted_value <<= hfinfo->bitshift;
}
@@ -1931,7 +1982,7 @@ fill_label_boolean(field_info *fi, gchar *label_str)
/* Fill in the textual info */
snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
"%s: %s", hfinfo->name,
- fi->value.numeric ? tfstring->true_string : tfstring->false_string);
+ value ? tfstring->true_string : tfstring->false_string);
}
@@ -1942,8 +1993,9 @@ fill_label_enumerated_bitfield(field_info *fi, gchar *label_str)
char *format = NULL, *p;
int bitfield_byte_length, bitwidth;
guint32 unshifted_value;
+ guint32 value;
- struct header_field_info *hfinfo = fi->hfinfo;
+ header_field_info *hfinfo = fi->hfinfo;
/* Figure out the bit width */
bitwidth = hfinfo_bitwidth(hfinfo);
@@ -1952,7 +2004,8 @@ fill_label_enumerated_bitfield(field_info *fi, gchar *label_str)
format = hfinfo_uint_vals_format(hfinfo);
/* Un-shift bits */
- unshifted_value = fi->value.numeric;
+ unshifted_value = fvalue_get_integer(fi->value);
+ value = unshifted_value;
if (hfinfo->bitshift > 0) {
unshifted_value <<= hfinfo->bitshift;
}
@@ -1964,8 +2017,7 @@ fill_label_enumerated_bitfield(field_info *fi, gchar *label_str)
/* Fill in the textual info using stored (shifted) value */
snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
format, hfinfo->name,
- val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"),
- fi->value.numeric);
+ val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
}
static void
@@ -1974,8 +2026,9 @@ fill_label_numeric_bitfield(field_info *fi, gchar *label_str)
char *format = NULL, *p;
int bitfield_byte_length, bitwidth;
guint32 unshifted_value;
+ guint32 value;
- struct header_field_info *hfinfo = fi->hfinfo;
+ header_field_info *hfinfo = fi->hfinfo;
/* Figure out the bit width */
bitwidth = hfinfo_bitwidth(hfinfo);
@@ -1984,7 +2037,8 @@ fill_label_numeric_bitfield(field_info *fi, gchar *label_str)
format = hfinfo_uint_format(hfinfo);
/* Un-shift bits */
- unshifted_value = fi->value.numeric;
+ unshifted_value = fvalue_get_integer(fi->value);
+ value = unshifted_value;
if (hfinfo->bitshift > 0) {
unshifted_value <<= hfinfo->bitshift;
}
@@ -1995,70 +2049,77 @@ fill_label_numeric_bitfield(field_info *fi, gchar *label_str)
/* Fill in the textual info using stored (shifted) value */
snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
- format, hfinfo->name, fi->value.numeric);
+ format, hfinfo->name, value);
}
static void
fill_label_enumerated_uint(field_info *fi, gchar *label_str)
{
char *format = NULL;
- struct header_field_info *hfinfo = fi->hfinfo;
+ header_field_info *hfinfo = fi->hfinfo;
+ guint32 value;
/* Pick the proper format string */
format = hfinfo_uint_vals_format(hfinfo);
+ value = fvalue_get_integer(fi->value);
+
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
format, hfinfo->name,
- val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"),
- fi->value.numeric);
+ val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
}
static void
fill_label_uint(field_info *fi, gchar *label_str)
{
char *format = NULL;
- struct header_field_info *hfinfo = fi->hfinfo;
+ header_field_info *hfinfo = fi->hfinfo;
+ guint32 value;
/* Pick the proper format string */
format = hfinfo_uint_format(hfinfo);
+ value = fvalue_get_integer(fi->value);
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
- format, hfinfo->name, fi->value.numeric);
+ format, hfinfo->name, value);
}
static void
fill_label_enumerated_int(field_info *fi, gchar *label_str)
{
char *format = NULL;
- struct header_field_info *hfinfo = fi->hfinfo;
+ header_field_info *hfinfo = fi->hfinfo;
+ guint32 value;
/* Pick the proper format string */
format = hfinfo_int_vals_format(hfinfo);
+ value = fvalue_get_integer(fi->value);
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
format, hfinfo->name,
- val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"),
- fi->value.numeric);
+ val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value);
}
static void
fill_label_int(field_info *fi, gchar *label_str)
{
char *format = NULL;
- struct header_field_info *hfinfo = fi->hfinfo;
+ header_field_info *hfinfo = fi->hfinfo;
+ guint32 value;
/* Pick the proper format string */
format = hfinfo_int_format(hfinfo);
+ value = fvalue_get_integer(fi->value);
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
- format, hfinfo->name, fi->value.numeric);
+ format, hfinfo->name, value);
}
-static int
+int
hfinfo_bitwidth(header_field_info *hfinfo)
{
int bitwidth = 0;
@@ -2267,7 +2328,7 @@ proto_registrar_n(void)
char*
proto_registrar_get_name(int n)
{
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
hfinfo = proto_registrar_get_nth(n);
if (hfinfo)
return hfinfo->name;
@@ -2277,7 +2338,7 @@ proto_registrar_get_name(int n)
char*
proto_registrar_get_abbrev(int n)
{
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
hfinfo = proto_registrar_get_nth(n);
if (hfinfo)
@@ -2289,7 +2350,7 @@ proto_registrar_get_abbrev(int n)
int
proto_registrar_get_ftype(int n)
{
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
hfinfo = proto_registrar_get_nth(n);
if (hfinfo)
@@ -2301,7 +2362,7 @@ proto_registrar_get_ftype(int n)
int
proto_registrar_get_parent(int n)
{
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
hfinfo = proto_registrar_get_nth(n);
if (hfinfo)
@@ -2313,7 +2374,7 @@ proto_registrar_get_parent(int n)
gboolean
proto_registrar_is_protocol(int n)
{
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
hfinfo = proto_registrar_get_nth(n);
if (hfinfo)
@@ -2329,54 +2390,13 @@ proto_registrar_is_protocol(int n)
gint
proto_registrar_get_length(int n)
{
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
hfinfo = proto_registrar_get_nth(n);
if (!hfinfo)
return -1;
- switch (hfinfo->type) {
- case FT_TEXT_ONLY: /* not filterable */
- case NUM_FIELD_TYPES: /* satisfy picky compilers */
- return -1;
-
- case FT_NONE:
- case FT_BYTES:
- case FT_BOOLEAN:
- case FT_STRING:
- case FT_STRINGZ:
- case FT_UINT_STRING:
- case FT_DOUBLE:
- case FT_ABSOLUTE_TIME:
- case FT_RELATIVE_TIME:
- return 0;
-
- case FT_UINT8:
- case FT_INT8:
- return 1;
-
- case FT_UINT16:
- case FT_INT16:
- return 2;
-
- case FT_UINT24:
- case FT_INT24:
- return 3;
-
- case FT_UINT32:
- case FT_INT32:
- case FT_IPXNET:
- case FT_IPv4:
- return 4;
-
- case FT_ETHER:
- return 6;
-
- case FT_IPv6:
- return 16;
- }
- g_assert_not_reached();
- return -1;
+ return ftype_length(hfinfo->type);
}
@@ -2577,91 +2597,193 @@ proto_registrar_dump(void)
parent_hfinfo = proto_registrar_get_nth(hfinfo->parent);
g_assert(parent_hfinfo);
- enum_name = proto_registrar_ftype_name(hfinfo->type);
+ enum_name = ftype_name(hfinfo->type);
printf("F\t%s\t%s\t%s\t%s\n", hfinfo->name, hfinfo->abbrev,
enum_name,parent_hfinfo->abbrev);
}
}
}
-
-/* Returns a string representing the field type */
-const char*
-proto_registrar_ftype_name(enum ftenum ftype)
+static char*
+hfinfo_numeric_format(header_field_info *hfinfo)
{
- const char *enum_name = NULL;
+ char *format = NULL;
- switch(ftype) {
- case FT_NONE:
- enum_name = "FT_NONE";
+ /* Pick the proper format string */
+ switch(hfinfo->display) {
+ case BASE_DEC:
+ case BASE_NONE:
+ case BASE_OCT: /* I'm lazy */
+ case BASE_BIN: /* I'm lazy */
+ switch(hfinfo->type) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ format = "%s == %u";
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ format = "%s == %d";
+ break;
+ default:
+ g_assert_not_reached();
+ ;
+ }
+ break;
+ case BASE_HEX:
+ switch(hfinfo->type) {
+ case FT_UINT8:
+ format = "%s == 0x%02x";
+ break;
+ case FT_UINT16:
+ format = "%s == 0x%04x";
+ break;
+ case FT_UINT24:
+ format = "%s == 0x%06x";
+ break;
+ case FT_UINT32:
+ format = "%s == 0x%08x";
+ break;
+ default:
+ g_assert_not_reached();
+ ;
+ }
break;
+ default:
+ g_assert_not_reached();
+ ;
+ }
+ return format;
+}
+
+char*
+proto_alloc_dfilter_string(field_info *finfo, guint8 *pd)
+{
+ header_field_info *hfinfo;
+ int abbrev_len;
+ char *buf, *stringified, *format, *ptr;
+ int dfilter_len, i;
+ guint8 *c;
+
+ hfinfo = finfo->hfinfo;
+ g_assert(hfinfo);
+ abbrev_len = strlen(hfinfo->abbrev);
+
+ switch(hfinfo->type) {
+
case FT_BOOLEAN:
- enum_name = "FT_BOOLEAN";
+ dfilter_len = abbrev_len + 2;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s%s", fvalue_get_integer(finfo->value) ? "" : "!",
+ hfinfo->abbrev);
break;
+
case FT_UINT8:
- enum_name = "FT_UINT8";
- break;
case FT_UINT16:
- enum_name = "FT_UINT16";
- break;
case FT_UINT24:
- enum_name = "FT_UINT24";
- break;
case FT_UINT32:
- enum_name = "FT_UINT32";
- break;
case FT_INT8:
- enum_name = "FT_INT8";
- break;
case FT_INT16:
- enum_name = "FT_INT16";
- break;
case FT_INT24:
- enum_name = "FT_INT24";
- break;
case FT_INT32:
- enum_name = "FT_INT32";
- break;
- case FT_DOUBLE:
- enum_name = "FT_DOUBLE";
+ dfilter_len = abbrev_len + 20;
+ buf = g_malloc0(dfilter_len);
+ format = hfinfo_numeric_format(hfinfo);
+ snprintf(buf, dfilter_len, format, hfinfo->abbrev, fvalue_get_integer(finfo->value));
break;
- case FT_ABSOLUTE_TIME:
- enum_name = "FT_ABSOLUTE_TIME";
- break;
- case FT_RELATIVE_TIME:
- enum_name = "FT_RELATIVE_TIME";
+
+ case FT_IPv4:
+ dfilter_len = abbrev_len + 4 + 15 + 1;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
+ ipv4_addr_str(fvalue_get(finfo->value)));
break;
- case FT_UINT_STRING:
- enum_name = "FT_UINT_STRING";
+
+ case FT_IPXNET:
+ dfilter_len = abbrev_len + 15;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
+ fvalue_get_integer(finfo->value));
break;
- case FT_STRING:
- enum_name = "FT_STRING";
+
+ case FT_IPv6:
+ stringified = ip6_to_str((struct e_in6_addr*) fvalue_get(finfo->value));
+ dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
+ stringified);
break;
- case FT_STRINGZ:
- enum_name = "FT_STRINGZ";
+
+ case FT_DOUBLE:
+ dfilter_len = abbrev_len + 30;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
+ fvalue_get_floating(finfo->value));
break;
+
case FT_ETHER:
- enum_name = "FT_ETHER";
- break;
- case FT_BYTES:
- enum_name = "FT_BYTES";
- break;
- case FT_IPv4:
- enum_name = "FT_IPv4";
- break;
- case FT_IPv6:
- enum_name = "FT_IPv6";
+ dfilter_len = abbrev_len + 22;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s == %s",
+ hfinfo->abbrev,
+ ether_to_str(fvalue_get(finfo->value)));
break;
- case FT_IPXNET:
- enum_name = "FT_IPXNET";
+#if 0
+
+ case FT_ABSOLUTE_TIME:
+ case FT_RELATIVE_TIME:
+ memcpy(&fi->value.time, va_arg(ap, struct timeval*),
+ sizeof(struct timeval));
break;
+
case FT_TEXT_ONLY:
- enum_name = "FT_TEXT_ONLY";
- break;
- case NUM_FIELD_TYPES:
- g_assert_not_reached();
+ ; /* nothing */
break;
+#endif
+
+ case FT_STRING:
+ dfilter_len = abbrev_len +
+ strlen(fvalue_get(finfo->value)) + 7;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s == \"%s\"",
+ hfinfo->abbrev, (char*)fvalue_get(finfo->value));
+ break;
+
+ case FT_BYTES:
+ dfilter_len = finfo->length*3 - 1;
+ dfilter_len += abbrev_len + 7;
+ buf = g_malloc0(dfilter_len);
+ snprintf(buf, dfilter_len, "%s == %s",
+ hfinfo->abbrev,
+ bytes_to_str_punct(fvalue_get(finfo->value), finfo->length,':'));
+ break;
+ default:
+ c = pd + finfo->start;
+ buf = g_malloc0(32 + finfo->length * 3);
+ ptr = buf;
+
+ sprintf(ptr, "frame[%d] == ", finfo->start);
+ ptr = buf+strlen(buf);
+
+ if (finfo->length == 1) {
+ sprintf(ptr, "0x%02x", *c++);
+ }
+ else {
+ for (i=0;i<finfo->length; i++) {
+ if (i == 0 ) {
+ sprintf(ptr, "%02x", *c++);
+ }
+ else {
+ sprintf(ptr, ":%02x", *c++);
+ }
+ ptr = buf+strlen(buf);
+ }
+ }
+ break;
}
- g_assert(enum_name);
- return enum_name;
+
+ return buf;
}
diff --git a/epan/proto.h b/epan/proto.h
index e5abb3fd13..ce6afbf284 100644
--- a/epan/proto.h
+++ b/epan/proto.h
@@ -1,7 +1,7 @@
/* proto.h
* Definitions for protocol display
*
- * $Id: proto.h,v 1.5 2001/02/01 07:34:30 guy Exp $
+ * $Id: proto.h,v 1.6 2001/02/01 20:21:16 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -43,6 +43,7 @@
#include "ipv4.h"
#include "tvbuff.h"
+#include "ftypes/ftypes.h"
/* needs glib.h */
typedef GNode proto_tree;
@@ -73,34 +74,6 @@ struct value_string;
} \
}
-/* field types */
-enum ftenum {
- FT_NONE, /* used for protocol labels (thus no field type) */
- FT_BOOLEAN, /* TRUE and FALSE come from <glib.h> */
- FT_UINT8,
- FT_UINT16,
- FT_UINT24, /* really a UINT32, but displayed as 3 hex-digits if FD_HEX*/
- FT_UINT32,
- FT_INT8,
- FT_INT16,
- FT_INT24,
- FT_INT32,
- FT_DOUBLE,
- FT_ABSOLUTE_TIME,
- FT_RELATIVE_TIME,
- FT_STRING,
- FT_STRINGZ, /* for use with proto_tree_add_item() */
- FT_UINT_STRING, /* for use with proto_tree_add_item() */
- FT_ETHER,
- FT_BYTES,
- FT_IPv4,
- FT_IPv6,
- FT_IPXNET,
- FT_TEXT_ONLY, /* non-filterable, used when converting ethereal
- from old-style proto_tree to new-style proto_tree */
- NUM_FIELD_TYPES /* last item number plus one */
-};
-
enum {
BASE_NONE,
BASE_DEC,
@@ -109,8 +82,11 @@ enum {
BASE_BIN
};
+typedef struct _header_field_info header_field_info;
+
/* information describing a header field */
-typedef struct header_field_info {
+struct _header_field_info {
+ /* ---------- set by dissector --------- */
char *name;
char *abbrev;
enum ftenum type;
@@ -119,10 +95,12 @@ typedef struct header_field_info {
guint32 bitmask;
char *blurb; /* Brief description of field. */
- int id; /* assigned by registration function, not programmer */
+ /* ---------- set by proto routines --------- */
+ int id; /* Field ID */
int parent; /* parent protocol */
int bitshift; /* bits to shift */
-} header_field_info;
+ header_field_info *same_name; /* Link to next hfinfo with same abbrev*/
+};
@@ -135,22 +113,13 @@ typedef struct hf_register_info {
/* Info stored in each proto_item GNode */
typedef struct field_info {
- struct header_field_info *hfinfo;
+ header_field_info *hfinfo;
gint start;
gint length;
gint tree_type; /* ETT_* */
char *representation; /* for GUI tree */
int visible;
- union {
- guint32 numeric;
- struct timeval time; /* the whole struct, not a pointer */
- gdouble floating;
- gchar *string;
- guint8 *bytes;
- guint8 ether[6];
- ipv4_addr ipv4;
- guint8 ipv6[16];
- } value;
+ fvalue_t *value;
} field_info;
@@ -213,6 +182,18 @@ proto_tree_add_item_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb,
/* Add a FT_NONE to a proto_tree */
#if __GNUC__ == 2
proto_item *
+proto_tree_add_none_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
+ gint length, const char *format, ...)
+ __attribute__((format (printf, 6, 7)));
+#else
+proto_item *
+proto_tree_add_none_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
+ gint length, const char *format, ...);
+#endif
+
+/* Add a FT_PROTOCOL to a proto_tree */
+#if __GNUC__ == 2
+proto_item *
proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
gint length, const char *format, ...)
__attribute__((format (printf, 6, 7)));
@@ -487,7 +468,7 @@ char* proto_registrar_get_name(int n);
char* proto_registrar_get_abbrev(int n);
/* get the header field information based upon a field or protocol id */
-struct header_field_info* proto_registrar_get_nth(int hfindex);
+header_field_info* proto_registrar_get_nth(int hfindex);
/* Returns enum ftenum for item # n */
int proto_registrar_get_ftype(int n);
@@ -569,7 +550,10 @@ extern int num_tree_types;
#define g_ptr_array_len(a) ((a)->len)
#endif
-/* Returns a string representing the field type */
-const char* proto_registrar_ftype_name(enum ftenum ftype);
+int
+hfinfo_bitwidth(header_field_info *hfinfo);
+
+char*
+proto_alloc_dfilter_string(field_info *finfo, guint8 *pd);
#endif /* proto.h */
diff --git a/file.c b/file.c
index ba2d45d867..78a5fa77f3 100644
--- a/file.c
+++ b/file.c
@@ -1,7 +1,7 @@
/* file.c
* File I/O routines
*
- * $Id: file.c,v 1.228 2001/01/28 21:17:26 guy Exp $
+ * $Id: file.c,v 1.229 2001/02/01 20:21:13 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -84,7 +84,7 @@
#include "ui_util.h"
#include "prefs.h"
#include "gtk/proto_draw.h"
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include "conversation.h"
#include "globals.h"
#include "gtk/colors.h"
@@ -224,7 +224,7 @@ close_cap_file(capture_file *cf, void *w)
cf->plist_chunk = NULL;
}
if (cf->rfcode != NULL) {
- dfilter_destroy(cf->rfcode);
+ dfilter_free(cf->rfcode);
cf->rfcode = NULL;
}
cf->plist = NULL;
@@ -575,9 +575,8 @@ finish_tail_cap_file(capture_file *cf, int *err)
typedef struct {
color_filter_t *colorf;
+ tvbuff_t *tvb;
proto_tree *protocol_tree;
- const guint8 *pd;
- frame_data *fdata;
} apply_color_filter_args;
/*
@@ -592,7 +591,7 @@ apply_color_filter(gpointer filter_arg, gpointer argp)
apply_color_filter_args *args = argp;
if (colorf->c_colorfilter != NULL && args->colorf == NULL) {
- if (dfilter_apply(colorf->c_colorfilter, args->protocol_tree, args->pd, args->fdata->cap_len))
+ if (dfilter_apply(colorf->c_colorfilter, args->tvb, args->protocol_tree))
args->colorf = colorf;
}
}
@@ -647,7 +646,7 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
if (cf->dfcode != NULL) {
if (refilter) {
if (cf->dfcode != NULL)
- fdata->flags.passed_dfilter = dfilter_apply(cf->dfcode, protocol_tree, buf, fdata->cap_len) ? 1 : 0;
+ fdata->flags.passed_dfilter = dfilter_apply(cf->dfcode, edt->tvb, protocol_tree) ? 1 : 0;
else
fdata->flags.passed_dfilter = 1;
}
@@ -658,9 +657,8 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
the color filters. */
if (fdata->flags.passed_dfilter) {
if (filter_list != NULL) {
+ args.tvb = edt->tvb;
args.protocol_tree = protocol_tree;
- args.pd = buf;
- args.fdata = fdata;
g_slist_foreach(filter_list, apply_color_filter, &args);
}
}
@@ -782,7 +780,7 @@ read_packet(capture_file *cf, int offset)
if (cf->rfcode) {
protocol_tree = proto_tree_create_root();
edt = epan_dissect_new(pseudo_header, buf, fdata, protocol_tree);
- passed = dfilter_apply(cf->rfcode, protocol_tree, buf, fdata->cap_len);
+ passed = dfilter_apply(cf->rfcode, edt->tvb, protocol_tree);
proto_tree_free(protocol_tree);
epan_dissect_free(edt);
}
@@ -814,7 +812,7 @@ read_packet(capture_file *cf, int offset)
int
filter_packets(capture_file *cf, gchar *dftext)
{
- dfilter *dfcode;
+ dfilter_t *dfcode;
if (dftext == NULL) {
/* The new filter is an empty filter (i.e., display all packets). */
@@ -823,7 +821,7 @@ filter_packets(capture_file *cf, gchar *dftext)
/*
* We have a filter; try to compile it.
*/
- if (dfilter_compile(dftext, &dfcode) != 0) {
+ if (!dfilter_compile(dftext, &dfcode)) {
/* The attempt failed; report an error. */
simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
return 0;
@@ -842,7 +840,7 @@ filter_packets(capture_file *cf, gchar *dftext)
g_free(cf->dfilter);
cf->dfilter = dftext;
if (cf->dfcode != NULL)
- dfilter_destroy(cf->dfcode);
+ dfilter_free(cf->dfcode);
cf->dfcode = dfcode;
/* Now rescan the packet list, applying the new filter, but not
@@ -1372,7 +1370,7 @@ clear_tree_and_hex_views(void)
}
gboolean
-find_packet(capture_file *cf, dfilter *sfcode)
+find_packet(capture_file *cf, dfilter_t *sfcode)
{
frame_data *start_fd;
frame_data *fdata;
@@ -1451,7 +1449,7 @@ find_packet(capture_file *cf, dfilter *sfcode)
wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
cf->pd, fdata->cap_len);
edt = epan_dissect_new(&cf->pseudo_header, cf->pd, fdata, protocol_tree);
- frame_matched = dfilter_apply(sfcode, protocol_tree, cf->pd, fdata->cap_len);
+ frame_matched = dfilter_apply(sfcode, edt->tvb, protocol_tree);
proto_tree_free(protocol_tree);
epan_dissect_free(edt);
if (frame_matched) {
@@ -2077,3 +2075,4 @@ copy_binary_file(char *from_filename, char *to_filename)
done:
return FALSE;
}
+
diff --git a/file.h b/file.h
index 196a18debb..b42cd582df 100644
--- a/file.h
+++ b/file.h
@@ -1,7 +1,7 @@
/* file.h
* Definitions for file structures and routines
*
- * $Id: file.h,v 1.79 2001/01/28 23:56:27 guy Exp $
+ * $Id: file.h,v 1.80 2001/02/01 20:21:13 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -31,12 +31,9 @@
#endif
#include "wiretap/wtap.h"
-
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include "print.h"
-
#include <errno.h>
-
#include <epan.h>
#ifdef HAVE_LIBZ
@@ -81,10 +78,10 @@ typedef struct _capture_file {
gchar *save_file; /* File that user saved capture to */
int save_file_fd; /* File descriptor for saved file */
wtap *wth; /* Wiretap session */
- dfilter *rfcode; /* Compiled read filter program */
+ dfilter_t *rfcode; /* Compiled read filter program */
gchar *dfilter; /* Display filter string */
struct _colfilter *colors; /* Colors for colorizing packet window */
- dfilter *dfcode; /* Compiled display filter program */
+ dfilter_t *dfcode; /* Compiled display filter program */
#ifdef HAVE_LIBPCAP
gchar *cfilter; /* Capture filter string */
#endif
@@ -126,7 +123,7 @@ void colorize_packets(capture_file *);
void redissect_packets(capture_file *cf);
int print_packets(capture_file *cf, print_args_t *print_args);
void change_time_formats(capture_file *);
-gboolean find_packet(capture_file *cf, dfilter *sfcode);
+gboolean find_packet(capture_file *cf, dfilter_t *sfcode);
typedef enum {
FOUND_FRAME, /* found the frame */
diff --git a/gtk/color_dlg.c b/gtk/color_dlg.c
index 672c5cbb2f..7b06dca3bb 100644
--- a/gtk/color_dlg.c
+++ b/gtk/color_dlg.c
@@ -1,7 +1,7 @@
/* color_dlg.c
* Definitions for dialog boxes for color filters
*
- * $Id: color_dlg.c,v 1.8 2000/08/24 13:21:29 deniel Exp $
+ * $Id: color_dlg.c,v 1.9 2001/02/01 20:21:21 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -43,7 +43,7 @@
#include "colors.h"
#include "color_dlg.h"
#include "file.h"
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include "simple_dialog.h"
#include "dlg_utils.h"
#include "ui_util.h"
@@ -884,7 +884,7 @@ edit_color_filter_ok_cb (GtkButton *button,
gchar *filter_name;
gchar *filter_text;
color_filter_t *colorf;
- dfilter *compiled_filter;
+ dfilter_t *compiled_filter;
GtkWidget *color_filters;
dialog = (GtkWidget *)user_data;
@@ -904,7 +904,7 @@ edit_color_filter_ok_cb (GtkButton *button,
return;
}
- if(dfilter_compile(filter_text, &compiled_filter) != 0 ){
+ if(!dfilter_compile(filter_text, &compiled_filter)) {
simple_dialog(ESD_TYPE_CRIT, NULL, "Filter \"%s\" did not compile correctly.\n"
" Please try again. Filter unchanged.\n%s\n", filter_name,
dfilter_error_msg);
@@ -927,7 +927,7 @@ edit_color_filter_ok_cb (GtkButton *button,
gtk_clist_set_background(GTK_CLIST(color_filters),
cfile.colors->row_selected, &new_bg_color);
if(colorf->c_colorfilter != NULL)
- dfilter_destroy(colorf->c_colorfilter);
+ dfilter_free(colorf->c_colorfilter);
colorf->c_colorfilter = compiled_filter;
/* gtk_clist_set_text frees old text (if any) and allocates new space */
gtk_clist_set_text(GTK_CLIST(color_filters),
diff --git a/gtk/colors.c b/gtk/colors.c
index 44da72220a..6bbfe41ff3 100644
--- a/gtk/colors.c
+++ b/gtk/colors.c
@@ -1,7 +1,7 @@
/* colors.c
* Definitions for color structures and routines
*
- * $Id: colors.c,v 1.6 2000/09/28 03:16:29 gram Exp $
+ * $Id: colors.c,v 1.7 2001/02/01 20:21:21 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -44,7 +44,7 @@
#include "packet.h"
#include "colors.h"
#include "file.h"
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include "simple_dialog.h"
#include "util.h"
@@ -120,8 +120,8 @@ colfilter_new(void)
default_colors[i].proto);
colorf->bg_color = color;
- if (dfilter_compile(default_colors[i].proto,
- &colorf->c_colorfilter) != 0) {
+ if (!dfilter_compile(default_colors[i].proto,
+ &colorf->c_colorfilter)) {
simple_dialog(ESD_TYPE_WARN, NULL,
"Cannot compile default color filter %s.\n%s",
default_colors[i].proto, dfilter_error_msg);
@@ -158,7 +158,7 @@ delete_color_filter(color_filter_t *colorf)
if (colorf->filter_text != NULL)
g_free(colorf->filter_text);
if (colorf->c_colorfilter != NULL)
- dfilter_destroy(colorf->c_colorfilter);
+ dfilter_free(colorf->c_colorfilter);
filter_list = g_slist_remove(filter_list, colorf);
g_free(colorf);
}
@@ -177,7 +177,7 @@ read_filters(colfilter *filter)
FILE *f;
gchar *path;
gchar *fname = PF_DIR "/colorfilters";
- dfilter *temp_dfilter;
+ dfilter_t *temp_dfilter;
/* decide what file to open (from dfilter code) */
@@ -217,7 +217,7 @@ read_filters(colfilter *filter)
name, filter_exp, &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 8){
/* we got a filter */
- if(dfilter_compile(filter_exp, &temp_dfilter) != 0){
+ if(!dfilter_compile(filter_exp, &temp_dfilter)) {
simple_dialog(ESD_TYPE_CRIT, NULL,
"Could not compile color filter %s from saved filters.\n%s",
name, dfilter_error_msg);
diff --git a/gtk/colors.h b/gtk/colors.h
index 8294e361b8..9c15030837 100644
--- a/gtk/colors.h
+++ b/gtk/colors.h
@@ -1,7 +1,7 @@
/* colors.h
* Definitions for color structures and routines
*
- * $Id: colors.h,v 1.3 2000/08/11 13:33:00 deniel Exp $
+ * $Id: colors.h,v 1.4 2001/02/01 20:21:21 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -26,7 +26,7 @@
#define __COLORS_H__
#include "proto.h"
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include <gtk/gtk.h>
#define MAXCOLORS 255
@@ -45,7 +45,7 @@ typedef struct _color_filter {
gchar *filter_text; /* text of the filter expression */
GdkColor bg_color; /* background color for packets that match */
GdkColor fg_color; /* foreground color for packets that match */
- dfilter *c_colorfilter; /* compiled filter expression */
+ dfilter_t *c_colorfilter; /* compiled filter expression */
GtkWidget *edit_dialog; /* if filter is being edited, dialog box for it */
} color_filter_t;
diff --git a/gtk/dfilter_expr_dlg.c b/gtk/dfilter_expr_dlg.c
index 657d01649c..97461259a1 100644
--- a/gtk/dfilter_expr_dlg.c
+++ b/gtk/dfilter_expr_dlg.c
@@ -7,7 +7,7 @@
* Copyright 2000, Jeffrey C. Foster<jfoste@woodward.com> and
* Guy Harris <guy@alum.mit.edu>
*
- * $Id: dfilter_expr_dlg.c,v 1.5 2001/01/10 23:36:35 guy Exp $
+ * $Id: dfilter_expr_dlg.c,v 1.6 2001/02/01 20:21:21 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -139,7 +139,7 @@ field_select_row_cb(GtkWidget *tree, GList *node, gint column,
E_DFILTER_EXPR_ACCEPT_BT_KEY);
header_field_info *hfinfo, *cur_hfinfo;
guint32 relations;
- char *value_type;
+ const char *value_type;
char value_label_string[1024+1]; /* XXX - should be large enough */
hfinfo = gtk_ctree_node_get_row_data(GTK_CTREE(tree),
@@ -246,95 +246,7 @@ field_select_row_cb(GtkWidget *tree, GList *node, gint column,
* Set the label for the value to indicate what type of value
* it is.
*/
- switch (hfinfo->type) {
-
- case FT_NONE:
- /*
- * You can only test for the field's presence; hide
- * the value stuff.
- */
- value_type = NULL;
- break;
-
- case FT_BOOLEAN:
- value_type = "Boolean";
- break;
-
- case FT_UINT8:
- value_type = "unsigned, byte";
- break;
-
- case FT_UINT16:
- value_type = "unsigned, 2 bytes";
- break;
-
- case FT_UINT24:
- value_type = "unsigned, 3 bytes";
- break;
-
- case FT_UINT32:
- value_type = "unsigned, 4 bytes";
- break;
-
- case FT_INT8:
- value_type = "signed, byte";
- break;
-
- case FT_INT16:
- value_type = "signed, 2 bytes";
- break;
-
- case FT_INT24:
- value_type = "signed, 3 bytes";
- break;
-
- case FT_INT32:
- value_type = "signed, 4 bytes";
- break;
-
- case FT_DOUBLE:
- value_type = "floating point";
- break;
-
- case FT_ABSOLUTE_TIME:
- value_type = "date and time";
- break;
-
- case FT_RELATIVE_TIME:
- value_type = "time";
- break;
-
- case FT_STRING:
- case FT_STRINGZ:
- case FT_UINT_STRING:
- value_type = "character string";
- break;
-
- case FT_ETHER:
- value_type = "Ethernet or other MAC address";
- break;
-
- case FT_BYTES:
- value_type = "sequence of bytes";
- break;
-
- case FT_IPv4:
- value_type = "IPv4 address";
- break;
-
- case FT_IPv6:
- value_type = "IPv6 address";
- break;
-
- case FT_IPXNET:
- value_type = "IPX network address";
- break;
-
- default:
- g_assert_not_reached();
- value_type = NULL;
- break;
- }
+ value_type = ftype_pretty_name(hfinfo->type);
if (value_type != NULL) {
/*
* Indicate what type of value it is.
diff --git a/gtk/file_dlg.c b/gtk/file_dlg.c
index 757df42631..fab7d1f42d 100644
--- a/gtk/file_dlg.c
+++ b/gtk/file_dlg.c
@@ -1,7 +1,7 @@
/* file_dlg.c
* Dialog boxes for handling files
*
- * $Id: file_dlg.c,v 1.35 2001/01/21 02:27:24 guy Exp $
+ * $Id: file_dlg.c,v 1.36 2001/02/01 20:21:21 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -149,13 +149,13 @@ static void
file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
gchar *cf_name, *rfilter, *s;
GtkWidget *filter_te, *resolv_cb;
- dfilter *rfcode = NULL;
+ dfilter_t *rfcode = NULL;
int err;
cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
filter_te = gtk_object_get_data(GTK_OBJECT(w), E_RFILTER_TE_KEY);
rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
- if (dfilter_compile(rfilter, &rfcode) != 0) {
+ if (!dfilter_compile(rfilter, &rfcode)) {
simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
return;
}
@@ -178,7 +178,7 @@ file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
dismiss the alert box popped up for the open error,
try again. */
if (rfcode != NULL)
- dfilter_destroy(rfcode);
+ dfilter_free(rfcode);
g_free(cf_name);
return;
}
diff --git a/gtk/find_dlg.c b/gtk/find_dlg.c
index 9203899baf..18b827f8f5 100644
--- a/gtk/find_dlg.c
+++ b/gtk/find_dlg.c
@@ -1,7 +1,7 @@
/* find_dlg.c
* Routines for "find frame" window
*
- * $Id: find_dlg.c,v 1.18 2001/01/21 02:27:24 guy Exp $
+ * $Id: find_dlg.c,v 1.19 2001/02/01 20:21:21 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -37,7 +37,7 @@
#include <glib.h>
#include "proto.h"
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include "globals.h"
#include "ui_util.h"
@@ -187,7 +187,7 @@ find_frame_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
{
GtkWidget *filter_te, *backward_rb;
gchar *filter_text;
- dfilter *sfcode;
+ dfilter_t *sfcode;
filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_FIND_FILT_KEY);
backward_rb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_FIND_BACKWARD_KEY);
@@ -197,7 +197,7 @@ find_frame_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
/*
* Try to compile the filter.
*/
- if (dfilter_compile(filter_text, &sfcode) != 0) {
+ if (!dfilter_compile(filter_text, &sfcode)) {
/* The attempt failed; report an error. */
simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
return;
diff --git a/gtk/help_dlg.c b/gtk/help_dlg.c
index aa18ca99e9..03deb5ffb2 100644
--- a/gtk/help_dlg.c
+++ b/gtk/help_dlg.c
@@ -1,6 +1,6 @@
/* help_dlg.c
*
- * $Id: help_dlg.c,v 1.12 2000/11/22 04:07:00 gram Exp $
+ * $Id: help_dlg.c,v 1.13 2001/02/01 20:21:21 gram Exp $
*
* Laurent Deniel <deniel@worldnet.fr>
*
@@ -378,7 +378,7 @@ static void set_help_text(GtkWidget *w, help_type_t type)
nb_lines += 2;
} else {
- type_name = proto_registrar_ftype_name(hfinfo->type);
+ type_name = ftype_name(hfinfo->type);
snprintf(buffer, BUFF_LEN, "%s%s%s%s(%s)\n",
hfinfo->abbrev,
&blanks[B_LEN - (maxlen - strlen(hfinfo->abbrev)) - 2],
diff --git a/gtk/main.c b/gtk/main.c
index 649a6dfcf6..df341080b8 100644
--- a/gtk/main.c
+++ b/gtk/main.c
@@ -1,6 +1,6 @@
/* main.c
*
- * $Id: main.c,v 1.176 2001/01/28 23:56:29 guy Exp $
+ * $Id: main.c,v 1.177 2001/02/01 20:21:21 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -120,7 +120,7 @@
#include "util.h"
#include "simple_dialog.h"
#include "proto_draw.h"
-#include "dfilter.h"
+#include "dfilter/dfilter.h"
#include "keys.h"
#include "packet_win.h"
#include "gtkglobals.h"
@@ -146,7 +146,6 @@ GtkStyle *item_style;
/* Specifies the field currently selected in the GUI protocol tree */
field_info *finfo_selected = NULL;
-static char* hfinfo_numeric_format(header_field_info *hfinfo);
static void create_main_window(gint, gint, gint, e_prefs*);
/* About Ethereal window */
@@ -171,10 +170,6 @@ match_selected_cb(GtkWidget *w, gpointer data)
{
char *buf;
GtkWidget *filter_te;
- char *ptr, *format, *stringified;
- int i, dfilter_len, abbrev_len;
- guint8 *c;
- header_field_info *hfinfo;
filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
@@ -186,123 +181,7 @@ match_selected_cb(GtkWidget *w, gpointer data)
return;
}
- hfinfo = finfo_selected->hfinfo;
- g_assert(hfinfo);
- abbrev_len = strlen(hfinfo->abbrev);
-
- switch(hfinfo->type) {
-
- case FT_BOOLEAN:
- dfilter_len = abbrev_len + 2;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!",
- hfinfo->abbrev);
- break;
-
- case FT_UINT8:
- case FT_UINT16:
- case FT_UINT24:
- case FT_UINT32:
- case FT_INT8:
- case FT_INT16:
- case FT_INT24:
- case FT_INT32:
- dfilter_len = abbrev_len + 20;
- buf = g_malloc0(dfilter_len);
- format = hfinfo_numeric_format(hfinfo);
- snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric);
- break;
-
- case FT_IPv4:
- dfilter_len = abbrev_len + 4 + 15 + 1;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
- ipv4_addr_str(&(finfo_selected->value.ipv4)));
- break;
-
- case FT_IPXNET:
- dfilter_len = abbrev_len + 15;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
- finfo_selected->value.numeric);
- break;
-
- case FT_IPv6:
- stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6));
- dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
- stringified);
- break;
-
- case FT_DOUBLE:
- dfilter_len = abbrev_len + 30;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
- finfo_selected->value.floating);
- break;
-
- case FT_ETHER:
- dfilter_len = abbrev_len + 22;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s == %s",
- hfinfo->abbrev,
- ether_to_str(finfo_selected->value.ether));
- break;
-#if 0
-
- case FT_ABSOLUTE_TIME:
- case FT_RELATIVE_TIME:
- memcpy(&fi->value.time, va_arg(ap, struct timeval*),
- sizeof(struct timeval));
- break;
-
- case FT_TEXT_ONLY:
- ; /* nothing */
- break;
-#endif
-
- case FT_STRING:
- dfilter_len = abbrev_len +
- strlen(finfo_selected->value.string) + 7;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s == \"%s\"",
- hfinfo->abbrev,
- finfo_selected->value.string);
- break;
-
- case FT_BYTES:
- dfilter_len = finfo_selected->length*3 - 1;
- dfilter_len += abbrev_len + 7;
- buf = g_malloc0(dfilter_len);
- snprintf(buf, dfilter_len, "%s == %s",
- hfinfo->abbrev,
- bytes_to_str_punct(finfo_selected->value.bytes, finfo_selected->length,':'));
- break;
- default:
- c = cfile.pd + finfo_selected->start;
- buf = g_malloc0(32 + finfo_selected->length * 3);
- ptr = buf;
-
- sprintf(ptr, "frame[%d] == ", finfo_selected->start);
- ptr = buf+strlen(buf);
-
- if (finfo_selected->length == 1) {
- sprintf(ptr, "0x%02x", *c++);
- }
- else {
- for (i=0;i<finfo_selected->length; i++) {
- if (i == 0 ) {
- sprintf(ptr, "%02x", *c++);
- }
- else {
- sprintf(ptr, ":%02x", *c++);
- }
- ptr = buf+strlen(buf);
- }
- }
- break;
- }
+ buf = proto_alloc_dfilter_string(finfo_selected, cfile.pd);
/* create a new one and set the display filter entry accordingly */
gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
@@ -313,60 +192,6 @@ match_selected_cb(GtkWidget *w, gpointer data)
/* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
}
-static char*
-hfinfo_numeric_format(header_field_info *hfinfo)
-{
- char *format = NULL;
-
- /* Pick the proper format string */
- switch(hfinfo->display) {
- case BASE_DEC:
- case BASE_NONE:
- case BASE_OCT: /* I'm lazy */
- case BASE_BIN: /* I'm lazy */
- switch(hfinfo->type) {
- case FT_UINT8:
- case FT_UINT16:
- case FT_UINT24:
- case FT_UINT32:
- format = "%s == %u";
- break;
- case FT_INT8:
- case FT_INT16:
- case FT_INT24:
- case FT_INT32:
- format = "%s == %d";
- break;
- default:
- g_assert_not_reached();
- ;
- }
- break;
- case BASE_HEX:
- switch(hfinfo->type) {
- case FT_UINT8:
- format = "%s == 0x%02x";
- break;
- case FT_UINT16:
- format = "%s == 0x%04x";
- break;
- case FT_UINT24:
- format = "%s == 0x%06x";
- break;
- case FT_UINT32:
- format = "%s == 0x%08x";
- break;
- default:
- g_assert_not_reached();
- ;
- }
- break;
- default:
- g_assert_not_reached();
- ;
- }
- return format;
-}
/* Run the current display filter on the current packet set, and
@@ -585,7 +410,8 @@ tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user
set_menus_for_selected_tree_row(TRUE);
- if (finfo->hfinfo && finfo->hfinfo->type != FT_TEXT_ONLY) {
+ /*if (finfo->hfinfo && finfo->hfinfo->type != FT_TEXT_ONLY) {*/
+ if (finfo->hfinfo) {
if (finfo->hfinfo->blurb != NULL &&
finfo->hfinfo->blurb[0] != '\0') {
has_blurb = TRUE;
@@ -872,7 +698,7 @@ main(int argc, char *argv[])
#endif
gint pl_size = 280, tv_size = 95, bv_size = 75;
gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
- dfilter *rfcode = NULL;
+ dfilter_t *rfcode = NULL;
gboolean rfilter_parse_failed = FALSE;
e_prefs *prefs;
char *bold_font_name;
@@ -1312,7 +1138,7 @@ main(int argc, char *argv[])
up on top of us. */
if (cf_name) {
if (rfilter != NULL) {
- if (dfilter_compile(rfilter, &rfcode) != 0) {
+ if (!dfilter_compile(rfilter, &rfcode)) {
simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
rfilter_parse_failed = TRUE;
}
@@ -1345,7 +1171,7 @@ main(int argc, char *argv[])
if (s != NULL)
last_open_dir = s;
} else {
- dfilter_destroy(rfcode);
+ dfilter_free(rfcode);
cfile.rfcode = NULL;
}
}
diff --git a/gtk/packet_win.c b/gtk/packet_win.c
index abf4e8a917..760033f8d8 100644
--- a/gtk/packet_win.c
+++ b/gtk/packet_win.c
@@ -3,7 +3,7 @@
*
* Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
*
- * $Id: packet_win.c,v 1.16 2000/10/06 10:11:40 gram Exp $
+ * $Id: packet_win.c,v 1.17 2001/02/01 20:21:22 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -64,7 +64,6 @@
#include "simple_dialog.h"
#include "ui_util.h"
#include "proto_draw.h"
-#include "dfilter.h"
#include "keys.h"
#include "gtkglobals.h"
#include "plugins.h"
diff --git a/packet-x11.c b/packet-x11.c
index 9b2f897c2a..881af38ab0 100644
--- a/packet-x11.c
+++ b/packet-x11.c
@@ -2,7 +2,7 @@
* Routines for X11 dissection
* Copyright 2000, Christophe Tronche <ch.tronche@computer.org>
*
- * $Id: packet-x11.c,v 1.16 2001/01/22 08:03:46 guy Exp $
+ * $Id: packet-x11.c,v 1.17 2001/02/01 20:21:13 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@unicom.net>
@@ -692,7 +692,7 @@ static void atom(tvbuff_t *tvb, proto_tree *t, int hf)
else if (v)
interpretation = "Not a predefined atom";
else {
- struct header_field_info *hfi = proto_registrar_get_nth(hf);
+ header_field_info *hfi = proto_registrar_get_nth(hf);
if (hfi -> strings)
interpretation = match_strval(v, cVALS(hfi -> strings));
}
@@ -793,7 +793,7 @@ static void listOfArc(tvbuff_t *tvb, int hf, int length)
gint16 angle1 = VALUE16(tvb, cur_offset + 8);
gint16 angle2 = VALUE16(tvb, cur_offset + 10);
- proto_tree *ttt = proto_tree_add_protocol_format(tt, hf_x11_arc, tvb, cur_offset, 12,
+ proto_tree *ttt = proto_tree_add_none_format(tt, hf_x11_arc, tvb, cur_offset, 12,
"arc: %dx%d+%d+%d, angle %d -> %d (%f° -> %f°)",
width, height, x, y, angle1, angle2,
angle1 / 64.0, angle2 / 64.0);
@@ -868,7 +868,7 @@ static void listOfColorItem(tvbuff_t *tvb, int hf, int length)
if (do_red_green_blue & 0x2) { bp += sprintf(bp, "%sgreen = %d", sep, green); sep = ", "; }
if (do_red_green_blue & 0x4) bp += sprintf(bp, "%sblue = %d", sep, blue);
- ttt = proto_tree_add_protocol_format(tt, hf_x11_coloritem, tvb, cur_offset, 12, "%s", buffer);
+ ttt = proto_tree_add_none_format(tt, hf_x11_coloritem, tvb, cur_offset, 12, "%s", buffer);
proto_tree_add_item(ttt, hf_x11_coloritem_pixel, tvb, cur_offset, 4, little_endian); cur_offset += 4;
proto_tree_add_item(ttt, hf_x11_coloritem_red, tvb, cur_offset, 2, little_endian); cur_offset += 2;
proto_tree_add_item(ttt, hf_x11_coloritem_green, tvb, cur_offset, 2, little_endian); cur_offset += 2;
@@ -945,7 +945,7 @@ static void listOfKeysyms(tvbuff_t *tvb, int hf, int hf_item, int keycode_count,
bp += sprintf(bp, " %s", keysymString(VALUE32(tvb, cur_offset + i * 4)));
}
*bp = '\0';
- ttt = proto_tree_add_protocol_format(tt, hf_item, tvb, cur_offset, keysyms_per_keycode * 4,
+ ttt = proto_tree_add_none_format(tt, hf_item, tvb, cur_offset, keysyms_per_keycode * 4,
"%s", buffer);
for(i = keysyms_per_keycode; i; i--) {
guint32 v = VALUE32(tvb, cur_offset);
@@ -970,7 +970,7 @@ static void listOfPoint(tvbuff_t *tvb, int hf, int length)
x = VALUE16(tvb, cur_offset);
y = VALUE16(tvb, cur_offset + 2);
- ttt = proto_tree_add_protocol_format(tt, hf_x11_point, tvb, cur_offset, 4, "point: (%d,%d)", x, y);
+ ttt = proto_tree_add_none_format(tt, hf_x11_point, tvb, cur_offset, 4, "point: (%d,%d)", x, y);
proto_tree_add_int(ttt, hf_x11_point_x, tvb, cur_offset, 2, x); cur_offset += 2;
proto_tree_add_int(ttt, hf_x11_point_y, tvb, cur_offset, 2, y); cur_offset += 2;
}
@@ -993,7 +993,7 @@ static void listOfRectangle(tvbuff_t *tvb, int hf, int length)
width = VALUE16(tvb, cur_offset + 4);
height = VALUE16(tvb, cur_offset + 6);
- ttt = proto_tree_add_protocol_format(tt, hf_x11_rectangle, tvb, cur_offset, 8,
+ ttt = proto_tree_add_none_format(tt, hf_x11_rectangle, tvb, cur_offset, 8,
"rectangle: %dx%d+%d+%d", width, height, x, y);
proto_tree_add_int(ttt, hf_x11_rectangle_x, tvb, cur_offset, 2, x); cur_offset += 2;
proto_tree_add_int(ttt, hf_x11_rectangle_y, tvb, cur_offset, 2, y); cur_offset += 2;
@@ -1018,7 +1018,7 @@ static void listOfSegment(tvbuff_t *tvb, int hf, int length)
x2 = VALUE16(tvb, cur_offset + 4);
y2 = VALUE16(tvb, cur_offset + 6);
- ttt = proto_tree_add_protocol_format(tt, hf_x11_segment, tvb, cur_offset, 8,
+ ttt = proto_tree_add_none_format(tt, hf_x11_segment, tvb, cur_offset, 8,
"segment: (%d,%d)-(%d,%d)", x1, y1, x2, y2);
proto_tree_add_item(ttt, hf_x11_segment_x1, tvb, cur_offset, 2, little_endian); cur_offset += 2;
proto_tree_add_item(ttt, hf_x11_segment_y1, tvb, cur_offset, 2, little_endian); cur_offset += 2;
@@ -1170,7 +1170,7 @@ static void listOfTextItem(tvbuff_t *tvb, int hf, int sizeIs16)
allocated = l + 1;
}
stringCopy(s, tvb_get_ptr(tvb, cur_offset + 2, l), l);
- ttt = proto_tree_add_protocol_format(tt, hf_x11_textitem_string, tvb, cur_offset, l + 2,
+ ttt = proto_tree_add_none_format(tt, hf_x11_textitem_string, tvb, cur_offset, l + 2,
"textitem (string): delta = %d, \"%s\"",
delta, s);
proto_tree_add_item(ttt, hf_x11_textitem_string_delta, tvb, cur_offset + 1, 1, little_endian);
@@ -1191,7 +1191,7 @@ static void listOfTextItem(tvbuff_t *tvb, int hf, int sizeIs16)
static guint32 field8(tvbuff_t *tvb, int hf)
{
guint32 v = VALUE8(tvb, cur_offset);
- struct header_field_info *hfi = proto_registrar_get_nth(hf);
+ header_field_info *hfi = proto_registrar_get_nth(hf);
gchar *enumValue = NULL;
gchar *nameAsChar = hfi -> name;
@@ -1216,7 +1216,7 @@ static guint32 field16(tvbuff_t *tvb, int hf)
static guint32 field32(tvbuff_t *tvb, int hf)
{
guint32 v = VALUE32(tvb, cur_offset);
- struct header_field_info *hfi = proto_registrar_get_nth(hf);
+ header_field_info *hfi = proto_registrar_get_nth(hf);
gchar *enumValue = NULL;
gchar *nameAsChar = hfi -> name;
diff --git a/tethereal.c b/tethereal.c
index 3b7527cb33..e20934d55e 100644
--- a/tethereal.c
+++ b/tethereal.c
@@ -1,6 +1,6 @@
/* tethereal.c
*
- * $Id: tethereal.c,v 1.63 2001/01/29 00:09:38 guy Exp $
+ * $Id: tethereal.c,v 1.64 2001/02/01 20:21:13 gram Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@@ -192,7 +192,7 @@ main(int argc, char *argv[])
#endif
int out_file_type = WTAP_FILE_PCAP;
gchar *cf_name = NULL, *rfilter = NULL;
- dfilter *rfcode = NULL;
+ dfilter_t *rfcode = NULL;
e_prefs *prefs;
/* Register all dissectors; we must do this before checking for the
@@ -470,7 +470,7 @@ main(int argc, char *argv[])
cfile.snap = MIN_PACKET_SIZE;
if (rfilter != NULL) {
- if (dfilter_compile(rfilter, &rfcode) != 0) {
+ if (!dfilter_compile(rfilter, &rfcode)) {
fprintf(stderr, "tethereal: %s\n", dfilter_error_msg);
epan_cleanup();
exit(2);
@@ -908,7 +908,7 @@ wtap_dispatch_cb_write(u_char *user, const struct wtap_pkthdr *phdr, int offset,
fill_in_fdata(&fdata, cf, phdr, pseudo_header, offset);
protocol_tree = proto_tree_create_root();
edt = epan_dissect_new(pseudo_header, buf, &fdata, protocol_tree);
- passed = dfilter_apply(cf->rfcode, protocol_tree, buf, fdata.cap_len);
+ passed = dfilter_apply_edt(cf->rfcode, edt);
} else {
protocol_tree = NULL;
passed = TRUE;
@@ -952,7 +952,7 @@ wtap_dispatch_cb_print(u_char *user, const struct wtap_pkthdr *phdr, int offset,
protocol_tree = NULL;
edt = epan_dissect_new(pseudo_header, buf, &fdata, protocol_tree);
if (cf->rfcode)
- passed = dfilter_apply(cf->rfcode, protocol_tree, buf, fdata.cap_len);
+ passed = dfilter_apply_edt(cf->rfcode, edt);
if (passed) {
/* The packet passed the read filter. */
if (verbose) {
diff --git a/tools/.cvsignore b/tools/.cvsignore
new file mode 100644
index 0000000000..78ae5f3829
--- /dev/null
+++ b/tools/.cvsignore
@@ -0,0 +1,3 @@
+.cvsignore
+Makefile
+Makefile.in
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000000..be70ac866e
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = lemon
diff --git a/tools/lemon/.cvsignore b/tools/lemon/.cvsignore
new file mode 100644
index 0000000000..2e02dc8833
--- /dev/null
+++ b/tools/lemon/.cvsignore
@@ -0,0 +1,8 @@
+.cvsignore
+.deps
+Makefile
+lemon
+lemon.exe
+lemon.obj
+.libs
+Makefile.in
diff --git a/tools/lemon/Makefile.am b/tools/lemon/Makefile.am
new file mode 100644
index 0000000000..1f811d5e7b
--- /dev/null
+++ b/tools/lemon/Makefile.am
@@ -0,0 +1,44 @@
+# Makefile.am
+#
+# $Id: Makefile.am,v 1.1 2001/02/01 20:21:24 gram Exp $
+#
+# Ethereal - Network traffic analyzer
+# By Gerald Combs <gerald@zing.org>
+# Copyright 2001 Gerald Combs
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# We produce an archive library. In the future, when libethereal is a
+# shared library, this will be linked into libethereal. While libethereal
+# is an archive library, any executable linking against libethereal will
+# also need to link against libftypes.
+
+noinst_PROGRAMS = lemon
+
+CLEANFILES = \
+ *~
+
+lemon_SOURCES = \
+ lemon.c
+
+# Makefile.nmake
+EXTRA_DIST = \
+ lemon.html \
+ lemonflex-head.inc \
+ lemonflex-tail.inc \
+ lempar.c \
+ README
+
diff --git a/tools/lemon/README b/tools/lemon/README
new file mode 100644
index 0000000000..52ce8b4c54
--- /dev/null
+++ b/tools/lemon/README
@@ -0,0 +1,15 @@
+$Id: README,v 1.1 2001/02/01 20:21:25 gram Exp $
+
+The Lemon Parser Generator's home page is:
+
+http://www.hwaci.com/sw/lemon/index.html
+
+The file in this directory, lemon.html, was obtained from:
+
+http://www.hwaci.com/sw/lemon/lemon.html
+
+lemon.c has been modified to include the t= and d= command-line
+arguments. The changes were submitted to the Lemon maintainer,
+but have not appeared in the official Lemon distribution.
+
+
diff --git a/tools/lemon/lemon.c b/tools/lemon/lemon.c
new file mode 100644
index 0000000000..0940cea457
--- /dev/null
+++ b/tools/lemon/lemon.c
@@ -0,0 +1,4116 @@
+/*
+** Copyright (c) 1991, 1994, 1997, 1998 D. Richard Hipp
+**
+** This file contains all sources (including headers) to the LEMON
+** LALR(1) parser generator. The sources have been combined into a
+** single file to make it easy to include LEMON as part of another
+** program.
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@acm.org
+** http://www.hwaci.com/drh/
+**
+** $Id: lemon.c,v 1.1 2001/02/01 20:21:25 gram Exp $
+*/
+#include <stdio.h>
+#include <varargs.h>
+#include <string.h>
+#include <ctype.h>
+
+extern void qsort();
+extern double strtod();
+extern long strtol();
+extern void free();
+extern int access();
+extern int atoi();
+
+#ifndef __WIN32__
+# if defined(_WIN32) || defined(WIN32)
+# define __WIN32__
+# endif
+#endif
+
+/* #define PRIVATE static */
+#define PRIVATE
+
+#ifdef TEST
+#define MAXRHS 5 /* Set low to exercise exception code */
+#else
+#define MAXRHS 1000
+#endif
+
+char *msort();
+extern void *malloc();
+
+/******** From the file "action.h" *************************************/
+struct action *Action_new();
+struct action *Action_sort();
+void Action_add();
+
+/********* From the file "assert.h" ************************************/
+void myassert();
+#ifndef NDEBUG
+# define assert(X) if(!(X))myassert(__FILE__,__LINE__)
+#else
+# define assert(X)
+#endif
+
+/********** From the file "build.h" ************************************/
+void FindRulePrecedences();
+void FindFirstSets();
+void FindStates();
+void FindLinks();
+void FindFollowSets();
+void FindActions();
+
+/********* From the file "configlist.h" *********************************/
+void Configlist_init(/* void */);
+struct config *Configlist_add(/* struct rule *, int */);
+struct config *Configlist_addbasis(/* struct rule *, int */);
+void Configlist_closure(/* void */);
+void Configlist_sort(/* void */);
+void Configlist_sortbasis(/* void */);
+struct config *Configlist_return(/* void */);
+struct config *Configlist_basis(/* void */);
+void Configlist_eat(/* struct config * */);
+void Configlist_reset(/* void */);
+
+/********* From the file "error.h" ***************************************/
+void ErrorMsg( /* char *, int, char *, ... */ );
+
+/****** From the file "option.h" ******************************************/
+struct s_options {
+ enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR,
+ OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type;
+ char *label;
+ char *arg;
+ char *message;
+};
+int optinit(/* char**,struct s_options*,FILE* */);
+int optnargs(/* void */);
+char *optarg(/* int */);
+void opterr(/* int */);
+void optprint(/* void */);
+
+/******** From the file "parse.h" *****************************************/
+void Parse(/* struct lemon *lemp */);
+
+/********* From the file "plink.h" ***************************************/
+struct plink *Plink_new(/* void */);
+void Plink_add(/* struct plink **, struct config * */);
+void Plink_copy(/* struct plink **, struct plink * */);
+void Plink_delete(/* struct plink * */);
+
+/********** From the file "report.h" *************************************/
+void Reprint(/* struct lemon * */);
+void ReportOutput(/* struct lemon * */);
+void ReportTable(/* struct lemon * */);
+void ReportHeader(/* struct lemon * */);
+void CompressTables(/* struct lemon * */);
+
+/********** From the file "set.h" ****************************************/
+void SetSize(/* int N */); /* All sets will be of size N */
+char *SetNew(/* void */); /* A new set for element 0..N */
+void SetFree(/* char* */); /* Deallocate a set */
+
+int SetAdd(/* char*,int */); /* Add element to a set */
+int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */
+
+#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */
+
+/********** From the file "struct.h" *************************************/
+/*
+** Principal data structures for the LEMON parser generator.
+*/
+
+typedef enum {FALSE=0, TRUE} Boolean;
+
+/* Symbols (terminals and nonterminals) of the grammar are stored
+** in the following: */
+struct symbol {
+ char *name; /* Name of the symbol */
+ int index; /* Index number for this symbol */
+ enum {
+ TERMINAL,
+ NONTERMINAL
+ } type; /* Symbols are all either TERMINALS or NTs */
+ struct rule *rule; /* Linked list of rules of this (if an NT) */
+ int prec; /* Precedence if defined (-1 otherwise) */
+ enum e_assoc {
+ LEFT,
+ RIGHT,
+ NONE,
+ UNK
+ } assoc; /* Associativity if predecence is defined */
+ char *firstset; /* First-set for all rules of this symbol */
+ Boolean lambda; /* True if NT and can generate an empty string */
+ char *destructor; /* Code which executes whenever this symbol is
+ ** popped from the stack during error processing */
+ int destructorln; /* Line number of destructor code */
+ char *datatype; /* The data type of information held by this
+ ** object. Only used if type==NONTERMINAL */
+ int dtnum; /* The data type number. In the parser, the value
+ ** stack is a union. The .yy%d element of this
+ ** union is the correct data type for this object */
+};
+
+/* Each production rule in the grammar is stored in the following
+** structure. */
+struct rule {
+ struct symbol *lhs; /* Left-hand side of the rule */
+ char *lhsalias; /* Alias for the LHS (NULL if none) */
+ int ruleline; /* Line number for the rule */
+ int nrhs; /* Number of RHS symbols */
+ struct symbol **rhs; /* The RHS symbols */
+ char **rhsalias; /* An alias for each RHS symbol (NULL if none) */
+ int line; /* Line number at which code begins */
+ char *code; /* The code executed when this rule is reduced */
+ struct symbol *precsym; /* Precedence symbol for this rule */
+ int index; /* An index number for this rule */
+ Boolean canReduce; /* True if this rule is ever reduced */
+ struct rule *nextlhs; /* Next rule with the same LHS */
+ struct rule *next; /* Next rule in the global list */
+};
+
+/* A configuration is a production rule of the grammar together with
+** a mark (dot) showing how much of that rule has been processed so far.
+** Configurations also contain a follow-set which is a list of terminal
+** symbols which are allowed to immediately follow the end of the rule.
+** Every configuration is recorded as an instance of the following: */
+struct config {
+ struct rule *rp; /* The rule upon which the configuration is based */
+ int dot; /* The parse point */
+ char *fws; /* Follow-set for this configuration only */
+ struct plink *fplp; /* Follow-set forward propagation links */
+ struct plink *bplp; /* Follow-set backwards propagation links */
+ struct state *stp; /* Pointer to state which contains this */
+ enum {
+ COMPLETE, /* The status is used during followset and */
+ INCOMPLETE /* shift computations */
+ } status;
+ struct config *next; /* Next configuration in the state */
+ struct config *bp; /* The next basis configuration */
+};
+
+/* Every shift or reduce operation is stored as one of the following */
+struct action {
+ struct symbol *sp; /* The look-ahead symbol */
+ enum e_action {
+ SHIFT,
+ ACCEPT,
+ REDUCE,
+ ERROR,
+ CONFLICT, /* Was a reduce, but part of a conflict */
+ SH_RESOLVED, /* Was a shift. Precedence resolved conflict */
+ RD_RESOLVED, /* Was reduce. Precedence resolved conflict */
+ NOT_USED /* Deleted by compression */
+ } type;
+ union {
+ struct state *stp; /* The new state, if a shift */
+ struct rule *rp; /* The rule, if a reduce */
+ } x;
+ struct action *next; /* Next action for this state */
+ struct action *collide; /* Next action with the same hash */
+};
+
+/* Each state of the generated parser's finite state machine
+** is encoded as an instance of the following structure. */
+struct state {
+ struct config *bp; /* The basis configurations for this state */
+ struct config *cfp; /* All configurations in this set */
+ int index; /* Sequencial number for this state */
+ struct action *ap; /* Array of actions for this state */
+ int naction; /* Number of actions for this state */
+ int tabstart; /* First index of the action table */
+ int tabdfltact; /* Default action */
+};
+
+/* A followset propagation link indicates that the contents of one
+** configuration followset should be propagated to another whenever
+** the first changes. */
+struct plink {
+ struct config *cfp; /* The configuration to which linked */
+ struct plink *next; /* The next propagate link */
+};
+
+/* The state vector for the entire parser generator is recorded as
+** follows. (LEMON uses no global variables and makes little use of
+** static variables. Fields in the following structure can be thought
+** of as begin global variables in the program.) */
+struct lemon {
+ struct state **sorted; /* Table of states sorted by state number */
+ struct rule *rule; /* List of all rules */
+ int nstate; /* Number of states */
+ int nrule; /* Number of rules */
+ int nsymbol; /* Number of terminal and nonterminal symbols */
+ int nterminal; /* Number of terminal symbols */
+ struct symbol **symbols; /* Sorted array of pointers to symbols */
+ int errorcnt; /* Number of errors */
+ struct symbol *errsym; /* The error symbol */
+ char *name; /* Name of the generated parser */
+ char *arg; /* Declaration of the 3th argument to parser */
+ char *tokentype; /* Type of terminal symbols in the parser stack */
+ char *start; /* Name of the start symbol for the grammar */
+ char *stacksize; /* Size of the parser stack */
+ char *include; /* Code to put at the start of the C file */
+ int includeln; /* Line number for start of include code */
+ char *error; /* Code to execute when an error is seen */
+ int errorln; /* Line number for start of error code */
+ char *overflow; /* Code to execute on a stack overflow */
+ int overflowln; /* Line number for start of overflow code */
+ char *failure; /* Code to execute on parser failure */
+ int failureln; /* Line number for start of failure code */
+ char *accept; /* Code to execute when the parser excepts */
+ int acceptln; /* Line number for the start of accept code */
+ char *extracode; /* Code appended to the generated file */
+ int extracodeln; /* Line number for the start of the extra code */
+ char *tokendest; /* Code to execute to destroy token data */
+ int tokendestln; /* Line number for token destroyer code */
+ char *filename; /* Name of the input file */
+ char *basename; /* Basename of inputer file (no directory or path */
+ char *outname; /* Name of the current output file */
+ char *outdirname; /* Name of the output directory, specified by user */
+ char *templatename; /* Name of template file to use, specified by user */
+ char *tokenprefix; /* A prefix added to token names in the .h file */
+ int nconflict; /* Number of parsing conflicts */
+ int tablesize; /* Size of the parse tables */
+ int basisflag; /* Print only basis configurations */
+ char *argv0; /* Name of the program */
+};
+
+#define MemoryCheck(X) if((X)==0){ \
+ extern void memory_error(); \
+ memory_error(); \
+}
+
+/**************** From the file "table.h" *********************************/
+/*
+** All code in this file has been automatically generated
+** from a specification in the file
+** "table.q"
+** by the associative array code building program "aagen".
+** Do not edit this file! Instead, edit the specification
+** file, then rerun aagen.
+*/
+/*
+** Code for processing tables in the LEMON parser generator.
+*/
+
+/* Routines for handling a strings */
+
+char *Strsafe();
+
+void Strsafe_init(/* void */);
+int Strsafe_insert(/* char * */);
+char *Strsafe_find(/* char * */);
+
+/* Routines for handling symbols of the grammar */
+
+struct symbol *Symbol_new();
+int Symbolcmpp(/* struct symbol **, struct symbol ** */);
+void Symbol_init(/* void */);
+int Symbol_insert(/* struct symbol *, char * */);
+struct symbol *Symbol_find(/* char * */);
+struct symbol *Symbol_Nth(/* int */);
+int Symbol_count(/* */);
+struct symbol **Symbol_arrayof(/* */);
+
+/* Routines to manage the state table */
+
+int Configcmp(/* struct config *, struct config * */);
+struct state *State_new();
+void State_init(/* void */);
+int State_insert(/* struct state *, struct config * */);
+struct state *State_find(/* struct config * */);
+struct state **State_arrayof(/* */);
+
+/* Routines used for efficiency in Configlist_add */
+
+void Configtable_init(/* void */);
+int Configtable_insert(/* struct config * */);
+struct config *Configtable_find(/* struct config * */);
+void Configtable_clear(/* int(*)(struct config *) */);
+/****************** From the file "action.c" *******************************/
+/*
+** Routines processing parser actions in the LEMON parser generator.
+*/
+
+/* Allocate a new parser action */
+struct action *Action_new(){
+ static struct action *freelist = 0;
+ struct action *new;
+
+ if( freelist==0 ){
+ int i;
+ int amt = 100;
+ freelist = (struct action *)malloc( sizeof(struct action)*amt );
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new parser action.");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1];
+ freelist[amt-1].next = 0;
+ }
+ new = freelist;
+ freelist = freelist->next;
+ return new;
+}
+
+/* Compare two actions */
+static int actioncmp(ap1,ap2)
+struct action *ap1;
+struct action *ap2;
+{
+ int rc;
+ rc = ap1->sp->index - ap2->sp->index;
+ if( rc==0 ) rc = (int)ap1->type - (int)ap2->type;
+ if( rc==0 ){
+ assert( ap1->type==REDUCE && ap2->type==REDUCE );
+ rc = ap1->x.rp->index - ap2->x.rp->index;
+ }
+ return rc;
+}
+
+/* Sort parser actions */
+struct action *Action_sort(ap)
+struct action *ap;
+{
+ ap = (struct action *)msort(ap,&ap->next,actioncmp);
+ return ap;
+}
+
+void Action_add(app,type,sp,arg)
+struct action **app;
+enum e_action type;
+struct symbol *sp;
+char *arg;
+{
+ struct action *new;
+ new = Action_new();
+ new->next = *app;
+ *app = new;
+ new->type = type;
+ new->sp = sp;
+ if( type==SHIFT ){
+ new->x.stp = (struct state *)arg;
+ }else{
+ new->x.rp = (struct rule *)arg;
+ }
+}
+/********************** From the file "assert.c" ****************************/
+/*
+** A more efficient way of handling assertions.
+*/
+void myassert(file,line)
+char *file;
+int line;
+{
+ fprintf(stderr,"Assertion failed on line %d of file \"%s\"\n",line,file);
+ exit(1);
+}
+/********************** From the file "build.c" *****************************/
+/*
+** Routines to construction the finite state machine for the LEMON
+** parser generator.
+*/
+
+/* Find a precedence symbol of every rule in the grammar.
+**
+** Those rules which have a precedence symbol coded in the input
+** grammar using the "[symbol]" construct will already have the
+** rp->precsym field filled. Other rules take as their precedence
+** symbol the first RHS symbol with a defined precedence. If there
+** are not RHS symbols with a defined precedence, the precedence
+** symbol field is left blank.
+*/
+void FindRulePrecedences(xp)
+struct lemon *xp;
+{
+ struct rule *rp;
+ for(rp=xp->rule; rp; rp=rp->next){
+ if( rp->precsym==0 ){
+ int i;
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhs[i]->prec>=0 ){
+ rp->precsym = rp->rhs[i];
+ break;
+ }
+ }
+ }
+ }
+ return;
+}
+
+/* Find all nonterminals which will generate the empty string.
+** Then go back and compute the first sets of every nonterminal.
+** The first set is the set of all terminal symbols which can begin
+** a string generated by that nonterminal.
+*/
+void FindFirstSets(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct rule *rp;
+ int progress;
+
+ for(i=0; i<lemp->nsymbol; i++){
+ lemp->symbols[i]->lambda = FALSE;
+ }
+ for(i=lemp->nterminal; i<lemp->nsymbol; i++){
+ lemp->symbols[i]->firstset = SetNew();
+ }
+
+ /* First compute all lambdas */
+ do{
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->lhs->lambda ) continue;
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhs[i]->lambda==FALSE ) break;
+ }
+ if( i==rp->nrhs ){
+ rp->lhs->lambda = TRUE;
+ progress = 1;
+ }
+ }
+ }while( progress );
+
+ /* Now compute all first sets */
+ do{
+ struct symbol *s1, *s2;
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ s1 = rp->lhs;
+ for(i=0; i<rp->nrhs; i++){
+ s2 = rp->rhs[i];
+ if( s2->type==TERMINAL ){
+ progress += SetAdd(s1->firstset,s2->index);
+ break;
+ }else if( s1==s2 ){
+ if( s1->lambda==FALSE ) break;
+ }else{
+ progress += SetUnion(s1->firstset,s2->firstset);
+ if( s2->lambda==FALSE ) break;
+ }
+ }
+ }
+ }while( progress );
+ return;
+}
+
+/* Compute all LR(0) states for the grammar. Links
+** are added to between some states so that the LR(1) follow sets
+** can be computed later.
+*/
+PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */
+void FindStates(lemp)
+struct lemon *lemp;
+{
+ struct symbol *sp;
+ struct rule *rp;
+
+ Configlist_init();
+
+ /* Find the start symbol */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ){
+ ErrorMsg(lemp->filename,0,
+"The specified start symbol \"%s\" is not \
+in a nonterminal of the grammar. \"%s\" will be used as the start \
+symbol instead.",lemp->start,lemp->rule->lhs->name);
+ lemp->errorcnt++;
+ sp = lemp->rule->lhs;
+ }
+ }else{
+ sp = lemp->rule->lhs;
+ }
+
+ /* Make sure the start symbol doesn't occur on the right-hand side of
+ ** any rule. Report an error if it does. (YACC would generate a new
+ ** start symbol in this case.) */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ int i;
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhs[i]==sp ){
+ ErrorMsg(lemp->filename,0,
+"The start symbol \"%s\" occurs on the \
+right-hand side of a rule. This will result in a parser which \
+does not work properly.",sp->name);
+ lemp->errorcnt++;
+ }
+ }
+ }
+
+ /* The basis configuration set for the first state
+ ** is all rules which have the start symbol as their
+ ** left-hand side */
+ for(rp=sp->rule; rp; rp=rp->nextlhs){
+ struct config *newcfp;
+ newcfp = Configlist_addbasis(rp,0);
+ SetAdd(newcfp->fws,0);
+ }
+
+ /* Compute the first state. All other states will be
+ ** computed automatically during the computation of the first one.
+ ** The returned pointer to the first state is not used. */
+ (void)getstate(lemp);
+ return;
+}
+
+/* Return a pointer to a state which is described by the configuration
+** list which has been built from calls to Configlist_add.
+*/
+PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */
+PRIVATE struct state *getstate(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *bp;
+ struct state *stp;
+
+ /* Extract the sorted basis of the new state. The basis was constructed
+ ** by prior calls to "Configlist_addbasis()". */
+ Configlist_sortbasis();
+ bp = Configlist_basis();
+
+ /* Get a state with the same basis */
+ stp = State_find(bp);
+ if( stp ){
+ /* A state with the same basis already exists! Copy all the follow-set
+ ** propagation links from the state under construction into the
+ ** preexisting state, then return a pointer to the preexisting state */
+ struct config *x, *y;
+ for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){
+ Plink_copy(&y->bplp,x->bplp);
+ Plink_delete(x->fplp);
+ x->fplp = x->bplp = 0;
+ }
+ cfp = Configlist_return();
+ Configlist_eat(cfp);
+ }else{
+ /* This really is a new state. Construct all the details */
+ Configlist_closure(lemp); /* Compute the configuration closure */
+ Configlist_sort(); /* Sort the configuration closure */
+ cfp = Configlist_return(); /* Get a pointer to the config list */
+ stp = State_new(); /* A new state structure */
+ MemoryCheck(stp);
+ stp->bp = bp; /* Remember the configuration basis */
+ stp->cfp = cfp; /* Remember the configuration closure */
+ stp->index = lemp->nstate++; /* Every state gets a sequence number */
+ stp->ap = 0; /* No actions, yet. */
+ State_insert(stp,stp->bp); /* Add to the state table */
+ buildshifts(lemp,stp); /* Recursively compute successor states */
+ }
+ return stp;
+}
+
+/* Construct all successor states to the given state. A "successor"
+** state is any state which can be reached by a shift action.
+*/
+PRIVATE void buildshifts(lemp,stp)
+struct lemon *lemp;
+struct state *stp; /* The state from which successors are computed */
+{
+ struct config *cfp; /* For looping thru the config closure of "stp" */
+ struct config *bcfp; /* For the inner loop on config closure of "stp" */
+ struct config *new; /* */
+ struct symbol *sp; /* Symbol following the dot in configuration "cfp" */
+ struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
+ struct state *newstp; /* A pointer to a successor state */
+
+ /* Each configuration becomes complete after it contibutes to a successor
+ ** state. Initially, all configurations are incomplete */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE;
+
+ /* Loop through all configurations of the state "stp" */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */
+ if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */
+ Configlist_reset(); /* Reset the new config set */
+ sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */
+
+ /* For every configuration in the state "stp" which has the symbol "sp"
+ ** following its dot, add the same configuration to the basis set under
+ ** construction but with the dot shifted one symbol to the right. */
+ for(bcfp=cfp; bcfp; bcfp=bcfp->next){
+ if( bcfp->status==COMPLETE ) continue; /* Already used */
+ if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */
+ bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */
+ if( bsp!=sp ) continue; /* Must be same as for "cfp" */
+ bcfp->status = COMPLETE; /* Mark this config as used */
+ new = Configlist_addbasis(bcfp->rp,bcfp->dot+1);
+ Plink_add(&new->bplp,bcfp);
+ }
+
+ /* Get a pointer to the state described by the basis configuration set
+ ** constructed in the preceding loop */
+ newstp = getstate(lemp);
+
+ /* The state "newstp" is reached from the state "stp" by a shift action
+ ** on the symbol "sp" */
+ Action_add(&stp->ap,SHIFT,sp,newstp);
+ }
+}
+
+/*
+** Construct the propagation links
+*/
+void FindLinks(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp, *other;
+ struct state *stp;
+ struct plink *plp;
+
+ /* Housekeeping detail:
+ ** Add to every propagate link a pointer back to the state to
+ ** which the link is attached. */
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ cfp->stp = stp;
+ }
+ }
+
+ /* Convert all backlinks into forward links. Only the forward
+ ** links are used in the follow-set computation. */
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ for(plp=cfp->bplp; plp; plp=plp->next){
+ other = plp->cfp;
+ Plink_add(&other->fplp,cfp);
+ }
+ }
+ }
+}
+
+/* Compute all followsets.
+**
+** A followset is the set of all symbols which can come immediately
+** after a configuration.
+*/
+void FindFollowSets(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp;
+ struct plink *plp;
+ int progress;
+ int change;
+
+ for(i=0; i<lemp->nstate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ cfp->status = INCOMPLETE;
+ }
+ }
+
+ do{
+ progress = 0;
+ for(i=0; i<lemp->nstate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue;
+ for(plp=cfp->fplp; plp; plp=plp->next){
+ change = SetUnion(plp->cfp->fws,cfp->fws);
+ if( change ){
+ plp->cfp->status = INCOMPLETE;
+ progress = 1;
+ }
+ }
+ cfp->status = COMPLETE;
+ }
+ }
+ }while( progress );
+}
+
+static int resolve_conflict();
+
+/* Compute the reduce actions, and resolve conflicts.
+*/
+void FindActions(lemp)
+struct lemon *lemp;
+{
+ int i,j;
+ struct config *cfp;
+ struct state *stp;
+ struct symbol *sp;
+ struct rule *rp;
+
+ /* Add all of the reduce actions
+ ** A reduce action is added for each element of the followset of
+ ** a configuration which has its dot at the extreme right.
+ */
+ for(i=0; i<lemp->nstate; i++){ /* Loop over all states */
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */
+ if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */
+ for(j=0; j<lemp->nterminal; j++){
+ if( SetFind(cfp->fws,j) ){
+ /* Add a reduce action to the state "stp" which will reduce by the
+ ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */
+ Action_add(&stp->ap,REDUCE,lemp->symbols[j],cfp->rp);
+ }
+ }
+ }
+ }
+ }
+
+ /* Add the accepting token */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ) sp = lemp->rule->lhs;
+ }else{
+ sp = lemp->rule->lhs;
+ }
+ /* Add to the first state (which is always the starting state of the
+ ** finite state machine) an action to ACCEPT if the lookahead is the
+ ** start nonterminal. */
+ Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0);
+
+ /* Resolve conflicts */
+ for(i=0; i<lemp->nstate; i++){
+ struct action *ap, *nap;
+ struct state *stp;
+ stp = lemp->sorted[i];
+ assert( stp->ap );
+ stp->ap = Action_sort(stp->ap);
+ for(ap=stp->ap; ap && ap->next; ap=nap){
+ for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){
+ /* The two actions "ap" and "nap" have the same lookahead.
+ ** Figure out which one should be used */
+ lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym);
+ }
+ }
+ }
+
+ /* Report an error for each rule that can never be reduced. */
+ for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = FALSE;
+ for(i=0; i<lemp->nstate; i++){
+ struct action *ap;
+ for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){
+ if( ap->type==REDUCE ) ap->x.rp->canReduce = TRUE;
+ }
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->canReduce ) continue;
+ ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n");
+ lemp->errorcnt++;
+ }
+}
+
+/* Resolve a conflict between the two given actions. If the
+** conflict can't be resolve, return non-zero.
+**
+** NO LONGER TRUE:
+** To resolve a conflict, first look to see if either action
+** is on an error rule. In that case, take the action which
+** is not associated with the error rule. If neither or both
+** actions are associated with an error rule, then try to
+** use precedence to resolve the conflict.
+**
+** If either action is a SHIFT, then it must be apx. This
+** function won't work if apx->type==REDUCE and apy->type==SHIFT.
+*/
+static int resolve_conflict(apx,apy,errsym)
+struct action *apx;
+struct action *apy;
+struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */
+{
+ struct symbol *spx, *spy;
+ int errcnt = 0;
+ assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */
+ if( apx->type==SHIFT && apy->type==REDUCE ){
+ spx = apx->sp;
+ spy = apy->x.rp->precsym;
+ if( spy==0 || spx->prec<0 || spy->prec<0 ){
+ /* Not enough precedence information. */
+ apy->type = CONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){ /* Lower precedence wins */
+ apy->type = RD_RESOLVED;
+ }else if( spx->prec<spy->prec ){
+ apx->type = SH_RESOLVED;
+ }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */
+ apy->type = RD_RESOLVED; /* associativity */
+ }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */
+ apx->type = SH_RESOLVED;
+ }else{
+ assert( spx->prec==spy->prec && spx->assoc==NONE );
+ apy->type = CONFLICT;
+ errcnt++;
+ }
+ }else if( apx->type==REDUCE && apy->type==REDUCE ){
+ spx = apx->x.rp->precsym;
+ spy = apy->x.rp->precsym;
+ if( spx==0 || spy==0 || spx->prec<0 ||
+ spy->prec<0 || spx->prec==spy->prec ){
+ apy->type = CONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){
+ apy->type = RD_RESOLVED;
+ }else if( spx->prec<spy->prec ){
+ apx->type = RD_RESOLVED;
+ }
+ }else{
+ /* Can't happen. Shifts have to come before Reduces on the
+ ** list because the reduces were added last. Hence, if apx->type==REDUCE
+ ** then it is impossible for apy->type==SHIFT */
+ }
+ return errcnt;
+}
+/********************* From the file "configlist.c" *************************/
+/*
+** Routines to processing a configuration list and building a state
+** in the LEMON parser generator.
+*/
+
+static struct config *freelist = 0; /* List of free configurations */
+static struct config *current = 0; /* Top of list of configurations */
+static struct config **currentend = 0; /* Last on list of configs */
+static struct config *basis = 0; /* Top of list of basis configs */
+static struct config **basisend = 0; /* End of list of basis configs */
+
+/* Return a pointer to a new configuration */
+PRIVATE struct config *newconfig(){
+ struct config *new;
+ if( freelist==0 ){
+ int i;
+ int amt = 3;
+ freelist = (struct config *)malloc( sizeof(struct config)*amt );
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new configuration.");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1];
+ freelist[amt-1].next = 0;
+ }
+ new = freelist;
+ freelist = freelist->next;
+ return new;
+}
+
+/* The configuration "old" is no longer used */
+PRIVATE void deleteconfig(old)
+struct config *old;
+{
+ old->next = freelist;
+ freelist = old;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_init(){
+ current = 0;
+ currentend = &current;
+ basis = 0;
+ basisend = &basis;
+ Configtable_init();
+ return;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_reset(){
+ current = 0;
+ currentend = &current;
+ basis = 0;
+ basisend = &basis;
+ Configtable_clear(0);
+ return;
+}
+
+/* Add another configuration to the configuration list */
+struct config *Configlist_add(rp,dot)
+struct rule *rp; /* The rule */
+int dot; /* Index into the RHS of the rule where the dot goes */
+{
+ struct config *cfp, model;
+
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Add a basis configuration to the configuration list */
+struct config *Configlist_addbasis(rp,dot)
+struct rule *rp;
+int dot;
+{
+ struct config *cfp, model;
+
+ assert( basisend!=0 );
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ *basisend = cfp;
+ basisend = &cfp->bp;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Compute the closure of the configuration list */
+void Configlist_closure(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *newcfp;
+ struct rule *rp, *newrp;
+ struct symbol *sp, *xsp;
+ int i, dot;
+
+ assert( currentend!=0 );
+ for(cfp=current; cfp; cfp=cfp->next){
+ rp = cfp->rp;
+ dot = cfp->dot;
+ if( dot>=rp->nrhs ) continue;
+ sp = rp->rhs[dot];
+ if( sp->type==NONTERMINAL ){
+ if( sp->rule==0 && sp!=lemp->errsym ){
+ ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.",
+ sp->name);
+ lemp->errorcnt++;
+ }
+ for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){
+ newcfp = Configlist_add(newrp,0);
+ for(i=dot+1; i<rp->nrhs; i++){
+ xsp = rp->rhs[i];
+ if( xsp->type==TERMINAL ){
+ SetAdd(newcfp->fws,xsp->index);
+ break;
+ }else{
+ SetUnion(newcfp->fws,xsp->firstset);
+ if( xsp->lambda==FALSE ) break;
+ }
+ }
+ if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp);
+ }
+ }
+ }
+ return;
+}
+
+/* Sort the configuration list */
+void Configlist_sort(){
+ current = (struct config *)msort(current,&(current->next),Configcmp);
+ currentend = 0;
+ return;
+}
+
+/* Sort the basis configuration list */
+void Configlist_sortbasis(){
+ basis = (struct config *)msort(current,&(current->bp),Configcmp);
+ basisend = 0;
+ return;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_return(){
+ struct config *old;
+ old = current;
+ current = 0;
+ currentend = 0;
+ return old;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_basis(){
+ struct config *old;
+ old = basis;
+ basis = 0;
+ basisend = 0;
+ return old;
+}
+
+/* Free all elements of the given configuration list */
+void Configlist_eat(cfp)
+struct config *cfp;
+{
+ struct config *nextcfp;
+ for(; cfp; cfp=nextcfp){
+ nextcfp = cfp->next;
+ assert( cfp->fplp==0 );
+ assert( cfp->bplp==0 );
+ if( cfp->fws ) SetFree(cfp->fws);
+ deleteconfig(cfp);
+ }
+ return;
+}
+/***************** From the file "error.c" *********************************/
+/*
+** Code for printing error message.
+*/
+
+/* Find a good place to break "msg" so that its length is at least "min"
+** but no more than "max". Make the point as close to max as possible.
+*/
+static int findbreak(msg,min,max)
+char *msg;
+int min;
+int max;
+{
+ int i,spot;
+ char c;
+ for(i=spot=min; i<=max; i++){
+ c = msg[i];
+ if( c=='\t' ) msg[i] = ' ';
+ if( c=='\n' ){ msg[i] = ' '; spot = i; break; }
+ if( c==0 ){ spot = i; break; }
+ if( c=='-' && i<max-1 ) spot = i+1;
+ if( c==' ' ) spot = i;
+ }
+ return spot;
+}
+
+/*
+** The error message is split across multiple lines if necessary. The
+** splits occur at a space, if there is a space available near the end
+** of the line.
+*/
+#define ERRMSGSIZE 10000 /* Hope this is big enough. No way to error check */
+#define LINEWIDTH 79 /* Max width of any output line */
+#define PREFIXLIMIT 30 /* Max width of the prefix on each line */
+void ErrorMsg(va_alist)
+va_dcl
+{
+ char *filename;
+ int lineno;
+ char *format;
+ char errmsg[ERRMSGSIZE];
+ char prefix[PREFIXLIMIT+10];
+ int errmsgsize;
+ int prefixsize;
+ int availablewidth;
+ va_list ap;
+ int end, restart, base;
+
+ va_start(ap);
+ filename = va_arg(ap,char*);
+ lineno = va_arg(ap,int);
+ format = va_arg(ap,char*);
+ /* Prepare a prefix to be prepended to every output line */
+ if( lineno>0 ){
+ sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno);
+ }else{
+ sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename);
+ }
+ prefixsize = strlen(prefix);
+ availablewidth = LINEWIDTH - prefixsize;
+
+ /* Generate the error message */
+ vsprintf(errmsg,format,ap);
+ va_end(ap);
+ errmsgsize = strlen(errmsg);
+ /* Remove trailing '\n's from the error message. */
+ while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){
+ errmsg[--errmsgsize] = 0;
+ }
+
+ /* Print the error message */
+ base = 0;
+ while( errmsg[base]!=0 ){
+ end = restart = findbreak(&errmsg[base],0,availablewidth);
+ restart += base;
+ while( errmsg[restart]==' ' ) restart++;
+ fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]);
+ base = restart;
+ }
+}
+/**************** From the file "main.c" ************************************/
+/*
+** Main program file for the LEMON parser generator.
+*/
+
+/* Report an out-of-memory condition and abort. This function
+** is used mostly by the "MemoryCheck" macro in struct.h
+*/
+void memory_error(){
+ fprintf(stderr,"Out of memory. Aborting...\n");
+ exit(1);
+}
+
+/* Locates the basename in a string possibly containing paths,
+ * including forward-slash and backward-slash delimiters on Windows,
+ * and allocates a new string containing just the basename.
+ * Returns the pointer to that string.
+ */
+PRIVATE char*
+make_basename(char* fullname)
+{
+ char *cp;
+ char *new_string;
+
+ /* Find the last forward slash */
+ cp = strrchr(fullname, '/');
+
+#ifdef WIN32
+ /* On Windows, if no forward slash was found, look ofr
+ * backslash also */
+ if (!cp)
+ cp = strrchr(fullname, '\\');
+#endif
+
+ if (!cp) {
+ new_string = malloc( strlen(fullname) );
+ strcpy(new_string, fullname);
+ }
+ else {
+ /* skip the slash */
+ cp++;
+ new_string = malloc( strlen(cp) );
+ strcpy(new_string, cp);
+ }
+
+ return new_string;
+}
+
+
+
+
+/* The main program. Parse the command line and do it... */
+int main(argc,argv)
+int argc;
+char **argv;
+{
+ static int version = 0;
+ static int rpflag = 0;
+ static int basisflag = 0;
+ static int compress = 0;
+ static int quiet = 0;
+ static int statistics = 0;
+ static int mhflag = 0;
+ static char *outdirname = NULL;
+ static char *templatename = NULL;
+ static struct s_options options[] = {
+ {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."},
+ {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."},
+ {OPT_STR, "d", (char*)&outdirname, "Output directory name."},
+ {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."},
+ {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"},
+ {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."},
+ {OPT_FLAG, "s", (char*)&statistics, "Print parser stats to standard output."},
+ {OPT_STR, "t", (char*)&templatename, "Template file to use."},
+ {OPT_FLAG, "x", (char*)&version, "Print the version number."},
+ {OPT_FLAG,0,0,0}
+ };
+ int i;
+ struct lemon lem;
+
+ optinit(argv,options,stderr);
+ if( version ){
+ printf("Lemon version 1.0\n"
+ "Copyright 1991-1997 by D. Richard Hipp\n"
+ "Freely distributable under the GNU Public License.\n"
+ );
+ exit(0);
+ }
+ if( optnargs()!=1 ){
+ fprintf(stderr,"Exactly one filename argument is required.\n");
+ exit(1);
+ }
+ lem.errorcnt = 0;
+
+ /* Initialize the machine */
+ Strsafe_init();
+ Symbol_init();
+ State_init();
+ lem.argv0 = argv[0];
+ lem.filename = optarg(0);
+ lem.basisflag = basisflag;
+ lem.nconflict = 0;
+ lem.name = lem.include = lem.arg = lem.tokentype = lem.start = 0;
+ lem.stacksize = 0;
+ lem.error = lem.overflow = lem.failure = lem.accept = lem.tokendest =
+ lem.tokenprefix = lem.outname = lem.extracode = 0;
+ lem.tablesize = 0;
+ Symbol_new("$");
+ lem.errsym = Symbol_new("error");
+ lem.outdirname = outdirname;
+ lem.templatename = templatename;
+ lem.basename = make_basename(lem.filename);
+
+ /* Parse the input file */
+ Parse(&lem);
+ if( lem.errorcnt ) exit(lem.errorcnt);
+ if( lem.rule==0 ){
+ fprintf(stderr,"Empty grammar.\n");
+ exit(1);
+ }
+
+ /* Count and index the symbols of the grammar */
+ lem.nsymbol = Symbol_count();
+ Symbol_new("{default}");
+ lem.symbols = Symbol_arrayof();
+ qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*),
+ (int(*)())Symbolcmpp);
+ for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ for(i=1; isupper(lem.symbols[i]->name[0]); i++);
+ lem.nterminal = i;
+
+ /* Generate a reprint of the grammar, if requested on the command line */
+ if( rpflag ){
+ Reprint(&lem);
+ }else{
+ /* Initialize the size for all follow and first sets */
+ SetSize(lem.nterminal);
+
+ /* Find the precedence for every production rule (that has one) */
+ FindRulePrecedences(&lem);
+
+ /* Compute the lambda-nonterminals and the first-sets for every
+ ** nonterminal */
+ FindFirstSets(&lem);
+
+ /* Compute all LR(0) states. Also record follow-set propagation
+ ** links so that the follow-set can be computed later */
+ lem.nstate = 0;
+ FindStates(&lem);
+ lem.sorted = State_arrayof();
+
+ /* Tie up loose ends on the propagation links */
+ FindLinks(&lem);
+
+ /* Compute the follow set of every reducible configuration */
+ FindFollowSets(&lem);
+
+ /* Compute the action tables */
+ FindActions(&lem);
+
+ /* Compress the action tables */
+ if( compress==0 ) CompressTables(&lem);
+
+ /* Generate a report of the parser generated. (the "y.output" file) */
+ if( !quiet ) ReportOutput(&lem);
+
+ /* Generate the source code for the parser */
+ ReportTable(&lem, mhflag);
+
+ /* Produce a header file for use by the scanner. (This step is
+ ** omitted if the "-m" option is used because makeheaders will
+ ** generate the file for us.) */
+ if( !mhflag ) ReportHeader(&lem);
+ }
+ if( statistics ){
+ printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n",
+ lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule);
+ printf(" %d states, %d parser table entries, %d conflicts\n",
+ lem.nstate, lem.tablesize, lem.nconflict);
+ }
+ if( lem.nconflict ){
+ fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict);
+ }
+ exit(lem.errorcnt + lem.nconflict);
+}
+/******************** From the file "msort.c" *******************************/
+/*
+** A generic merge-sort program.
+**
+** USAGE:
+** Let "ptr" be a pointer to some structure which is at the head of
+** a null-terminated list. Then to sort the list call:
+**
+** ptr = msort(ptr,&(ptr->next),cmpfnc);
+**
+** In the above, "cmpfnc" is a pointer to a function which compares
+** two instances of the structure and returns an integer, as in
+** strcmp. The second argument is a pointer to the pointer to the
+** second element of the linked list. This address is used to compute
+** the offset to the "next" field within the structure. The offset to
+** the "next" field must be constant for all structures in the list.
+**
+** The function returns a new pointer which is the head of the list
+** after sorting.
+**
+** ALGORITHM:
+** Merge-sort.
+*/
+
+/*
+** Return a pointer to the next structure in the linked list.
+*/
+#define NEXT(A) (*(char**)(((int)A)+offset))
+
+/*
+** Inputs:
+** a: A sorted, null-terminated linked list. (May be null).
+** b: A sorted, null-terminated linked list. (May be null).
+** cmp: A pointer to the comparison function.
+** offset: Offset in the structure to the "next" field.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** of both a and b.
+**
+** Side effects:
+** The "next" pointers for elements in the lists a and b are
+** changed.
+*/
+static char *merge(a,b,cmp,offset)
+char *a;
+char *b;
+int (*cmp)();
+int offset;
+{
+ char *ptr, *head;
+
+ if( a==0 ){
+ head = b;
+ }else if( b==0 ){
+ head = a;
+ }else{
+ if( (*cmp)(a,b)<0 ){
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ ptr = b;
+ b = NEXT(b);
+ }
+ head = ptr;
+ while( a && b ){
+ if( (*cmp)(a,b)<0 ){
+ NEXT(ptr) = a;
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ NEXT(ptr) = b;
+ ptr = b;
+ b = NEXT(b);
+ }
+ }
+ if( a ) NEXT(ptr) = a;
+ else NEXT(ptr) = b;
+ }
+ return head;
+}
+
+/*
+** Inputs:
+** list: Pointer to a singly-linked list of structures.
+** next: Pointer to pointer to the second element of the list.
+** cmp: A comparison function.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** orginally in list.
+**
+** Side effects:
+** The "next" pointers for elements in list are changed.
+*/
+#define LISTSIZE 30
+char *msort(list,next,cmp)
+char *list;
+char **next;
+int (*cmp)();
+{
+ int offset;
+ char *ep;
+ char *set[LISTSIZE];
+ int i;
+ offset = (int)next - (int)list;
+ for(i=0; i<LISTSIZE; i++) set[i] = 0;
+ while( list ){
+ ep = list;
+ list = NEXT(list);
+ NEXT(ep) = 0;
+ for(i=0; i<LISTSIZE-1 && set[i]!=0; i++){
+ ep = merge(ep,set[i],cmp,offset);
+ set[i] = 0;
+ }
+ set[i] = ep;
+ }
+ ep = 0;
+ for(i=0; i<LISTSIZE; i++) if( set[i] ) ep = merge(ep,set[i],cmp,offset);
+ return ep;
+}
+/************************ From the file "option.c" **************************/
+static char **argv;
+static struct s_options *op;
+static FILE *errstream;
+
+#define ISOPT(X) ((X)[0]=='-'||(X)[0]=='+'||strchr((X),'=')!=0)
+
+/*
+** Print the command line with a carrot pointing to the k-th character
+** of the n-th field.
+*/
+static void errline(n,k,err)
+int n;
+int k;
+FILE *err;
+{
+ int spcnt, i;
+ spcnt = 0;
+ if( argv[0] ) fprintf(err,"%s",argv[0]);
+ spcnt = strlen(argv[0]) + 1;
+ for(i=1; i<n && argv[i]; i++){
+ fprintf(err," %s",argv[i]);
+ spcnt += strlen(argv[i]+1);
+ }
+ spcnt += k;
+ for(; argv[i]; i++) fprintf(err," %s",argv[i]);
+ if( spcnt<20 ){
+ fprintf(err,"\n%*s^-- here\n",spcnt,"");
+ }else{
+ fprintf(err,"\n%*shere --^\n",spcnt-7,"");
+ }
+}
+
+/*
+** Return the index of the N-th non-switch argument. Return -1
+** if N is out of range.
+*/
+static int argindex(n)
+int n;
+{
+ int i;
+ int dashdash = 0;
+ if( argv!=0 && *argv!=0 ){
+ for(i=1; argv[i]; i++){
+ if( dashdash || !ISOPT(argv[i]) ){
+ if( n==0 ) return i;
+ n--;
+ }
+ if( strcmp(argv[i],"--")==0 ) dashdash = 1;
+ }
+ }
+ return -1;
+}
+
+static char emsg[] = "Command line syntax error: ";
+
+/*
+** Process a flag command line argument.
+*/
+static int handleflags(i,err)
+int i;
+FILE *err;
+{
+ int v;
+ int errcnt = 0;
+ int j;
+ for(j=0; op[j].label; j++){
+ if( strcmp(&argv[i][1],op[j].label)==0 ) break;
+ }
+ v = argv[i][0]=='-' ? 1 : 0;
+ if( op[j].label==0 ){
+ if( err ){
+ fprintf(err,"%sundefined option.\n",emsg);
+ errline(i,1,err);
+ }
+ errcnt++;
+ }else if( op[j].type==OPT_FLAG ){
+ *((int*)op[j].arg) = v;
+ }else if( op[j].type==OPT_FFLAG ){
+ (*(void(*)())(op[j].arg))(v);
+ }else{
+ if( err ){
+ fprintf(err,"%smissing argument on switch.\n",emsg);
+ errline(i,1,err);
+ }
+ errcnt++;
+ }
+ return errcnt;
+}
+
+/*
+** Process a command line switch which has an argument.
+*/
+static int handleswitch(i,err)
+int i;
+FILE *err;
+{
+ int lv = 0;
+ double dv = 0.0;
+ char *sv = 0, *end;
+ char *cp;
+ int j;
+ int errcnt = 0;
+ cp = strchr(argv[i],'=');
+ *cp = 0;
+ for(j=0; op[j].label; j++){
+ if( strcmp(argv[i],op[j].label)==0 ) break;
+ }
+ *cp = '=';
+ if( op[j].label==0 ){
+ if( err ){
+ fprintf(err,"%sundefined option.\n",emsg);
+ errline(i,0,err);
+ }
+ errcnt++;
+ }else{
+ cp++;
+ switch( op[j].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ if( err ){
+ fprintf(err,"%soption requires an argument.\n",emsg);
+ errline(i,0,err);
+ }
+ errcnt++;
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ dv = strtod(cp,&end);
+ if( *end ){
+ if( err ){
+ fprintf(err,"%sillegal character in floating-point argument.\n",emsg);
+ errline(i,((int)end)-(int)argv[i],err);
+ }
+ errcnt++;
+ }
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ lv = strtol(cp,&end,0);
+ if( *end ){
+ if( err ){
+ fprintf(err,"%sillegal character in integer argument.\n",emsg);
+ errline(i,((int)end)-(int)argv[i],err);
+ }
+ errcnt++;
+ }
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ sv = cp;
+ break;
+ }
+ switch( op[j].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ break;
+ case OPT_DBL:
+ *(double*)(op[j].arg) = dv;
+ break;
+ case OPT_FDBL:
+ (*(void(*)())(op[j].arg))(dv);
+ break;
+ case OPT_INT:
+ *(int*)(op[j].arg) = lv;
+ break;
+ case OPT_FINT:
+ (*(void(*)())(op[j].arg))((int)lv);
+ break;
+ case OPT_STR:
+ *(char**)(op[j].arg) = sv;
+ break;
+ case OPT_FSTR:
+ (*(void(*)())(op[j].arg))(sv);
+ break;
+ }
+ }
+ return errcnt;
+}
+
+int optinit(a,o,err)
+char **a;
+struct s_options *o;
+FILE *err;
+{
+ int errcnt = 0;
+ argv = a;
+ op = o;
+ errstream = err;
+ if( argv && *argv && op ){
+ int i;
+ for(i=1; argv[i]; i++){
+ if( argv[i][0]=='+' || argv[i][0]=='-' ){
+ errcnt += handleflags(i,err);
+ }else if( strchr(argv[i],'=') ){
+ errcnt += handleswitch(i,err);
+ }
+ }
+ }
+ if( errcnt>0 ){
+ fprintf(err,"Valid command line options for \"%s\" are:\n",*a);
+ optprint();
+ exit(1);
+ }
+ return 0;
+}
+
+int optnargs(){
+ int cnt = 0;
+ int dashdash = 0;
+ int i;
+ if( argv!=0 && argv[0]!=0 ){
+ for(i=1; argv[i]; i++){
+ if( dashdash || !ISOPT(argv[i]) ) cnt++;
+ if( strcmp(argv[i],"--")==0 ) dashdash = 1;
+ }
+ }
+ return cnt;
+}
+
+char *optarg(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ return i>=0 ? argv[i] : 0;
+}
+
+void opterr(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ if( i>=0 ) errline(i,0,errstream);
+}
+
+void optprint(){
+ int i;
+ int max, len;
+ max = 0;
+ for(i=0; op[i].label; i++){
+ len = strlen(op[i].label) + 1;
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ len += 9; /* length of "<integer>" */
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ len += 6; /* length of "<real>" */
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ len += 8; /* length of "<string>" */
+ break;
+ }
+ if( len>max ) max = len;
+ }
+ for(i=0; op[i].label; i++){
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ fprintf(errstream," -%-*s %s\n",max,op[i].label,op[i].message);
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ fprintf(errstream," %s=<integer>%*s %s\n",op[i].label,
+ max-strlen(op[i].label)-9,"",op[i].message);
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ fprintf(errstream," %s=<real>%*s %s\n",op[i].label,
+ max-strlen(op[i].label)-6,"",op[i].message);
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ fprintf(errstream," %s=<string>%*s %s\n",op[i].label,
+ max-strlen(op[i].label)-8,"",op[i].message);
+ break;
+ }
+ }
+}
+/*********************** From the file "parse.c" ****************************/
+/*
+** Input file parser for the LEMON parser generator.
+*/
+
+/* The state of the parser */
+struct pstate {
+ char *filename; /* Name of the input file */
+ int tokenlineno; /* Linenumber at which current token starts */
+ int errorcnt; /* Number of errors so far */
+ char *tokenstart; /* Text of current token */
+ struct lemon *gp; /* Global state vector */
+ enum e_state {
+ INITIALIZE,
+ WAITING_FOR_DECL_OR_RULE,
+ WAITING_FOR_DECL_KEYWORD,
+ WAITING_FOR_DECL_ARG,
+ WAITING_FOR_PRECEDENCE_SYMBOL,
+ WAITING_FOR_ARROW,
+ IN_RHS,
+ LHS_ALIAS_1,
+ LHS_ALIAS_2,
+ LHS_ALIAS_3,
+ RHS_ALIAS_1,
+ RHS_ALIAS_2,
+ PRECEDENCE_MARK_1,
+ PRECEDENCE_MARK_2,
+ RESYNC_AFTER_RULE_ERROR,
+ RESYNC_AFTER_DECL_ERROR,
+ WAITING_FOR_DESTRUCTOR_SYMBOL,
+ WAITING_FOR_DATATYPE_SYMBOL
+ } state; /* The state of the parser */
+ struct symbol *lhs; /* Left-hand side of current rule */
+ char *lhsalias; /* Alias for the LHS */
+ int nrhs; /* Number of right-hand side symbols seen */
+ struct symbol *rhs[MAXRHS]; /* RHS symbols */
+ char *alias[MAXRHS]; /* Aliases for each RHS symbol (or NULL) */
+ struct rule *prevrule; /* Previous rule parsed */
+ char *declkeyword; /* Keyword of a declaration */
+ char **declargslot; /* Where the declaration argument should be put */
+ int *decllnslot; /* Where the declaration linenumber is put */
+ enum e_assoc declassoc; /* Assign this association to decl arguments */
+ int preccounter; /* Assign this precedence to decl arguments */
+ struct rule *firstrule; /* Pointer to first rule in the grammar */
+ struct rule *lastrule; /* Pointer to the most recently parsed rule */
+};
+
+/* Parse a single token */
+static void parseonetoken(psp)
+struct pstate *psp;
+{
+ char *x;
+ x = Strsafe(psp->tokenstart); /* Save the token permanently */
+#if 0
+ printf("%s:%d: Token=[%s] state=%d\n",psp->filename,psp->tokenlineno,
+ x,psp->state);
+#endif
+ switch( psp->state ){
+ case INITIALIZE:
+ psp->prevrule = 0;
+ psp->preccounter = 0;
+ psp->firstrule = psp->lastrule = 0;
+ psp->gp->nrule = 0;
+ /* Fall thru to next case */
+ case WAITING_FOR_DECL_OR_RULE:
+ if( x[0]=='%' ){
+ psp->state = WAITING_FOR_DECL_KEYWORD;
+ }else if( islower(x[0]) ){
+ psp->lhs = Symbol_new(x);
+ psp->nrhs = 0;
+ psp->lhsalias = 0;
+ psp->state = WAITING_FOR_ARROW;
+ }else if( x[0]=='{' ){
+ if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"There is not prior rule opon which to attach the code \
+fragment which begins on this line.");
+ psp->errorcnt++;
+ }else if( psp->prevrule->code!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Code fragment beginning on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->line = psp->tokenlineno;
+ psp->prevrule->code = &x[1];
+ }
+ }else if( x[0]=='[' ){
+ psp->state = PRECEDENCE_MARK_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Token \"%s\" should be either \"%%\" or a nonterminal name.",
+ x);
+ psp->errorcnt++;
+ }
+ break;
+ case PRECEDENCE_MARK_1:
+ if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "The precedence symbol must be a terminal.");
+ psp->errorcnt++;
+ }else if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "There is no prior rule to assign precedence \"[%s]\".",x);
+ psp->errorcnt++;
+ }else if( psp->prevrule->precsym!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Precedence mark on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->precsym = Symbol_new(x);
+ }
+ psp->state = PRECEDENCE_MARK_2;
+ break;
+ case PRECEDENCE_MARK_2:
+ if( x[0]!=']' ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"]\" on precedence mark.");
+ psp->errorcnt++;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ break;
+ case WAITING_FOR_ARROW:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else if( x[0]=='(' ){
+ psp->state = LHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Expected to see a \":\" following the LHS symbol \"%s\".",
+ psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->lhsalias = x;
+ psp->state = LHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the LHS \"%s\"\n",
+ x,psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = LHS_ALIAS_3;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_3:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"->\" following: \"%s(%s)\".",
+ psp->lhs->name,psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case IN_RHS:
+ if( x[0]=='.' ){
+ struct rule *rp;
+ rp = (struct rule *)malloc( sizeof(struct rule) +
+ sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs );
+ if( rp==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't allocate enough memory for this rule.");
+ psp->errorcnt++;
+ psp->prevrule = 0;
+ }else{
+ int i;
+ rp->ruleline = psp->tokenlineno;
+ rp->rhs = (struct symbol**)&rp[1];
+ rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]);
+ for(i=0; i<psp->nrhs; i++){
+ rp->rhs[i] = psp->rhs[i];
+ rp->rhsalias[i] = psp->alias[i];
+ }
+ rp->lhs = psp->lhs;
+ rp->lhsalias = psp->lhsalias;
+ rp->nrhs = psp->nrhs;
+ rp->code = 0;
+ rp->precsym = 0;
+ rp->index = psp->gp->nrule++;
+ rp->nextlhs = rp->lhs->rule;
+ rp->lhs->rule = rp;
+ rp->next = 0;
+ if( psp->firstrule==0 ){
+ psp->firstrule = psp->lastrule = rp;
+ }else{
+ psp->lastrule->next = rp;
+ psp->lastrule = rp;
+ }
+ psp->prevrule = rp;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isalpha(x[0]) ){
+ if( psp->nrhs>=MAXRHS ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Too many symbol on RHS or rule beginning at \"%s\".",
+ x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }else{
+ psp->rhs[psp->nrhs] = Symbol_new(x);
+ psp->alias[psp->nrhs] = 0;
+ psp->nrhs++;
+ }
+ }else if( x[0]=='(' && psp->nrhs>0 ){
+ psp->state = RHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal character on RHS of rule: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->alias[psp->nrhs-1] = x;
+ psp->state = RHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n",
+ x,psp->rhs[psp->nrhs-1]->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case WAITING_FOR_DECL_KEYWORD:
+ if( isalpha(x[0]) ){
+ psp->declkeyword = x;
+ psp->declargslot = 0;
+ psp->decllnslot = 0;
+ psp->state = WAITING_FOR_DECL_ARG;
+ if( strcmp(x,"name")==0 ){
+ psp->declargslot = &(psp->gp->name);
+ }else if( strcmp(x,"include")==0 ){
+ psp->declargslot = &(psp->gp->include);
+ psp->decllnslot = &psp->gp->includeln;
+ }else if( strcmp(x,"code")==0 ){
+ psp->declargslot = &(psp->gp->extracode);
+ psp->decllnslot = &psp->gp->extracodeln;
+ }else if( strcmp(x,"token_destructor")==0 ){
+ psp->declargslot = &psp->gp->tokendest;
+ psp->decllnslot = &psp->gp->tokendestln;
+ }else if( strcmp(x,"token_prefix")==0 ){
+ psp->declargslot = &psp->gp->tokenprefix;
+ }else if( strcmp(x,"syntax_error")==0 ){
+ psp->declargslot = &(psp->gp->error);
+ psp->decllnslot = &psp->gp->errorln;
+ }else if( strcmp(x,"parse_accept")==0 ){
+ psp->declargslot = &(psp->gp->accept);
+ psp->decllnslot = &psp->gp->acceptln;
+ }else if( strcmp(x,"parse_failure")==0 ){
+ psp->declargslot = &(psp->gp->failure);
+ psp->decllnslot = &psp->gp->failureln;
+ }else if( strcmp(x,"stack_overflow")==0 ){
+ psp->declargslot = &(psp->gp->overflow);
+ psp->decllnslot = &psp->gp->overflowln;
+ }else if( strcmp(x,"extra_argument")==0 ){
+ psp->declargslot = &(psp->gp->arg);
+ }else if( strcmp(x,"token_type")==0 ){
+ psp->declargslot = &(psp->gp->tokentype);
+ }else if( strcmp(x,"stack_size")==0 ){
+ psp->declargslot = &(psp->gp->stacksize);
+ }else if( strcmp(x,"start_symbol")==0 ){
+ psp->declargslot = &(psp->gp->start);
+ }else if( strcmp(x,"left")==0 ){
+ psp->preccounter++;
+ psp->declassoc = LEFT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"right")==0 ){
+ psp->preccounter++;
+ psp->declassoc = RIGHT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"nonassoc")==0 ){
+ psp->preccounter++;
+ psp->declassoc = NONE;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"destructor")==0 ){
+ psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL;
+ }else if( strcmp(x,"type")==0 ){
+ psp->state = WAITING_FOR_DATATYPE_SYMBOL;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Unknown declaration keyword: \"%%%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal declaration keyword: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case WAITING_FOR_DESTRUCTOR_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->destructor;
+ psp->decllnslot = &sp->destructorln;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_DATATYPE_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->datatype;
+ psp->decllnslot = 0;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_PRECEDENCE_SYMBOL:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isupper(x[0]) ){
+ struct symbol *sp;
+ sp = Symbol_new(x);
+ if( sp->prec>=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol \"%s\" has already be given a precedence.",x);
+ psp->errorcnt++;
+ }else{
+ sp->prec = psp->preccounter;
+ sp->assoc = psp->declassoc;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't assign a precedence to \"%s\".",x);
+ psp->errorcnt++;
+ }
+ break;
+ case WAITING_FOR_DECL_ARG:
+ if( (x[0]=='{' || x[0]=='\"' || isalnum(x[0])) ){
+ if( *(psp->declargslot)!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "The argument \"%s\" to declaration \"%%%s\" is not the first.",
+ x[0]=='\"' ? &x[1] : x,psp->declkeyword);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ *(psp->declargslot) = (x[0]=='\"' || x[0]=='{') ? &x[1] : x;
+ if( psp->decllnslot ) *psp->decllnslot = psp->tokenlineno;
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal argument to %%%s: %s",psp->declkeyword,x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case RESYNC_AFTER_RULE_ERROR:
+/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+** break; */
+ case RESYNC_AFTER_DECL_ERROR:
+ if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+ if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD;
+ break;
+ }
+}
+
+/* In spite of its name, this function is really a scanner. It read
+** in the entire input file (all at once) then tokenizes it. Each
+** token is passed to the function "parseonetoken" which builds all
+** the appropriate data structures in the global state vector "gp".
+*/
+void Parse(gp)
+struct lemon *gp;
+{
+ struct pstate ps;
+ FILE *fp;
+ char *filebuf;
+ int filesize;
+ int lineno;
+ int c;
+ char *cp, *nextcp;
+ int startline = 0;
+
+ ps.gp = gp;
+ ps.filename = gp->filename;
+ ps.errorcnt = 0;
+ ps.state = INITIALIZE;
+
+ /* Begin by reading the input file */
+ fp = fopen(ps.filename,"rb");
+ if( fp==0 ){
+ ErrorMsg(ps.filename,0,"Can't open this file for reading.");
+ gp->errorcnt++;
+ return;
+ }
+ fseek(fp,0,2);
+ filesize = ftell(fp);
+ rewind(fp);
+ filebuf = (char *)malloc( filesize+1 );
+ if( filebuf==0 ){
+ ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.",
+ filesize+1);
+ gp->errorcnt++;
+ return;
+ }
+ if( fread(filebuf,1,filesize,fp)!=filesize ){
+ ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.",
+ filesize);
+ free(filebuf);
+ gp->errorcnt++;
+ return;
+ }
+ fclose(fp);
+ filebuf[filesize] = 0;
+
+ /* Now scan the text of the input file */
+ lineno = 1;
+ for(cp=filebuf; (c= *cp)!=0; ){
+ if( c=='\n' ) lineno++; /* Keep track of the line number */
+ if( isspace(c) ){ cp++; continue; } /* Skip all white space */
+ if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ continue;
+ }
+ if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c ) cp++;
+ continue;
+ }
+ ps.tokenstart = cp; /* Mark the beginning of the token */
+ ps.tokenlineno = lineno; /* Linenumber on which token begins */
+ if( c=='\"' ){ /* String literals */
+ cp++;
+ while( (c= *cp)!=0 && c!='\"' ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,startline,
+"String starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( c=='{' ){ /* A block of C code */
+ int level;
+ cp++;
+ for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){
+ if( c=='\n' ) lineno++;
+ else if( c=='{' ) level++;
+ else if( c=='}' ) level--;
+ else if( c=='/' && cp[1]=='*' ){ /* Skip comments */
+ int prevc;
+ cp = &cp[2];
+ prevc = 0;
+ while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){
+ if( c=='\n' ) lineno++;
+ prevc = c;
+ cp++;
+ }
+ }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */
+ cp = &cp[2];
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ if( c ) lineno++;
+ }else if( c=='\'' || c=='\"' ){ /* String a character literals */
+ int startchar, prevc;
+ startchar = c;
+ prevc = 0;
+ for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){
+ if( c=='\n' ) lineno++;
+ if( prevc=='\\' ) prevc = 0;
+ else prevc = c;
+ }
+ }
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,startline,
+"C code starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( isalnum(c) ){ /* Identifiers */
+ while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++;
+ nextcp = cp;
+ }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */
+ cp += 3;
+ nextcp = cp;
+ }else{ /* All other (one character) operators */
+ cp++;
+ nextcp = cp;
+ }
+ c = *cp;
+ *cp = 0; /* Null terminate the token */
+ parseonetoken(&ps); /* Parse the token */
+ *cp = c; /* Restore the buffer */
+ cp = nextcp;
+ }
+ free(filebuf); /* Release the buffer after parsing */
+ gp->rule = ps.firstrule;
+ gp->errorcnt = ps.errorcnt;
+}
+/*************************** From the file "plink.c" *********************/
+/*
+** Routines processing configuration follow-set propagation links
+** in the LEMON parser generator.
+*/
+static struct plink *plink_freelist = 0;
+
+/* Allocate a new plink */
+struct plink *Plink_new(){
+ struct plink *new;
+
+ if( plink_freelist==0 ){
+ int i;
+ int amt = 100;
+ plink_freelist = (struct plink *)malloc( sizeof(struct plink)*amt );
+ if( plink_freelist==0 ){
+ fprintf(stderr,
+ "Unable to allocate memory for a new follow-set propagation link.\n");
+ exit(1);
+ }
+ for(i=0; i<amt-1; i++) plink_freelist[i].next = &plink_freelist[i+1];
+ plink_freelist[amt-1].next = 0;
+ }
+ new = plink_freelist;
+ plink_freelist = plink_freelist->next;
+ return new;
+}
+
+/* Add a plink to a plink list */
+void Plink_add(plpp,cfp)
+struct plink **plpp;
+struct config *cfp;
+{
+ struct plink *new;
+ new = Plink_new();
+ new->next = *plpp;
+ *plpp = new;
+ new->cfp = cfp;
+}
+
+/* Transfer every plink on the list "from" to the list "to" */
+void Plink_copy(to,from)
+struct plink **to;
+struct plink *from;
+{
+ struct plink *nextpl;
+ while( from ){
+ nextpl = from->next;
+ from->next = *to;
+ *to = from;
+ from = nextpl;
+ }
+}
+
+/* Delete every plink on the list */
+void Plink_delete(plp)
+struct plink *plp;
+{
+ struct plink *nextpl;
+
+ while( plp ){
+ nextpl = plp->next;
+ plp->next = plink_freelist;
+ plink_freelist = plp;
+ plp = nextpl;
+ }
+}
+/*********************** From the file "report.c" **************************/
+/*
+** Procedures for generating reports and tables in the LEMON parser generator.
+*/
+
+/* Generate a filename with the given suffix. Space to hold the
+** name comes from malloc() and must be freed by the calling
+** function.
+*/
+PRIVATE char *file_makename(pattern,suffix)
+char *pattern;
+char *suffix;
+{
+ char *name;
+ char *cp;
+
+ name = malloc( strlen(pattern) + strlen(suffix) + 5 );
+ if( name==0 ){
+ fprintf(stderr,"Can't allocate space for a filename.\n");
+ exit(1);
+ }
+ strcpy(name,pattern);
+ cp = strrchr(name,'.');
+ if( cp ) *cp = 0;
+ strcat(name,suffix);
+ return name;
+}
+
+/* Generate a filename with the given suffix. Uses only
+** the basename of the input file, not the entire path. This
+** is useful for creating output files when using outdirname.
+** Space to hold this name comes from malloc() and must be
+** freed by the calling function.
+*/
+PRIVATE char *file_makename_using_basename(lemp,suffix)
+struct lemon *lemp;
+char *suffix;
+{
+ return file_makename(lemp->basename, suffix);
+}
+
+/* Open a file with a name based on the name of the input file,
+** but with a different (specified) suffix, and return a pointer
+** to the stream. Prepend outdirname for both reads and writes, because
+** the only time we read is when checking for an already-produced
+** header file, which should exist in the output directory, not the
+** input directory. If we ever need to file_open(,,"r") on the input
+** side, we should add another arg to file_open() indicating which
+** directory, ("input, "output", or "other") we should deal with.
+*/
+PRIVATE FILE *file_open(lemp,suffix,mode)
+struct lemon *lemp;
+char *suffix;
+char *mode;
+{
+ FILE *fp;
+ char *name;
+
+ if( lemp->outname ) free(lemp->outname);
+ name = file_makename_using_basename(lemp, suffix);
+
+ if ( lemp->outdirname != NULL ) {
+ lemp->outname = malloc( strlen(lemp->outdirname) + strlen(name) + 2);
+ if ( lemp->outname == 0 ) {
+ fprintf(stderr, "Can't allocate space for dir/filename");
+ exit(1);
+ }
+ strcpy(lemp->outname, lemp->outdirname);
+#ifdef __WIN32__
+ strcat(lemp->outname, "\\");
+#else
+ strcat(lemp->outname, "/");
+#endif
+ strcat(lemp->outname, name);
+ free(name);
+ }
+ else {
+ lemp->outname = name;
+ }
+
+ fp = fopen(lemp->outname,mode);
+ if( fp==0 && *mode=='w' ){
+ fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return fp;
+}
+
+/* Duplicate the input file without comments and without actions
+** on rules */
+void Reprint(lemp)
+struct lemon *lemp;
+{
+ struct rule *rp;
+ struct symbol *sp;
+ int i, j, maxlen, len, ncolumns, skip;
+ printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename);
+ maxlen = 10;
+ for(i=0; i<lemp->nsymbol; i++){
+ sp = lemp->symbols[i];
+ len = strlen(sp->name);
+ if( len>maxlen ) maxlen = len;
+ }
+ ncolumns = 76/(maxlen+5);
+ if( ncolumns<1 ) ncolumns = 1;
+ skip = (lemp->nsymbol + ncolumns - 1)/ncolumns;
+ for(i=0; i<skip; i++){
+ printf("//");
+ for(j=i; j<lemp->nsymbol; j+=skip){
+ sp = lemp->symbols[j];
+ assert( sp->index==j );
+ printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name);
+ }
+ printf("\n");
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ printf("%s",rp->lhs->name);
+/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */
+ printf(" ::=");
+ for(i=0; i<rp->nrhs; i++){
+ printf(" %s",rp->rhs[i]->name);
+/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */
+ }
+ printf(".");
+ if( rp->precsym ) printf(" [%s]",rp->precsym->name);
+/* if( rp->code ) printf("\n %s",rp->code); */
+ printf("\n");
+ }
+}
+
+void ConfigPrint(fp,cfp)
+FILE *fp;
+struct config *cfp;
+{
+ struct rule *rp;
+ int i;
+ rp = cfp->rp;
+ fprintf(fp,"%s ::=",rp->lhs->name);
+ for(i=0; i<=rp->nrhs; i++){
+ if( i==cfp->dot ) fprintf(fp," *");
+ if( i==rp->nrhs ) break;
+ fprintf(fp," %s",rp->rhs[i]->name);
+ }
+}
+
+/* #define TEST */
+#ifdef TEST
+/* Print a set */
+PRIVATE void SetPrint(out,set,lemp)
+FILE *out;
+char *set;
+struct lemon *lemp;
+{
+ int i;
+ char *spacer;
+ spacer = "";
+ fprintf(out,"%12s[","");
+ for(i=0; i<lemp->nterminal; i++){
+ if( SetFind(set,i) ){
+ fprintf(out,"%s%s",spacer,lemp->symbols[i]->name);
+ spacer = " ";
+ }
+ }
+ fprintf(out,"]\n");
+}
+
+/* Print a plink chain */
+PRIVATE void PlinkPrint(out,plp,tag)
+FILE *out;
+struct plink *plp;
+char *tag;
+{
+ while( plp ){
+ fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->index);
+ ConfigPrint(out,plp->cfp);
+ fprintf(out,"\n");
+ plp = plp->next;
+ }
+}
+#endif
+
+/* Print an action to the given file descriptor. Return FALSE if
+** nothing was actually printed.
+*/
+int PrintAction(struct action *ap, FILE *fp, int indent){
+ int result = 1;
+ switch( ap->type ){
+ case SHIFT:
+ fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->index);
+ break;
+ case REDUCE:
+ fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case ACCEPT:
+ fprintf(fp,"%*s accept",indent,ap->sp->name);
+ break;
+ case ERROR:
+ fprintf(fp,"%*s error",indent,ap->sp->name);
+ break;
+ case CONFLICT:
+ fprintf(fp,"%*s reduce %-3d ** Parsing conflict **",
+ indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case SH_RESOLVED:
+ case RD_RESOLVED:
+ case NOT_USED:
+ result = 0;
+ break;
+ }
+ return result;
+}
+
+/* Generate the "y.output" log file */
+void ReportOutput(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct state *stp;
+ struct config *cfp;
+ struct action *ap;
+ FILE *fp;
+
+ fp = file_open(lemp,".out","w");
+ if( fp==0 ) return;
+ fprintf(fp," \b");
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ fprintf(fp,"State %d:\n",stp->index);
+ if( lemp->basisflag ) cfp=stp->bp;
+ else cfp=stp->cfp;
+ while( cfp ){
+ char buf[20];
+ if( cfp->dot==cfp->rp->nrhs ){
+ sprintf(buf,"(%d)",cfp->rp->index);
+ fprintf(fp," %5s ",buf);
+ }else{
+ fprintf(fp," ");
+ }
+ ConfigPrint(fp,cfp);
+ fprintf(fp,"\n");
+#ifdef TEST
+ SetPrint(fp,cfp->fws,lemp);
+ PlinkPrint(fp,cfp->fplp,"To ");
+ PlinkPrint(fp,cfp->bplp,"From");
+#endif
+ if( lemp->basisflag ) cfp=cfp->bp;
+ else cfp=cfp->next;
+ }
+ fprintf(fp,"\n");
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( PrintAction(ap,fp,30) ) fprintf(fp,"\n");
+ }
+ fprintf(fp,"\n");
+ }
+ fclose(fp);
+ return;
+}
+
+/* Search for the file "name" which is in the same directory as
+** the exacutable */
+PRIVATE char *pathsearch(argv0,name,modemask)
+char *argv0;
+char *name;
+int modemask;
+{
+ char *pathlist;
+ char *path,*cp;
+ char c;
+ extern int access();
+
+#ifdef __WIN32__
+ cp = strrchr(argv0,'\\');
+#else
+ cp = strrchr(argv0,'/');
+#endif
+ if( cp ){
+ c = *cp;
+ *cp = 0;
+ path = (char *)malloc( strlen(argv0) + strlen(name) + 2 );
+ if( path ) sprintf(path,"%s/%s",argv0,name);
+ *cp = c;
+ }else{
+ extern char *getenv();
+ pathlist = getenv("PATH");
+ if( pathlist==0 ) pathlist = ".:/bin:/usr/bin";
+ path = (char *)malloc( strlen(pathlist)+strlen(name)+2 );
+ if( path!=0 ){
+ while( *pathlist ){
+ cp = strchr(pathlist,':');
+ if( cp==0 ) cp = &pathlist[strlen(pathlist)];
+ c = *cp;
+ *cp = 0;
+ sprintf(path,"%s/%s",pathlist,name);
+ *cp = c;
+ if( c==0 ) pathlist = "";
+ else pathlist = &cp[1];
+ if( access(path,modemask)==0 ) break;
+ }
+ }
+ }
+ return path;
+}
+
+/* Given an action, compute the integer value for that action
+** which is to be put in the action table of the generated machine.
+** Return negative if no action should be generated.
+*/
+PRIVATE int compute_action(lemp,ap)
+struct lemon *lemp;
+struct action *ap;
+{
+ int act;
+ switch( ap->type ){
+ case SHIFT: act = ap->x.stp->index; break;
+ case REDUCE: act = ap->x.rp->index + lemp->nstate; break;
+ case ERROR: act = lemp->nstate + lemp->nrule; break;
+ case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break;
+ default: act = -1; break;
+ }
+ return act;
+}
+
+#define LINESIZE 1000
+/* The next cluster of routines are for reading the template file
+** and writing the results to the generated parser */
+/* The first function transfers data from "in" to "out" until
+** a line is seen which begins with "%%". The line number is
+** tracked.
+**
+** if name!=0, then any word that begin with "Parse" is changed to
+** begin with *name instead.
+*/
+PRIVATE void tplt_xfer(name,in,out,lineno)
+char *name;
+FILE *in;
+FILE *out;
+int *lineno;
+{
+ int i, iStart;
+ char line[LINESIZE];
+ while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){
+ (*lineno)++;
+ iStart = 0;
+ if( name ){
+ for(i=0; line[i]; i++){
+ if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0
+ && (i==0 || !isalpha(line[i-1]))
+ ){
+ if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]);
+ fprintf(out,"%s",name);
+ i += 4;
+ iStart = i+1;
+ }
+ }
+ }
+ fprintf(out,"%s",&line[iStart]);
+ }
+}
+
+/* The next function finds the template file and opens it, returning
+** a pointer to the opened file. */
+PRIVATE FILE *tplt_open(lemp)
+struct lemon *lemp;
+{
+ static char templatename[] = "lempar.c";
+ char buf[1000];
+ FILE *in;
+ char *tpltname;
+ char *cp;
+
+ if (lemp->templatename) {
+ tpltname = lemp->templatename;
+ }
+ else {
+ cp = strrchr(lemp->filename,'.');
+ if( cp ){
+ sprintf(buf,"%.*s.lt",(int)cp-(int)lemp->filename,lemp->filename);
+ }else{
+ sprintf(buf,"%s.lt",lemp->filename);
+ }
+ if( access(buf,004)==0 ){
+ tpltname = buf;
+ }else{
+ tpltname = pathsearch(lemp->argv0,templatename,0);
+ }
+ }
+ if( tpltname==0 ){
+ fprintf(stderr,"Can't find the parser driver template file \"%s\".\n",
+ templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ in = fopen(tpltname,"r");
+ if( in==0 ){
+ fprintf(stderr,"Can't open the template file \"%s\".\n",templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return in;
+}
+
+/* Print a string to the file and keep the linenumber up to date */
+PRIVATE void tplt_print(out,lemp,str,strln,lineno)
+FILE *out;
+struct lemon *lemp;
+char *str;
+int strln;
+int *lineno;
+{
+ if( str==0 ) return;
+ fprintf(out,"#line %d \"%s\"\n",strln,lemp->filename); (*lineno)++;
+ while( *str ){
+ if( *str=='\n' ) (*lineno)++;
+ putc(*str,out);
+ str++;
+ }
+ fprintf(out,"\n#line %d \"%s\"\n",*lineno+2,lemp->outname); (*lineno)+=2;
+ return;
+}
+
+/*
+** The following routine emits code for the destructor for the
+** symbol sp
+*/
+void emit_destructor_code(out,sp,lemp,lineno)
+FILE *out;
+struct symbol *sp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp;
+
+ int linecnt = 0;
+ if( sp->type==TERMINAL ){
+ cp = lemp->tokendest;
+ if( cp==0 ) return;
+ fprintf(out,"#line %d \"%s\"\n{",lemp->tokendestln,lemp->filename);
+ }else{
+ cp = sp->destructor;
+ if( cp==0 ) return;
+ fprintf(out,"#line %d \"%s\"\n{",sp->destructorln,lemp->filename);
+ }
+ for(; *cp; cp++){
+ if( *cp=='$' && cp[1]=='$' ){
+ fprintf(out,"(yypminor->yy%d)",sp->dtnum);
+ cp++;
+ continue;
+ }
+ if( *cp=='\n' ) linecnt++;
+ fputc(*cp,out);
+ }
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname);
+ return;
+}
+
+/*
+** Return TRUE (non-zero) if the given symbol has a distructor.
+*/
+int has_destructor(sp, lemp)
+struct symbol *sp;
+struct lemon *lemp;
+{
+ int ret;
+ if( sp->type==TERMINAL ){
+ ret = lemp->tokendest!=0;
+ }else{
+ ret = sp->destructor!=0;
+ }
+ return ret;
+}
+
+/*
+** Generate code which executes when the rule "rp" is reduced. Write
+** the code to "out". Make sure lineno stays up-to-date.
+*/
+PRIVATE void emit_code(out,rp,lemp,lineno)
+FILE *out;
+struct rule *rp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp, *xp;
+ int linecnt = 0;
+ int i;
+ char lhsused = 0; /* True if the LHS element has been used */
+ char used[MAXRHS]; /* True for each RHS element which is used */
+
+ for(i=0; i<rp->nrhs; i++) used[i] = 0;
+ lhsused = 0;
+
+ /* Generate code to do the reduce action */
+ if( rp->code ){
+ fprintf(out,"#line %d \"%s\"\n{",rp->line,lemp->filename);
+ for(cp=rp->code; *cp; cp++){
+ if( isalpha(*cp) && (cp==rp->code || !isalnum(cp[-1])) ){
+ char saved;
+ for(xp= &cp[1]; isalnum(*xp); xp++);
+ saved = *xp;
+ *xp = 0;
+ if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){
+ fprintf(out,"yygotominor.yy%d",rp->lhs->dtnum);
+ cp = xp;
+ lhsused = 1;
+ }else{
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){
+ fprintf(out,"yymsp[%d].minor.yy%d",i-rp->nrhs+1,rp->rhs[i]->dtnum);
+ cp = xp;
+ used[i] = 1;
+ break;
+ }
+ }
+ }
+ *xp = saved;
+ }
+ if( *cp=='\n' ) linecnt++;
+ fputc(*cp,out);
+ } /* End loop */
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname);
+ } /* End if( rp->code ) */
+
+ /* Check to make sure the LHS has been used */
+ if( rp->lhsalias && !lhsused ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label \"%s\" for \"%s(%s)\" is never used.",
+ rp->lhsalias,rp->lhs->name,rp->lhsalias);
+ lemp->errorcnt++;
+ }
+
+ /* Generate destructor code for RHS symbols which are not used in the
+ ** reduce code */
+ for(i=0; i<rp->nrhs; i++){
+ if( rp->rhsalias[i] && !used[i] ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label $%s$ for \"%s(%s)\" is never used.",
+ rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]);
+ lemp->errorcnt++;
+ }else if( rp->rhsalias[i]==0 ){
+ if( has_destructor(rp->rhs[i],lemp) ){
+ fprintf(out," yy_destructor(%d,&yymsp[%d].minor);\n",
+ rp->rhs[i]->index,i-rp->nrhs+1); (*lineno)++;
+ }else{
+ fprintf(out," /* No destructor defined for %s */\n",
+ rp->rhs[i]->name);
+ (*lineno)++;
+ }
+ }
+ }
+ return;
+}
+
+/*
+** Print the definition of the union used for the parser's data stack.
+** This union contains fields for every possible data type for tokens
+** and nonterminals. In the process of computing and printing this
+** union, also set the ".dtnum" field of every terminal and nonterminal
+** symbol.
+*/
+void print_stack_union(out,lemp,plineno,mhflag)
+FILE *out; /* The output stream */
+struct lemon *lemp; /* The main info structure for this parser */
+int *plineno; /* Pointer to the line number */
+int mhflag; /* True if generating makeheaders output */
+{
+ int lineno = *plineno; /* The line number of the output */
+ char **types; /* A hash table of datatypes */
+ int arraysize; /* Size of the "types" array */
+ int maxdtlength; /* Maximum length of any ".datatype" field. */
+ char *stddt; /* Standardized name for a datatype */
+ int i,j; /* Loop counters */
+ int hash; /* For hashing the name of a type */
+ char *name; /* Name of the parser */
+
+ /* Allocate and initialize types[] and allocate stddt[] */
+ arraysize = lemp->nsymbol * 2;
+ types = (char**)malloc( arraysize * sizeof(char*) );
+ for(i=0; i<arraysize; i++) types[i] = 0;
+ maxdtlength = 0;
+ for(i=0; i<lemp->nsymbol; i++){
+ int len;
+ struct symbol *sp = lemp->symbols[i];
+ if( sp->datatype==0 ) continue;
+ len = strlen(sp->datatype);
+ if( len>maxdtlength ) maxdtlength = len;
+ }
+ stddt = (char*)malloc( maxdtlength*2 + 1 );
+ if( types==0 || stddt==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+
+ /* Build a hash table of datatypes. The ".dtnum" field of each symbol
+ ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is
+ ** used for terminal symbols and for nonterminals which don't specify
+ ** a datatype using the %type directive. */
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ char *cp;
+ if( sp==lemp->errsym ){
+ sp->dtnum = arraysize+1;
+ continue;
+ }
+ if( sp->type!=NONTERMINAL || sp->datatype==0 ){
+ sp->dtnum = 0;
+ continue;
+ }
+ cp = sp->datatype;
+ j = 0;
+ while( isspace(*cp) ) cp++;
+ while( *cp ) stddt[j++] = *cp++;
+ while( j>0 && isspace(stddt[j-1]) ) j--;
+ stddt[j] = 0;
+ hash = 0;
+ for(j=0; stddt[j]; j++){
+ hash = hash*53 + stddt[j];
+ }
+ if( hash<0 ) hash = -hash;
+ hash = hash%arraysize;
+ while( types[hash] ){
+ if( strcmp(types[hash],stddt)==0 ){
+ sp->dtnum = hash + 1;
+ break;
+ }
+ hash++;
+ if( hash>=arraysize ) hash = 0;
+ }
+ if( types[hash]==0 ){
+ sp->dtnum = hash + 1;
+ types[hash] = (char*)malloc( strlen(stddt)+1 );
+ if( types[hash]==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+ strcpy(types[hash],stddt);
+ }
+ }
+
+ /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */
+ name = lemp->name ? lemp->name : "Parse";
+ lineno = *plineno;
+ if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; }
+ fprintf(out,"#define %sTOKENTYPE %s\n",name,
+ lemp->tokentype?lemp->tokentype:"void*"); lineno++;
+ if( mhflag ){ fprintf(out,"#endif\n"); lineno++; }
+ fprintf(out,"typedef union {\n"); lineno++;
+ fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++;
+ for(i=0; i<arraysize; i++){
+ if( types[i]==0 ) continue;
+ fprintf(out," %s yy%d;\n",types[i],i+1); lineno++;
+ free(types[i]);
+ }
+ fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++;
+ free(stddt);
+ free(types);
+ fprintf(out,"} YYMINORTYPE;\n"); lineno++;
+ *plineno = lineno;
+}
+
+/* Generate C source code for the parser */
+void ReportTable(lemp, mhflag)
+struct lemon *lemp;
+int mhflag; /* Output in makeheaders format if true */
+{
+ FILE *out, *in;
+ char line[LINESIZE];
+ int lineno;
+ struct state *stp;
+ struct action *ap;
+ struct rule *rp;
+ int i;
+ int tablecnt;
+ char *name;
+
+ in = tplt_open(lemp);
+ if( in==0 ) return;
+ out = file_open(lemp,".c","w");
+ if( out==0 ){
+ fclose(in);
+ return;
+ }
+ lineno = 1;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the include code, if any */
+ tplt_print(out,lemp,lemp->include,lemp->includeln,&lineno);
+ if( mhflag ){
+ char *name = file_makename_using_basename(lemp, ".h");
+ fprintf(out,"#include \"%s\"\n", name); lineno++;
+ free(name);
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate #defines for all tokens */
+ if( mhflag ){
+ char *prefix;
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ for(i=1; i<lemp->nterminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ lineno++;
+ }
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the defines */
+ fprintf(out,"/* \001 */\n");
+ fprintf(out,"#define YYCODETYPE %s\n",
+ lemp->nsymbol>250?"int":"unsigned char"); lineno++;
+ fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++;
+ fprintf(out,"#define YYACTIONTYPE %s\n",
+ lemp->nstate+lemp->nrule>250?"int":"unsigned char"); lineno++;
+ print_stack_union(out,lemp,&lineno,mhflag);
+ if( lemp->stacksize ){
+ if( atoi(lemp->stacksize)<=0 ){
+ ErrorMsg(lemp->filename,0,
+"Illegal stack size: [%s]. The stack size should be an integer constant.",
+ lemp->stacksize);
+ lemp->errorcnt++;
+ lemp->stacksize = "100";
+ }
+ fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++;
+ }else{
+ fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++;
+ }
+ if( mhflag ){
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ }
+ name = lemp->name ? lemp->name : "Parse";
+ if( lemp->arg && lemp->arg[0] ){
+ int i;
+ i = strlen(lemp->arg);
+ while( i>=1 && isspace(lemp->arg[i-1]) ) i--;
+ while( i>=1 && isalnum(lemp->arg[i-1]) ) i--;
+ fprintf(out,"#define %sARGDECL ,%s\n",name,&lemp->arg[i]); lineno++;
+ fprintf(out,"#define %sXARGDECL %s;\n",name,lemp->arg); lineno++;
+ fprintf(out,"#define %sANSIARGDECL ,%s\n",name,lemp->arg); lineno++;
+ }else{
+ fprintf(out,"#define %sARGDECL\n",name); lineno++;
+ fprintf(out,"#define %sXARGDECL\n",name); lineno++;
+ fprintf(out,"#define %sANSIARGDECL\n",name); lineno++;
+ }
+ if( mhflag ){
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++;
+ fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++;
+ fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
+ fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the action table.
+ **
+ ** Each entry in the action table is an element of the following
+ ** structure:
+ ** struct yyActionEntry {
+ ** YYCODETYPE lookahead;
+ ** YYACTIONTYPE action;
+ ** struct yyActionEntry *next;
+ ** }
+ **
+ ** The entries are grouped into hash tables, one hash table for each
+ ** parser state. The hash table has a size which is the smallest
+ ** power of two needed to hold all entries.
+ */
+ tablecnt = 0;
+
+ /* Loop over parser states */
+ for(i=0; i<lemp->nstate; i++){
+ int tablesize; /* size of the hash table */
+ int j,k; /* Loop counter */
+ int collide[2048]; /* The collision chain for the table */
+ struct action *table[2048]; /* Build the hash table here */
+
+ /* Find the number of actions and initialize the hash table */
+ stp = lemp->sorted[i];
+ stp->tabstart = tablecnt;
+ stp->naction = 0;
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( ap->sp->index!=lemp->nsymbol && compute_action(lemp,ap)>=0 ){
+ stp->naction++;
+ }
+ }
+ tablesize = 1;
+ while( tablesize<stp->naction ) tablesize += tablesize;
+ assert( tablesize<= sizeof(table)/sizeof(table[0]) );
+ for(j=0; j<tablesize; j++){
+ table[j] = 0;
+ collide[j] = -1;
+ }
+
+ /* Hash the actions into the hash table */
+ stp->tabdfltact = lemp->nstate + lemp->nrule;
+ for(ap=stp->ap; ap; ap=ap->next){
+ int action = compute_action(lemp,ap);
+ int h;
+ if( ap->sp->index==lemp->nsymbol ){
+ stp->tabdfltact = action;
+ }else if( action>=0 ){
+ h = ap->sp->index & (tablesize-1);
+ ap->collide = table[h];
+ table[h] = ap;
+ }
+ }
+
+ /* Resolve collisions */
+ for(j=k=0; j<tablesize; j++){
+ if( table[j] && table[j]->collide ){
+ while( table[k] ) k++;
+ table[k] = table[j]->collide;
+ collide[j] = k;
+ table[j]->collide = 0;
+ if( k<j ) j = k-1;
+ }
+ }
+
+ /* Print the hash table */
+ fprintf(out,"/* State %d */\n",stp->index); lineno++;
+ for(j=0; j<tablesize; j++){
+ if( table[j]==0 ){
+ fprintf(out,
+ " {YYNOCODE,0,0}, /* Unused */\n");
+ }else{
+ fprintf(out," {%4d,%4d, ",
+ table[j]->sp->index,
+ compute_action(lemp,table[j]));
+ if( collide[j]>=0 ){
+ fprintf(out,"&yyActionTable[%4d] }, /* ",
+ collide[j] + tablecnt);
+ }else{
+ fprintf(out,"0 }, /* ");
+ }
+ PrintAction(table[j],out,22);
+ fprintf(out," */\n");
+ }
+ lineno++;
+ }
+
+ /* Update the table count */
+ tablecnt += tablesize;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+ lemp->tablesize = tablecnt;
+
+ /* Generate the state table
+ **
+ ** Each entry is an element of the following structure:
+ ** struct yyStateEntry {
+ ** struct yyActionEntry *hashtbl;
+ ** int mask;
+ ** YYACTIONTYPE actionDefault;
+ ** }
+ */
+ for(i=0; i<lemp->nstate; i++){
+ int tablesize;
+ stp = lemp->sorted[i];
+ tablesize = 1;
+ while( tablesize<stp->naction ) tablesize += tablesize;
+ fprintf(out," { &yyActionTable[%d], %d, %d},\n",
+ stp->tabstart,
+ tablesize - 1,
+ stp->tabdfltact); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate a table containing the symbolic name of every symbol */
+ for(i=0; i<lemp->nsymbol; i++){
+ sprintf(line,"\"%s\",",lemp->symbols[i]->name);
+ fprintf(out," %-15s",line);
+ if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; }
+ }
+ if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes every time a symbol is popped from
+ ** the stack while processing errors or while destroying the parser.
+ ** (In other words, generate the %destructor actions) */
+ if( lemp->tokendest ){
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type!=TERMINAL ) continue;
+ fprintf(out," case %d:\n",sp->index); lineno++;
+ }
+ for(i=0; i<lemp->nsymbol && lemp->symbols[i]->type!=TERMINAL; i++);
+ if( i<lemp->nsymbol ){
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ }
+ for(i=0; i<lemp->nsymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue;
+ fprintf(out," case %d:\n",sp->index); lineno++;
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes whenever the parser stack overflows */
+ tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the table of rule information
+ **
+ ** Note: This code depends on the fact that rules are number
+ ** sequentually beginning with 0.
+ */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which execution during each REDUCE action */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ fprintf(out," case %d:\n",rp->index); lineno++;
+ fprintf(out," YYTRACE(\"%s ::=",rp->lhs->name);
+ for(i=0; i<rp->nrhs; i++) fprintf(out," %s",rp->rhs[i]->name);
+ fprintf(out,"\")\n"); lineno++;
+ emit_code(out,rp,lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes if a parse fails */
+ tplt_print(out,lemp,lemp->failure,lemp->failureln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when a syntax error occurs */
+ tplt_print(out,lemp,lemp->error,lemp->errorln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when the parser accepts its input */
+ tplt_print(out,lemp,lemp->accept,lemp->acceptln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Append any addition code the user desires */
+ tplt_print(out,lemp,lemp->extracode,lemp->extracodeln,&lineno);
+
+ fclose(in);
+ fclose(out);
+ return;
+}
+
+/* Generate a header file for the parser */
+void ReportHeader(lemp)
+struct lemon *lemp;
+{
+ FILE *out, *in;
+ char *prefix;
+ char line[LINESIZE];
+ char pattern[LINESIZE];
+ int i;
+
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ in = file_open(lemp,".h","r");
+ if( in ){
+ for(i=1; i<lemp->nterminal && fgets(line,LINESIZE,in); i++){
+ sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ if( strcmp(line,pattern) ) break;
+ }
+ fclose(in);
+ if( i==lemp->nterminal ){
+ /* No change in the file. Don't rewrite it. */
+ return;
+ }
+ }
+ out = file_open(lemp,".h","w");
+ if( out ){
+ for(i=1; i<lemp->nterminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ }
+ fclose(out);
+ }
+ return;
+}
+
+/* Reduce the size of the action tables, if possible, by making use
+** of defaults.
+**
+** In this version, if all REDUCE actions use the same rule, make
+** them the default. Only default them if there are more than one.
+*/
+void CompressTables(lemp)
+struct lemon *lemp;
+{
+ struct state *stp;
+ struct action *ap;
+ struct rule *rp;
+ int i;
+ int cnt;
+
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+
+ /* Find the first REDUCE action */
+ for(ap=stp->ap; ap && ap->type!=REDUCE; ap=ap->next);
+ if( ap==0 ) continue;
+
+ /* Remember the rule used */
+ rp = ap->x.rp;
+
+ /* See if all other REDUCE acitons use the same rule */
+ cnt = 1;
+ for(ap=ap->next; ap; ap=ap->next){
+ if( ap->type==REDUCE ){
+ if( ap->x.rp!=rp ) break;
+ cnt++;
+ }
+ }
+ if( ap || cnt==1 ) continue;
+
+ /* Combine all REDUCE actions into a single default */
+ for(ap=stp->ap; ap && ap->type!=REDUCE; ap=ap->next);
+ assert( ap );
+ ap->sp = Symbol_new("{default}");
+ for(ap=ap->next; ap; ap=ap->next){
+ if( ap->type==REDUCE ) ap->type = NOT_USED;
+ }
+ stp->ap = Action_sort(stp->ap);
+ }
+}
+/***************** From the file "set.c" ************************************/
+/*
+** Set manipulation routines for the LEMON parser generator.
+*/
+
+static int size = 0;
+
+/* Set the set size */
+void SetSize(n)
+int n;
+{
+ size = n+1;
+}
+
+/* Allocate a new set */
+char *SetNew(){
+ char *s;
+ int i;
+ s = (char*)malloc( size );
+ if( s==0 ){
+ extern void memory_error();
+ memory_error();
+ }
+ for(i=0; i<size; i++) s[i] = 0;
+ return s;
+}
+
+/* Deallocate a set */
+void SetFree(s)
+char *s;
+{
+ free(s);
+}
+
+/* Add a new element to the set. Return TRUE if the element was added
+** and FALSE if it was already there. */
+int SetAdd(s,e)
+char *s;
+int e;
+{
+ int rv;
+ rv = s[e];
+ s[e] = 1;
+ return !rv;
+}
+
+/* Add every element of s2 to s1. Return TRUE if s1 changes. */
+int SetUnion(s1,s2)
+char *s1;
+char *s2;
+{
+ int i, progress;
+ progress = 0;
+ for(i=0; i<size; i++){
+ if( s2[i]==0 ) continue;
+ if( s1[i]==0 ){
+ progress = 1;
+ s1[i] = 1;
+ }
+ }
+ return progress;
+}
+/********************** From the file "table.c" ****************************/
+/*
+** All code in this file has been automatically generated
+** from a specification in the file
+** "table.q"
+** by the associative array code building program "aagen".
+** Do not edit this file! Instead, edit the specification
+** file, then rerun aagen.
+*/
+/*
+** Code for processing tables in the LEMON parser generator.
+*/
+
+PRIVATE int strhash(x)
+char *x;
+{
+ int h = 0;
+ while( *x) h = h*13 + *(x++);
+ return h;
+}
+
+/* Works like strdup, sort of. Save a string in malloced memory, but
+** keep strings in a table so that the same string is not in more
+** than one place.
+*/
+char *Strsafe(y)
+char *y;
+{
+ char *z;
+
+ z = Strsafe_find(y);
+ if( z==0 && (z=malloc( strlen(y)+1 ))!=0 ){
+ strcpy(z,y);
+ Strsafe_insert(z);
+ }
+ MemoryCheck(z);
+ return z;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x1".
+*/
+struct s_x1 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x1node *tbl; /* The data stored here */
+ struct s_x1node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x1".
+*/
+typedef struct s_x1node {
+ char *data; /* The data */
+ struct s_x1node *next; /* Next entry with the same hash */
+ struct s_x1node **from; /* Previous link */
+} x1node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x1 *x1a;
+
+/* Allocate a new associative array */
+void Strsafe_init(){
+ if( x1a ) return;
+ x1a = (struct s_x1*)malloc( sizeof(struct s_x1) );
+ if( x1a ){
+ x1a->size = 1024;
+ x1a->count = 0;
+ x1a->tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*1024 );
+ if( x1a->tbl==0 ){
+ free(x1a);
+ x1a = 0;
+ }else{
+ int i;
+ x1a->ht = (x1node**)&(x1a->tbl[1024]);
+ for(i=0; i<1024; i++) x1a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Strsafe_insert(data)
+char *data;
+{
+ x1node *np;
+ int h;
+ int ph;
+
+ if( x1a==0 ) return 0;
+ ph = strhash(data);
+ h = ph & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x1a->count>=x1a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x1 array;
+ array.size = size = x1a->size*2;
+ array.count = x1a->count;
+ array.tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x1node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x1a->count; i++){
+ x1node *oldnp, *newnp;
+ oldnp = &(x1a->tbl[i]);
+ h = strhash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x1a->tbl);
+ *x1a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x1a->size-1);
+ np = &(x1a->tbl[x1a->count++]);
+ np->data = data;
+ if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next);
+ np->next = x1a->ht[h];
+ x1a->ht[h] = np;
+ np->from = &(x1a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+char *Strsafe_find(key)
+char *key;
+{
+ int h;
+ x1node *np;
+
+ if( x1a==0 ) return 0;
+ h = strhash(key) & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return a pointer to the (terminal or nonterminal) symbol "x".
+** Create a new symbol if this is the first time "x" has been seen.
+*/
+struct symbol *Symbol_new(x)
+char *x;
+{
+ struct symbol *sp;
+
+ sp = Symbol_find(x);
+ if( sp==0 ){
+ sp = (struct symbol *)malloc( sizeof(struct symbol) );
+ MemoryCheck(sp);
+ sp->name = Strsafe(x);
+ sp->type = isupper(*x) ? TERMINAL : NONTERMINAL;
+ sp->rule = 0;
+ sp->prec = -1;
+ sp->assoc = UNK;
+ sp->firstset = 0;
+ sp->lambda = FALSE;
+ sp->destructor = 0;
+ sp->datatype = 0;
+ Symbol_insert(sp,sp->name);
+ }
+ return sp;
+}
+
+/* Compare two symbols */
+int Symbolcmpp(a,b)
+struct symbol **a;
+struct symbol **b;
+{
+ return strcmp((**a).name,(**b).name);
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x2".
+*/
+struct s_x2 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x2node *tbl; /* The data stored here */
+ struct s_x2node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x2".
+*/
+typedef struct s_x2node {
+ struct symbol *data; /* The data */
+ char *key; /* The key */
+ struct s_x2node *next; /* Next entry with the same hash */
+ struct s_x2node **from; /* Previous link */
+} x2node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x2 *x2a;
+
+/* Allocate a new associative array */
+void Symbol_init(){
+ if( x2a ) return;
+ x2a = (struct s_x2*)malloc( sizeof(struct s_x2) );
+ if( x2a ){
+ x2a->size = 128;
+ x2a->count = 0;
+ x2a->tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*128 );
+ if( x2a->tbl==0 ){
+ free(x2a);
+ x2a = 0;
+ }else{
+ int i;
+ x2a->ht = (x2node**)&(x2a->tbl[128]);
+ for(i=0; i<128; i++) x2a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Symbol_insert(data,key)
+struct symbol *data;
+char *key;
+{
+ x2node *np;
+ int h;
+ int ph;
+
+ if( x2a==0 ) return 0;
+ ph = strhash(key);
+ h = ph & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x2a->count>=x2a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x2 array;
+ array.size = size = x2a->size*2;
+ array.count = x2a->count;
+ array.tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x2node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x2a->count; i++){
+ x2node *oldnp, *newnp;
+ oldnp = &(x2a->tbl[i]);
+ h = strhash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x2a->tbl);
+ *x2a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x2a->size-1);
+ np = &(x2a->tbl[x2a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next);
+ np->next = x2a->ht[h];
+ x2a->ht[h] = np;
+ np->from = &(x2a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct symbol *Symbol_find(key)
+char *key;
+{
+ int h;
+ x2node *np;
+
+ if( x2a==0 ) return 0;
+ h = strhash(key) & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return the n-th data. Return NULL if n is out of range. */
+struct symbol *Symbol_Nth(n)
+int n;
+{
+ struct symbol *data;
+ if( x2a && n>0 && n<=x2a->count ){
+ data = x2a->tbl[n-1].data;
+ }else{
+ data = 0;
+ }
+ return data;
+}
+
+/* Return the size of the array */
+int Symbol_count()
+{
+ return x2a ? x2a->count : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct symbol **Symbol_arrayof()
+{
+ struct symbol **array;
+ int i,size;
+ if( x2a==0 ) return 0;
+ size = x2a->count;
+ array = (struct symbol **)malloc( sizeof(struct symbol *)*size );
+ if( array ){
+ for(i=0; i<size; i++) array[i] = x2a->tbl[i].data;
+ }
+ return array;
+}
+
+/* Compare two configurations */
+int Configcmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int x;
+ x = a->rp->index - b->rp->index;
+ if( x==0 ) x = a->dot - b->dot;
+ return x;
+}
+
+/* Compare two states */
+PRIVATE int statecmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int rc;
+ for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){
+ rc = a->rp->index - b->rp->index;
+ if( rc==0 ) rc = a->dot - b->dot;
+ }
+ if( rc==0 ){
+ if( a ) rc = 1;
+ if( b ) rc = -1;
+ }
+ return rc;
+}
+
+/* Hash a state */
+PRIVATE int statehash(a)
+struct config *a;
+{
+ int h=0;
+ while( a ){
+ h = h*571 + a->rp->index*37 + a->dot;
+ a = a->bp;
+ }
+ return h;
+}
+
+/* Allocate a new state structure */
+struct state *State_new()
+{
+ struct state *new;
+ new = (struct state *)malloc( sizeof(struct state) );
+ MemoryCheck(new);
+ return new;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x3".
+*/
+struct s_x3 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x3node *tbl; /* The data stored here */
+ struct s_x3node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x3".
+*/
+typedef struct s_x3node {
+ struct state *data; /* The data */
+ struct config *key; /* The key */
+ struct s_x3node *next; /* Next entry with the same hash */
+ struct s_x3node **from; /* Previous link */
+} x3node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x3 *x3a;
+
+/* Allocate a new associative array */
+void State_init(){
+ if( x3a ) return;
+ x3a = (struct s_x3*)malloc( sizeof(struct s_x3) );
+ if( x3a ){
+ x3a->size = 128;
+ x3a->count = 0;
+ x3a->tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*128 );
+ if( x3a->tbl==0 ){
+ free(x3a);
+ x3a = 0;
+ }else{
+ int i;
+ x3a->ht = (x3node**)&(x3a->tbl[128]);
+ for(i=0; i<128; i++) x3a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int State_insert(data,key)
+struct state *data;
+struct config *key;
+{
+ x3node *np;
+ int h;
+ int ph;
+
+ if( x3a==0 ) return 0;
+ ph = statehash(key);
+ h = ph & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x3a->count>=x3a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x3 array;
+ array.size = size = x3a->size*2;
+ array.count = x3a->count;
+ array.tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x3node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x3a->count; i++){
+ x3node *oldnp, *newnp;
+ oldnp = &(x3a->tbl[i]);
+ h = statehash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x3a->tbl);
+ *x3a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x3a->size-1);
+ np = &(x3a->tbl[x3a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next);
+ np->next = x3a->ht[h];
+ x3a->ht[h] = np;
+ np->from = &(x3a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct state *State_find(key)
+struct config *key;
+{
+ int h;
+ x3node *np;
+
+ if( x3a==0 ) return 0;
+ h = statehash(key) & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct state **State_arrayof()
+{
+ struct state **array;
+ int i,size;
+ if( x3a==0 ) return 0;
+ size = x3a->count;
+ array = (struct state **)malloc( sizeof(struct state *)*size );
+ if( array ){
+ for(i=0; i<size; i++) array[i] = x3a->tbl[i].data;
+ }
+ return array;
+}
+
+/* Hash a configuration */
+PRIVATE int confighash(a)
+struct config *a;
+{
+ int h=0;
+ h = h*571 + a->rp->index*37 + a->dot;
+ return h;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x4".
+*/
+struct s_x4 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x4node *tbl; /* The data stored here */
+ struct s_x4node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x4".
+*/
+typedef struct s_x4node {
+ struct config *data; /* The data */
+ struct s_x4node *next; /* Next entry with the same hash */
+ struct s_x4node **from; /* Previous link */
+} x4node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x4 *x4a;
+
+/* Allocate a new associative array */
+void Configtable_init(){
+ if( x4a ) return;
+ x4a = (struct s_x4*)malloc( sizeof(struct s_x4) );
+ if( x4a ){
+ x4a->size = 64;
+ x4a->count = 0;
+ x4a->tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*64 );
+ if( x4a->tbl==0 ){
+ free(x4a);
+ x4a = 0;
+ }else{
+ int i;
+ x4a->ht = (x4node**)&(x4a->tbl[64]);
+ for(i=0; i<64; i++) x4a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Configtable_insert(data)
+struct config *data;
+{
+ x4node *np;
+ int h;
+ int ph;
+
+ if( x4a==0 ) return 0;
+ ph = confighash(data);
+ h = ph & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x4a->count>=x4a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x4 array;
+ array.size = size = x4a->size*2;
+ array.count = x4a->count;
+ array.tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x4node**)&(array.tbl[size]);
+ for(i=0; i<size; i++) array.ht[i] = 0;
+ for(i=0; i<x4a->count; i++){
+ x4node *oldnp, *newnp;
+ oldnp = &(x4a->tbl[i]);
+ h = confighash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x4a->tbl);
+ *x4a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x4a->size-1);
+ np = &(x4a->tbl[x4a->count++]);
+ np->data = data;
+ if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next);
+ np->next = x4a->ht[h];
+ x4a->ht[h] = np;
+ np->from = &(x4a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct config *Configtable_find(key)
+struct config *key;
+{
+ int h;
+ x4node *np;
+
+ if( x4a==0 ) return 0;
+ h = confighash(key) & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Remove all data from the table. Pass each data to the function "f"
+** as it is removed. ("f" may be null to avoid this step.) */
+void Configtable_clear(f)
+int(*f)(/* struct config * */);
+{
+ int i;
+ if( x4a==0 || x4a->count==0 ) return;
+ if( f ) for(i=0; i<x4a->count; i++) (*f)(x4a->tbl[i].data);
+ for(i=0; i<x4a->size; i++) x4a->ht[i] = 0;
+ x4a->count = 0;
+ return;
+}
diff --git a/tools/lemon/lemon.html b/tools/lemon/lemon.html
new file mode 100644
index 0000000000..9b4648f46c
--- /dev/null
+++ b/tools/lemon/lemon.html
@@ -0,0 +1,861 @@
+<html>
+<head>
+<title>The Lemon Parser Generator</title>
+</head>
+<body bgcolor=white>
+<h1 align=center>The Lemon Parser Generator</h1>
+
+<p>Lemon is an LALR(1) parser generator for C or C++.
+It does the same job as ``bison'' and ``yacc''.
+But lemon is not another bison or yacc clone. It
+uses a different grammar syntax which is designed to
+reduce the number of coding errors. Lemon also uses a more
+sophisticated parsing engine that is faster than yacc and
+bison and which is both reentrant and thread-safe.
+Furthermore, Lemon implements features that can be used
+to eliminate resource leaks, making is suitable for use
+in long-running programs such as graphical user interfaces
+or embedded controllers.</p>
+
+<p>This document is an introduction to the Lemon
+parser generator.</p>
+
+<h2>Theory of Operation</h2>
+
+<p>The main goal of Lemon is to translate a context free grammar (CFG)
+for a particular language into C code that implements a parser for
+that language.
+The program has two inputs:
+<ul>
+<li>The grammar specification.
+<li>A parser template file.
+</ul>
+Typically, only the grammar specification is supplied by the programmer.
+Lemon comes with a default parser template which works fine for most
+applications. But the user is free to substitute a different parser
+template if desired.</p>
+
+<p>Depending on command-line options, Lemon will generate between
+one and three files of outputs.
+<ul>
+<li>C code to implement the parser.
+<li>A header file defining an integer ID for each terminal symbol.
+<li>An information file that describes the states of the generated parser
+ automaton.
+</ul>
+By default, all three of these output files are generated.
+The header file is suppressed if the ``-m'' command-line option is
+used and the report file is omitted when ``-q'' is selected.</p>
+
+<p>The grammar specification file uses a ``.y'' suffix, by convention.
+In the examples used in this document, we'll assume the name of the
+grammar file is ``gram.y''. A typical use of Lemon would be the
+following command:
+<pre>
+ lemon gram.y
+</pre>
+This command will generate three output files named ``gram.c'',
+``gram.h'' and ``gram.out''.
+The first is C code to implement the parser. The second
+is the header file that defines numerical values for all
+terminal symbols, and the last is the report that explains
+the states used by the parser automaton.</p>
+
+<h3>Command Line Options</h3>
+
+<p>The behavior of Lemon can be modified using command-line options.
+You can obtain a list of the available command-line options together
+with a brief explanation of what each does by typing
+<pre>
+ lemon -?
+</pre>
+As of this writing, the following command-line options are supported:
+<ul>
+<li><tt>-b</tt>
+<li><tt>-c</tt>
+<li><tt>-g</tt>
+<li><tt>-m</tt>
+<li><tt>-q</tt>
+<li><tt>-s</tt>
+<li><tt>-x</tt>
+</ul>
+The ``-b'' option reduces the amount of text in the report file by
+printing only the basis of each parser state, rather than the full
+configuration.
+The ``-c'' option suppresses action table compression. Using -c
+will make the parser a little larger and slower but it will detect
+syntax errors sooner.
+The ``-g'' option causes no output files to be generated at all.
+Instead, the input grammar file is printed on standard output but
+with all comments, actions and other extraneous text deleted. This
+is a useful way to get a quick summary of a grammar.
+The ``-m'' option causes the output C source file to be compatible
+with the ``makeheaders'' program.
+Makeheaders is a program that automatically generates header files
+from C source code. When the ``-m'' option is used, the header
+file is not output since the makeheaders program will take care
+of generated all header files automatically.
+The ``-q'' option suppresses the report file.
+Using ``-s'' causes a brief summary of parser statistics to be
+printed. Like this:
+<pre>
+ Parser statistics: 74 terminals, 70 nonterminals, 179 rules
+ 340 states, 2026 parser table entries, 0 conflicts
+</pre>
+Finally, the ``-x'' option causes Lemon to print its version number
+and copyright information
+and then stop without attempting to read the grammar or generate a parser.</p>
+
+<h3>The Parser Interface</h3>
+
+<p>Lemon doesn't generate a complete, working program. It only generates
+a few subroutines that implement a parser. This section describes
+the interface to those subroutines. It is up to the programmer to
+call these subroutines in an appropriate way in order to produce a
+complete system.</p>
+
+<p>Before a program begins using a Lemon-generated parser, the program
+must first create the parser.
+A new parser is created as follows:
+<pre>
+ void *pParser = ParseAlloc( malloc );
+</pre>
+The ParseAlloc() routine allocates and initializes a new parser and
+returns a pointer to it.
+The actual data structure used to represent a parser is opaque --
+its internal structure is not visible or usable by the calling routine.
+For this reason, the ParseAlloc() routine returns a pointer to void
+rather than a pointer to some particular structure.
+The sole argument to the ParseAlloc() routine is a pointer to the
+subroutine used to allocate memory. Typically this means ``malloc()''.</p>
+
+<p>After a program is finished using a parser, it can reclaim all
+memory allocated by that parser by calling
+<pre>
+ ParseFree(pParser, free);
+</pre>
+The first argument is the same pointer returned by ParseAlloc(). The
+second argument is a pointer to the function used to release bulk
+memory back to the system.</p>
+
+<p>After a parser has been allocated using ParseAlloc(), the programmer
+must supply the parser with a sequence of tokens (terminal symbols) to
+be parsed. This is accomplished by calling the following function
+once for each token:
+<pre>
+ Parse(pParser, hTokenID, sTokenData, pArg);
+</pre>
+The first argument to the Parse() routine is the pointer returned by
+ParseAlloc().
+The second argument is a small positive integer that tells the parse the
+type of the next token in the data stream.
+There is one token type for each terminal symbol in the grammar.
+The gram.h file generated by Lemon contains #define statements that
+map symbolic terminal symbol names into appropriate integer values.
+(A value of 0 for the second argument is a special flag to the
+parser to indicate that the end of input has been reached.)
+The third argument is the value of the given token. By default,
+the type of the third argument is integer, but the grammar will
+usually redefine this type to be some kind of structure.
+Typically the second argument will be a broad category of tokens
+such as ``identifier'' or ``number'' and the third argument will
+be the name of the identifier or the value of the number.</p>
+
+<p>The Parse() function may have either three or four arguments,
+depending on the grammar. If the grammar specification file request
+it, the Parse() function will have a fourth parameter that can be
+of any type chosen by the programmer. The parser doesn't do anything
+with this argument except to pass it through to action routines.
+This is a convenient mechanism for passing state information down
+to the action routines without having to use global variables.</p>
+
+<p>A typical use of a Lemon parser might look something like the
+following:
+<pre>
+ 01 ParseTree *ParseFile(const char *zFilename){
+ 02 Tokenizer *pTokenizer;
+ 03 void *pParser;
+ 04 Token sToken;
+ 05 int hTokenId;
+ 06 ParserState sState;
+ 07
+ 08 pTokenizer = TokenizerCreate(zFilename);
+ 09 pParser = ParseAlloc( malloc );
+ 10 InitParserState(&sState);
+ 11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
+ 12 Parse(pParser, hTokenId, sToken, &sState);
+ 13 }
+ 14 Parse(pParser, 0, sToken, &sState);
+ 15 ParseFree(pParser, free );
+ 16 TokenizerFree(pTokenizer);
+ 17 return sState.treeRoot;
+ 18 }
+</pre>
+This example shows a user-written routine that parses a file of
+text and returns a pointer to the parse tree.
+(We've omitted all error-handling from this example to keep it
+simple.)
+We assume the existence of some kind of tokenizer which is created
+using TokenizerCreate() on line 8 and deleted by TokenizerFree()
+on line 16. The GetNextToken() function on line 11 retrieves the
+next token from the input file and puts its type in the
+integer variable hTokenId. The sToken variable is assumed to be
+some kind of structure that contains details about each token,
+such as its complete text, what line it occurs on, etc. </p>
+
+<p>This example also assumes the existence of structure of type
+ParserState that holds state information about a particular parse.
+An instance of such a structure is created on line 6 and initialized
+on line 10. A pointer to this structure is passed into the Parse()
+routine as the optional 4th argument.
+The action routine specified by the grammar for the parser can use
+the ParserState structure to hold whatever information is useful and
+appropriate. In the example, we note that the treeRoot field of
+the ParserState structure is left pointing to the root of the parse
+tree.</p>
+
+<p>The core of this example as it relates to Lemon is as follows:
+<pre>
+ ParseFile(){
+ pParser = ParseAlloc( malloc );
+ while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
+ Parse(pParser, hTokenId, sToken);
+ }
+ Parse(pParser, 0, sToken);
+ ParseFree(pParser, free );
+ }
+</pre>
+Basically, what a program has to do to use a Lemon-generated parser
+is first create the parser, then send it lots of tokens obtained by
+tokenizing an input source. When the end of input is reached, the
+Parse() routine should be called one last time with a token type
+of 0. This step is necessary to inform the parser that the end of
+input has been reached. Finally, we reclaim memory used by the
+parser by calling ParseFree().</p>
+
+<p>There is one other interface routine that should be mentioned
+before we move on.
+The ParseTrace() function can be used to generate debugging output
+from the parser. A prototype for this routine is as follows:
+<pre>
+ ParseTrace(FILE *stream, char *zPrefix);
+</pre>
+After this routine is called, a short (one-line) message is written
+to the designated output stream every time the parser changes states
+or calls an action routine. Each such message is prefaced using
+the text given by zPrefix. This debugging output can be turned off
+by calling ParseTrace() again with a first argument of NULL (0).</p>
+
+<h3>Differences With YACC and BISON</h3>
+
+<p>Programmers who have previously used the yacc or bison parser
+generator will notice several important differences between yacc and/or
+bison and Lemon.
+<ul>
+<li>In yacc and bison, the parser calls the tokenizer. In Lemon,
+ the tokenizer calls the parser.
+<li>Lemon uses no global variables. Yacc and bison use global variables
+ to pass information between the tokenizer and parser.
+<li>Lemon allows multiple parsers to be running simultaneously. Yacc
+ and bison do not.
+</ul>
+These differences may cause some initial confusion for programmers
+with prior yacc and bison experience.
+But after years of experience using Lemon, I firmly
+believe that the Lemon way of doing things is better.</p>
+
+<h2>Input File Syntax</h2>
+
+<p>The main purpose of the grammar specification file for Lemon is
+to define the grammar for the parser. But the input file also
+specifies additional information Lemon requires to do its job.
+Most of the work in using Lemon is in writing an appropriate
+grammar file.</p>
+
+<p>The grammar file for lemon is, for the most part, free format.
+It does not have sections or divisions like yacc or bison. Any
+declaration can occur at any point in the file.
+Lemon ignores whitespace (except where it is needed to separate
+tokens) and it honors the same commenting conventions as C and C++.</p>
+
+<h3>Terminals and Nonterminals</h3>
+
+<p>A terminal symbol (token) is any string of alphanumeric
+and underscore characters
+that begins with an upper case letter.
+A terminal can contain lower class letters after the first character,
+but the usual convention is to make terminals all upper case.
+A nonterminal, on the other hand, is any string of alphanumeric
+and underscore characters than begins with a lower case letter.
+Again, the usual convention is to make nonterminals use all lower
+case letters.</p>
+
+<p>In Lemon, terminal and nonterminal symbols do not need to
+be declared or identified in a separate section of the grammar file.
+Lemon is able to generate a list of all terminals and nonterminals
+by examining the grammar rules, and it can always distinguish a
+terminal from a nonterminal by checking the case of the first
+character of the name.</p>
+
+<p>Yacc and bison allow terminal symbols to have either alphanumeric
+names or to be individual characters included in single quotes, like
+this: ')' or '$'. Lemon does not allow this alternative form for
+terminal symbols. With Lemon, all symbols, terminals and nonterminals,
+must have alphanumeric names.</p>
+
+<h3>Grammar Rules</h3>
+
+<p>The main component of a Lemon grammar file is a sequence of grammar
+rules.
+Each grammar rule consists of a nonterminal symbol followed by
+the special symbol ``::='' and then a list of terminals and/or nonterminals.
+The rule is terminated by a period.
+The list of terminals and nonterminals on the right-hand side of the
+rule can be empty.
+Rules can occur in any order, except that the left-hand side of the
+first rule is assumed to be the start symbol for the grammar (unless
+specified otherwise using the <tt>%start</tt> directive described below.)
+A typical sequence of grammar rules might look something like this:
+<pre>
+ expr ::= expr PLUS expr.
+ expr ::= expr TIMES expr.
+ expr ::= LPAREN expr RPAREN.
+ expr ::= VALUE.
+</pre>
+</p>
+
+<p>There is one non-terminal in this example, ``expr'', and five
+terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'',
+``RPAREN'' and ``VALUE''.</p>
+
+<p>Like yacc and bison, Lemon allows the grammar to specify a block
+of C code that will be executed whenever a grammar rule is reduced
+by the parser.
+In Lemon, this action is specified by putting the C code (contained
+within curly braces <tt>{...}</tt>) immediately after the
+period that closes the rule.
+For example:
+<pre>
+ expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
+</pre>
+</p>
+
+<p>In order to be useful, grammar actions must normally be linked to
+their associated grammar rules.
+In yacc and bison, this is accomplished by embedding a ``$$'' in the
+action to stand for the value of the left-hand side of the rule and
+symbols ``$1'', ``$2'', and so forth to stand for the value of
+the terminal or nonterminal at position 1, 2 and so forth on the
+right-hand side of the rule.
+This idea is very powerful, but it is also very error-prone. The
+single most common source of errors in a yacc or bison grammar is
+to miscount the number of symbols on the right-hand side of a grammar
+rule and say ``$7'' when you really mean ``$8''.</p>
+
+<p>Lemon avoids the need to count grammar symbols by assigning symbolic
+names to each symbol in a grammar rule and then using those symbolic
+names in the action.
+In yacc or bison, one would write this:
+<pre>
+ expr -> expr PLUS expr { $$ = $1 + $3; };
+</pre>
+But in Lemon, the same rule becomes the following:
+<pre>
+ expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
+</pre>
+In the Lemon rule, any symbol in parentheses after a grammar rule
+symbol becomes a place holder for that symbol in the grammar rule.
+This place holder can then be used in the associated C action to
+stand for the value of that symbol.<p>
+
+<p>The Lemon notation for linking a grammar rule with its reduce
+action is superior to yacc/bison on several counts.
+First, as mentioned above, the Lemon method avoids the need to
+count grammar symbols.
+Secondly, if a terminal or nonterminal in a Lemon grammar rule
+includes a linking symbol in parentheses but that linking symbol
+is not actually used in the reduce action, then an error message
+is generated.
+For example, the rule
+<pre>
+ expr(A) ::= expr(B) PLUS expr(C). { A = B; }
+</pre>
+will generate an error because the linking symbol ``C'' is used
+in the grammar rule but not in the reduce action.</p>
+
+<p>The Lemon notation for linking grammar rules to reduce actions
+also facilitates the use of destructors for reclaiming memory
+allocated by the values of terminals and nonterminals on the
+right-hand side of a rule.</p>
+
+<h3>Precedence Rules</h3>
+
+<p>Lemon resolves parsing ambiguities in exactly the same way as
+yacc and bison. A shift-reduce conflict is resolved in favor
+of the shift, and a reduce-reduce conflict is resolved by reducing
+whichever rule comes first in the grammar file.</p>
+
+<p>Just like in
+yacc and bison, Lemon allows a measure of control
+over the resolution of paring conflicts using precedence rules.
+A precedence value can be assigned to any terminal symbol
+using the %left, %right or %nonassoc directives. Terminal symbols
+mentioned in earlier directives have a lower precedence that
+terminal symbols mentioned in later directives. For example:</p>
+
+<p><pre>
+ %left AND.
+ %left OR.
+ %nonassoc EQ NE GT GE LT LE.
+ %left PLUS MINUS.
+ %left TIMES DIVIDE MOD.
+ %right EXP NOT.
+</pre></p>
+
+<p>In the preceding sequence of directives, the AND operator is
+defined to have the lowest precedence. The OR operator is one
+precedence level higher. And so forth. Hence, the grammar would
+attempt to group the ambiguous expression
+<pre>
+ a AND b OR c
+</pre>
+like this
+<pre>
+ a AND (b OR c).
+</pre>
+The associativity (left, right or nonassoc) is used to determine
+the grouping when the precedence is the same. AND is left-associative
+in our example, so
+<pre>
+ a AND b AND c
+</pre>
+is parsed like this
+<pre>
+ (a AND b) AND c.
+</pre>
+The EXP operator is right-associative, though, so
+<pre>
+ a EXP b EXP c
+</pre>
+is parsed like this
+<pre>
+ a EXP (b EXP c).
+</pre>
+The nonassoc precedence is used for non-associative operators.
+So
+<pre>
+ a EQ b EQ c
+</pre>
+is an error.</p>
+
+<p>The precedence of non-terminals is transferred to rules as follows:
+The precedence of a grammar rule is equal to the precedence of the
+left-most terminal symbol in the rule for which a precedence is
+defined. This is normally what you want, but in those cases where
+you want to precedence of a grammar rule to be something different,
+you can specify an alternative precedence symbol by putting the
+symbol in square braces after the period at the end of the rule and
+before any C-code. For example:</p>
+
+<p><pre>
+ expr = MINUS expr. [NOT]
+</pre></p>
+
+<p>This rule has a precedence equal to that of the NOT symbol, not the
+MINUS symbol as would have been the case by default.</p>
+
+<p>With the knowledge of how precedence is assigned to terminal
+symbols and individual
+grammar rules, we can now explain precisely how parsing conflicts
+are resolved in Lemon. Shift-reduce conflicts are resolved
+as follows:
+<ul>
+<li> If either the token to be shifted or the rule to be reduced
+ lacks precedence information, then resolve in favor of the
+ shift, but report a parsing conflict.
+<li> If the precedence of the token to be shifted is greater than
+ the precedence of the rule to reduce, then resolve in favor
+ of the shift. No parsing conflict is reported.
+<li> If the precedence of the token it be shifted is less than the
+ precedence of the rule to reduce, then resolve in favor of the
+ reduce action. No parsing conflict is reported.
+<li> If the precedences are the same and the shift token is
+ right-associative, then resolve in favor of the shift.
+ No parsing conflict is reported.
+<li> If the precedences are the same the the shift token is
+ left-associative, then resolve in favor of the reduce.
+ No parsing conflict is reported.
+<li> Otherwise, resolve the conflict by doing the shift and
+ report the parsing conflict.
+</ul>
+Reduce-reduce conflicts are resolved this way:
+<ul>
+<li> If either reduce rule
+ lacks precedence information, then resolve in favor of the
+ rule that appears first in the grammar and report a parsing
+ conflict.
+<li> If both rules have precedence and the precedence is different
+ then resolve the dispute in favor of the rule with the highest
+ precedence and do not report a conflict.
+<li> Otherwise, resolve the conflict by reducing by the rule that
+ appears first in the grammar and report a parsing conflict.
+</ul>
+
+<h3>Special Directives</h3>
+
+<p>The input grammar to Lemon consists of grammar rules and special
+directives. We've described all the grammar rules, so now we'll
+talk about the special directives.</p>
+
+<p>Directives in lemon can occur in any order. You can put them before
+the grammar rules, or after the grammar rules, or in the mist of the
+grammar rules. It doesn't matter. The relative order of
+directives used to assign precedence to terminals is important, but
+other than that, the order of directives in Lemon is arbitrary.</p>
+
+<p>Lemon supports the following special directives:
+<ul>
+<li><tt>%destructor</tt>
+<li><tt>%extra_argument</tt>
+<li><tt>%include</tt>
+<li><tt>%left</tt>
+<li><tt>%name</tt>
+<li><tt>%nonassoc</tt>
+<li><tt>%parse_accept</tt>
+<li><tt>%parse_failure </tt>
+<li><tt>%right</tt>
+<li><tt>%stack_overflow</tt>
+<li><tt>%stack_size</tt>
+<li><tt>%start_symbol</tt>
+<li><tt>%syntax_error</tt>
+<li><tt>%token_destructor</tt>
+<li><tt>%token_prefix</tt>
+<li><tt>%token_type</tt>
+<li><tt>%type</tt>
+</ul>
+Each of these directives will be described separately in the
+following sections:</p>
+
+<h4>The <tt>%destructor</tt> directive</h4>
+
+<p>The %destructor directive is used to specify a destructor for
+a non-terminal symbol.
+(See also the %token_destructor directive which is used to
+specify a destructor for terminal symbols.)</p>
+
+<p>A non-terminal's destructor is called to dispose of the
+non-terminal's value whenever the non-terminal is popped from
+the stack. This includes all of the following circumstances:
+<ul>
+<li> When a rule reduces and the value of a non-terminal on
+ the right-hand side is not linked to C code.
+<li> When the stack is popped during error processing.
+<li> When the ParseFree() function runs.
+</ul>
+The destructor can do whatever it wants with the value of
+the non-terminal, but its design is to deallocate memory
+or other resources held by that non-terminal.</p>
+
+<p>Consider an example:
+<pre>
+ %type nt {void*}
+ %destructor nt { free($$); }
+ nt(A) ::= ID NUM. { A = malloc( 100 ); }
+</pre>
+This example is a bit contrived but it serves to illustrate how
+destructors work. The example shows a non-terminal named
+``nt'' that holds values of type ``void*''. When the rule for
+an ``nt'' reduces, it sets the value of the non-terminal to
+space obtained from malloc(). Later, when the nt non-terminal
+is popped from the stack, the destructor will fire and call
+free() on this malloced space, thus avoiding a memory leak.
+(Note that the symbol ``$$'' in the destructor code is replaced
+by the value of the non-terminal.)</p>
+
+<p>It is important to note that the value of a non-terminal is passed
+to the destructor whenever the non-terminal is removed from the
+stack, unless the non-terminal is used in a C-code action. If
+the non-terminal is used by C-code, then it is assumed that the
+C-code will take care of destroying it if it should really
+be destroyed. More commonly, the value is used to build some
+larger structure and we don't want to destroy it, which is why
+the destructor is not called in this circumstance.</p>
+
+<p>By appropriate use of destructors, it is possible to
+build a parser using Lemon that can be used within a long-running
+program, such as a GUI, that will not leak memory or other resources.
+To do the same using yacc or bison is much more difficult.</p>
+
+<h4>The <tt>%extra_argument</tt> directive</h4>
+
+The %extra_argument directive instructs Lemon to add a 4th parameter
+to the parameter list of the Parse() function it generates. Lemon
+doesn't do anything itself with this extra argument, but it does
+make the argument available to C-code action routines, destructors,
+and so forth. For example, if the grammar file contains:</p>
+
+<p><pre>
+ %extra_argument { MyStruct *pAbc }
+</pre></p>
+
+<p>Then the Parse() function generated will have an 4th parameter
+of type ``MyStruct*'' and all action routines will have access to
+a variable named ``pAbc'' that is the value of the 4th parameter
+in the most recent call to Parse().</p>
+
+<h4>The <tt>%include</tt> directive</h4>
+
+<p>The %include directive specifies C code that is included at the
+top of the generated parser. You can include any text you want --
+the Lemon parser generator copies to blindly. If you have multiple
+%include directives in your grammar file, their values are concatenated
+before being put at the beginning of the generated parser.</p>
+
+<p>The %include directive is very handy for getting some extra #include
+preprocessor statements at the beginning of the generated parser.
+For example:</p>
+
+<p><pre>
+ %include {#include &lt;unistd.h&gt;}
+</pre></p>
+
+<p>This might be needed, for example, if some of the C actions in the
+grammar call functions that are prototyed in unistd.h.</p>
+
+<h4>The <tt>%left</tt> directive</h4>
+
+The %left directive is used (along with the %right and
+%nonassoc directives) to declare precedences of terminal
+symbols. Every terminal symbol whose name appears after
+a %left directive but before the next period (``.'') is
+given the same left-associative precedence value. Subsequent
+%left directives have higher precedence. For example:</p>
+
+<p><pre>
+ %left AND.
+ %left OR.
+ %nonassoc EQ NE GT GE LT LE.
+ %left PLUS MINUS.
+ %left TIMES DIVIDE MOD.
+ %right EXP NOT.
+</pre></p>
+
+<p>Note the period that terminates each %left, %right or %nonassoc
+directive.</p>
+
+<p>LALR(1) grammars can get into a situation where they require
+a large amount of stack space if you make heavy use or right-associative
+operators. For this reason, it is recommended that you use %left
+rather than %right whenever possible.</p>
+
+<h4>The <tt>%name</tt> directive</h4>
+
+<p>By default, the functions generated by Lemon all begin with the
+five-character string ``Parse''. You can change this string to something
+different using the %name directive. For instance:</p>
+
+<p><pre>
+ %name Abcde
+</pre></p>
+
+<p>Putting this directive in the grammar file will cause Lemon to generate
+functions named
+<ul>
+<li> AbcdeAlloc(),
+<li> AbcdeFree(),
+<li> AbcdeTrace(), and
+<li> Abcde().
+</ul>
+The %name directive allows you to generator two or more different
+parsers and link them all into the same executable.
+</p>
+
+<h4>The <tt>%nonassoc</tt> directive</h4>
+
+<p>This directive is used to assign non-associative precedence to
+one or more terminal symbols. See the section on precedence rules
+or on the %left directive for additional information.</p>
+
+<h4>The <tt>%parse_accept</tt> directive</h4>
+
+<p>The %parse_accept directive specifies a block of C code that is
+executed whenever the parser accepts its input string. To ``accept''
+an input string means that the parser was able to process all tokens
+without error.</p>
+
+<p>For example:</p>
+
+<p><pre>
+ %parse_accept {
+ printf("parsing complete!\n");
+ }
+</pre></p>
+
+
+<h4>The <tt>%parse_failure</tt> directive</h4>
+
+<p>The %parse_failure directive specifies a block of C code that
+is executed whenever the parser fails complete. This code is not
+executed until the parser has tried and failed to resolve an input
+error using is usual error recovery strategy. The routine is
+only invoked when parsing is unable to continue.</p>
+
+<p><pre>
+ %parse_failure {
+ fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
+ }
+</pre></p>
+
+<h4>The <tt>%right</tt> directive</h4>
+
+<p>This directive is used to assign right-associative precedence to
+one or more terminal symbols. See the section on precedence rules
+or on the %left directive for additional information.</p>
+
+<h4>The <tt>%stack_overflow</tt> directive</h4>
+
+<p>The %stack_overflow directive specifies a block of C code that
+is executed if the parser's internal stack ever overflows. Typically
+this just prints an error message. After a stack overflow, the parser
+will be unable to continue and must be reset.</p>
+
+<p><pre>
+ %stack_overflow {
+ fprintf(stderr,"Giving up. Parser stack overflow\n");
+ }
+</pre></p>
+
+<p>You can help prevent parser stack overflows by avoiding the use
+of right recursion and right-precedence operators in your grammar.
+Use left recursion and and left-precedence operators instead, to
+encourage rules to reduce sooner and keep the stack size down.
+For example, do rules like this:
+<pre>
+ list ::= list element. // left-recursion. Good!
+ list ::= .
+</pre>
+Not like this:
+<pre>
+ list ::= element list. // right-recursion. Bad!
+ list ::= .
+</pre>
+
+<h4>The <tt>%stack_size</tt> directive</h4>
+
+<p>If stack overflow is a problem and you can't resolve the trouble
+by using left-recursion, then you might want to increase the size
+of the parser's stack using this directive. Put an positive integer
+after the %stack_size directive and Lemon will generate a parse
+with a stack of the requested size. The default value is 100.</p>
+
+<p><pre>
+ %stack_size 2000
+</pre></p>
+
+<h4>The <tt>%start_symbol</tt> directive</h4>
+
+<p>By default, the start-symbol for the grammar that Lemon generates
+is the first non-terminal that appears in the grammar file. But you
+can choose a different start-symbol using the %start_symbol directive.</p>
+
+<p><pre>
+ %start_symbol prog
+</pre></p>
+
+<h4>The <tt>%token_destructor</tt> directive</h4>
+
+<p>The %destructor directive assigns a destructor to a non-terminal
+symbol. (See the description of the %destructor directive above.)
+This directive does the same thing for all terminal symbols.</p>
+
+<p>Unlike non-terminal symbols which may each have a different data type
+for their values, terminals all use the same data type (defined by
+the %token_type directive) and so they use a common destructor. Other
+than that, the token destructor works just like the non-terminal
+destructors.</p>
+
+<h4>The <tt>%token_prefix</tt> directive</h4>
+
+<p>Lemon generates #defines that assign small integer constants
+to each terminal symbol in the grammar. If desired, Lemon will
+add a prefix specified by this directive
+to each of the #defines it generates.
+So if the default output of Lemon looked like this:
+<pre>
+ #define AND 1
+ #define MINUS 2
+ #define OR 3
+ #define PLUS 4
+</pre>
+You can insert a statement into the grammar like this:
+<pre>
+ %token_prefix TOKEN_
+</pre>
+to cause Lemon to produce these symbols instead:
+<pre>
+ #define TOKEN_AND 1
+ #define TOKEN_MINUS 2
+ #define TOKEN_OR 3
+ #define TOKEN_PLUS 4
+</pre>
+
+<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4>
+
+<p>These directives are used to specify the data types for values
+on the parser's stack associated with terminal and non-terminal
+symbols. The values of all terminal symbols must be of the same
+type. This turns out to be the same data type as the 3rd parameter
+to the Parse() function generated by Lemon. Typically, you will
+make the value of a terminal symbol by a pointer to some kind of
+token structure. Like this:</p>
+
+<p><pre>
+ %token_type {Token*}
+</pre></p>
+
+<p>If the data type of terminals is not specified, the default value
+is ``int''.</p>
+
+<p>Non-terminal symbols can each have their own data types. Typically
+the data type of a non-terminal is a pointer to the root of a parse-tree
+structure that contains all information about that non-terminal.
+For example:</p>
+
+<p><pre>
+ %type expr {Expr*}
+</pre></p>
+
+<p>Each entry on the parser's stack is actually a union containing
+instances of all data types for every non-terminal and terminal symbol.
+Lemon will automatically use the correct element of this union depending
+on what the corresponding non-terminal or terminal symbol is. But
+the grammar designer should keep in mind that the size of the union
+will be the size of its largest element. So if you have a single
+non-terminal whose data type requires 1K of storage, then your 100
+entry parser stack will require 100K of heap space. If you are willing
+and able to pay that price, fine. You just need to know.</p>
+
+<h3>Error Processing</h3>
+
+<p>After extensive experimentation over several years, it has been
+discovered that the error recovery strategy used by yacc is about
+as good as it gets. And so that is what Lemon uses.</p>
+
+<p>When a Lemon-generated parser encounters a syntax error, it
+first invokes the code specified by the %syntax_error directive, if
+any. It then enters its error recovery strategy. The error recovery
+strategy is to begin popping the parsers stack until it enters a
+state where it is permitted to shift a special non-terminal symbol
+named ``error''. It then shifts this non-terminal and continues
+parsing. But the %syntax_error routine will not be called again
+until at least three new tokens have been successfully shifted.</p>
+
+<p>If the parser pops its stack until the stack is empty, and it still
+is unable to shift the error symbol, then the %parse_failed routine
+is invoked and the parser resets itself to its start state, ready
+to begin parsing a new file. This is what will happen at the very
+first syntax error, of course, if there are no instances of the
+``error'' non-terminal in your grammar.</p>
+
+</body>
+</html>
diff --git a/tools/lemon/lemonflex-head.inc b/tools/lemon/lemonflex-head.inc
new file mode 100644
index 0000000000..0387b03b4c
--- /dev/null
+++ b/tools/lemon/lemonflex-head.inc
@@ -0,0 +1,34 @@
+/* $Id: lemonflex-head.inc,v 1.1 2001/02/01 20:21:25 gram Exp $ */
+
+/* This file is #include'd at the top of a Lex/Flex scanner
+for use with the Lemon parser. You must have #define'd:
+
+LVAL name of lval variable
+LVAL_TYPE type of lval variable
+LVAL_INIT_VAL Initial value of lval variable
+
+*/
+
+/* Flex has a few routines which help us get the scanner to read
+ * from a string rather than from a file. POSIX lex only provides
+ * for reading from a file; any method of reading from a string
+ * is inherently non-portable. Besides reading from a string,
+ * we have to worry about resetting the scanner after a bad
+ * parse; this too is non-portable. Combine the reset with
+ * a string input, and you have major non-portability. I'll provide
+ * the routines for flex here. If you really want to modify the
+ * scanner and use a non-flex lex implementation, you may
+ * add more ifdef's below.
+ */
+
+
+/* If we don't need yyunput, use this macro to get it out of the
+ * generated C file, avoiding a compiler warning about its lack of use */
+#define YY_NO_UNPUT 1
+
+
+/* Yup, I'm using a non-standard type for lval, unlike usual lex/yacc implementations.
+ * I can do so because I'm *not* using yacc, I'm using Lemon, where I have
+ * more control of the interaction between scanner and parser. */
+LVAL_TYPE LVAL = LVAL_INIT_VAL;
+
diff --git a/tools/lemon/lemonflex-tail.inc b/tools/lemon/lemonflex-tail.inc
new file mode 100644
index 0000000000..04eaf2ab2d
--- /dev/null
+++ b/tools/lemon/lemonflex-tail.inc
@@ -0,0 +1,59 @@
+/* $Id: lemonflex-tail.inc,v 1.1 2001/02/01 20:21:25 gram Exp $ */
+
+/* This file is #include'd at the bottom of a Lex/Flex scanner
+for use with the Lemon parser. You must have #define'd:
+
+MODNAME module name for creating function names:
+
+Prototypes:
+
+void MODNAME_scanner_text(char *text);
+void MODNAME_scanner_cleanup(void);
+int MODNAME_wrap(void);
+*/
+
+#include <cppmagic.h>
+
+#define TEXT_FUNC CONCAT(MODNAME,_scanner_text)
+#define FILE_FUNC CONCAT(MODNAME,_scanner_file)
+#define CLEANUP_FUNC CONCAT(MODNAME,_scanner_cleanup)
+#define WRAP_FUNC CONCAT(MODNAME,_wrap)
+
+
+/* Resets scanner and assigns the char* argument
+ * as the text to scan
+ */
+void
+TEXT_FUNC (const char *text)
+{
+ yy_scan_string(text);
+}
+
+void
+FILE_FUNC (FILE* fh)
+{
+ YY_BUFFER_STATE new_buffer;
+
+ new_buffer = yy_create_buffer(fh, YY_BUF_SIZE);
+ yy_switch_to_buffer(new_buffer);
+}
+
+void
+CLEANUP_FUNC (void)
+{
+ BEGIN(INITIAL);
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+}
+
+/* Flex has an option '%option noyywrap' so that I don't have to
+ * provide this yywrap function, but in order to maintain portability,
+ * I'll just use this yywrap() function.
+ */
+int
+WRAP_FUNC ()
+{
+ return 1; /* stop at EOF, instead of looking for next file */
+}
+
+
+
diff --git a/tools/lemon/lempar.c b/tools/lemon/lempar.c
new file mode 100644
index 0000000000..73783a3d35
--- /dev/null
+++ b/tools/lemon/lempar.c
@@ -0,0 +1,598 @@
+/* Driver template for the LEMON parser generator.
+** Copyright 1991-1995 by D. Richard Hipp.
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This library is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** Library General Public License for more details.
+**
+** You should have received a copy of the GNU Library General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Modified 1997 to make it suitable for use with makeheaders.
+*/
+/* First off, code is include which follows the "include" declaration
+** in the input file. */
+#include <stdio.h>
+%%
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/*
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands.
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+%%
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+** YYCODETYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 terminals
+** and nonterminals. "int" is used otherwise.
+** YYNOCODE is a number of type YYCODETYPE which corresponds
+** to no legal terminal or nonterminal number. This
+** number is used to fill in empty slots of the hash
+** table.
+** YYACTIONTYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 rules and
+** states combined. "int" is used otherwise.
+** ParseTOKENTYPE is the data type used for minor tokens given
+** directly to the parser from the tokenizer.
+** YYMINORTYPE is the data type used for all minor tokens.
+** This is typically a union of many types, one of
+** which is ParseTOKENTYPE. The entry in the union
+** for base tokens is called "yy0".
+** YYSTACKDEPTH is the maximum depth of the parser's stack.
+** ParseARGDECL is a declaration of a 3rd argument to the
+** parser, or null if there is no extra argument.
+** ParseKRARGDECL A version of ParseARGDECL for K&R C.
+** ParseANSIARGDECL A version of ParseARGDECL for ANSI C.
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+*/
+%%
+#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
+/* Next is the action table. Each entry in this table contains
+**
+** + An integer which is the number representing the look-ahead
+** token
+**
+** + An integer indicating what action to take. Number (N) between
+** 0 and YYNSTATE-1 mean shift the look-ahead and go to state N.
+** Numbers between YYNSTATE and YYNSTATE+YYNRULE-1 mean reduce by
+** rule N-YYNSTATE. Number YYNSTATE+YYNRULE means that a syntax
+** error has occurred. Number YYNSTATE+YYNRULE+1 means the parser
+** accepts its input.
+**
+** + A pointer to the next entry with the same hash value.
+**
+** The action table is really a series of hash tables. Each hash
+** table contains a number of entries which is a power of two. The
+** "state" table (which follows) contains information about the starting
+** point and size of each hash table.
+*/
+struct yyActionEntry {
+ YYCODETYPE lookahead; /* The value of the look-ahead token */
+ YYACTIONTYPE action; /* Action to take for this look-ahead */
+ struct yyActionEntry *next; /* Next look-ahead with the same hash, or NULL */
+};
+static struct yyActionEntry yyActionTable[] = {
+%%
+};
+
+/* The state table contains information needed to look up the correct
+** action in the action table, given the current state of the parser.
+** Information needed includes:
+**
+** + A pointer to the start of the action hash table in yyActionTable.
+**
+** + A mask used to hash the look-ahead token. The mask is an integer
+** which is one less than the size of the hash table.
+**
+** + The default action. This is the action to take if no entry for
+** the given look-ahead is found in the action hash table.
+*/
+struct yyStateEntry {
+ struct yyActionEntry *hashtbl; /* Start of the hash table in yyActionTable */
+ int mask; /* Mask used for hashing the look-ahead */
+ YYACTIONTYPE actionDefault; /* Default action if look-ahead not found */
+};
+static struct yyStateEntry yyStateTable[] = {
+%%
+};
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+ int stateno; /* The state-number */
+ int major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+ int idx; /* Index of top element in stack */
+ int errcnt; /* Shifts left before out of the error */
+ struct yyStackEntry *top; /* Pointer to the top stack element */
+ struct yyStackEntry stack[YYSTACKDEPTH]; /* The parser's stack */
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
+ yyTraceFILE = TraceFILE;
+ yyTracePrompt = zTracePrompt;
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static char *yyTokenName[] = {
+%%
+};
+#define YYTRACE(X) if( yyTraceFILE ) fprintf(yyTraceFILE,"%sReduce [%s].\n",yyTracePrompt,X);
+#else
+#define YYTRACE(X)
+#endif
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to Parse and ParseFree.
+*/
+void *ParseAlloc(void *(*mallocProc)()){
+ yyParser *pParser;
+ pParser = (yyParser*)(*mallocProc)( sizeof(yyParser), __FILE__, __LINE__ );
+ if( pParser ){
+ pParser->idx = -1;
+ }
+ return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol. The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
+ switch( yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are not used
+ ** inside the C code.
+ */
+%%
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+ YYCODETYPE yymajor;
+
+ if( pParser->idx<0 ) return 0;
+#ifndef NDEBUG
+ if( yyTraceFILE && pParser->idx>=0 ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[pParser->top->major]);
+ }
+#endif
+ yymajor = pParser->top->major;
+ yy_destructor( yymajor, &pParser->top->minor);
+ pParser->idx--;
+ pParser->top--;
+ return yymajor;
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser. This should be a pointer
+** obtained from ParseAlloc.
+** <li> A pointer to a function used to reclaim memory obtained
+** from malloc.
+** </ul>
+*/
+void ParseFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)() /* Function used to reclaim memory */
+){
+ yyParser *pParser = (yyParser*)p;
+ if( pParser==0 ) return;
+ while( pParser->idx>=0 ) yy_pop_parser_stack(pParser);
+ (*freeProc)(pParser, __FILE__, __LINE__);
+}
+
+/*
+** Find the appropriate action for a parser given the look-ahead token.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_parser_action(
+ yyParser *pParser, /* The parser */
+ int iLookAhead /* The look-ahead token */
+){
+ struct yyStateEntry *pState; /* Appropriate entry in the state table */
+ struct yyActionEntry *pAction; /* Action appropriate for the look-ahead */
+
+ /* if( pParser->idx<0 ) return YY_NO_ACTION; */
+ pState = &yyStateTable[pParser->top->stateno];
+ if( iLookAhead!=YYNOCODE ){
+ pAction = &pState->hashtbl[iLookAhead & pState->mask];
+ while( pAction ){
+ if( pAction->lookahead==iLookAhead ) return pAction->action;
+ pAction = pAction->next;
+ }
+ }else if( pState->mask!=0 || pState->hashtbl->lookahead!=YYNOCODE ){
+ return YY_NO_ACTION;
+ }
+ return pState->actionDefault;
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+ yyParser *yypParser, /* The parser to be shifted */
+ int yyNewState, /* The new state to shift in */
+ int yyMajor, /* The major token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
+){
+ yypParser->idx++;
+ yypParser->top++;
+ if( yypParser->idx>=YYSTACKDEPTH ){
+ yypParser->idx--;
+ yypParser->top--;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+%%
+ return;
+ }
+ yypParser->top->stateno = yyNewState;
+ yypParser->top->major = yyMajor;
+ yypParser->top->minor = *yypMinor;
+#ifndef NDEBUG
+ if( yyTraceFILE && yypParser->idx>0 ){
+ int i;
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->idx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->stack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static struct {
+ YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+%%
+};
+
+static void yy_accept(); /* Forward declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+ yyParser *yypParser, /* The parser */
+ int yyruleno /* Number of the rule by which to reduce */
+ ParseANSIARGDECL
+){
+ int yygoto; /* The next state */
+ int yyact; /* The next action */
+ YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
+ struct yyStackEntry *yymsp; /* The top of the parser's stack */
+ int yysize; /* Amount to pop the stack */
+ yymsp = yypParser->top;
+ switch( yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** YYTRACE("<text of the rule>");
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+%%
+ };
+ yygoto = yyRuleInfo[yyruleno].lhs;
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ yypParser->idx -= yysize;
+ yypParser->top -= yysize;
+ yyact = yy_find_parser_action(yypParser,yygoto);
+ if( yyact < YYNSTATE ){
+ yy_shift(yypParser,yyact,yygoto,&yygotominor);
+ }else if( yyact == YYNSTATE + YYNRULE + 1 ){
+ yy_accept(yypParser ParseARGDECL);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+static void yy_parse_failed(
+ yyParser *yypParser /* The parser */
+ ParseANSIARGDECL /* Extra arguments (if any) */
+){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+%%
+}
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+ yyParser *yypParser, /* The parser */
+ int yymajor, /* The major type of the error token */
+ YYMINORTYPE yyminor /* The minor type of the error token */
+ ParseANSIARGDECL /* Extra arguments (if any) */
+){
+#define TOKEN (yyminor.yy0)
+%%
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+ yyParser *yypParser /* The parser */
+ ParseANSIARGDECL /* Extra arguments (if any) */
+){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+%%
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "ParseAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void Parse(
+ void *yyp, /* The parser */
+ int yymajor, /* The major token code number */
+ ParseTOKENTYPE yyminor /* The value for the token */
+ ParseANSIARGDECL
+){
+ YYMINORTYPE yyminorunion;
+ int yyact; /* The parser action. */
+ int yyendofinput; /* True if we are at the end of input */
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
+ yyParser *yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ yypParser = (yyParser*)yyp;
+ if( yypParser->idx<0 ){
+ if( yymajor==0 ) return;
+ yypParser->idx = 0;
+ yypParser->errcnt = -1;
+ yypParser->top = &yypParser->stack[0];
+ yypParser->top->stateno = 0;
+ yypParser->top->major = 0;
+ }
+ yyminorunion.yy0 = yyminor;
+ yyendofinput = (yymajor==0);
+
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+
+ do{
+ yyact = yy_find_parser_action(yypParser,yymajor);
+ if( yyact<YYNSTATE ){
+ yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ yypParser->errcnt--;
+ if( yyendofinput && yypParser->idx>=0 ){
+ yymajor = 0;
+ }else{
+ yymajor = YYNOCODE;
+ }
+ }else if( yyact < YYNSTATE + YYNRULE ){
+ yy_reduce(yypParser,yyact-YYNSTATE ParseARGDECL);
+ }else if( yyact == YY_ERROR_ACTION ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+ }
+#endif
+#ifdef YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( yypParser->errcnt<0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
+ }
+ if( yypParser->top->major==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+ yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+ yy_destructor(yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+ }else{
+ while(
+ yypParser->idx >= 0 &&
+ yypParser->top->major != YYERRORSYMBOL &&
+ (yyact = yy_find_parser_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
+ ){
+ yy_pop_parser_stack(yypParser);
+ }
+ if( yypParser->idx < 0 || yymajor==0 ){
+ yy_destructor(yymajor,&yyminorunion);
+ yy_parse_failed(yypParser ParseARGDECL);
+ yymajor = YYNOCODE;
+ }else if( yypParser->top->major!=YYERRORSYMBOL ){
+ YYMINORTYPE u2;
+ u2.YYERRSYMDT = 0;
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ }
+ }
+ yypParser->errcnt = 3;
+ yyerrorhit = 1;
+#else /* YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( yypParser->errcnt<=0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
+ }
+ yypParser->errcnt = 3;
+ yy_destructor(yymajor,&yyminorunion);
+ if( yyendofinput ){
+ yy_parse_failed(yypParser ParseARGDECL);
+ }
+ yymajor = YYNOCODE;
+#endif
+ }else{
+ yy_accept(yypParser ParseARGDECL);
+ yymajor = YYNOCODE;
+ }
+ }while( yymajor!=YYNOCODE && yypParser->idx>=0 );
+ return;
+}