Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sebastian Schrader
Kea
Commits
39919c3b
Commit
39919c3b
authored
Dec 06, 2016
by
Tomek Mrugalski
🛰
Browse files
[5036] Code cleanup
parent
4204443a
Changes
6
Hide whitespace changes
Inline
Side-by-side
src/bin/dhcp6/dhcp6_lexer.ll
View file @
39919c3b
...
...
@@ -102,29 +102,28 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
if
(
start_token_flag
)
{
start_token_flag
=
false
;
switch
(
start_token_value
)
{
case
Parser6Context:
:
PARSER_
GENERIC_
JSON:
case
Parser6Context:
:
SUB
PARSER_JSON:
default:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_TOPLEVEL_GENERIC_JSON
(
driver
.
loc_
)
;
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_JSON
(
driver
.
loc_
)
;
//
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_TOPLEVEL_GENERIC_JSON
(
driver
.
loc_
)
;
case
Parser6Context:
:
PARSER_DHCP6:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_TOPLEVEL_DHCP6
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUBPARSER_DHCP6:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_DHCP6
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUB
PARSER_INTERFACES
6
:
case
Parser6Context:
:
PARSER_INTERFACES:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_INTERFACES6
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUB
PARSER_SUBNET6:
case
Parser6Context:
:
PARSER_SUBNET6:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_SUBNET6
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUB
PARSER_POOL6:
case
Parser6Context:
:
PARSER_POOL6:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_POOL6
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUB
PARSER_PD_POOL:
case
Parser6Context:
:
PARSER_PD_POOL:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_PD_POOL
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUB
PARSER_HOST_RESERVATION
6
:
case
Parser6Context:
:
PARSER_HOST_RESERVATION:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_RESERVATION
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUB
PARSER_OPTION_DATA:
case
Parser6Context:
:
PARSER_OPTION_DATA:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_OPTION_DATA
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUB
PARSER_HOOKS_LIBRARY:
case
Parser6Context:
:
PARSER_HOOKS_LIBRARY:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_HOOKS_LIBRARY
(
driver
.
loc_
)
;
case
Parser6Context:
:
SUBPARSER_JSON:
return
isc:
:
dhcp:
:
Dhcp6Parser:
:make_SUB_JSON
(
driver
.
loc_
)
;
}
}
%
}
...
...
src/bin/dhcp6/dhcp6_parser.yy
View file @
39919c3b
...
...
@@ -39,6 +39,7 @@ using namespace std;
%define api.token.prefix {TOKEN_}
// Tokens in an order which makes sense and related to the intented use.
// Actual regexps for tokens are defined in dhcp6_lexer.ll.
%token
END 0 "end of file"
COMMA ","
...
...
src/bin/dhcp6/kea_controller.cc
View file @
39919c3b
...
...
@@ -57,12 +57,8 @@ void configure(const std::string& file_name) {
}
// Read contents of the file and parse it as JSON
#if 0
json = isc::data::Element::fromJSONFile(file_name, true);
#else
Parser6Context
parser
;
json
=
parser
.
parseFile
(
file_name
,
Parser6Context
::
PARSER_DHCP6
);
#endif
if
(
!
json
)
{
isc_throw
(
isc
::
BadValue
,
"no configuration found"
);
}
...
...
src/bin/dhcp6/parser_context.h
View file @
39919c3b
...
...
@@ -23,6 +23,8 @@ namespace isc {
namespace
dhcp
{
/// @brief Evaluation error exception raised when trying to parse.
///
/// @todo: This probably should be common for Dhcp4 and Dhcp6.
class
Dhcp6ParseError
:
public
isc
::
Exception
{
public:
Dhcp6ParseError
(
const
char
*
file
,
size_t
line
,
const
char
*
what
)
:
...
...
@@ -35,26 +37,53 @@ class Parser6Context
{
public:
/// @brief Defines currently support the content supported
/// @brief Defines currently supported scopes
///
/// Dhcp6Parser is able to parse several types of scope. Usually, when it
/// parses a config file, it expects the data to have a map with Dhcp6 in it
/// and all the parameters within that Dhcp6 map. However, sometimes the
/// parser is expected to parse only a subset of that information. For example,
/// it may be asked to parse a structure that is host-reservation only, without
/// the global 'Dhcp6' or 'reservations' around it. In such case the parser
/// is being told to start parsing as SUBPARSER_HOST_RESERVATION6.
typedef
enum
{
PARSER_GENERIC_JSON
,
// This will parse the content as generic JSON
PARSER_DHCP6
,
// This will parse the content as DHCP6 config
// DHCP6 config subparsers
/// This parser will parse the content as generic JSON.
//PARSER_GENERIC_JSON,
SUBPARSER_JSON
,
/// This parser will parse the content as Dhcp6 config wrapped in a map (that's
/// the regular config file)
PARSER_DHCP6
,
/// This parser will parse the content of Dhcp6. It is mostly used
/// in unit-tests as most of the unit-tests do not define the outer
/// map and Dhcp6 entity, just the contents of it.
SUBPARSER_DHCP6
,
SUBPARSER_INTERFACES6
,
SUBPARSER_SUBNET6
,
SUBPARSER_POOL6
,
SUBPARSER_PD_POOL
,
SUBPARSER_HOST_RESERVATION6
,
// Common DHCP subparsers
SUBPARSER_OPTION_DEF
,
SUBPARSER_OPTION_DATA
,
SUBPARSER_HOOKS_LIBRARY
,
// SUBPARSER_CONTROL_SOCKET,
// SUBPARSER_D2_CLIENT,
// SUBPARSER_LEASE_EXPIRATION,
// JSON value subparser
SUBPARSER_JSON
/// This will parse the conde as Subnet6 content.
PARSER_SUBNET6
,
/// This parser will parse pool6 content.
PARSER_POOL6
,
/// This parser will parse the interfaces content.
PARSER_INTERFACES
,
/// This parser will parse the content as pd-pool.
PARSER_PD_POOL
,
/// This parser will parse the content as host-reservation
PARSER_HOST_RESERVATION
,
/// This parser will parse the content as option definition.
PARSER_OPTION_DEF
,
/// This parser will parse the content as option data.
PARSER_OPTION_DATA
,
/// This parser will parse the content as hooks-library
PARSER_HOOKS_LIBRARY
}
ParserType
;
/// @brief Default constructor.
...
...
@@ -108,7 +137,7 @@ public:
/// Used by YY_FATAL_ERROR macro so required to be static.
static
void
fatal
(
const
std
::
string
&
what
);
/// @brief Convert
position
/// @brief Convert
s bison's position to one understandable by isc::data::Element
///
/// Convert a bison location into an element position
/// (take the begin, the end is lost)
...
...
@@ -166,7 +195,7 @@ public:
std
::
vector
<
isc
::
dhcp
::
location
>
locs_
;
/// @brief Lexer state stack
std
::
vector
<
struct
yy_buffer_state
*>
states_
;
;
std
::
vector
<
struct
yy_buffer_state
*>
states_
;
/// @brief sFile (aka FILE)
FILE
*
sfile_
;
...
...
src/bin/dhcp6/tests/dhcp6_test_utils.h
View file @
39919c3b
...
...
@@ -644,7 +644,7 @@ inline isc::data::ConstElementPtr
parseJSON
(
const
std
::
string
&
in
)
{
isc
::
dhcp
::
Parser6Context
ctx
;
return
(
ctx
.
parseString
(
in
,
isc
::
dhcp
::
Parser6Context
::
PARSER_
GENERIC_
JSON
));
return
(
ctx
.
parseString
(
in
,
isc
::
dhcp
::
Parser6Context
::
SUB
PARSER_JSON
));
}
// For parser testing (DHCP6)
...
...
@@ -669,7 +669,7 @@ parseOPTION_DEF(const std::string& in)
{
try
{
isc
::
dhcp
::
Parser6Context
ctx
;
return
(
ctx
.
parseString
(
in
,
isc
::
dhcp
::
Parser6Context
::
SUB
PARSER_OPTION_DEF
));
return
(
ctx
.
parseString
(
in
,
isc
::
dhcp
::
Parser6Context
::
PARSER_OPTION_DEF
));
}
catch
(
const
std
::
exception
&
ex
)
{
#ifdef ENABLE_DEBUG
...
...
src/bin/dhcp6/tests/parser_unittest.cc
View file @
39919c3b
...
...
@@ -65,7 +65,7 @@ void testParser2(const std::string& txt, Parser6Context::ParserType parser_type)
TEST
(
ParserTest
,
mapInMap
)
{
string
txt
=
"{
\"
xyzzy
\"
: {
\"
foo
\"
: 123,
\"
baz
\"
: 456 } }"
;
testParser
(
txt
,
Parser6Context
::
PARSER_
GENERIC_
JSON
);
testParser
(
txt
,
Parser6Context
::
SUB
PARSER_JSON
);
}
TEST
(
ParserTest
,
listInList
)
{
...
...
@@ -76,7 +76,7 @@ TEST(ParserTest, listInList) {
TEST
(
ParserTest
,
nestedMaps
)
{
string
txt
=
"{
\"
europe
\"
: {
\"
UK
\"
: {
\"
London
\"
: {
\"
street
\"
:
\"
221B Baker
\"
}}}}"
;
testParser
(
txt
,
Parser6Context
::
PARSER_
GENERIC_
JSON
);
testParser
(
txt
,
Parser6Context
::
SUB
PARSER_JSON
);
}
TEST
(
ParserTest
,
nestedLists
)
{
...
...
@@ -87,7 +87,7 @@ TEST(ParserTest, nestedLists) {
TEST
(
ParserTest
,
listsInMaps
)
{
string
txt
=
"{
\"
constellations
\"
: {
\"
orion
\"
: [
\"
rigel
\"
,
\"
betelguese
\"
], "
"
\"
cygnus
\"
: [
\"
deneb
\"
,
\"
albireo
\"
] } }"
;
testParser
(
txt
,
Parser6Context
::
PARSER_
GENERIC_
JSON
);
testParser
(
txt
,
Parser6Context
::
SUB
PARSER_JSON
);
}
TEST
(
ParserTest
,
mapsInLists
)
{
...
...
@@ -103,7 +103,7 @@ TEST(ParserTest, types) {
"
\"
map
\"
: {
\"
foo
\"
:
\"
bar
\"
},"
"
\"
list
\"
: [ 1, 2, 3 ],"
"
\"
null
\"
: null }"
;
testParser
(
txt
,
Parser6Context
::
PARSER_
GENERIC_
JSON
);
testParser
(
txt
,
Parser6Context
::
SUB
PARSER_JSON
);
}
TEST
(
ParserTest
,
keywordJSON
)
{
...
...
@@ -111,7 +111,7 @@ TEST(ParserTest, keywordJSON) {
"
\"
type
\"
:
\"
password
\"
,"
"
\"
user
\"
:
\"
name
\"
,"
"
\"
password
\"
:
\"
type
\"
}"
;
testParser
(
txt
,
Parser6Context
::
PARSER_
GENERIC_
JSON
);
testParser
(
txt
,
Parser6Context
::
SUB
PARSER_JSON
);
}
TEST
(
ParserTest
,
keywordDhcp6
)
{
...
...
@@ -267,148 +267,142 @@ void testError(const std::string& txt,
// Check errors
TEST
(
ParserTest
,
errors
)
{
// no input
testError
(
""
,
Parser6Context
::
PARSER_GENERIC_JSON
,
"<string>:1.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
" "
,
Parser6Context
::
PARSER_GENERIC_JSON
,
"<string>:1.2: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
"
\n
"
,
Parser6Context
::
PARSER_GENERIC_JSON
,
"<string>:2.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
""
,
Parser6Context
::
SUBPARSER_JSON
,
"<string>:1.1: syntax error, unexpected end of file"
);
testError
(
" "
,
Parser6Context
::
SUBPARSER_JSON
,
"<string>:1.2: syntax error, unexpected end of file"
);
testError
(
"
\n
"
,
Parser6Context
::
SUBPARSER_JSON
,
"<string>:2.1: syntax error, unexpected end of file"
);
// comments
testError
(
"# nothing
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:2.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
" #
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:2.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
"// nothing
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:2.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
"/* nothing */
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:2.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
"/* no
\n
thing */
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:3.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
"/* no
\n
thing */
\n\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:4.1: syntax error, unexpected end of file, "
"expecting {"
);
testError
(
"/* nothing
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"Comment not closed. (/* in line 1"
);
testError
(
"
\n\n\n
/* nothing
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"Comment not closed. (/* in line 4"
);
testError
(
"{ /* */*/ }
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.3-8: Invalid character: *"
);
testError
(
"{ /* // *// }
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.3-11: Invalid character: /"
);
testError
(
"{ /* // */// }
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:2.1: syntax error, unexpected end of file, "
"expecting }"
);
// includes
testError
(
"<?
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"Directive not closed."
);
testError
(
"<?include
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"Directive not closed."
);
string
file
=
string
(
CFG_EXAMPLES
)
+
"/"
+
"stateless.json"
;
testError
(
"<?include
\"
"
+
file
+
"
\"\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"Directive not closed."
);
testError
(
"<?include
\"
/foo/bar
\"
?>/n"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"Can't open include file /foo/bar"
);
// numbers
testError
(
"123"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-3: syntax error, unexpected integer, "
"expecting {"
);
testError
(
"-456"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-4: syntax error, unexpected integer, "
"expecting {"
);
testError
(
"-0001"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-5: syntax error, unexpected integer, "
"expecting {"
);
testError
(
"1234567890123456789012345678901234567890"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-40: Failed to convert "
"1234567890123456789012345678901234567890"
" to an integer."
);
testError
(
"-3.14e+0"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-8: syntax error, unexpected floating point, "
"expecting {"
);
testError
(
"1e50000"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-7: Failed to convert 1e50000 "
"to a floating point."
);
// strings
testError
(
"
\"
aabb
\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-6: syntax error, unexpected constant string, "
"expecting {"
);
testError
(
"{
\"
aabb
\"
err"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.9: Invalid character: e"
);
testError
(
"{ err
\"
aabb
\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.3: Invalid character: e"
);
testError
(
"
\"
a
\n\t
b
\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-6: Invalid control in
\"
a
\n\t
b
\"
"
);
testError
(
"
\"
a
\\
n
\\
tb
\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-8: syntax error, unexpected constant string, "
"expecting {"
);
testError
(
"
\"
a
\\
x01b
\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-8: Bad escape in
\"
a
\\
x01b
\"
"
);
testError
(
"
\"
a
\\
u0062
\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-9: Unsupported unicode escape in
\"
a
\\
u0062
\"
"
);
testError
(
"
\"
a
\\
u062z
\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-9: Bad escape in
\"
a
\\
u062z
\"
"
);
testError
(
"
\"
abc
\\\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1-6: Overflow escape in
\"
abc
\\\"
"
);
// from data_unittest.c
testError
(
"
\\
a"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1: Invalid character:
\\
"
);
testError
(
"
\\
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1: Invalid character:
\\
"
);
testError
(
"
\\\"\\\"
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1: Invalid character:
\\
"
);
// want a map
testError
(
"[]
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.1: syntax error, unexpected [, "
"expecting {"
);
testError
(
"[]
\n
"
,
...
...
@@ -416,14 +410,14 @@ TEST(ParserTest, errors) {
"<string>:1.1: syntax error, unexpected [, "
"expecting {"
);
testError
(
"{ 123 }
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.3-5: syntax error, unexpected integer, "
"expecting }"
);
testError
(
"{ 123 }
\n
"
,
Parser6Context
::
PARSER_DHCP6
,
"<string>:1.3-5: syntax error, unexpected integer"
);
testError
(
"{
\"
foo
\"
}
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.9: syntax error, unexpected }, "
"expecting :"
);
testError
(
"{
\"
foo
\"
}
\n
"
,
...
...
@@ -442,21 +436,21 @@ TEST(ParserTest, errors) {
"<string>:2.1: syntax error, unexpected end of file, "
"expecting
\"
,
\"
or }"
);
testError
(
"{}{}
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.3: syntax error, unexpected {, "
"expecting end of file"
);
// bad commas
testError
(
"{ , }
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.3: syntax error, unexpected
\"
,
\"
, "
"expecting }"
);
testError
(
"{ ,
\"
foo
\"
:true }
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.3: syntax error, unexpected
\"
,
\"
, "
"expecting }"
);
testError
(
"{
\"
foo
\"
:true, }
\n
"
,
Parser6Context
::
PARSER_
GENERIC_
JSON
,
Parser6Context
::
SUB
PARSER_JSON
,
"<string>:1.15: syntax error, unexpected }, "
"expecting constant string"
);
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment