9b8ffd99fb
Change-Id: If2d57a213fc2fec996db26df332910c3d2105f97 Signed-off-by: Dave Barach <dave@barachs.net>
528 lines
16 KiB
C
528 lines
16 KiB
C
/*
|
|
* Copyright (c) 2015 Cisco and/or its affiliates.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include <vlib/parse.h>
|
|
#include <vlib/unix/unix.h>
|
|
|
|
static u8 *
|
|
format_value_v4_address (u8 * s, va_list * args)
|
|
{
|
|
vlib_parse_value_t *v = va_arg (*args, vlib_parse_value_t *);
|
|
u32 a = v->value.as_uword;
|
|
|
|
s = format (s, "%d.%d.%d.%d",
|
|
(a >> 24) & 0xFF,
|
|
(a >> 16) & 0xFF, (a >> 8) & 0xFF, (a >> 0) & 0xFF);
|
|
|
|
return s;
|
|
}
|
|
|
|
static vlib_parse_match_t
|
|
v4_address_match (vlib_parse_main_t * pm, vlib_parse_type_t * type,
|
|
vlib_lex_token_t * t, vlib_parse_value_t * valuep)
|
|
{
|
|
u32 digit;
|
|
u32 value = 0;
|
|
int i;
|
|
|
|
if (vec_len (pm->tokens) - (t - pm->tokens) < 7)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
|
|
/* NUMBER DOT NUMBER DOT NUMBER DOT NUMBER */
|
|
|
|
for (i = 0; i < 7; i++)
|
|
{
|
|
if ((i & 1) == 0)
|
|
{
|
|
if (t[i].token != VLIB_LEX_number)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
if (t[i].value.as_uword > 0xff)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
digit = t[i].value.as_uword;
|
|
value = (value << 8) | digit;
|
|
}
|
|
else
|
|
{
|
|
if (t[i].token != VLIB_LEX_dot)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
}
|
|
}
|
|
/* note: caller advances by 1 */
|
|
pm->current_token_index += 6;
|
|
valuep->value.as_uword = value;
|
|
return VLIB_PARSE_MATCH_VALUE;
|
|
}
|
|
|
|
PARSE_TYPE_INIT (v4_address, v4_address_match, 0, format_value_v4_address)
|
|
static u8 *format_value_v4_address_and_mask (u8 * s, va_list * args)
|
|
{
|
|
vlib_parse_value_t *v = va_arg (*args, vlib_parse_value_t *);
|
|
u32 *a = v->value.as_pointer;
|
|
|
|
s = format (s, "%d.%d.%d.%d",
|
|
(a[0] >> 24) & 0xFF,
|
|
(a[0] >> 16) & 0xFF, (a[0] >> 8) & 0xFF, (a[0] >> 0) & 0xFF);
|
|
s = format (s, "/%d", a[1]);
|
|
|
|
return s;
|
|
}
|
|
|
|
static vlib_parse_match_t
|
|
v4_address_and_mask_match (vlib_parse_main_t * pm, vlib_parse_type_t * type,
|
|
vlib_lex_token_t * t, vlib_parse_value_t * valuep)
|
|
{
|
|
u32 digit;
|
|
u32 address = 0;
|
|
u32 *rv = 0;
|
|
int i;
|
|
|
|
if (vec_len (pm->tokens) - (t - pm->tokens) < 9)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
|
|
/* NUMBER DOT NUMBER DOT NUMBER DOT NUMBER */
|
|
|
|
for (i = 0; i < 7; i++)
|
|
{
|
|
if ((i & 1) == 0)
|
|
{
|
|
if (t[i].token != VLIB_LEX_number)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
if (t[i].value.as_uword > 0xff)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
digit = t[i].value.as_uword;
|
|
address = (address << 8) | digit;
|
|
}
|
|
else
|
|
{
|
|
if (t[i].token != VLIB_LEX_dot)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
}
|
|
}
|
|
|
|
if (t[7].token != VLIB_LEX_slash || t[8].token != VLIB_LEX_number)
|
|
return VLIB_PARSE_MATCH_FAIL;
|
|
|
|
vec_add1 (rv, address);
|
|
vec_add1 (rv, t[8].value.as_uword);
|
|
|
|
/* note: caller advances by 1 */
|
|
pm->current_token_index += 8;
|
|
valuep->value.as_pointer = rv;
|
|
return VLIB_PARSE_MATCH_VALUE;
|
|
}
|
|
|
|
void
|
|
v4_address_and_mask_cleanup (vlib_parse_value_t * valuep)
|
|
{
|
|
u32 *trash = valuep->value.as_pointer;
|
|
vec_free (trash);
|
|
}
|
|
|
|
PARSE_TYPE_INIT (v4_address_and_mask, v4_address_and_mask_match,
|
|
v4_address_and_mask_cleanup,
|
|
format_value_v4_address_and_mask)
|
|
vlib_lex_main_t vlib_lex_main;
|
|
|
|
|
|
|
|
vlib_parse_match_t eval_factor0 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item,
|
|
vlib_parse_value_t * value)
|
|
{
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_factor1 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_factor2 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
word a;
|
|
int index = vec_len (pm->parse_value) - 1;
|
|
|
|
a = pm->parse_value[index].value.as_word;
|
|
|
|
pm->parse_value[index].value.as_word = -a;
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_term0 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_term1 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
uword a, b;
|
|
int index = vec_len (pm->parse_value) - 2;
|
|
|
|
a = pm->parse_value[index].value.as_uword;
|
|
b = pm->parse_value[index + 1].value.as_uword;
|
|
|
|
pm->parse_value[index].value.as_uword = a * b;
|
|
_vec_len (pm->parse_value) -= 1;
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_term2 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
uword a, b;
|
|
int index = vec_len (pm->parse_value) - 2;
|
|
|
|
a = pm->parse_value[index].value.as_uword;
|
|
b = pm->parse_value[index + 1].value.as_uword;
|
|
|
|
pm->parse_value[index].value.as_uword = a / b;
|
|
_vec_len (pm->parse_value) -= 1;
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_exp0 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_exp1 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
uword a, b;
|
|
int index = vec_len (pm->parse_value) - 2;
|
|
|
|
a = pm->parse_value[index].value.as_uword;
|
|
b = pm->parse_value[index + 1].value.as_uword;
|
|
|
|
pm->parse_value[index].value.as_uword = a + b;
|
|
_vec_len (pm->parse_value) -= 1;
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_exp2 (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
uword a, b;
|
|
int index = vec_len (pm->parse_value) - 2;
|
|
|
|
a = pm->parse_value[index].value.as_uword;
|
|
b = pm->parse_value[index + 1].value.as_uword;
|
|
|
|
pm->parse_value[index].value.as_uword = a - b;
|
|
_vec_len (pm->parse_value) -= 1;
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
eval_result (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
return VLIB_PARSE_MATCH_DONE;
|
|
}
|
|
|
|
vlib_parse_match_t
|
|
noop_match_rule (vlib_parse_main_t * pm,
|
|
vlib_parse_item_t * item, vlib_parse_value_t * value)
|
|
{
|
|
clib_warning ("%U", format_vlib_parse_value, pm);
|
|
return VLIB_PARSE_MATCH_RULE;
|
|
}
|
|
|
|
#if 0
|
|
PARSE_INIT (t1, "moo", eval0);
|
|
PARSE_INIT (t2, "moo cow mumble", eval1);
|
|
PARSE_INIT (t3, "moo cow", eval2);
|
|
PARSE_INIT (t4, "moo cow mumble grunch", eval3);
|
|
#endif
|
|
|
|
#if 0
|
|
PARSE_INIT (r1, "eval <exp>", eval_result);
|
|
|
|
PARSE_INIT (r2, "<exp> = <term><exp2>", eval_exp0);
|
|
PARSE_INIT (r3, "<exp2> = <plus> <exp>", eval_exp1);
|
|
PARSE_INIT (r4, "<exp2> = <minus> <exp>", eval_exp2);
|
|
PARSE_INIT (r5, "<exp2> = ", noop_match_rule);
|
|
PARSE_TYPE_INIT (exp, rule_match, 0, 0);
|
|
PARSE_TYPE_INIT (exp2, rule_match, 0, 0);
|
|
|
|
PARSE_INIT (r6, "<term> = <factor><term2>", eval_term0);
|
|
PARSE_INIT (r7, "<term2> = <star> <term>", eval_term1);
|
|
PARSE_INIT (r8, "<term2> = <slash> <term>", eval_term2);
|
|
PARSE_INIT (r9, "<term2> = ", noop_match_rule);
|
|
PARSE_TYPE_INIT (term, rule_match, 0, 0);
|
|
PARSE_TYPE_INIT (term2, rule_match, 0, 0);
|
|
|
|
PARSE_INIT (r11, "<factor> = <lpar> <exp> <rpar>", eval_factor1);
|
|
PARSE_INIT (r10, "<factor> = <number>", eval_factor0);
|
|
PARSE_INIT (r12, "<factor> = <minus> <factor>", eval_factor2);
|
|
|
|
PARSE_TYPE_INIT (factor, rule_match, 0, 0);
|
|
#endif
|
|
|
|
PARSE_INIT (r1, "eval <exp>", eval_result);
|
|
|
|
#if 1
|
|
PARSE_INIT (r2, "<exp> = <term><exp2>", eval_exp0);
|
|
PARSE_INIT (r3, "<exp2> = <plus> <exp>", eval_exp1);
|
|
PARSE_INIT (r4, "<exp2> = <minus> <exp>", eval_exp2);
|
|
PARSE_INIT (r5, "<exp2> = ", noop_match_rule);
|
|
PARSE_TYPE_INIT (exp, rule_match, 0, 0);
|
|
PARSE_TYPE_INIT (exp2, rule_match, 0, 0);
|
|
|
|
PARSE_INIT (r6, "<term> = <factor><term2>", eval_term0);
|
|
PARSE_INIT (r7, "<term2> = <star> <term>", eval_term1);
|
|
PARSE_INIT (r8, "<term2> = <slash> <term>", eval_term2);
|
|
PARSE_INIT (r9, "<term2> = ", noop_match_rule);
|
|
PARSE_TYPE_INIT (term, rule_match, 0, 0);
|
|
PARSE_TYPE_INIT (term2, rule_match, 0, 0);
|
|
|
|
PARSE_INIT (r11, "<factor> = <lpar> <exp> <rpar>", eval_factor1);
|
|
PARSE_INIT (r10, "<factor> = <number>", eval_factor0);
|
|
PARSE_INIT (r12, "<factor> = <minus> <factor>", eval_factor2);
|
|
|
|
PARSE_TYPE_INIT (factor, rule_match, 0, 0);
|
|
#endif
|
|
|
|
#if 0
|
|
PARSE_TYPE_INIT (exp, rule_match, 0, 0);
|
|
PARSE_INIT (r6, "<exp> = a b", eval_term0);
|
|
PARSE_INIT (r7, "<exp> = c d", eval_term1);
|
|
PARSE_INIT (r9, "<exp> = ", noop_match_rule);
|
|
#endif
|
|
|
|
#if 0
|
|
#define foreach_rule_evaluator \
|
|
_(0) \
|
|
_(1) \
|
|
_(2) \
|
|
_(3)
|
|
|
|
#define _(n) \
|
|
vlib_parse_match_t eval##n (vlib_parse_main_t *pm, \
|
|
vlib_parse_item_t *item, \
|
|
vlib_parse_value_t *value) \
|
|
{ \
|
|
clib_warning ("%U", format_vlib_parse_value, pm); \
|
|
return VLIB_PARSE_MATCH_DONE; \
|
|
}
|
|
foreach_rule_evaluator
|
|
#undef _
|
|
PARSE_INIT (r1, "eval <moo>", eval_result);
|
|
|
|
PARSE_INIT (r2, "<moo> = cow", eval0);
|
|
PARSE_INIT (r4, "<moo> = ", eval1);
|
|
PARSE_TYPE_INIT (moo, rule_match, 0, 0);
|
|
#endif
|
|
|
|
|
|
clib_error_t *
|
|
test_init (vlib_main_t * vm)
|
|
{
|
|
clib_error_t *error;
|
|
|
|
if ((error = vlib_call_init_function (vm, parse_init)))
|
|
return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VLIB_INIT_FUNCTION (test_init);
|
|
|
|
clib_error_t *
|
|
vlib_stdlex_init (vlib_main_t * vm)
|
|
{
|
|
vlib_lex_main_t *lm = &vlib_lex_main;
|
|
u16 top_index;
|
|
u16 slash_index, slash_star_index, slash_slash_index, slash_star_star_index;
|
|
u16 slash_token;
|
|
u16 word_index;
|
|
u16 zero_index, octal_index, decimal_index, hex_index, binary_index;
|
|
|
|
top_index = vlib_lex_add_table ("top");
|
|
|
|
#define foreach_top_level_single_character_token \
|
|
_('(', lpar) \
|
|
_(')', rpar) \
|
|
_(';', semi) \
|
|
_('[', lbrack) \
|
|
_(']', rbrack) \
|
|
_('{', lcurly) \
|
|
_('}', rcurly) \
|
|
_('+', plus) \
|
|
_('-', minus) \
|
|
_('*', star) \
|
|
_('%', percent) \
|
|
_('@', atsign) \
|
|
_(',', comma) \
|
|
_('.', dot) \
|
|
_('?', qmark)
|
|
|
|
#define _(c,t) \
|
|
vlib_lex_set_action_range(top_index,c,c,VLIB_LEX_RETURN,vlib_lex_add_token(lm, #t), top_index);
|
|
foreach_top_level_single_character_token;
|
|
#undef _
|
|
|
|
/* Numbers */
|
|
zero_index = vlib_lex_add_table ("zero");
|
|
octal_index = vlib_lex_add_table ("octal");
|
|
decimal_index = vlib_lex_add_table ("decimal");
|
|
hex_index = vlib_lex_add_table ("hex");
|
|
binary_index = vlib_lex_add_table ("binary");
|
|
|
|
/* Support 0x 0b 0t and 0123 [octal] */
|
|
vlib_lex_set_action_range (top_index, '0', '0', VLIB_LEX_START_NUMBER, 10,
|
|
zero_index);
|
|
vlib_lex_set_action_range (top_index, '1', '9', VLIB_LEX_START_NUMBER, 10,
|
|
decimal_index);
|
|
|
|
vlib_lex_set_action_range (zero_index, 0, 0x7F, VLIB_LEX_RETURN_AND_RESCAN,
|
|
VLIB_LEX_number, top_index);
|
|
|
|
vlib_lex_set_action_range (zero_index, 'x', 'x', VLIB_LEX_IGNORE, ~0,
|
|
hex_index);
|
|
vlib_lex_set_action_range (zero_index, 'b', 'b', VLIB_LEX_IGNORE, ~0,
|
|
binary_index);
|
|
vlib_lex_set_action_range (zero_index, 't', 't', VLIB_LEX_IGNORE, ~0,
|
|
decimal_index);
|
|
vlib_lex_set_action_range (zero_index, '0', '7', VLIB_LEX_START_NUMBER, 8,
|
|
octal_index);
|
|
|
|
/* Octal */
|
|
vlib_lex_set_action_range (octal_index, 0, 0x7f, VLIB_LEX_RETURN_AND_RESCAN,
|
|
VLIB_LEX_number, top_index);
|
|
vlib_lex_set_action_range (octal_index, '0', '7', VLIB_LEX_ADD_TO_NUMBER, 8,
|
|
octal_index);
|
|
|
|
/* Decimal */
|
|
vlib_lex_set_action_range (decimal_index, 0, 0x7f,
|
|
VLIB_LEX_RETURN_AND_RESCAN, VLIB_LEX_number,
|
|
top_index);
|
|
vlib_lex_set_action_range (decimal_index, '0', '9', VLIB_LEX_ADD_TO_NUMBER,
|
|
10, decimal_index);
|
|
|
|
/* Hex */
|
|
vlib_lex_set_action_range (hex_index, 0, 0x7f, VLIB_LEX_RETURN_AND_RESCAN,
|
|
VLIB_LEX_number, top_index);
|
|
vlib_lex_set_action_range (hex_index, '0', '9', VLIB_LEX_ADD_TO_NUMBER, 16,
|
|
hex_index);
|
|
vlib_lex_set_action_range (hex_index, 'a', 'f', VLIB_LEX_ADD_TO_NUMBER, 16,
|
|
hex_index);
|
|
vlib_lex_set_action_range (hex_index, 'A', 'F', VLIB_LEX_ADD_TO_NUMBER, 16,
|
|
hex_index);
|
|
|
|
/* Binary */
|
|
vlib_lex_set_action_range (binary_index, 0, 0x7f,
|
|
VLIB_LEX_RETURN_AND_RESCAN, VLIB_LEX_number,
|
|
top_index);
|
|
vlib_lex_set_action_range (binary_index, '0', '1', VLIB_LEX_ADD_TO_NUMBER,
|
|
2, binary_index);
|
|
|
|
/* c/c++ comment syntax is the worst... */
|
|
|
|
slash_index = vlib_lex_add_table ("slash");
|
|
slash_star_index = vlib_lex_add_table ("slash_star");
|
|
slash_star_star_index = vlib_lex_add_table ("slash_star_star");
|
|
slash_slash_index = vlib_lex_add_table ("slash_slash");
|
|
slash_token = vlib_lex_add_token (lm, "slash");
|
|
|
|
/* Top level: see a slash, ignore, go to slash table */
|
|
vlib_lex_set_action_range (top_index, '/', '/', VLIB_LEX_IGNORE, ~0,
|
|
slash_index);
|
|
|
|
/* default for slash table: return SLASH, go to top table */
|
|
vlib_lex_set_action_range (slash_index, 1, 0x7F, VLIB_LEX_RETURN_AND_RESCAN,
|
|
slash_token, top_index);
|
|
/* see slash-slash, go to s-s table */
|
|
vlib_lex_set_action_range (slash_index, '/', '/', VLIB_LEX_IGNORE, ~0,
|
|
slash_slash_index);
|
|
/* see slash-star, go to s-* table */
|
|
vlib_lex_set_action_range (slash_index, '*', '*', VLIB_LEX_IGNORE, ~0,
|
|
slash_star_index);
|
|
|
|
/* EOL in s-s table, ignore, go to top table */
|
|
vlib_lex_set_action_range (slash_slash_index, '\n', '\n', VLIB_LEX_IGNORE,
|
|
~0, top_index);
|
|
|
|
/* slash-star blah blah star */
|
|
vlib_lex_set_action_range (slash_star_index, '*', '*', VLIB_LEX_IGNORE, ~0,
|
|
slash_star_star_index);
|
|
|
|
/* slash star blah blah star slash */
|
|
vlib_lex_set_action_range (slash_star_star_index, '/', '/', VLIB_LEX_IGNORE,
|
|
~0, top_index);
|
|
|
|
/* LT, =, GT */
|
|
vlib_lex_set_action_range (top_index, '<', '<', VLIB_LEX_RETURN,
|
|
VLIB_LEX_lt, top_index);
|
|
vlib_lex_set_action_range (top_index, '=', '=', VLIB_LEX_RETURN,
|
|
VLIB_LEX_equals, top_index);
|
|
vlib_lex_set_action_range (top_index, '>', '>', VLIB_LEX_RETURN,
|
|
VLIB_LEX_gt, top_index);
|
|
|
|
/* words, key and otherwise */
|
|
word_index = vlib_lex_add_table ("word");
|
|
|
|
vlib_lex_set_action_range (top_index, 'a', 'z', VLIB_LEX_ADD_TO_TOKEN, ~0,
|
|
word_index);
|
|
vlib_lex_set_action_range (top_index, 'A', 'Z', VLIB_LEX_ADD_TO_TOKEN, ~0,
|
|
word_index);
|
|
|
|
vlib_lex_set_action_range (word_index, 0, 0x7f, VLIB_LEX_KEYWORD_CHECK, ~0,
|
|
top_index);
|
|
|
|
vlib_lex_set_action_range (word_index, 'a', 'z', VLIB_LEX_ADD_TO_TOKEN, ~0,
|
|
word_index);
|
|
vlib_lex_set_action_range (word_index, 'A', 'Z', VLIB_LEX_ADD_TO_TOKEN, ~0,
|
|
word_index);
|
|
vlib_lex_set_action_range (word_index, '_', '_', VLIB_LEX_ADD_TO_TOKEN, ~0,
|
|
word_index);
|
|
vlib_lex_set_action_range (word_index, '0', '9', VLIB_LEX_ADD_TO_TOKEN, ~0,
|
|
word_index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|