Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
O
Ocarina
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
2
Issues
2
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
Operations
Operations
Incidents
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
TASTE
Ocarina
Commits
b9ab6417
Commit
b9ab6417
authored
Jun 05, 2015
by
Jerome Hugues
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #38 from Ellidiss/master
API Python, error management
parents
e1061afa
8b0c42a8
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
801 additions
and
97 deletions
+801
-97
resources/runtime/python/Makefile.am
resources/runtime/python/Makefile.am
+2
-1
resources/runtime/python/ocarina.py
resources/runtime/python/ocarina.py
+443
-70
resources/runtime/python/ocarina_common_tools.py
resources/runtime/python/ocarina_common_tools.py
+133
-0
src/backends/ocarina-backends-deos_conf-partitions.adb
src/backends/ocarina-backends-deos_conf-partitions.adb
+2
-4
src/backends/ocarina-backends-vxworks653_conf-connections.adb
...backends/ocarina-backends-vxworks653_conf-connections.adb
+1
-2
src/backends/ocarina-backends-vxworks653_conf-naming.adb
src/backends/ocarina-backends-vxworks653_conf-naming.adb
+2
-4
src/python/ocarina-python_cmd.adb
src/python/ocarina-python_cmd.adb
+23
-0
tools/mknodes/parser.adb
tools/mknodes/parser.adb
+195
-16
No files found.
resources/runtime/python/Makefile.am
View file @
b9ab6417
AUTOMAKE_OPTIONS
=
no-dependencies
PYTHON_FILES
=
$(srcdir)
/ocarina.py
PYTHON_FILES
=
$(srcdir)
/ocarina.py
\
$(srcdir)
/ocarina_common_tools.py
if
INSTALL_PYTHON
PYTHON_FILES
+=
\
...
...
resources/runtime/python/ocarina.py
View file @
b9ab6417
This diff is collapsed.
Click to expand it.
resources/runtime/python/ocarina_common_tools.py
0 → 100755
View file @
b9ab6417
#! /usr/bin/python
'''
:mod:`ocarina_common_tools` -- Tools used by Python binding
to the Ocarina AADL processor
==============================================================
.. moduleauthor:: Jerome Hugues, Arnaud Schach
This module provides tools to be used by the Python scripts
form the Python binding to the Ocarina AADL processor.
'''
################################################################################
try
:
import
sys
import
StringIO
from
contextlib
import
contextmanager
import
ctypes
import
io
import
os
import
tempfile
except
ImportError
:
pass
################################################################################
def
getErrorMessage
():
'''Get the error message from the raised error
'''
keep
=
False
msg
=
''
for
line
in
StringIO
.
StringIO
(
sys
.
exc_info
()[
1
]):
if
line
.
lower
().
startswith
(
'message:'
):
keep
=
True
if
line
.
lower
().
startswith
(
'call stack traceback locations:'
):
break
if
keep
:
msg
=
msg
+
line
[
9
:]
+
'
\n
'
return
msg
################################################################################
def
sortStderrMessages
(
messages
):
'''Get the error and warning messages from the stderr
:param messages: the messages written on stderr
return a pair of the form [ warnings , errors ]
'''
msgType
=
'error'
warningMsg
=
''
errorMsg
=
''
warningMsgList
=
[]
errorMsgList
=
[]
for
line
in
StringIO
.
StringIO
(
messages
):
if
line
.
lower
().
startswith
(
'error:'
):
if
warningMsg
.
strip
()
!=
''
:
warningMsgList
.
append
(
warningMsg
.
strip
())
warningMsg
=
''
if
errorMsg
.
strip
()
!=
''
:
errorMsgList
.
append
(
errorMsg
.
strip
())
errorMsg
=
''
msgType
=
'error'
errorMsg
=
warningMsg
+
line
[
7
:]
+
'
\n
'
elif
line
.
lower
().
startswith
(
'warning:'
):
if
warningMsg
.
strip
()
!=
''
:
warningMsgList
.
append
(
warningMsg
.
strip
())
warningMsg
=
''
if
errorMsg
.
strip
()
!=
''
:
errorMsgList
.
append
(
errorMsg
.
strip
())
errorMsg
=
''
msgType
=
'warning'
warningMsg
=
warningMsg
+
line
[
9
:]
+
'
\n
'
else
:
if
msgType
==
'warning'
:
warningMsg
=
warningMsg
+
line
+
'
\n
'
elif
msgType
==
'error'
:
errorMsg
=
errorMsg
+
line
+
'
\n
'
if
warningMsg
.
strip
()
!=
''
:
warningMsgList
.
append
(
warningMsg
.
strip
())
if
errorMsg
.
strip
()
!=
''
:
errorMsgList
.
append
(
errorMsg
.
strip
())
return
[
warningMsgList
,
errorMsgList
]
################################################################################
@
contextmanager
def
std_redirector
(
stdoutStream
,
stderrStream
):
libc
=
ctypes
.
CDLL
(
None
)
c_stdout
=
ctypes
.
c_void_p
.
in_dll
(
libc
,
'stdout'
)
c_stderr
=
ctypes
.
c_void_p
.
in_dll
(
libc
,
'stderr'
)
original_stdout_fd
=
sys
.
stdout
.
fileno
()
original_stderr_fd
=
sys
.
stderr
.
fileno
()
def
_redirect_stdout
(
to_fd
):
libc
.
fflush
(
c_stdout
)
sys
.
stdout
.
close
()
os
.
dup2
(
to_fd
,
original_stdout_fd
)
sys
.
stdout
=
os
.
fdopen
(
original_stdout_fd
,
'wb'
)
def
_redirect_stderr
(
to_fd
):
libc
.
fflush
(
c_stderr
)
sys
.
stderr
.
close
()
os
.
dup2
(
to_fd
,
original_stderr_fd
)
sys
.
stderr
=
os
.
fdopen
(
original_stderr_fd
,
'wb'
)
saved_stdout_fd
=
os
.
dup
(
original_stdout_fd
)
saved_stderr_fd
=
os
.
dup
(
original_stderr_fd
)
try
:
stdoutfile
=
tempfile
.
TemporaryFile
(
mode
=
'w+b'
)
_redirect_stdout
(
stdoutfile
.
fileno
())
stderrfile
=
tempfile
.
TemporaryFile
(
mode
=
'w+b'
)
_redirect_stderr
(
stderrfile
.
fileno
())
yield
_redirect_stdout
(
saved_stdout_fd
)
_redirect_stderr
(
saved_stderr_fd
)
stdoutfile
.
flush
()
stdoutfile
.
seek
(
0
,
io
.
SEEK_SET
)
stdoutStream
.
write
(
stdoutfile
.
read
())
stderrfile
.
flush
()
stderrfile
.
seek
(
0
,
io
.
SEEK_SET
)
stderrStream
.
write
(
stderrfile
.
read
())
finally
:
stdoutfile
.
close
()
os
.
close
(
saved_stdout_fd
)
stderrfile
.
close
()
os
.
close
(
saved_stderr_fd
)
src/backends/ocarina-backends-deos_conf-partitions.adb
View file @
b9ab6417
...
...
@@ -517,15 +517,13 @@ package body Ocarina.Backends.Deos_Conf.Partitions is
and
then
Get_Connection_Pattern
(
F
)
=
Inter_Process
then
if
Is_Data
(
F
)
and
then
not
Is_Event
(
F
)
then
if
Is_Data
(
F
)
and
then
not
Is_Event
(
F
)
then
Append_Node_To_List
(
Map_Sampling_Port
(
F
),
XTN
.
Subitems
(
Sampling_Ports
));
end
if
;
if
Is_Data
(
F
)
and
then
Is_Event
(
F
)
then
if
Is_Data
(
F
)
and
then
Is_Event
(
F
)
then
Append_Node_To_List
(
Map_Queuing_Port
(
F
),
XTN
.
Subitems
(
Queuing_Ports
));
...
...
src/backends/ocarina-backends-vxworks653_conf-connections.adb
View file @
b9ab6417
...
...
@@ -183,8 +183,7 @@ package body Ocarina.Backends.Vxworks653_Conf.Connections is
Feature
:=
First_Node
(
Features
(
Corresponding_Process
));
while
Present
(
Feature
)
loop
if
Is_Data
(
Feature
)
and
then
Is_Out
(
Feature
)
then
if
Is_Data
(
Feature
)
and
then
Is_Out
(
Feature
)
then
Port_Source
:=
Feature
;
Port_Destination
:=
Item
(
First_Node
(
Destinations
(
Feature
)));
Partition_Destination
:=
Parent_Component
(
Port_Destination
);
...
...
src/backends/ocarina-backends-vxworks653_conf-naming.adb
View file @
b9ab6417
...
...
@@ -272,8 +272,7 @@ package body Ocarina.Backends.Vxworks653_Conf.Naming is
Feature
:=
First_Node
(
Features
(
Corresponding_Process
));
while
Present
(
Feature
)
loop
if
Is_Event
(
Feature
)
and
then
Is_Data
(
Feature
)
then
if
Is_Event
(
Feature
)
and
then
Is_Data
(
Feature
)
then
Size
:=
To_Bytes
(
Get_Data_Size
(
Corresponding_Instance
(
Feature
)));
Queue_Size
:=
Get_Queue_Size
(
Feature
);
...
...
@@ -326,8 +325,7 @@ package body Ocarina.Backends.Vxworks653_Conf.Naming is
XTN
.
Subitems
(
Ports_Node
));
end
if
;
if
not
Is_Event
(
Feature
)
and
then
Is_Data
(
Feature
)
then
if
not
Is_Event
(
Feature
)
and
then
Is_Data
(
Feature
)
then
Size
:=
To_Bytes
(
Get_Data_Size
(
Corresponding_Instance
(
Feature
)));
...
...
src/python/ocarina-python_cmd.adb
View file @
b9ab6417
...
...
@@ -135,6 +135,24 @@ package body Ocarina.Python_Cmd is
Ocarina
.
Utils
.
Instantiate
(
Nth_Arg
(
Data
,
1
,
""
));
end
On_Instantiate
;
----------------------
-- On_Get_AADL_Root --
----------------------
procedure
On_Get_AADL_Root
(
Data
:
in
out
Callback_Data
'
Class
;
Command
:
String
);
procedure
On_Get_AADL_Root
(
Data
:
in
out
Callback_Data
'
Class
;
Command
:
String
)
is
pragma
Unreferenced
(
Command
);
begin
Set_Return_Value
(
Data
,
Integer
'
Image
(
Integer
(
Ocarina
.
ME_AADL
.
AADL_Instances
.
Nodes
.
Root_System
(
Ocarina
.
Utils
.
Get_AADL_Root
))));
end
On_Get_AADL_Root
;
----------------
-- On_Analyze --
----------------
...
...
@@ -647,6 +665,11 @@ package body Ocarina.Python_Cmd is
(
Repo
,
"instantiate"
,
1
,
1
,
Handler
=>
On_Instantiate
'
Unrestricted_Access
);
-- getRoot() function
Register_Command
(
Repo
,
"getRoot"
,
0
,
0
,
Handler
=>
On_Get_AADL_Root
'
Unrestricted_Access
);
-- generate() function
Register_Command
(
Repo
,
"generate"
,
1
,
1
,
...
...
tools/mknodes/parser.adb
View file @
b9ab6417
...
...
@@ -1797,8 +1797,7 @@ package body Parser is
Output_File
:=
GNAT
.
OS_Lib
.
Standout
;
when
't'
=>
if
GNAT
.
Command_Line
.
Parameter
=
"python"
then
if
GNAT
.
Command_Line
.
Parameter
=
"python"
then
Target_Language
:=
2
;
end
if
;
...
...
@@ -2009,8 +2008,8 @@ package body Parser is
end
if
;
if
Target_Language
=
1
then
-- If the output is not the standard output, compute the spec
-- filename and redirect output.
-- If the output is not the standard output, compute the spec
-- filename and redirect output.
if
Output_Name
/=
Types
.
No_Name
then
Output_File
:=
...
...
@@ -2037,8 +2036,8 @@ package body Parser is
end
if
;
if
Target_Language
=
2
then
-- If the output is not the standard output, compute the body
-- filename and redirect output.
-- If the output is not the standard output, compute the body
-- filename and redirect output.
if
Output_Name
/=
Types
.
No_Name
then
Output_Name
:=
Utils
.
Remove_Suffix_From_Name
(
...
...
@@ -2672,7 +2671,11 @@ package body Parser is
Output
.
Write_Eol
;
Output
.
Write_Eol
;
Output
.
Write_Str
(
"import libocarina_python; # Ocarina bindings"
);
Output
.
Write_Str
(
"import libocarina_python"
);
Output
.
Write_Eol
;
Output
.
Write_Str
(
"# Ocarina bindings"
);
Output
.
Write_Eol
;
Output
.
Write_Str
(
"from ocarina_common_tools import *"
);
Output
.
Write_Eol
;
Output
.
Write_Eol
;
...
...
@@ -2722,12 +2725,56 @@ package body Parser is
Output
.
Write_Str
(
" (N):"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"return libocarina_python."
);
Output
.
Write_Str
(
"info = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"error = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"raisedError = []"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"res = ''"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"with std_redirector(info,error):"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"try:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"res = libocarina_python."
);
Output
.
Write_Str
(
Ada
.
Directories
.
Base_Name
(
Namet
.
Get_Name_String
(
Output_Name
)));
Output
.
Write_Str
(
"_"
);
Output
.
Write_Str
(
A
);
Output
.
Write_Str
(
" (N);"
);
Output
.
Write_Str
(
" ("
);
Output
.
Write_Eol
;
W_Indentation
(
4
);
Output
.
Write_Str
(
"N)"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"except:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"raisedError.append(getErrorMessage())"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"stderrMsg = sortStderrMessages(error"
);
Output
.
Write_Str
(
".getvalue().decode('utf-8'))"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"if stderrMsg[1]!=[]:"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError.append(stderrMsg[1])"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"return [ res , info.getvalue()."
);
Output
.
Write_Str
(
"decode('utf-8'), stderrMsg[0] , "
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError ]"
);
Output
.
Write_Eol
;
Output
.
Write_Eol
;
...
...
@@ -2739,12 +2786,56 @@ package body Parser is
Output
.
Write_Str
(
" (N, V):"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"info = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"error = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"raisedError = []"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"res = ''"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"with std_redirector(info,error):"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"try:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"libocarina_python."
);
Output
.
Write_Str
(
Ada
.
Directories
.
Base_Name
(
Namet
.
Get_Name_String
(
Output_Name
)));
Output
.
Write_Str
(
"_"
);
Output
.
Write_Str
(
WS
(
A
));
Output
.
Write_Str
(
" (N, V);"
);
Output
.
Write_Str
(
" ("
);
Output
.
Write_Eol
;
W_Indentation
(
4
);
Output
.
Write_Str
(
"N, V)"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"except:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"raisedError.append(getErrorMessage())"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"stderrMsg = sortStderrMessages(error."
);
Output
.
Write_Str
(
"getvalue().decode('utf-8'))"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"if stderrMsg[1]!=[]:"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError.append(stderrMsg[1])"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"return [ res , info.getvalue()."
);
Output
.
Write_Str
(
"decode('utf-8'), stderrMsg[0] , "
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError ]"
);
Output
.
Write_Eol
;
Output
.
Write_Eol
;
...
...
@@ -2770,12 +2861,56 @@ package body Parser is
Output
.
Write_Str
(
" (N):"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"return libocarina_python."
);
Output
.
Write_Str
(
Ada
.
Directories
.
Base_Name
(
Namet
.
Get_Name_String
(
Output_Name
))
&
"_python"
);
Output
.
Write_Str
(
"info = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"error = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"raisedError = []"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"res = ''"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"with std_redirector(info,error):"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"try:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"res = libocarina_python."
);
Output
.
Write_Str
(
Ada
.
Directories
.
Base_Name
(
Namet
.
Get_Name_String
(
Output_Name
))
&
"_python"
);
Output
.
Write_Str
(
"_"
);
Output
.
Write_Str
(
GNS
(
Identifier
(
A
)));
Output
.
Write_Str
(
" (N);"
);
Output
.
Write_Str
(
" ("
);
Output
.
Write_Eol
;
W_Indentation
(
4
);
Output
.
Write_Str
(
"N)"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"except:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"raisedError.append(getErrorMessage())"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"stderrMsg = sortStderrMessages(error."
);
Output
.
Write_Str
(
"getvalue().decode('utf-8'))"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"if stderrMsg[1]!=[]:"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError.append(stderrMsg[1])"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"return [ res , info.getvalue()."
);
Output
.
Write_Str
(
"decode('utf-8'), stderrMsg[0] , "
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError ]"
);
Output
.
Write_Eol
;
Output
.
Write_Eol
;
...
...
@@ -2787,12 +2922,56 @@ package body Parser is
Output
.
Write_Str
(
" (N, V):"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"info = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"error = io.BytesIO()"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"raisedError = []"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"res = ''"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"with std_redirector(info,error):"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"try:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"libocarina_python."
);
Output
.
Write_Str
(
Ada
.
Directories
.
Base_Name
(
Namet
.
Get_Name_String
(
Output_Name
))
&
"_python"
);
(
Namet
.
Get_Name_String
(
Output_Name
))
&
"_python"
);
Output
.
Write_Str
(
"_"
);
Output
.
Write_Str
(
WS
(
GNS
(
Identifier
(
A
))));
Output
.
Write_Str
(
" (N, V);"
);
Output
.
Write_Str
(
" ("
);
Output
.
Write_Eol
;
W_Indentation
(
4
);
Output
.
Write_Str
(
"N, V)"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"except:"
);
Output
.
Write_Eol
;
W_Indentation
(
3
);
Output
.
Write_Str
(
"raisedError.append(getErrorMessage())"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"stderrMsg = sortStderrMessages(error."
);
Output
.
Write_Str
(
"getvalue().decode('utf-8'))"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"if stderrMsg[1]!=[]:"
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError.append(stderrMsg[1])"
);
Output
.
Write_Eol
;
W_Indentation
(
1
);
Output
.
Write_Str
(
"return [ res , info.getvalue()."
);
Output
.
Write_Str
(
"decode('utf-8'), stderrMsg[0] , "
);
Output
.
Write_Eol
;
W_Indentation
(
2
);
Output
.
Write_Str
(
"raisedError ]"
);
Output
.
Write_Eol
;
Output
.
Write_Eol
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a 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