diff options
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, ðer_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 */ @@ -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; } + @@ -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 = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + 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 <unistd.h>} +</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; +} |