Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
2fff4f01
Commit
2fff4f01
authored
Dec 31, 2002
by
unknown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
XML parser
parent
bc090e5a
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
522 additions
and
2 deletions
+522
-2
include/Makefile.am
include/Makefile.am
+1
-1
include/my_xml.h
include/my_xml.h
+59
-0
mysys/Makefile.am
mysys/Makefile.am
+4
-1
mysys/test_xml
mysys/test_xml
+0
-0
mysys/test_xml.c
mysys/test_xml.c
+89
-0
mysys/xml.c
mysys/xml.c
+369
-0
No files found.
include/Makefile.am
View file @
2fff4f01
...
@@ -16,7 +16,7 @@
...
@@ -16,7 +16,7 @@
# MA 02111-1307, USA
# MA 02111-1307, USA
BUILT_SOURCES
=
mysql_version.h m_ctype.h my_config.h
BUILT_SOURCES
=
mysql_version.h m_ctype.h my_config.h
pkginclude_HEADERS
=
dbug.h m_string.h my_sys.h my_list.h
\
pkginclude_HEADERS
=
dbug.h m_string.h my_sys.h my_list.h
my_xml.h
\
mysql.h mysql_com.h mysqld_error.h mysql_embed.h
\
mysql.h mysql_com.h mysqld_error.h mysql_embed.h
\
my_semaphore.h my_pthread.h my_no_pthread.h raid.h
\
my_semaphore.h my_pthread.h my_no_pthread.h raid.h
\
errmsg.h my_global.h my_net.h my_alloc.h
\
errmsg.h my_global.h my_net.h my_alloc.h
\
...
...
include/my_xml.h
0 → 100644
View file @
2fff4f01
/* Copyright (C) 2000 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef _my_xml_h
#define _my_xml_h
#ifdef __cplusplus
extern
"C"
{
#endif
#define MY_XML_OK 0
#define MY_XML_ERROR 1
typedef
struct
xml_stack_st
{
char
errstr
[
128
];
char
attr
[
128
];
char
*
attrend
;
const
char
*
beg
;
const
char
*
cur
;
const
char
*
end
;
int
(
*
enter
)(
struct
xml_stack_st
*
st
,
const
char
*
val
,
uint
len
);
int
(
*
value
)(
struct
xml_stack_st
*
st
,
const
char
*
val
,
uint
len
);
int
(
*
leave
)(
struct
xml_stack_st
*
st
,
const
char
*
val
,
uint
len
);
}
MY_XML_PARSER
;
void
my_xml_parser_create
(
MY_XML_PARSER
*
st
);
void
my_xml_parser_free
(
MY_XML_PARSER
*
st
);
int
my_xml_parse
(
MY_XML_PARSER
*
st
,
const
char
*
str
,
uint
len
);
void
my_xml_set_value_handler
(
MY_XML_PARSER
*
st
,
int
(
*
)(
MY_XML_PARSER
*
,
const
char
*
,
uint
len
));
void
my_xml_set_enter_handler
(
MY_XML_PARSER
*
st
,
int
(
*
)(
MY_XML_PARSER
*
,
const
char
*
,
uint
len
));
void
my_xml_set_leave_handler
(
MY_XML_PARSER
*
st
,
int
(
*
)(
MY_XML_PARSER
*
,
const
char
*
,
uint
len
));
uint
my_xml_error_pos
(
MY_XML_PARSER
*
st
);
uint
my_xml_error_lineno
(
MY_XML_PARSER
*
st
);
const
char
*
my_xml_error_string
(
MY_XML_PARSER
*
st
);
#ifdef __cplusplus
}
#endif
#endif
/* _my_xml_h */
mysys/Makefile.am
View file @
2fff4f01
...
@@ -50,7 +50,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\
...
@@ -50,7 +50,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\
my_getopt.c my_mkdir.c
\
my_getopt.c my_mkdir.c
\
default.c my_compress.c checksum.c raid.cc
\
default.c my_compress.c checksum.c raid.cc
\
my_net.c my_semaphore.c my_port.c
\
my_net.c my_semaphore.c my_port.c
\
my_vsnprintf.c charset.c my_bitmap.c my_bit.c md5.c
\
my_vsnprintf.c charset.c
xml.c
my_bitmap.c my_bit.c md5.c
\
my_gethostbyname.c rijndael.c my_aes.c sha1.c
\
my_gethostbyname.c rijndael.c my_aes.c sha1.c
\
my_handler.c
my_handler.c
EXTRA_DIST
=
thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c
\
EXTRA_DIST
=
thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c
\
...
@@ -104,6 +104,9 @@ test_dir: test_dir.c $(LIBRARIES)
...
@@ -104,6 +104,9 @@ test_dir: test_dir.c $(LIBRARIES)
test_charset$(EXEEXT)
:
test_charset.c $(LIBRARIES)
test_charset$(EXEEXT)
:
test_charset.c $(LIBRARIES)
$(LINK)
$(FLAGS)
-DMAIN
$(srcdir)
/test_charset.c
$(LDADD)
$(LIBS)
$(LINK)
$(FLAGS)
-DMAIN
$(srcdir)
/test_charset.c
$(LDADD)
$(LIBS)
test_xml$(EXEEXT)
:
test_xml.c $(LIBRARIES)
$(LINK)
$(FLAGS)
-DMAIN
$(srcdir)
/test_xml.c
$(LDADD)
$(LIBS)
charset2html$(EXEEXT)
:
charset2html.c $(LIBRARIES)
charset2html$(EXEEXT)
:
charset2html.c $(LIBRARIES)
$(LINK)
$(FLAGS)
-DMAIN
$(srcdir)
/charset2html.c
$(LDADD)
$(LIBS)
$(LINK)
$(FLAGS)
-DMAIN
$(srcdir)
/charset2html.c
$(LDADD)
$(LIBS)
...
...
mysys/test_xml
0 → 100755
View file @
2fff4f01
File added
mysys/test_xml.c
0 → 100644
View file @
2fff4f01
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "my_xml.h"
static
void
mstr
(
char
*
str
,
const
char
*
src
,
uint
l1
,
uint
l2
)
{
l1
=
l1
<
l2
?
l1
:
l2
;
memcpy
(
str
,
src
,
l1
);
str
[
l1
]
=
'\0'
;
}
static
int
dstr
(
MY_XML_PARSER
*
st
,
const
char
*
attr
,
uint
len
)
{
char
str
[
1024
];
mstr
(
str
,
attr
,
len
,
sizeof
(
str
)
-
1
);
printf
(
"VALUE '%s'
\n
"
,
str
);
return
MY_XML_OK
;
}
static
int
bstr
(
MY_XML_PARSER
*
st
,
const
char
*
attr
,
uint
len
)
{
char
str
[
1024
];
mstr
(
str
,
attr
,
len
,
sizeof
(
str
)
-
1
);
printf
(
"ENTER %s
\n
"
,
str
);
return
MY_XML_OK
;
}
static
int
estr
(
MY_XML_PARSER
*
st
,
const
char
*
attr
,
uint
len
)
{
char
str
[
1024
];
mstr
(
str
,
attr
,
len
,
sizeof
(
str
)
-
1
);
printf
(
"LEAVE %s
\n
"
,
str
);
return
MY_XML_OK
;
}
static
void
usage
(
const
char
*
prog
)
{
printf
(
"Usage:
\n
"
);
printf
(
"%s xmlfile
\n
"
,
prog
);
}
int
main
(
int
ac
,
char
**
av
)
{
char
str
[
1024
*
64
]
=
""
;
const
char
*
fn
;
int
f
;
uint
len
;
MY_XML_PARSER
p
;
if
(
ac
<
2
)
{
usage
(
av
[
0
]);
return
0
;
}
fn
=
av
[
1
]
?
av
[
1
]
:
"test.xml"
;
if
((
f
=
open
(
fn
,
O_RDONLY
))
<
0
)
{
fprintf
(
stderr
,
"Err '%s'
\n
"
,
fn
);
return
1
;
}
len
=
read
(
f
,
str
,
sizeof
(
str
)
-
1
);
str
[
len
]
=
'\0'
;
my_xml_parser_create
(
&
p
);
my_xml_set_enter_handler
(
&
p
,
bstr
);
my_xml_set_value_handler
(
&
p
,
dstr
);
my_xml_set_leave_handler
(
&
p
,
estr
);
if
(
MY_XML_OK
!=
(
f
=
my_xml_parse
(
&
p
,
str
,
len
)))
{
printf
(
"ERROR at line %d pos %d '%s'
\n
"
,
my_xml_error_lineno
(
&
p
)
+
1
,
my_xml_error_pos
(
&
p
),
my_xml_error_string
(
&
p
));
}
my_xml_parser_free
(
&
p
);
return
0
;
}
mysys/xml.c
0 → 100644
View file @
2fff4f01
/* Copyright (C) 2000 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "my_global.h"
#include "m_string.h"
#include "my_xml.h"
#define MY_XML_EOF 'E'
#define MY_XML_STRING 'S'
#define MY_XML_IDENT 'I'
#define MY_XML_EQ '='
#define MY_XML_LT '<'
#define MY_XML_GT '>'
#define MY_XML_SLASH '/'
#define MY_XML_COMMENT 'C'
#define MY_XML_TEXT 'T'
#define MY_XML_QUESTION '?'
#define MY_XML_EXCLAM '!'
typedef
struct
xml_attr_st
{
const
char
*
beg
;
const
char
*
end
;
}
MY_XML_ATTR
;
static
const
char
*
lex2str
(
int
lex
)
{
switch
(
lex
)
{
case
MY_XML_EOF
:
return
"EOF"
;
case
MY_XML_STRING
:
return
"STRING"
;
case
MY_XML_IDENT
:
return
"IDENT"
;
case
MY_XML_EQ
:
return
"'='"
;
case
MY_XML_LT
:
return
"'<'"
;
case
MY_XML_GT
:
return
"'>'"
;
case
MY_XML_SLASH
:
return
"'/'"
;
case
MY_XML_COMMENT
:
return
"COMMENT"
;
case
MY_XML_TEXT
:
return
"TEXT"
;
case
MY_XML_QUESTION
:
return
"'?'"
;
case
MY_XML_EXCLAM
:
return
"'!'"
;
}
return
"UNKNOWN"
;
}
static
void
my_xml_norm_text
(
MY_XML_ATTR
*
a
)
{
for
(
;
(
a
->
beg
<
a
->
end
)
&&
strchr
(
"
\t\r\n
"
,
a
->
beg
[
0
])
;
a
->
beg
++
);
for
(
;
(
a
->
beg
<
a
->
end
)
&&
strchr
(
"
\t\r\n
"
,
a
->
end
[
-
1
])
;
a
->
end
--
);
}
static
int
my_xml_scan
(
MY_XML_PARSER
*
p
,
MY_XML_ATTR
*
a
)
{
int
lex
;
for
(
;
(
p
->
cur
<
p
->
end
)
&&
strchr
(
"
\t\r\n
"
,
p
->
cur
[
0
])
;
p
->
cur
++
);
if
(
p
->
cur
>=
p
->
end
)
{
a
->
beg
=
p
->
end
;
a
->
end
=
p
->
end
;
lex
=
MY_XML_EOF
;
goto
ret
;
}
a
->
beg
=
p
->
cur
;
a
->
end
=
p
->
cur
;
if
(
!
memcmp
(
p
->
cur
,
"<!--"
,
4
))
{
for
(
;
(
p
->
cur
<
p
->
end
)
&&
memcmp
(
p
->
cur
,
"-->"
,
3
);
p
->
cur
++
);
if
(
!
memcmp
(
p
->
cur
,
"-->"
,
3
))
p
->
cur
+=
3
;
a
->
end
=
p
->
cur
;
lex
=
MY_XML_COMMENT
;
}
else
if
(
strchr
(
"?=/<>!"
,
p
->
cur
[
0
]))
{
p
->
cur
++
;
a
->
end
=
p
->
cur
;
lex
=
a
->
beg
[
0
];
}
else
if
(
(
p
->
cur
[
0
]
==
'"'
)
||
(
p
->
cur
[
0
]
==
'\''
)
)
{
p
->
cur
++
;
for
(
;
(
p
->
cur
<
p
->
end
)
&&
(
p
->
cur
[
0
]
!=
a
->
beg
[
0
]);
p
->
cur
++
);
a
->
end
=
p
->
cur
;
if
(
a
->
beg
[
0
]
==
p
->
cur
[
0
])
p
->
cur
++
;
a
->
beg
++
;
my_xml_norm_text
(
a
);
lex
=
MY_XML_STRING
;
}
else
{
for
(
;
(
p
->
cur
<
p
->
end
)
&&
!
strchr
(
"?'
\"
=/<>
\t\r\n
"
,
p
->
cur
[
0
]);
p
->
cur
++
);
a
->
end
=
p
->
cur
;
my_xml_norm_text
(
a
);
lex
=
MY_XML_IDENT
;
}
#if 0
printf("LEX=%s[%d]\n",lex2str(lex),a->end-a->beg);
#endif
ret:
return
lex
;
}
static
int
my_xml_value
(
MY_XML_PARSER
*
st
,
const
char
*
str
,
uint
len
)
{
return
(
st
->
value
)
?
(
st
->
value
)(
st
,
str
,
len
)
:
MY_XML_OK
;
}
static
int
my_xml_enter
(
MY_XML_PARSER
*
st
,
const
char
*
str
,
uint
len
)
{
if
(
(
st
->
attrend
-
st
->
attr
+
len
+
1
)
>
sizeof
(
st
->
attr
))
{
sprintf
(
st
->
errstr
,
"To deep XML"
);
return
MY_XML_ERROR
;
}
if
(
st
->
attrend
>
st
->
attr
)
{
st
->
attrend
[
0
]
=
'.'
;
st
->
attrend
++
;
}
memcpy
(
st
->
attrend
,
str
,
len
);
st
->
attrend
+=
len
;
st
->
attrend
[
0
]
=
'\0'
;
return
st
->
enter
?
st
->
enter
(
st
,
st
->
attr
,
st
->
attrend
-
st
->
attr
)
:
MY_XML_OK
;
}
static
void
mstr
(
char
*
s
,
const
char
*
src
,
uint
l1
,
uint
l2
)
{
l1
=
l1
<
l2
?
l1
:
l2
;
memcpy
(
s
,
src
,
l1
);
s
[
l1
]
=
'\0'
;
}
static
int
my_xml_leave
(
MY_XML_PARSER
*
p
,
const
char
*
str
,
uint
slen
)
{
char
*
e
;
uint
glen
;
char
s
[
32
];
char
g
[
32
];
int
rc
;
/* Find previous '.' or beginning */
for
(
e
=
p
->
attrend
;
(
e
>
p
->
attr
)
&&
(
e
[
0
]
!=
'.'
)
;
e
--
);
glen
=
(
e
[
0
]
==
'.'
)
?
(
p
->
attrend
-
e
-
1
)
:
p
->
attrend
-
e
;
if
(
str
&&
(
slen
!=
glen
))
{
mstr
(
s
,
str
,
sizeof
(
s
)
-
1
,
slen
);
mstr
(
g
,
e
+
1
,
sizeof
(
g
)
-
1
,
glen
),
sprintf
(
p
->
errstr
,
"'</%s>' unexpected ('</%s>' wanted)"
,
s
,
g
);
return
MY_XML_ERROR
;
}
rc
=
p
->
leave
?
p
->
leave
(
p
,
p
->
attr
,
p
->
attrend
-
p
->
attr
)
:
MY_XML_OK
;
*
e
=
'\0'
;
p
->
attrend
=
e
;
return
rc
;
}
int
my_xml_parse
(
MY_XML_PARSER
*
p
,
const
char
*
str
,
uint
len
)
{
p
->
attrend
=
p
->
attr
;
p
->
beg
=
str
;
p
->
cur
=
str
;
p
->
end
=
str
+
len
;
while
(
p
->
cur
<
p
->
end
)
{
MY_XML_ATTR
a
;
if
(
p
->
cur
[
0
]
==
'<'
)
{
int
lex
;
int
question
=
0
;
int
exclam
=
0
;
lex
=
my_xml_scan
(
p
,
&
a
);
if
(
MY_XML_COMMENT
==
lex
)
{
continue
;
}
lex
=
my_xml_scan
(
p
,
&
a
);
if
(
MY_XML_SLASH
==
lex
)
{
if
(
MY_XML_IDENT
!=
(
lex
=
my_xml_scan
(
p
,
&
a
)))
{
sprintf
(
p
->
errstr
,
"1: %s unexpected (ident wanted)"
,
lex2str
(
lex
));
return
MY_XML_ERROR
;
}
if
(
MY_XML_OK
!=
my_xml_leave
(
p
,
a
.
beg
,
a
.
end
-
a
.
beg
))
return
MY_XML_ERROR
;
lex
=
my_xml_scan
(
p
,
&
a
);
goto
gt
;
}
if
(
MY_XML_EXCLAM
==
lex
)
{
lex
=
my_xml_scan
(
p
,
&
a
);
exclam
=
1
;
}
else
if
(
MY_XML_QUESTION
==
lex
)
{
lex
=
my_xml_scan
(
p
,
&
a
);
question
=
1
;
}
if
(
MY_XML_IDENT
==
lex
)
{
if
(
MY_XML_OK
!=
my_xml_enter
(
p
,
a
.
beg
,
a
.
end
-
a
.
beg
))
return
MY_XML_ERROR
;
}
else
{
sprintf
(
p
->
errstr
,
"3: %s unexpected (ident or '/' wanted)"
,
lex2str
(
lex
));
return
MY_XML_ERROR
;
}
while
((
MY_XML_IDENT
==
(
lex
=
my_xml_scan
(
p
,
&
a
)))
||
(
MY_XML_STRING
==
lex
))
{
MY_XML_ATTR
b
;
if
(
MY_XML_EQ
==
(
lex
=
my_xml_scan
(
p
,
&
b
)))
{
lex
=
my_xml_scan
(
p
,
&
b
);
if
(
(
lex
==
MY_XML_IDENT
)
||
(
lex
=
MY_XML_STRING
)
)
{
if
((
MY_XML_OK
!=
my_xml_enter
(
p
,
a
.
beg
,
a
.
end
-
a
.
beg
))
||
(
MY_XML_OK
!=
my_xml_value
(
p
,
b
.
beg
,
b
.
end
-
b
.
beg
))
||
(
MY_XML_OK
!=
my_xml_leave
(
p
,
a
.
beg
,
a
.
end
-
a
.
beg
)))
return
MY_XML_ERROR
;
}
else
{
sprintf
(
p
->
errstr
,
"4: %s unexpected (ident or string wanted)"
,
lex2str
(
lex
));
return
MY_XML_ERROR
;
}
}
else
if
(
(
MY_XML_STRING
==
lex
)
||
(
MY_XML_IDENT
==
lex
)
)
{
if
((
MY_XML_OK
!=
my_xml_enter
(
p
,
a
.
beg
,
a
.
end
-
a
.
beg
))
||
(
MY_XML_OK
!=
my_xml_leave
(
p
,
a
.
beg
,
a
.
end
-
a
.
beg
)))
return
MY_XML_ERROR
;
}
else
break
;
}
if
(
lex
==
MY_XML_SLASH
)
{
if
(
MY_XML_OK
!=
my_xml_leave
(
p
,
NULL
,
0
))
return
MY_XML_ERROR
;
lex
=
my_xml_scan
(
p
,
&
a
);
}
gt:
if
(
question
)
{
if
(
lex
!=
MY_XML_QUESTION
)
{
sprintf
(
p
->
errstr
,
"6: %s unexpected ('?' wanted)"
,
lex2str
(
lex
));
return
MY_XML_ERROR
;
}
if
(
MY_XML_OK
!=
my_xml_leave
(
p
,
NULL
,
0
))
return
MY_XML_ERROR
;
lex
=
my_xml_scan
(
p
,
&
a
);
}
if
(
exclam
)
{
if
(
MY_XML_OK
!=
my_xml_leave
(
p
,
NULL
,
0
))
return
MY_XML_ERROR
;
}
if
(
lex
!=
MY_XML_GT
)
{
sprintf
(
p
->
errstr
,
"5: %s unexpected ('>' wanted)"
,
lex2str
(
lex
));
return
MY_XML_ERROR
;
}
}
else
{
a
.
beg
=
p
->
cur
;
for
(
;
(
p
->
cur
<
p
->
end
)
&&
(
p
->
cur
[
0
]
!=
'<'
)
;
p
->
cur
++
);
a
.
end
=
p
->
cur
;
my_xml_norm_text
(
&
a
);
if
(
a
.
beg
!=
a
.
end
)
{
my_xml_value
(
p
,
a
.
beg
,
a
.
end
-
a
.
beg
);
}
}
}
return
MY_XML_OK
;
}
void
my_xml_parser_create
(
MY_XML_PARSER
*
p
)
{
bzero
((
void
*
)
p
,
sizeof
(
p
[
0
]));
}
void
my_xml_parser_free
(
MY_XML_PARSER
*
p
__attribute__
((
unused
)))
{
}
void
my_xml_set_value_handler
(
MY_XML_PARSER
*
p
,
int
(
*
action
)(
MY_XML_PARSER
*
p
,
const
char
*
s
,
uint
l
))
{
p
->
value
=
action
;
}
void
my_xml_set_enter_handler
(
MY_XML_PARSER
*
p
,
int
(
*
action
)(
MY_XML_PARSER
*
p
,
const
char
*
s
,
uint
l
))
{
p
->
enter
=
action
;
}
void
my_xml_set_leave_handler
(
MY_XML_PARSER
*
p
,
int
(
*
action
)(
MY_XML_PARSER
*
p
,
const
char
*
s
,
uint
l
))
{
p
->
leave
=
action
;
}
const
char
*
my_xml_error_string
(
MY_XML_PARSER
*
p
)
{
return
p
->
errstr
;
}
uint
my_xml_error_pos
(
MY_XML_PARSER
*
p
)
{
const
char
*
beg
=
p
->
beg
;
const
char
*
s
;
for
(
s
=
p
->
beg
;
s
<
p
->
cur
;
s
++
)
if
(
s
[
0
]
==
'\n'
)
beg
=
s
;
return
p
->
cur
-
beg
;
}
uint
my_xml_error_lineno
(
MY_XML_PARSER
*
p
)
{
uint
res
=
0
;
const
char
*
s
;
for
(
s
=
p
->
beg
;
s
<
p
->
cur
;
s
++
)
if
(
s
[
0
]
==
'\n'
)
res
++
;
return
res
;
}
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