From 550a6488c9e2241e2979317c04d6d73752618402 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Tue, 2 Jul 2019 17:39:56 +0200 Subject: [PATCH 1/7] Filter: documentation of the M4 preprocessor --- filter/decl.m4 | 351 +++++++++++++++++++++++++++++++----------------- filter/f-inst.c | 11 +- 2 files changed, 234 insertions(+), 128 deletions(-) diff --git a/filter/decl.m4 b/filter/decl.m4 index cc069485..20119c1d 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -6,149 +6,70 @@ m4_divert(-1)m4_dnl # # Can be freely distributed and used under the terms of the GNU GPL. # +# THIS IS A M4 MACRO FILE GENERATING 3 FILES ALTOGETHER. +# KEEP YOUR HANDS OFF UNLESS YOU KNOW WHAT YOU'RE DOING. +# EDITING AND DEBUGGING THIS FILE MAY DAMAGE YOUR BRAIN SERIOUSLY. # -# Global Diversions: -# 4 enum fi_code -# 5 enum fi_code to string -# 6 dump line item -# 7 dump line item callers -# 8 linearize -# 9 same (filter comparator) -# 1 union in struct f_inst -# 3 constructors + interpreter +# But you're welcome to read and edit and debug if you aren't scared. +# +# Uncomment the following line to get exhaustive debug output. +# m4_debugmode(aceflqtx) +# +# How it works: +# 1) Instruction to code conversion (uses diversions 100..199) +# 2) Code wrapping (uses diversions 1..99) +# 3) Final preparation (uses diversions 200..299) +# 4) Shipout +# +# See below for detailed description. +# +# +# 1) Instruction to code conversion +# The code provided in f-inst.c between consecutive INST() calls +# is interleaved for many different places. It is here processed +# and split into separate instances where split-by-instruction +# happens. These parts are stored in temporary diversions listed: # -# Per-inst Diversions: # 101 content of per-inst struct # 102 constructor arguments # 103 constructor body # 104 dump line item content +# (there may be nothing in dump-line content and +# it must be handled specially in phase 2) # 105 linearize body # 106 comparator body # 107 struct f_line_item content # 108 interpreter body # -# Final diversions -# 200+ completed text before it is flushed to output - -m4_dnl m4_debugmode(aceflqtx) - -m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */') -m4_define(FID_INST, `FID_ZONE(1, Instruction structure for config)') -m4_define(FID_LINE, `FID_ZONE(2, Instruction structure for interpreter)') -m4_define(FID_NEW, `FID_ZONE(3, Constructor)') -m4_define(FID_ENUM, `FID_ZONE(4, Code enum)') -m4_define(FID_ENUM_STR, `FID_ZONE(5, Code enum to string)') -m4_define(FID_DUMP, `FID_ZONE(6, Dump line)') -m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)') -m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)') -m4_define(FID_SAME, `FID_ZONE(9, Comparison)') - +# Here are macros to allow you to _divert to the right directions. m4_define(FID_STRUCT_IN, `m4_divert(101)') m4_define(FID_NEW_ARGS, `m4_divert(102)') m4_define(FID_NEW_BODY, `m4_divert(103)') m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])') -m4_define(FID_LINEARIZE_BODY, `m4_divert(105)m4_define([[FID_LINEARIZE_BODY_EXISTS]])') +m4_define(FID_LINEARIZE_BODY, `m4_divert(105)') m4_define(FID_SAME_BODY, `m4_divert(106)') m4_define(FID_LINE_IN, `m4_divert(107)') m4_define(FID_INTERPRET_BODY, `m4_divert(108)') -m4_define(FID_ALL, `FID_INTERPRET_BODY'); +# Sometimes you want slightly different code versions in different +# outputs. +# Use FID_HIC(code for inst-gen.h, code for inst-gen.c, code for inst-interpret.c) +# and put it into [[ ]] quotes if it shall contain commas. m4_define(FID_HIC, `m4_ifelse(TARGET, [[H]], [[$1]], TARGET, [[I]], [[$2]], TARGET, [[C]], [[$3]])') +# In interpreter code, this is quite common. m4_define(FID_INTERPRET_EXEC, `FID_HIC(,[[FID_INTERPRET_BODY()]],[[m4_divert(-1)]])') m4_define(FID_INTERPRET_NEW, `FID_HIC(,[[m4_divert(-1)]],[[FID_INTERPRET_BODY()]])') + +# If the instruction is never converted to constant, the interpret +# code is not produced at all for constructor m4_define(NEVER_CONSTANT, `m4_define([[INST_NEVER_CONSTANT]])') m4_define(FID_IFCONST, `m4_ifdef([[INST_NEVER_CONSTANT]],[[$2]],[[$1]])') -m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[ -FID_ENUM -INST_NAME(), -FID_ENUM_STR -[INST_NAME()] = "INST_NAME()", -FID_INST -struct { -m4_undivert(101) -} i_[[]]INST_NAME(); -FID_LINE -struct { -m4_undivert(107) -} i_[[]]INST_NAME(); -FID_NEW -FID_HIC( -[[ -struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code -m4_undivert(102) -);]], -[[ - case INST_NAME(): - #define whati (&(what->i_]]INST_NAME()[[)) - m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]]) - m4_undivert(108) - #undef whati - break; -]], -[[ -struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code -m4_undivert(102) -) - { - struct f_inst *what = fi_new(fi_code); - FID_IFCONST([[uint constargs = 1;]]) - #define whati (&(what->i_]]INST_NAME()[[)) - m4_undivert(103) - FID_IFCONST([[if (!constargs)]]) - return what; - FID_IFCONST([[m4_undivert(108)]]) - #undef whati - } -]]) - -FID_DUMP_CALLER -case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break; - -FID_DUMP -m4_ifdef([[FID_DUMP_BODY_EXISTS]], -[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]], -[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]]) -m4_undefine([[FID_DUMP_BODY_EXISTS]]) -{ -#define item (&(item_->i_]]INST_NAME()[[)) -m4_undivert(104) -#undef item -} - -FID_LINEARIZE -case INST_NAME(): { -#define whati (&(what->i_]]INST_NAME()[[)) -#define item (&(dest->items[pos].i_]]INST_NAME()[[)) - m4_undivert(105) -#undef whati -#undef item - dest->items[pos].fi_code = what->fi_code; - dest->items[pos].lineno = what->lineno; - break; -} -m4_undefine([[FID_LINEARIZE_BODY_EXISTS]]) - -FID_SAME -case INST_NAME(): -#define f1 (&(f1_->i_]]INST_NAME()[[)) -#define f2 (&(f2_->i_]]INST_NAME()[[)) -m4_undivert(106) -#undef f1 -#undef f2 -break; -m4_divert(-1)FID_FLUSH(101,200) -]])') - -m4_define(INST, `m4_dnl -INST_FLUSH()m4_dnl -m4_define([[INST_NAME]], [[$1]])m4_dnl -m4_define([[INST_INVAL]], [[$2]])m4_dnl -m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl -FID_ALL() m4_dnl -') - +# If the instruction has some attributes (here called members), +# these are typically carried with the instruction from constructor +# to interpreter. This yields a line of code everywhere on the path. +# FID_MEMBER is a macro to help with this task. m4_define(FID_MEMBER, `m4_dnl FID_LINE_IN $1 $2; @@ -170,8 +91,14 @@ debug("%s$4\n", INDENT, $5); ]]) FID_INTERPRET_EXEC const $1 $2 = whati->$2 -FID_ALL') +FID_INTERPRET_BODY') +# Instruction arguments are needed only until linearization is done. +# This puts the arguments into the filter line to be executed before +# the instruction itself. +# +# To achieve this, ARG_ANY must be called before anything writes into +# the instruction line as it moves the instruction pointer forward. m4_define(ARG_ANY, ` FID_STRUCT_IN struct f_inst * f$1; @@ -188,14 +115,17 @@ FID_IFCONST([[ } FID_LINEARIZE_BODY pos = linearize(dest, whati->f$1, pos); -FID_ALL()') +FID_INTERPRET_BODY()') +# Some arguments need to check their type. After that, ARG_ANY is called. m4_define(ARG, `ARG_ANY($1) FID_INTERPRET_EXEC() if (v$1.type != $2) runtime("Argument $1 of instruction %s must be of type $2, got 0x%02x", f_instruction_name(what->fi_code), v$1.type)m4_dnl -FID_ALL()') +FID_INTERPRET_BODY()') -m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_ALL()') +# Executing another filter line. This replaces the recursion +# that was needed in the former implementation. +m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()') m4_define(LINEX_, `do { fstk->estk[fstk->ecnt].pos = 0; fstk->estk[fstk->ecnt].line = $1; @@ -226,13 +156,16 @@ do { if (whati->fl$1) { } } while(0) FID_INTERPRET_NEW return whati->f$1 -FID_ALL()') +FID_INTERPRET_BODY()') +# Some of the instructions have a result. These constructions +# state the result and put it to the right place. m4_define(RESULT, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])') m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; fstk->vcnt++; } while (0)]], [[return fi_constant(what, $1)]])') m4_define(RESULT_VOID, `RESULT_VAL([[ (struct f_val) { .type = T_VOID } ]])') +# Some common filter instruction members m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], symbol %s, item->sym->name)') m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], route table %s, item->rtc->name)') @@ -240,13 +173,174 @@ m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)') m4_define(ACCESS_RTE, `NEVER_CONSTANT()') +# 2) Code wrapping +# The code produced in 1xx temporary diversions is a raw code without +# any auxiliary commands and syntactical structures around. When the +# instruction is done, INST_FLUSH is called. More precisely, it is called +# at the beginning of INST() call and at the end of file. +# +# INST_FLUSH picks all the temporary diversions, wraps their content +# into appropriate headers and structures and saves them into global +# diversions listed: +# +# 4 enum fi_code +# 5 enum fi_code to string +# 6 dump line item +# 7 dump line item callers +# 8 linearize +# 9 same (filter comparator) +# 1 union in struct f_inst +# 3 constructors + interpreter +# +# These global diversions contain blocks of code that can be directly +# put into the final file, yet it still can't be written out now as +# every instruction writes to all of these diversions. + +# Code wrapping diversion names +m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */') +m4_define(FID_INST, `FID_ZONE(1, Instruction structure for config)') +m4_define(FID_LINE, `FID_ZONE(2, Instruction structure for interpreter)') +m4_define(FID_NEW, `FID_ZONE(3, Constructor)') +m4_define(FID_ENUM, `FID_ZONE(4, Code enum)') +m4_define(FID_ENUM_STR, `FID_ZONE(5, Code enum to string)') +m4_define(FID_DUMP, `FID_ZONE(6, Dump line)') +m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)') +m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)') +m4_define(FID_SAME, `FID_ZONE(9, Comparison)') + +# This macro does all the code wrapping. See inline comments. +m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[ +FID_ENUM m4_dnl Contents of enum fi_code { ... } +INST_NAME(), +FID_ENUM_STR m4_dnl Contents of const char * indexed by enum fi_code +[INST_NAME()] = "INST_NAME()", +FID_INST m4_dnl Anonymous structure inside struct f_inst +struct { +m4_undivert(101) +} i_[[]]INST_NAME(); +FID_LINE m4_dnl Anonymous structure inside struct f_line_item +struct { +m4_undivert(107) +} i_[[]]INST_NAME(); +FID_NEW m4_dnl Constructor and interpreter code together +FID_HIC( +[[ m4_dnl Public declaration of constructor in H file +struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code +m4_undivert(102) +);]], +[[ m4_dnl The one case in The Big Switch inside interpreter + case INST_NAME(): + #define whati (&(what->i_]]INST_NAME()[[)) + m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]]) + m4_undivert(108) + #undef whati + break; +]], +[[ m4_dnl Constructor itself +struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code +m4_undivert(102) +) + { + /* Allocate the structure */ + struct f_inst *what = fi_new(fi_code); + FID_IFCONST([[uint constargs = 1;]]) + + /* Initialize all the members */ + #define whati (&(what->i_]]INST_NAME()[[)) + m4_undivert(103) + + /* If not constant, return the instruction itself */ + FID_IFCONST([[if (!constargs)]]) + return what; + + /* Try to pre-calculate the result */ + FID_IFCONST([[m4_undivert(108)]]) + #undef whati + } +]]) + +FID_DUMP_CALLER m4_dnl Case in another big switch used in instruction dumping (debug) +case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break; + +FID_DUMP m4_dnl The dumper itself +m4_ifdef([[FID_DUMP_BODY_EXISTS]], +[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]], +[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]]) +m4_undefine([[FID_DUMP_BODY_EXISTS]]) +{ +#define item (&(item_->i_]]INST_NAME()[[)) +m4_undivert(104) +#undef item +} + +FID_LINEARIZE m4_dnl The linearizer +case INST_NAME(): { +#define whati (&(what->i_]]INST_NAME()[[)) +#define item (&(dest->items[pos].i_]]INST_NAME()[[)) + m4_undivert(105) +#undef whati +#undef item + dest->items[pos].fi_code = what->fi_code; + dest->items[pos].lineno = what->lineno; + break; +} + +FID_SAME m4_dnl This code compares two f_line"s while reconfiguring +case INST_NAME(): +#define f1 (&(f1_->i_]]INST_NAME()[[)) +#define f2 (&(f2_->i_]]INST_NAME()[[)) +m4_undivert(106) +#undef f1 +#undef f2 +break; + +m4_divert(-1)FID_FLUSH(101,200) m4_dnl And finally this flushes all the unused diversions +]])') + +m4_define(INST, `m4_dnl This macro is called on beginning of each instruction. +INST_FLUSH()m4_dnl First, old data is flushed +m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name, +m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count +m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl and reset NEVER_CONSTANT trigger. +FID_INTERPRET_BODY() m4_dnl By default, every code is interpreter code. +') + +# 3) Final preparation +# +# Now we prepare all the code around the global diversions. +# It must be here, not in m4wrap, as we want M4 to mark the code +# by #line directives correctly, not to claim that every single line +# is at the beginning of the m4wrap directive. +# +# This part is split by the final file. +# H for inst-gen.h +# I for inst-interpret.c +# C for inst-gen.c +# +# So we in cycle: +# A. open a diversion +# B. send there some code +# C. close that diversion +# D. flush a global diversion +# E. open another diversion and goto B. +# +# Final diversions +# 200+ completed text before it is flushed to output + +# This is a list of output diversions m4_define(FID_WR_PUT_LIST) + +# This macro does the steps C to E, see before. m4_define(FID_WR_PUT_ALSO, `m4_define([[FID_WR_PUT_LIST]],FID_WR_PUT_LIST()[[FID_WR_DPUT(]]FID_WR_DIDX[[)FID_WR_DPUT(]]$1[[)]])m4_define([[FID_WR_DIDX]],m4_eval(FID_WR_DIDX+1))m4_divert(FID_WR_DIDX)') +# These macros do the splitting between H/I/C m4_define(FID_WR_DIRECT, `m4_ifelse(TARGET,[[$1]],[[FID_WR_INIT()]],[[FID_WR_STOP()]])') m4_define(FID_WR_INIT, `m4_define([[FID_WR_DIDX]],200)m4_define([[FID_WR_PUT]],[[FID_WR_PUT_ALSO($]][[@)]])m4_divert(200)') m4_define(FID_WR_STOP, `m4_define([[FID_WR_PUT]])m4_divert(-1)') +# Here is the direct code to be put into the output files +# together with the undiversions, being hidden under FID_WR_PUT() + m4_changequote([[,]]) FID_WR_DIRECT(I) FID_WR_PUT(3) @@ -412,13 +506,24 @@ struct f_line_item { /* Instruction constructors */ FID_WR_PUT(3) - m4_divert(-1) + +# 4) Shipout +# +# Everything is prepared in FID_WR_PUT_LIST now. Let's go! + m4_changequote(`,') +# Flusher auxiliary macro m4_define(FID_FLUSH, `m4_ifelse($1,$2,,[[m4_undivert($1)FID_FLUSH(m4_eval($1+1),$2)]])') + +# Defining the macro used in FID_WR_PUT_LIST m4_define(FID_WR_DPUT, `m4_undivert($1)') +# After the code is read and parsed, we: m4_m4wrap(`INST_FLUSH()m4_divert(0)FID_WR_PUT_LIST()m4_divert(-1)FID_FLUSH(1,200)') m4_changequote([[,]]) +# And now M4 is going to parse f-inst.c, fill the diversions +# and after the file is done, the content of m4_m4wrap (see before) +# is executed. diff --git a/filter/f-inst.c b/filter/f-inst.c index edc97794..5f30ee38 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -167,7 +167,7 @@ } whati->f1 = NULL; } - FID_ALL + FID_INTERPRET_BODY FID_INTERPRET_EXEC if (fstk->vcnt < whati->count) /* TODO: make this check systematic */ @@ -198,7 +198,7 @@ FID_INTERPRET_EXEC fstk->vcnt -= whati->count; - FID_ALL + FID_INTERPRET_BODY pm->len = whati->count; RESULT(T_PATH_MASK, path_mask, pm); @@ -337,7 +337,7 @@ FID_LINEARIZE_BODY { uint opos = pos; - FID_ALL + FID_INTERPRET_BODY ARG_ANY(1); @@ -345,7 +345,7 @@ if (opos < pos) dest->items[pos].flags |= FIF_PRINTED; } - FID_ALL + FID_INTERPRET_BODY FID_MEMBER(enum filter_return, fret, f1->fret != f2->fret, %s, filter_return_str(item->fret)); @@ -1045,7 +1045,8 @@ INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */ NEVER_CONSTANT; ARG(1, T_BOOL); - FID_MEMBER(char *, s, [[strcmp(f1->s, f2->s)]], string \"%s\", item->s); + + FID_MEMBER(char *, s, [[strcmp(f1->s, f2->s)]], string %s, item->s); ASSERT(s); From 84c58aabd0d7a6a3d1508de004a29fd22423fde6 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Tue, 2 Jul 2019 17:59:21 +0200 Subject: [PATCH 2/7] Filter: Nicer whitespaces in generated inst-gen.h --- filter/decl.m4 | 93 +++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/filter/decl.m4 b/filter/decl.m4 index 20119c1d..26a1a166 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -71,12 +71,12 @@ m4_define(FID_IFCONST, `m4_ifdef([[INST_NEVER_CONSTANT]],[[$2]],[[$1]])') # to interpreter. This yields a line of code everywhere on the path. # FID_MEMBER is a macro to help with this task. m4_define(FID_MEMBER, `m4_dnl -FID_LINE_IN -$1 $2; -FID_STRUCT_IN -$1 $2; -FID_NEW_ARGS -, $1 $2 +FID_LINE_IN()m4_dnl + $1 $2; +FID_STRUCT_IN()m4_dnl + $1 $2; +FID_NEW_ARGS()m4_dnl + , $1 $2 FID_NEW_BODY whati->$2 = $2; FID_LINEARIZE_BODY @@ -100,10 +100,10 @@ FID_INTERPRET_BODY') # To achieve this, ARG_ANY must be called before anything writes into # the instruction line as it moves the instruction pointer forward. m4_define(ARG_ANY, ` -FID_STRUCT_IN -struct f_inst * f$1; -FID_NEW_ARGS -, struct f_inst * f$1 +FID_STRUCT_IN()m4_dnl + struct f_inst * f$1; +FID_NEW_ARGS()m4_dnl + , struct f_inst * f$1 FID_NEW_BODY whati->f$1 = f$1; for (const struct f_inst *child = f$1; child; child = child->next) { @@ -136,12 +136,12 @@ m4_define(LINEX_, `do { } while (0)') m4_define(LINE, ` -FID_LINE_IN -const struct f_line * fl$1; -FID_STRUCT_IN -struct f_inst * f$1; -FID_NEW_ARGS -, struct f_inst * f$1 +FID_LINE_IN()m4_dnl + const struct f_line * fl$1; +FID_STRUCT_IN()m4_dnl + struct f_inst * f$1; +FID_NEW_ARGS()m4_dnl + , struct f_inst * f$1 FID_NEW_BODY whati->f$1 = f$1; FID_DUMP_BODY @@ -166,8 +166,7 @@ m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; fstk->vcnt++; } while (0)]], m4_define(RESULT_VOID, `RESULT_VAL([[ (struct f_val) { .type = T_VOID } ]])') # Some common filter instruction members -m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, -[[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], symbol %s, item->sym->name)') +m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], symbol %s, item->sym->name)') m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], route table %s, item->rtc->name)') m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f2->sa.sa_code,,)') m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)') @@ -196,8 +195,10 @@ m4_define(ACCESS_RTE, `NEVER_CONSTANT()') # put into the final file, yet it still can't be written out now as # every instruction writes to all of these diversions. -# Code wrapping diversion names -m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */') +# Code wrapping diversion names. Here we want an explicit newline +# after the C comment. +m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */ +') m4_define(FID_INST, `FID_ZONE(1, Instruction structure for config)') m4_define(FID_LINE, `FID_ZONE(2, Instruction structure for interpreter)') m4_define(FID_NEW, `FID_ZONE(3, Constructor)') @@ -210,25 +211,25 @@ m4_define(FID_SAME, `FID_ZONE(9, Comparison)') # This macro does all the code wrapping. See inline comments. m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[ -FID_ENUM m4_dnl Contents of enum fi_code { ... } -INST_NAME(), -FID_ENUM_STR m4_dnl Contents of const char * indexed by enum fi_code -[INST_NAME()] = "INST_NAME()", -FID_INST m4_dnl Anonymous structure inside struct f_inst -struct { -m4_undivert(101) -} i_[[]]INST_NAME(); -FID_LINE m4_dnl Anonymous structure inside struct f_line_item -struct { -m4_undivert(107) -} i_[[]]INST_NAME(); -FID_NEW m4_dnl Constructor and interpreter code together +FID_ENUM()m4_dnl Contents of enum fi_code { ... } + INST_NAME(), +FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code + [INST_NAME()] = "INST_NAME()", +FID_INST()m4_dnl Anonymous structure inside struct f_inst + struct { +m4_undivert(101)m4_dnl + } i_[[]]INST_NAME(); +FID_LINE()m4_dnl Anonymous structure inside struct f_line_item + struct { +m4_undivert(107)m4_dnl + } i_[[]]INST_NAME(); +FID_NEW()m4_dnl Constructor and interpreter code together FID_HIC( -[[ m4_dnl Public declaration of constructor in H file +[[m4_dnl Public declaration of constructor in H file struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code -m4_undivert(102) +m4_undivert(102)m4_dnl );]], -[[ m4_dnl The one case in The Big Switch inside interpreter +[[m4_dnl The one case in The Big Switch inside interpreter case INST_NAME(): #define whati (&(what->i_]]INST_NAME()[[)) m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]]) @@ -236,7 +237,7 @@ m4_undivert(102) #undef whati break; ]], -[[ m4_dnl Constructor itself +[[m4_dnl Constructor itself struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code m4_undivert(102) ) @@ -259,10 +260,10 @@ m4_undivert(102) } ]]) -FID_DUMP_CALLER m4_dnl Case in another big switch used in instruction dumping (debug) +FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping (debug) case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break; -FID_DUMP m4_dnl The dumper itself +FID_DUMP()m4_dnl The dumper itself m4_ifdef([[FID_DUMP_BODY_EXISTS]], [[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]], [[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]]) @@ -273,7 +274,7 @@ m4_undivert(104) #undef item } -FID_LINEARIZE m4_dnl The linearizer +FID_LINEARIZE()m4_dnl The linearizer case INST_NAME(): { #define whati (&(what->i_]]INST_NAME()[[)) #define item (&(dest->items[pos].i_]]INST_NAME()[[)) @@ -285,7 +286,7 @@ case INST_NAME(): { break; } -FID_SAME m4_dnl This code compares two f_line"s while reconfiguring +FID_SAME()m4_dnl This code compares two f_line"s while reconfiguring case INST_NAME(): #define f1 (&(f1_->i_]]INST_NAME()[[)) #define f2 (&(f2_->i_]]INST_NAME()[[)) @@ -294,7 +295,7 @@ m4_undivert(106) #undef f2 break; -m4_divert(-1)FID_FLUSH(101,200) m4_dnl And finally this flushes all the unused diversions +m4_divert(-1)FID_FLUSH(101,200)m4_dnl And finally this flushes all the unused diversions ]])') m4_define(INST, `m4_dnl This macro is called on beginning of each instruction. @@ -302,7 +303,7 @@ INST_FLUSH()m4_dnl First, old data is flushed m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name, m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl and reset NEVER_CONSTANT trigger. -FID_INTERPRET_BODY() m4_dnl By default, every code is interpreter code. +FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code. ') # 3) Final preparation @@ -480,7 +481,7 @@ FID_WR_PUT(9) FID_WR_DIRECT(H) /* Filter instruction codes */ enum f_instruction_code { -FID_WR_PUT(4) +FID_WR_PUT(4)m4_dnl } PACKED; /* Filter instruction structure for config */ @@ -490,7 +491,7 @@ struct f_inst { int size; /* How many instructions are underneath */ int lineno; /* Line number */ union { - FID_WR_PUT(1) +FID_WR_PUT(1)m4_dnl }; }; @@ -500,7 +501,7 @@ struct f_line_item { enum f_instruction_flags flags; /* Flags, instruction-specific */ uint lineno; /* Where */ union { - FID_WR_PUT(2) +FID_WR_PUT(2)m4_dnl }; }; From 263fa2c4a6bf226172a6aef9a6b8198a5901c5bf Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Tue, 2 Jul 2019 22:57:00 +0200 Subject: [PATCH 3/7] Filter: Dropped some more irrelevant whitespace from generated files --- filter/decl.m4 | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/filter/decl.m4 b/filter/decl.m4 index 26a1a166..42515c13 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -77,19 +77,19 @@ FID_STRUCT_IN()m4_dnl $1 $2; FID_NEW_ARGS()m4_dnl , $1 $2 -FID_NEW_BODY +FID_NEW_BODY()m4_dnl whati->$2 = $2; -FID_LINEARIZE_BODY +FID_LINEARIZE_BODY()m4_dnl item->$2 = whati->$2; m4_ifelse($3,,,[[ -FID_SAME_BODY +FID_SAME_BODY()m4_dnl if ($3) return 0; ]]) m4_ifelse($4,,,[[ -FID_DUMP_BODY +FID_DUMP_BODY()m4_dnl debug("%s$4\n", INDENT, $5); ]]) -FID_INTERPRET_EXEC +FID_INTERPRET_EXEC()m4_dnl const $1 $2 = whati->$2 FID_INTERPRET_BODY') @@ -119,7 +119,7 @@ FID_INTERPRET_BODY()') # Some arguments need to check their type. After that, ARG_ANY is called. m4_define(ARG, `ARG_ANY($1) -FID_INTERPRET_EXEC() +FID_INTERPRET_EXEC()m4_dnl if (v$1.type != $2) runtime("Argument $1 of instruction %s must be of type $2, got 0x%02x", f_instruction_name(what->fi_code), v$1.type)m4_dnl FID_INTERPRET_BODY()') @@ -142,19 +142,19 @@ FID_STRUCT_IN()m4_dnl struct f_inst * f$1; FID_NEW_ARGS()m4_dnl , struct f_inst * f$1 -FID_NEW_BODY +FID_NEW_BODY()m4_dnl whati->f$1 = f$1; -FID_DUMP_BODY +FID_DUMP_BODY()m4_dnl f_dump_line(item->fl$1, indent + 1); -FID_LINEARIZE_BODY +FID_LINEARIZE_BODY()m4_dnl item->fl$1 = f_linearize(whati->f$1); -FID_SAME_BODY +FID_SAME_BODY()m4_dnl if (!f_same(f1->fl$1, f2->fl$1)) return 0; -FID_INTERPRET_EXEC +FID_INTERPRET_EXEC()m4_dnl do { if (whati->fl$1) { LINEX_(whati->fl$1); } } while(0) -FID_INTERPRET_NEW +FID_INTERPRET_NEW()m4_dnl return whati->f$1 FID_INTERPRET_BODY()') @@ -233,13 +233,13 @@ m4_undivert(102)m4_dnl case INST_NAME(): #define whati (&(what->i_]]INST_NAME()[[)) m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]]) - m4_undivert(108) + m4_undivert(108)m4_dnl #undef whati break; ]], [[m4_dnl Constructor itself struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code -m4_undivert(102) +m4_undivert(102)m4_dnl ) { /* Allocate the structure */ @@ -248,14 +248,14 @@ m4_undivert(102) /* Initialize all the members */ #define whati (&(what->i_]]INST_NAME()[[)) - m4_undivert(103) + m4_undivert(103)m4_dnl /* If not constant, return the instruction itself */ FID_IFCONST([[if (!constargs)]]) return what; /* Try to pre-calculate the result */ - FID_IFCONST([[m4_undivert(108)]]) + FID_IFCONST([[m4_undivert(108)]])m4_dnl #undef whati } ]]) @@ -270,7 +270,7 @@ m4_ifdef([[FID_DUMP_BODY_EXISTS]], m4_undefine([[FID_DUMP_BODY_EXISTS]]) { #define item (&(item_->i_]]INST_NAME()[[)) -m4_undivert(104) +m4_undivert(104)m4_dnl #undef item } @@ -278,7 +278,7 @@ FID_LINEARIZE()m4_dnl The linearizer case INST_NAME(): { #define whati (&(what->i_]]INST_NAME()[[)) #define item (&(dest->items[pos].i_]]INST_NAME()[[)) - m4_undivert(105) + m4_undivert(105)m4_dnl #undef whati #undef item dest->items[pos].fi_code = what->fi_code; @@ -290,7 +290,7 @@ FID_SAME()m4_dnl This code compares two f_line"s while reconfiguring case INST_NAME(): #define f1 (&(f1_->i_]]INST_NAME()[[)) #define f2 (&(f2_->i_]]INST_NAME()[[)) -m4_undivert(106) +m4_undivert(106)m4_dnl #undef f1 #undef f2 break; From 78976974e711e52c3b8fa6a80b290cf2fa4f692d Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Wed, 3 Jul 2019 00:00:11 +0200 Subject: [PATCH 4/7] Dynamic attributes definition split whether it is bitmask or not. --- filter/f-inst.h | 6 +++-- filter/f-util.c | 2 +- nest/config.Y | 2 +- proto/babel/config.Y | 2 +- proto/bgp/config.Y | 24 +++++++++--------- proto/ospf/config.Y | 8 +++--- proto/radv/config.Y | 4 +-- proto/rip/config.Y | 4 +-- sysdep/linux/netlink.Y | 56 +++++++++++++++++++++--------------------- sysdep/unix/krt.Y | 4 +-- 10 files changed, 57 insertions(+), 55 deletions(-) diff --git a/filter/f-inst.h b/filter/f-inst.h index 2a2d050e..58a60f0f 100644 --- a/filter/f-inst.h +++ b/filter/f-inst.h @@ -80,8 +80,10 @@ static inline struct f_line *f_linearize(const struct f_inst *root) void f_dump_line(const struct f_line *, uint indent); struct filter *f_new_where(struct f_inst *); -static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ -{ return (struct f_dynamic_attr) { .type = type, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ +static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ +{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ +static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ +{ return (struct f_dynamic_attr) { .type = EAF_TYPE_BITFIELD, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly) { return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; } struct f_inst *f_generate_complex(enum f_instruction_code fi_code, struct f_dynamic_attr da, struct f_inst *argument); diff --git a/filter/f-util.c b/filter/f-util.c index 4b580fb9..b4105aad 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -174,7 +174,7 @@ ca_lookup(pool *p, const char *name, int f_type) } cas = mb_allocz(&root_pool, sizeof(struct ca_storage) + strlen(name) + 1); - cas->fda = f_new_dynamic_attr(ea_type, 0, f_type, EA_CUSTOM(id)); + cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id)); cas->uc = 1; strcpy(cas->name, name); diff --git a/nest/config.Y b/nest/config.Y index e4dedc66..430c9f29 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -791,7 +791,7 @@ proto_patt2: | TEXT { $$.ptr = $1; $$.patt = 1; } ; -dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_GEN_IGP_METRIC); } ; +dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); } ; CF_CODE diff --git a/proto/babel/config.Y b/proto/babel/config.Y index 78175323..b6bc70fa 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -125,7 +125,7 @@ babel_iface_opt_list: babel_iface: babel_iface_start iface_patt_list_nopx babel_iface_opt_list babel_iface_finish; -dynamic_attr: BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_BABEL_METRIC); } ; +dynamic_attr: BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_BABEL_METRIC); } ; CF_CLI_HELP(SHOW BABEL, ..., [[Show information about Babel protocol]]); diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index f9e5efaf..ac8d024a 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -272,29 +272,29 @@ bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end; dynamic_attr: BGP_ORIGIN - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); } ; dynamic_attr: BGP_PATH - { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, 0, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); } ; dynamic_attr: BGP_NEXT_HOP - { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, 0, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); } ; dynamic_attr: BGP_MED - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); } ; dynamic_attr: BGP_LOCAL_PREF - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); } ; dynamic_attr: BGP_ATOMIC_AGGR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, 0, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); } ; dynamic_attr: BGP_AGGREGATOR - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ; dynamic_attr: BGP_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, 0, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); } ; dynamic_attr: BGP_ORIGINATOR_ID - { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, 0, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); } ; dynamic_attr: BGP_CLUSTER_LIST - { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, 0, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ; dynamic_attr: BGP_EXT_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, 0, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ; dynamic_attr: BGP_LARGE_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, 0, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ; diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 66cf60c1..2e9ed0ac 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -498,10 +498,10 @@ ospf_iface: ospf_iface_start ospf_iface_patt_list ospf_iface_opt_list { ospf_iface_finish(); } ; -dynamic_attr: OSPF_METRIC1 { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_OSPF_METRIC1); } ; -dynamic_attr: OSPF_METRIC2 { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_OSPF_METRIC2); } ; -dynamic_attr: OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_OSPF_TAG); } ; -dynamic_attr: OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, 0, T_QUAD, EA_OSPF_ROUTER_ID); } ; +dynamic_attr: OSPF_METRIC1 { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_OSPF_METRIC1); } ; +dynamic_attr: OSPF_METRIC2 { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_OSPF_METRIC2); } ; +dynamic_attr: OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_OSPF_TAG); } ; +dynamic_attr: OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_OSPF_ROUTER_ID); } ; CF_CLI_HELP(SHOW OSPF, ..., [[Show information about OSPF protocol]]); CF_CLI(SHOW OSPF, optproto, [], [[Show information about OSPF protocol]]) diff --git a/proto/radv/config.Y b/proto/radv/config.Y index b8eeb439..53715f77 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -332,8 +332,8 @@ radv_sensitive: | SENSITIVE bool { $$ = $2; } ; -dynamic_attr: RA_PREFERENCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_ENUM_RA_PREFERENCE, EA_RA_PREFERENCE); } ; -dynamic_attr: RA_LIFETIME { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_RA_LIFETIME); } ; +dynamic_attr: RA_PREFERENCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_RA_PREFERENCE, EA_RA_PREFERENCE); } ; +dynamic_attr: RA_LIFETIME { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_RA_LIFETIME); } ; CF_CODE diff --git a/proto/rip/config.Y b/proto/rip/config.Y index 4ab793d1..5b5f94a0 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -186,8 +186,8 @@ rip_iface: rip_iface_start iface_patt_list_nopx rip_iface_opt_list rip_iface_finish; -dynamic_attr: RIP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_RIP_METRIC); } ; -dynamic_attr: RIP_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_RIP_TAG); } ; +dynamic_attr: RIP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_RIP_METRIC); } ; +dynamic_attr: RIP_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_RIP_TAG); } ; CF_CLI_HELP(SHOW RIP, ..., [[Show information about RIP protocol]]); diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index 8f0a91c1..7097f577 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -26,39 +26,39 @@ kern_sys_item: | METRIC expr { THIS_KRT->sys.metric = $2; } ; -dynamic_attr: KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, 0, T_IP, EA_KRT_PREFSRC); } ; -dynamic_attr: KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_REALM); } ; -dynamic_attr: KRT_SCOPE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_SCOPE); } ; +dynamic_attr: KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); } ; +dynamic_attr: KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); } ; +dynamic_attr: KRT_SCOPE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SCOPE); } ; -dynamic_attr: KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_MTU); } ; -dynamic_attr: KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_WINDOW); } ; -dynamic_attr: KRT_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_RTT); } ; -dynamic_attr: KRT_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_RTTVAR); } ; -dynamic_attr: KRT_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_SSTRESH); } ; -dynamic_attr: KRT_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_CWND); } ; -dynamic_attr: KRT_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_ADVMSS); } ; -dynamic_attr: KRT_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_REORDERING); } ; -dynamic_attr: KRT_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_HOPLIMIT); } ; -dynamic_attr: KRT_INITCWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_INITCWND); } ; -dynamic_attr: KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_RTO_MIN); } ; -dynamic_attr: KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_INITRWND); } ; -dynamic_attr: KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_QUICKACK); } ; +dynamic_attr: KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU); } ; +dynamic_attr: KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); } ; +dynamic_attr: KRT_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTT); } ; +dynamic_attr: KRT_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTTVAR); } ; +dynamic_attr: KRT_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SSTRESH); } ; +dynamic_attr: KRT_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_CWND); } ; +dynamic_attr: KRT_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_ADVMSS); } ; +dynamic_attr: KRT_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REORDERING); } ; +dynamic_attr: KRT_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_HOPLIMIT); } ; +dynamic_attr: KRT_INITCWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITCWND); } ; +dynamic_attr: KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTO_MIN); } ; +dynamic_attr: KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITRWND); } ; +dynamic_attr: KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_QUICKACK); } ; /* Bits of EA_KRT_LOCK, based on RTAX_* constants */ -dynamic_attr: KRT_LOCK_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 2, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 3, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 4, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 5, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 6, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 7, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 8, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 9, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 10, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 13, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_MTU { $$ = f_new_dynamic_attr_bit(2, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr_bit(3, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_RTT { $$ = f_new_dynamic_attr_bit(4, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr_bit(5, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr_bit(6, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_CWND { $$ = f_new_dynamic_attr_bit(7, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr_bit(8, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr_bit(9, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr_bit(10, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr_bit(13, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_FEATURE_ECN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 0, T_BOOL, EA_KRT_FEATURES); } ; -dynamic_attr: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, 3, T_BOOL, EA_KRT_FEATURES); } ; +dynamic_attr: KRT_FEATURE_ECN { $$ = f_new_dynamic_attr_bit(0, T_BOOL, EA_KRT_FEATURES); } ; +dynamic_attr: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(3, T_BOOL, EA_KRT_FEATURES); } ; CF_CODE diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y index e3f6271c..95b54d65 100644 --- a/sysdep/unix/krt.Y +++ b/sysdep/unix/krt.Y @@ -122,8 +122,8 @@ kif_iface: kif_iface_start iface_patt_list_nopx kif_iface_opt_list; -dynamic_attr: KRT_SOURCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_SOURCE); } ; -dynamic_attr: KRT_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_KRT_METRIC); } ; +dynamic_attr: KRT_SOURCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SOURCE); } ; +dynamic_attr: KRT_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_METRIC); } ; CF_CODE From 3265c9169dfc185ccdb787c6c83d9d8e2d0429c1 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Wed, 3 Jul 2019 00:04:24 +0200 Subject: [PATCH 5/7] Removed obsolete comment at as_path_cut() --- nest/a-path.c | 1 - 1 file changed, 1 deletion(-) diff --git a/nest/a-path.c b/nest/a-path.c index 62369af3..a1b7c42f 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -271,7 +271,6 @@ as_path_to_old(struct linpool *pool, const struct adata *path) /* * Cut the path to the length @num, measured to the usual path metric. Note that * AS_CONFED_* segments have zero length and must be added if they are on edge. - * In contrast to other as_path_* functions, @path is modified in place. */ struct adata * as_path_cut(struct linpool *pool, const struct adata *path, uint num) From 0206c070ace90c48a806a74fac52ba6e6ff9858b Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Wed, 3 Jul 2019 01:23:49 +0200 Subject: [PATCH 6/7] Filter: Split printing and dying --- conf/confbase.Y | 4 +++- filter/config.Y | 51 ++++++++++++++++++++++++++++++++++--------------- filter/decl.m4 | 19 ++++++++++++++++++ filter/f-inst.c | 43 ++++++++++++++++++++++------------------- filter/f-util.c | 8 ++++---- 5 files changed, 85 insertions(+), 40 deletions(-) diff --git a/conf/confbase.Y b/conf/confbase.Y index bcfd3f1a..775af22d 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -64,7 +64,9 @@ CF_DECLS struct rtable_config *r; struct channel_config *cc; struct f_inst *x; - struct f_inst *xp[2]; + struct { + struct f_inst *begin, *end; + } xp; enum filter_return fret; enum ec_subtype ecs; struct f_dynamic_attr fda; diff --git a/filter/config.Y b/filter/config.Y index ff2b966e..c40f28d4 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -447,7 +447,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc ELSE %type cmds_int -%type term block cmd cmds constant constructor print_one print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail +%type term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail %type dynamic_attr %type static_attr %type filter where_filter @@ -621,11 +621,21 @@ function_def: /* Programs */ cmds: /* EMPTY */ { $$ = NULL; } - | cmds_int { $$ = $1[0]; } + | cmds_int { $$ = $1.begin; } ; -cmds_int: cmd { $$[0] = $$[1] = $1; } - | cmds_int cmd { $$[1] = $2; $1[1]->next = $2; $$[0] = $1[0]; } +cmds_int: cmd { + $$.begin = $$.end = $1; + while ($$.end->next) + $$.end = $$.end->next; + } + | cmds_int cmd { + $$.begin = $1.begin; + $1.end->next = $2; + $$.end = $2; + while ($$.end->next) + $$.end = $$.end->next; + } ; block: @@ -960,17 +970,13 @@ break_command: | PRINTN { $$ = F_NONL; } ; -print_one: - term { $$ = f_new_inst(FI_PRINT, $1); } - ; - print_list: /* EMPTY */ { $$ = NULL; } - | print_one { $$ = $1; } - | print_one ',' print_list { - if ($1) { - $1->next = $3; - $$ = $1; - } else $$ = $3; + | term { $$ = $1; } + | term ',' print_list { + ASSERT($1); + ASSERT($1->next == NULL); + $1->next = $3; + $$ = $1; } ; @@ -1011,7 +1017,22 @@ cmd: | UNSET '(' dynamic_attr ')' ';' { $$ = f_new_inst(FI_EA_UNSET, $3); } - | break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE, $2, $1); } + | break_command print_list ';' { + struct f_inst *breaker = NULL; + struct f_inst *printer = NULL; + if ($2) + printer = f_new_inst(FI_PRINT, $2); + if ($1 != F_NONL) + breaker = f_new_inst(FI_DIE, $1); + + if (printer && breaker) + printer->next = breaker; + + if (printer) + $$ = printer; + else + $$ = breaker; + } | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); } | CASE term '{' switch_body '}' { $$ = f_new_inst(FI_SWITCH, $2, build_tree($4)); diff --git a/filter/decl.m4 b/filter/decl.m4 index 42515c13..9bd9a140 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -93,6 +93,25 @@ FID_INTERPRET_EXEC()m4_dnl const $1 $2 = whati->$2 FID_INTERPRET_BODY') +m4_define(FID_MEMBER_IN, `m4_dnl +FID_LINE_IN()m4_dnl + $1 $2; +FID_STRUCT_IN()m4_dnl + $1 $2; +FID_LINEARIZE_BODY()m4_dnl +item->$2 = whati->$2; +m4_ifelse($3,,,[[ +FID_SAME_BODY()m4_dnl +if ($3) return 0; +]]) +m4_ifelse($4,,,[[ +FID_DUMP_BODY()m4_dnl +debug("%s$4\n", INDENT, $5); +]]) +FID_INTERPRET_EXEC()m4_dnl +const $1 $2 = whati->$2 +FID_INTERPRET_BODY') + # Instruction arguments are needed only until linearization is done. # This puts the arguments into the filter line to be executed before # the instruction itself. diff --git a/filter/f-inst.c b/filter/f-inst.c index 5f30ee38..8d5c6656 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -173,7 +173,7 @@ if (fstk->vcnt < whati->count) /* TODO: make this check systematic */ runtime("Construction of BGP path mask from %u elements must have at least that number of elements", whati->count); -#define pv fstk->vstk[fstk->vcnt - count + i] +#define pv fstk->vstk[fstk->vcnt - whati->count + i] FID_INTERPRET_NEW #define pv items[i]->i_FI_CONSTANT.val @@ -320,11 +320,6 @@ RESULT_VAL(val); } - INST(FI_PRINT, 1, 0) { - NEVER_CONSTANT; - ARG_ANY(1); - val_format(&(v1), &fs->buf); - } INST(FI_CONDITION, 1, 0) { ARG(1, T_BOOL); if (v1.val.i) @@ -332,28 +327,37 @@ else LINE(3,1); } - INST(FI_PRINT_AND_DIE, 0, 0) { + + INST(FI_PRINT, 0, 0) { NEVER_CONSTANT; - FID_LINEARIZE_BODY - { - uint opos = pos; - FID_INTERPRET_BODY - ARG_ANY(1); + FID_MEMBER_IN(uint, count, f1->count != f2->count, number of items %u, item->count); + + FID_NEW_BODY + uint len = 0; + for (const struct f_inst *tt = f1; tt; tt = tt->next, len++) + ; + whati->count = len; - FID_LINEARIZE_BODY - if (opos < pos) - dest->items[pos].flags |= FIF_PRINTED; - } FID_INTERPRET_BODY +#define pv fstk->vstk[fstk->vcnt - whati->count + i] + if (whati->count) + for (uint i=0; icount; i++) + val_format(&(pv), &fs->buf); +#undef pv + + fstk->vcnt -= whati->count; + } + + INST(FI_DIE, 0, 0) { + NEVER_CONSTANT; FID_MEMBER(enum filter_return, fret, f1->fret != f2->fret, %s, filter_return_str(item->fret)); - if ((fret == F_NOP || (fret != F_NONL && (what->flags & FIF_PRINTED))) && - !(fs->flags & FF_SILENT)) + if (fs->buf.start < fs->buf.pos) log_commit(*L_INFO, &fs->buf); - switch (fret) { + switch (whati->fret) { case F_QUITBIRD: die( "Filter asked me to die" ); case F_ACCEPT: @@ -361,7 +365,6 @@ case F_ERROR: case F_REJECT: /* FIXME (noncritical) Should print complete route along with reason to reject route */ return fret; /* We have to return now, no more processing. */ - case F_NONL: case F_NOP: break; default: diff --git a/filter/f-util.c b/filter/f-util.c index b4105aad..e61949f2 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -33,17 +33,17 @@ filter_name(const struct filter *filter) struct filter *f_new_where(struct f_inst *where) { struct f_inst acc = { - .fi_code = FI_PRINT_AND_DIE, + .fi_code = FI_DIE, .lineno = ifs->lino, .size = 1, - .i_FI_PRINT_AND_DIE = { .fret = F_ACCEPT, }, + .i_FI_DIE = { .fret = F_ACCEPT, }, }; struct f_inst rej = { - .fi_code = FI_PRINT_AND_DIE, + .fi_code = FI_DIE, .lineno = ifs->lino, .size = 1, - .i_FI_PRINT_AND_DIE = { .fret = F_REJECT, }, + .i_FI_DIE = { .fret = F_REJECT, }, }; struct f_inst i = { From 84ac62d3967f5294f4dc208b803a273a101744a8 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Wed, 3 Jul 2019 08:13:07 +0200 Subject: [PATCH 7/7] Filter: CLI command to dump all the linearized filters --- filter/filter.c | 38 ++++++++++++++++++++++++++++++++++++++ filter/filter.h | 6 ++++-- nest/config.Y | 2 ++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index ed0b21bc..d9c98872 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -482,3 +482,41 @@ filter_commit(struct config *new, struct config *old) break; } } + +void filters_dump_all(void) +{ + struct symbol *sym; + WALK_LIST(sym, config->symbols) { + switch (sym->class) { + case SYM_FILTER: + debug("Named filter %s:\n", sym->name); + f_dump_line(sym->filter->root, 1); + break; + case SYM_FUNCTION: + debug("Function %s:\n", sym->name); + f_dump_line(sym->function, 1); + break; + case SYM_PROTO: + { + debug("Protocol %s:\n", sym->name); + struct channel *c; + WALK_LIST(c, sym->proto->proto->channels) { + debug(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]); + if (c->in_filter == FILTER_ACCEPT) + debug(" ALL\n"); + else if (c->in_filter == FILTER_REJECT) + debug(" NONE\n"); + else if (c->in_filter == FILTER_UNDEF) + debug(" UNDEF\n"); + else if (c->in_filter->sym) { + ASSERT(c->in_filter->sym->filter == c->in_filter); + debug(" named filter %s\n", c->in_filter->sym->name); + } else { + debug("\n"); + f_dump_line(c->in_filter->root, 2); + } + } + } + } + } +} diff --git a/filter/filter.h b/filter/filter.h index 36b63e7c..9d997efb 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -64,9 +64,11 @@ int f_same(const struct f_line *f1, const struct f_line *f2); void filter_commit(struct config *new, struct config *old); +void filters_dump_all(void); + #define FILTER_ACCEPT NULL -#define FILTER_REJECT ((void *) 1) -#define FILTER_UNDEF ((void *) 2) /* Used in BGP */ +#define FILTER_REJECT ((struct filter *) 1) +#define FILTER_UNDEF ((struct filter *) 2) /* Used in BGP */ #define FF_SILENT 2 /* Silent filter execution */ diff --git a/nest/config.Y b/nest/config.Y index 430c9f29..e97b8fb3 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -731,6 +731,8 @@ CF_CLI(DUMP ROUTES,,, [[Dump routing table]]) { rt_dump_all(); cli_msg(0, ""); } ; CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]]) { protos_dump_all(); cli_msg(0, ""); } ; +CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]]) +{ filters_dump_all(); cli_msg(0, ""); } ; CF_CLI(EVAL, term, , [[Evaluate an expression]]) { cmd_eval(f_linearize($2)); } ;