forked from osmocom/wireshark
![Guy Harris](/assets/img/avatar_default.png)
Add an FT_CHAR type, which is like FT_UINT8 except that the value is displayed as a C-style character constant. Allow use of C-style character constants in filter expressions; they can be used in comparisons with all integral types, and in "contains" operators. Use that type for some fields that appear (based on the way they're displayed, or on the use of C-style character constants in their value_string tables) to be 1-byte characters rather than 8-bit numbers. Change-Id: I39a9f0dda0bd7f4fa02a9ca8373216206f4d7135 Reviewed-on: https://code.wireshark.org/review/17787 Reviewed-by: Guy Harris <guy@alum.mit.edu>
353 lines
7.8 KiB
Text
353 lines
7.8 KiB
Text
|
|
%include {
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "dfilter-int.h"
|
|
#include "syntax-tree.h"
|
|
#include "sttype-range.h"
|
|
#include "sttype-test.h"
|
|
#include "sttype-function.h"
|
|
#include "sttype-set.h"
|
|
#include "drange.h"
|
|
|
|
#include "grammar.h"
|
|
|
|
#ifdef _WIN32
|
|
#pragma warning(disable:4671)
|
|
#endif
|
|
|
|
/* 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 {
|
|
(void) dfw; /* Mark unused, similar to Q_UNUSED */
|
|
stnode_free($$);
|
|
}
|
|
|
|
%type sentence {stnode_t*}
|
|
|
|
|
|
%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($$);}
|
|
|
|
%type drnode {drange_node*}
|
|
%destructor drnode {drange_node_free($$);}
|
|
|
|
%type drnode_list {GSList*}
|
|
%destructor drnode_list {drange_node_free_list($$);}
|
|
|
|
%type funcparams {GSList*}
|
|
%destructor funcparams {st_funcparams_free($$);}
|
|
|
|
%type setnode_list {GSList*}
|
|
%destructor setnode_list {set_nodelist_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(dfw, "Unexpected end of filter string.");
|
|
dfw->syntax_error = TRUE;
|
|
return;
|
|
}
|
|
|
|
switch(stnode_type_id(TOKEN)) {
|
|
case STTYPE_UNINITIALIZED:
|
|
dfilter_fail(dfw, "Syntax error.");
|
|
break;
|
|
case STTYPE_TEST:
|
|
dfilter_fail(dfw, "Syntax error, TEST.");
|
|
break;
|
|
case STTYPE_STRING:
|
|
dfilter_fail(dfw, "The string \"%s\" was unexpected in this context.",
|
|
(char *)stnode_data(TOKEN));
|
|
break;
|
|
case STTYPE_CHARCONST:
|
|
dfilter_fail(dfw, "The character constant \"%s\" was unexpected in this context.",
|
|
(char *)stnode_data(TOKEN));
|
|
break;
|
|
case STTYPE_UNPARSED:
|
|
dfilter_fail(dfw, "\"%s\" was unexpected in this context.",
|
|
(char *)stnode_data(TOKEN));
|
|
break;
|
|
case STTYPE_INTEGER:
|
|
dfilter_fail(dfw, "The integer %d was unexpected in this context.",
|
|
stnode_value(TOKEN));
|
|
break;
|
|
case STTYPE_FIELD:
|
|
hfinfo = (header_field_info *)stnode_data(TOKEN);
|
|
dfilter_fail(dfw, "Syntax error near \"%s\".", hfinfo->abbrev);
|
|
break;
|
|
case STTYPE_FUNCTION:
|
|
dfilter_fail(dfw, "The function s was unexpected in this context.");
|
|
break;
|
|
case STTYPE_SET:
|
|
dfilter_fail(dfw, "Syntax error, SET.");
|
|
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;
|
|
}
|
|
dfw->syntax_error = TRUE;
|
|
}
|
|
|
|
/* 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 TEST_CONTAINS TEST_MATCHES TEST_BITWISE_AND.
|
|
%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; }
|
|
|
|
|
|
/* 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) ::= entity(E).
|
|
{
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set1(T, TEST_OP_EXISTS, E);
|
|
}
|
|
|
|
|
|
|
|
/* Entities, or things that can be compared/tested/checked */
|
|
entity(E) ::= FIELD(F). { E = F; }
|
|
entity(E) ::= STRING(S). { E = S; }
|
|
entity(E) ::= CHARCONST(C). { E = C; }
|
|
entity(E) ::= UNPARSED(U). { E = U; }
|
|
entity(E) ::= range(R). { E = R; }
|
|
|
|
range_body(B) ::= FIELD(F). { B = F; }
|
|
range_body(B) ::= STRING(S). { B = S; }
|
|
range_body(B) ::= range(R). { B = R; }
|
|
|
|
/* Ranges */
|
|
range(R) ::= range_body(B) LBRACKET drnode_list(L) RBRACKET.
|
|
{
|
|
R = stnode_new(STTYPE_RANGE, NULL);
|
|
sttype_range_set(R, B, L);
|
|
|
|
/* Delete the list, but not the drange_nodes that
|
|
* the list contains. */
|
|
g_slist_free(L);
|
|
}
|
|
|
|
drnode_list(L) ::= drnode(D).
|
|
{
|
|
L = g_slist_append(NULL, D);
|
|
}
|
|
|
|
drnode_list(L) ::= drnode_list(P) COMMA drnode(D).
|
|
{
|
|
L = g_slist_append(P, D);
|
|
}
|
|
|
|
/* x:y is offset:length */
|
|
drnode(D) ::= INTEGER(X) COLON INTEGER(Y).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_length(D, stnode_value(Y));
|
|
|
|
stnode_free(X);
|
|
stnode_free(Y);
|
|
}
|
|
|
|
/* x-y == offset:offset */
|
|
drnode(D) ::= INTEGER(X) HYPHEN INTEGER(Y).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_end_offset(D, stnode_value(Y));
|
|
|
|
stnode_free(X);
|
|
stnode_free(Y);
|
|
}
|
|
|
|
|
|
/* :y == from start to offset */
|
|
drnode(D) ::= COLON INTEGER(Y).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, 0);
|
|
drange_node_set_length(D, stnode_value(Y));
|
|
|
|
stnode_free(Y);
|
|
}
|
|
|
|
/* x: from offset to end */
|
|
drnode(D) ::= INTEGER(X) COLON.
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_to_the_end(D);
|
|
|
|
stnode_free(X);
|
|
}
|
|
|
|
/* x == x:1 */
|
|
drnode(D) ::= INTEGER(X).
|
|
{
|
|
D = drange_node_new();
|
|
drange_node_set_start_offset(D, stnode_value(X));
|
|
drange_node_set_length(D, 1);
|
|
|
|
stnode_free(X);
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* 'a == b == c' or 'a < b <= c <= d < e' */
|
|
relation_test(T) ::= entity(E) rel_op2(O) relation_test(R).
|
|
{
|
|
stnode_t *L, *F;
|
|
/* for now generate it like E O F TEST_OP_AND F P G, later it could be optimized
|
|
or semantically checked (to make a <= b >= c or a == b != c invalid)?
|
|
*/
|
|
|
|
F = R;
|
|
do {
|
|
g_assert(F != NULL && stnode_type_id(F) == STTYPE_TEST);
|
|
sttype_test_get(F, NULL, &F, NULL);
|
|
} while (stnode_type_id(F) == STTYPE_TEST);
|
|
|
|
L = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set2(L, O, E, stnode_dup(F));
|
|
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
sttype_test_set2(T, TEST_OP_AND, L, R);
|
|
}
|
|
|
|
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; }
|
|
rel_op2(O) ::= TEST_BITWISE_AND. { O = TEST_OP_BITWISE_AND; }
|
|
rel_op2(O) ::= TEST_CONTAINS. { O = TEST_OP_CONTAINS; }
|
|
rel_op2(O) ::= TEST_MATCHES. { O = TEST_OP_MATCHES; }
|
|
|
|
relation_test(T) ::= entity(E) TEST_IN LBRACE setnode_list(L) RBRACE.
|
|
{
|
|
stnode_t *S;
|
|
T = stnode_new(STTYPE_TEST, NULL);
|
|
S = stnode_new(STTYPE_SET, L);
|
|
sttype_test_set2(T, TEST_OP_IN, E, S);
|
|
}
|
|
|
|
setnode_list(L) ::= entity(E).
|
|
{
|
|
L = g_slist_append(NULL, E);
|
|
}
|
|
|
|
setnode_list(L) ::= setnode_list(P) entity(E).
|
|
{
|
|
L = g_slist_append(P, E);
|
|
}
|
|
|
|
/* Functions */
|
|
|
|
/* A function can have one or more parameters */
|
|
entity(E) ::= FUNCTION(F) LPAREN funcparams(P) RPAREN.
|
|
{
|
|
E = F;
|
|
sttype_function_set_params(E, P);
|
|
}
|
|
|
|
/* A function can have zero parameters. */
|
|
entity(E) ::= FUNCTION(F) LPAREN RPAREN.
|
|
{
|
|
E = F;
|
|
}
|
|
|
|
funcparams(P) ::= entity(E).
|
|
{
|
|
P = g_slist_append(NULL, E);
|
|
}
|
|
|
|
funcparams(P) ::= funcparams(L) COMMA entity(E).
|
|
{
|
|
P = g_slist_append(L, E);
|
|
}
|
|
|
|
|
|
/* Any expression inside parens is simply that expression */
|
|
expr(X) ::= LPAREN expr(Y) RPAREN.
|
|
{
|
|
X = Y;
|
|
stnode_set_bracket(X, TRUE);
|
|
}
|
|
|