Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
cf87ebf2
Commit
cf87ebf2
authored
Mar 23, 2010
by
Michel Martens
Browse files
Use linenoise for line editing on redis-cli.
parent
aab055ae
Changes
4
Hide whitespace changes
Inline
Side-by-side
Makefile
View file @
cf87ebf2
...
@@ -16,7 +16,7 @@ DEBUG?= -g -rdynamic -ggdb
...
@@ -16,7 +16,7 @@ DEBUG?= -g -rdynamic -ggdb
OBJ
=
adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o
OBJ
=
adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o
BENCHOBJ
=
ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
BENCHOBJ
=
ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ
=
anet.o sds.o adlist.o redis-cli.o zmalloc.o
CLIOBJ
=
anet.o sds.o adlist.o redis-cli.o zmalloc.o
linenoise.o
CHECKDUMPOBJ
=
redis-check-dump.o lzf_c.o lzf_d.o
CHECKDUMPOBJ
=
redis-check-dump.o lzf_c.o lzf_d.o
PRGNAME
=
redis-server
PRGNAME
=
redis-server
...
@@ -34,12 +34,15 @@ ae_kqueue.o: ae_kqueue.c
...
@@ -34,12 +34,15 @@ ae_kqueue.o: ae_kqueue.c
ae_select.o
:
ae_select.c
ae_select.o
:
ae_select.c
anet.o
:
anet.c fmacros.h anet.h
anet.o
:
anet.c fmacros.h anet.h
dict.o
:
dict.c fmacros.h dict.h zmalloc.h
dict.o
:
dict.c fmacros.h dict.h zmalloc.h
linenoise.o
:
linenoise.c
lzf_c.o
:
lzf_c.c lzfP.h
lzf_c.o
:
lzf_c.c lzfP.h
lzf_d.o
:
lzf_d.c lzfP.h
lzf_d.o
:
lzf_d.c lzfP.h
pqsort.o
:
pqsort.c
pqsort.o
:
pqsort.c
redis-benchmark.o
:
redis-benchmark.c fmacros.h ae.h anet.h sds.h adlist.h
\
redis-benchmark.o
:
redis-benchmark.c fmacros.h ae.h anet.h sds.h adlist.h
\
zmalloc.h
zmalloc.h
redis-cli.o
:
redis-cli.c fmacros.h anet.h sds.h adlist.h zmalloc.h
redis-check-dump.o
:
redis-check-dump.c lzf.h
redis-cli.o
:
redis-cli.c fmacros.h anet.h sds.h adlist.h zmalloc.h
\
linenoise.h
redis.o
:
redis.c fmacros.h config.h redis.h ae.h sds.h anet.h dict.h
\
redis.o
:
redis.c fmacros.h config.h redis.h ae.h sds.h anet.h dict.h
\
adlist.h zmalloc.h lzf.h pqsort.h zipmap.h staticsymbols.h
adlist.h zmalloc.h lzf.h pqsort.h zipmap.h staticsymbols.h
sds.o
:
sds.c sds.h zmalloc.h
sds.o
:
sds.c sds.h zmalloc.h
...
...
linenoise.c
0 → 100644
View file @
cf87ebf2
/* linenoise.c -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*
* You can find the latest source code at:
*
* http://github.com/antirez/linenoise
*
* Does a number of crazy assumptions that happen to be true in 99.9999% of
* the 2010 UNIX computers around.
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* References:
* - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
* - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
*
* Todo list:
* - Switch to gets() if $TERM is something we can't support.
* - Filter bogus Ctrl+<char> combinations.
* - Win32 support
*
* Bloat:
* - Completion?
* - History search like Ctrl+r in readline?
*
* List of escape sequences used by this program, we do everything just
* with three sequences. In order to be so cheap we may have some
* flickering effect with some slow terminal, but the lesser sequences
* the more compatible.
*
* CHA (Cursor Horizontal Absolute)
* Sequence: ESC [ n G
* Effect: moves cursor to column n
*
* EL (Erase Line)
* Sequence: ESC [ n K
* Effect: if n is 0 or missing, clear from cursor to end of line
* Effect: if n is 1, clear from beginning of line to cursor
* Effect: if n is 2, clear entire line
*
* CUF (CUrsor Forward)
* Sequence: ESC [ n C
* Effect: moves cursor forward of n chars
*
*/
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define LINENOISE_MAX_LINE 4096
static
struct
termios
orig_termios
;
/* in order to restore at exit */
static
int
rawmode
=
0
;
/* for atexit() function to check if restore is needed*/
static
int
atexit_registered
=
0
;
/* register atexit just 1 time */
static
int
history_max_len
=
100
;
static
int
history_len
=
0
;
char
**
history
=
NULL
;
static
void
linenoiseAtExit
(
void
);
int
linenoiseHistoryAdd
(
char
*
line
);
static
void
freeHistory
(
void
)
{
if
(
history
)
{
int
j
;
for
(
j
=
0
;
j
<
history_len
;
j
++
)
free
(
history
[
j
]);
free
(
history
);
}
}
static
int
enableRawMode
(
int
fd
)
{
struct
termios
raw
;
if
(
!
isatty
(
STDIN_FILENO
))
goto
fatal
;
if
(
!
atexit_registered
)
{
atexit
(
linenoiseAtExit
);
atexit_registered
=
1
;
}
if
(
tcgetattr
(
fd
,
&
orig_termios
)
==
-
1
)
goto
fatal
;
raw
=
orig_termios
;
/* modify the original mode */
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start/stop output control. */
raw
.
c_iflag
&=
~
(
BRKINT
|
ICRNL
|
INPCK
|
ISTRIP
|
IXON
);
/* output modes - disable post processing */
raw
.
c_oflag
&=
~
(
OPOST
);
/* control modes - set 8 bit chars */
raw
.
c_cflag
|=
(
CS8
);
/* local modes - choing off, canonical off, no extended functions,
* no signal chars (^Z,^C) */
raw
.
c_lflag
&=
~
(
ECHO
|
ICANON
|
IEXTEN
|
ISIG
);
/* control chars - set return condition: min number of bytes and timer.
* We want read to return every single byte, without timeout. */
raw
.
c_cc
[
VMIN
]
=
1
;
raw
.
c_cc
[
VTIME
]
=
0
;
/* 1 byte, no timer */
/* put terminal in raw mode after flushing */
if
(
tcsetattr
(
fd
,
TCSAFLUSH
,
&
raw
)
<
0
)
goto
fatal
;
rawmode
=
1
;
return
0
;
fatal:
errno
=
ENOTTY
;
return
-
1
;
}
static
void
disableRawMode
(
int
fd
)
{
/* Don't even check the return value as it's too late. */
if
(
rawmode
&&
tcsetattr
(
fd
,
TCSAFLUSH
,
&
orig_termios
)
!=
-
1
)
rawmode
=
0
;
}
/* At exit we'll try to fix the terminal to the initial conditions. */
static
void
linenoiseAtExit
(
void
)
{
disableRawMode
(
STDIN_FILENO
);
freeHistory
();
}
static
int
getColumns
(
void
)
{
struct
winsize
ws
;
if
(
ioctl
(
1
,
TIOCGWINSZ
,
&
ws
)
==
-
1
)
return
80
;
return
ws
.
ws_col
;
}
static
void
refreshLine
(
int
fd
,
const
char
*
prompt
,
char
*
buf
,
size_t
len
,
size_t
pos
,
size_t
cols
)
{
char
seq
[
64
];
size_t
plen
=
strlen
(
prompt
);
while
((
plen
+
pos
)
>=
cols
)
{
buf
++
;
len
--
;
pos
--
;
}
while
(
plen
+
len
>
cols
)
{
len
--
;
}
/* Cursor to left edge */
snprintf
(
seq
,
64
,
"
\x1b
[0G"
);
if
(
write
(
fd
,
seq
,
strlen
(
seq
))
==
-
1
)
return
;
/* Write the prompt and the current buffer content */
if
(
write
(
fd
,
prompt
,
strlen
(
prompt
))
==
-
1
)
return
;
if
(
write
(
fd
,
buf
,
len
)
==
-
1
)
return
;
/* Erase to right */
snprintf
(
seq
,
64
,
"
\x1b
[0K"
);
if
(
write
(
fd
,
seq
,
strlen
(
seq
))
==
-
1
)
return
;
/* Move cursor to original position. */
snprintf
(
seq
,
64
,
"
\x1b
[0G
\x1b
[%dC"
,
(
int
)(
pos
+
plen
));
if
(
write
(
fd
,
seq
,
strlen
(
seq
))
==
-
1
)
return
;
}
static
int
linenoisePrompt
(
int
fd
,
char
*
buf
,
size_t
buflen
,
const
char
*
prompt
)
{
size_t
plen
=
strlen
(
prompt
);
size_t
pos
=
0
;
size_t
len
=
0
;
size_t
cols
=
getColumns
();
int
history_index
=
0
;
buf
[
0
]
=
'\0'
;
buflen
--
;
/* Make sure there is always space for the nulterm */
/* The latest history entry is always our current buffer, that
* initially is just an empty string. */
linenoiseHistoryAdd
(
""
);
if
(
write
(
fd
,
prompt
,
plen
)
==
-
1
)
return
-
1
;
while
(
1
)
{
char
c
;
int
nread
;
char
seq
[
2
];
nread
=
read
(
fd
,
&
c
,
1
);
if
(
nread
<=
0
)
return
len
;
switch
(
c
)
{
case
13
:
/* enter */
history_len
--
;
return
len
;
case
4
:
/* ctrl-d */
history_len
--
;
return
(
len
==
0
)
?
-
1
:
(
int
)
len
;
case
3
:
/* ctrl-c */
errno
=
EAGAIN
;
return
-
1
;
case
127
:
/* backspace */
case
8
:
/* ctrl-h */
if
(
pos
>
0
&&
len
>
0
)
{
memmove
(
buf
+
pos
-
1
,
buf
+
pos
,
len
-
pos
);
pos
--
;
len
--
;
buf
[
len
]
=
'\0'
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
}
break
;
case
20
:
/* ctrl-t */
if
(
pos
>
0
&&
pos
<
len
)
{
int
aux
=
buf
[
pos
-
1
];
buf
[
pos
-
1
]
=
buf
[
pos
];
buf
[
pos
]
=
aux
;
if
(
pos
!=
len
-
1
)
pos
++
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
}
break
;
case
2
:
/* ctrl-b */
goto
left_arrow
;
case
6
:
/* ctrl-f */
goto
right_arrow
;
case
16
:
/* ctrl-p */
seq
[
1
]
=
65
;
goto
up_down_arrow
;
case
14
:
/* ctrl-n */
seq
[
1
]
=
66
;
goto
up_down_arrow
;
break
;
case
27
:
/* escape sequence */
if
(
read
(
fd
,
seq
,
2
)
==
-
1
)
break
;
if
(
seq
[
0
]
==
91
&&
seq
[
1
]
==
68
)
{
left_arrow:
/* left arrow */
if
(
pos
>
0
)
{
pos
--
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
}
}
else
if
(
seq
[
0
]
==
91
&&
seq
[
1
]
==
67
)
{
right_arrow:
/* right arrow */
if
(
pos
!=
len
)
{
pos
++
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
}
}
else
if
(
seq
[
0
]
==
91
&&
(
seq
[
1
]
==
65
||
seq
[
1
]
==
66
))
{
up_down_arrow:
/* up and down arrow: history */
if
(
history_len
>
1
)
{
/* Update the current history entry before to
* overwrite it with tne next one. */
free
(
history
[
history_len
-
1
-
history_index
]);
history
[
history_len
-
1
-
history_index
]
=
strdup
(
buf
);
/* Show the new entry */
history_index
+=
(
seq
[
1
]
==
65
)
?
1
:
-
1
;
if
(
history_index
<
0
)
{
history_index
=
0
;
break
;
}
else
if
(
history_index
>=
history_len
)
{
history_index
=
history_len
-
1
;
break
;
}
strncpy
(
buf
,
history
[
history_len
-
1
-
history_index
],
buflen
);
buf
[
buflen
]
=
'\0'
;
len
=
pos
=
strlen
(
buf
);
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
}
}
break
;
default:
if
(
len
<
buflen
)
{
if
(
len
==
pos
)
{
buf
[
pos
]
=
c
;
pos
++
;
len
++
;
buf
[
len
]
=
'\0'
;
if
(
plen
+
len
<
cols
)
{
/* Avoid a full update of the line in the
* trivial case. */
if
(
write
(
fd
,
&
c
,
1
)
==
-
1
)
return
-
1
;
}
else
{
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
}
}
else
{
memmove
(
buf
+
pos
+
1
,
buf
+
pos
,
len
-
pos
);
buf
[
pos
]
=
c
;
len
++
;
pos
++
;
buf
[
len
]
=
'\0'
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
}
}
break
;
case
21
:
/* Ctrl+u, delete the whole line. */
buf
[
0
]
=
'\0'
;
pos
=
len
=
0
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
break
;
case
11
:
/* Ctrl+k, delete from current to end of line. */
buf
[
pos
]
=
'\0'
;
len
=
pos
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
break
;
case
1
:
/* Ctrl+a, go to the start of the line */
pos
=
0
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
break
;
case
5
:
/* ctrl+e, go to the end of the line */
pos
=
len
;
refreshLine
(
fd
,
prompt
,
buf
,
len
,
pos
,
cols
);
break
;
}
}
return
len
;
}
static
int
linenoiseRaw
(
char
*
buf
,
size_t
buflen
,
const
char
*
prompt
)
{
int
fd
=
STDIN_FILENO
;
int
count
;
if
(
buflen
==
0
)
{
errno
=
EINVAL
;
return
-
1
;
}
if
(
enableRawMode
(
fd
)
==
-
1
)
return
-
1
;
count
=
linenoisePrompt
(
fd
,
buf
,
buflen
,
prompt
);
disableRawMode
(
fd
);
printf
(
"
\n
"
);
return
count
;
}
char
*
linenoise
(
const
char
*
prompt
)
{
char
buf
[
LINENOISE_MAX_LINE
];
int
count
;
count
=
linenoiseRaw
(
buf
,
LINENOISE_MAX_LINE
,
prompt
);
if
(
count
==
-
1
)
return
NULL
;
return
strdup
(
buf
);
}
/* Using a circular buffer is smarter, but a bit more complex to handle. */
int
linenoiseHistoryAdd
(
char
*
line
)
{
if
(
history_max_len
==
0
)
return
0
;
if
(
history
==
0
)
{
history
=
malloc
(
sizeof
(
char
*
)
*
history_max_len
);
if
(
history
==
NULL
)
return
0
;
memset
(
history
,
0
,(
sizeof
(
char
*
)
*
history_max_len
));
}
line
=
strdup
(
line
);
if
(
!
line
)
return
0
;
if
(
history_len
==
history_max_len
)
{
memmove
(
history
,
history
+
1
,
sizeof
(
char
*
)
*
(
history_max_len
-
1
));
history_len
--
;
}
history
[
history_len
]
=
line
;
history_len
++
;
return
1
;
}
int
linenoiseHistorySetMaxLen
(
int
len
)
{
char
**
new
;
if
(
len
<
1
)
return
0
;
if
(
history
)
{
int
tocopy
=
history_len
;
new
=
malloc
(
sizeof
(
char
*
)
*
len
);
if
(
new
==
NULL
)
return
0
;
if
(
len
<
tocopy
)
tocopy
=
len
;
memcpy
(
new
,
history
+
(
history_max_len
-
tocopy
),
sizeof
(
char
*
)
*
tocopy
);
free
(
history
);
history
=
new
;
}
history_max_len
=
len
;
if
(
history_len
>
history_max_len
)
history_len
=
history_max_len
;
return
1
;
}
linenoise.h
0 → 100644
View file @
cf87ebf2
/* linenoise.h -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
char
*
linenoise
(
const
char
*
prompt
);
int
linenoiseHistoryAdd
(
char
*
line
);
int
linenoiseHistorySetMaxLen
(
int
len
);
#endif
/* __LINENOISE_H */
redis-cli.c
View file @
cf87ebf2
...
@@ -39,6 +39,7 @@
...
@@ -39,6 +39,7 @@
#include "sds.h"
#include "sds.h"
#include "adlist.h"
#include "adlist.h"
#include "zmalloc.h"
#include "zmalloc.h"
#include "linenoise.h"
#define REDIS_CMD_INLINE 1
#define REDIS_CMD_INLINE 1
#define REDIS_CMD_BULK 2
#define REDIS_CMD_BULK 2
...
@@ -451,39 +452,30 @@ static char **convertToSds(int count, char** args) {
...
@@ -451,39 +452,30 @@ static char **convertToSds(int count, char** args) {
return
sds
;
return
sds
;
}
}
static
char
*
prompt
(
char
*
line
,
int
size
)
{
char
*
retval
;
do
{
printf
(
">> "
);
retval
=
fgets
(
line
,
size
,
stdin
);
}
while
(
retval
&&
*
line
==
'\n'
);
line
[
strlen
(
line
)
-
1
]
=
'\0'
;
return
retval
;
}
static
void
repl
()
{
static
void
repl
()
{
int
size
=
4096
,
max
=
size
>>
1
,
argc
;
int
size
=
4096
,
max
=
size
>>
1
,
argc
;
char
buffer
[
size
];
char
*
line
;
char
*
line
=
buffer
;
char
**
ap
,
*
args
[
max
];
char
**
ap
,
*
args
[
max
];
while
(
prompt
(
line
,
size
))
{
while
((
line
=
linenoise
(
">> "
))
!=
NULL
)
{
argc
=
0
;
if
(
line
[
0
]
!=
'\0'
)
{
linenoiseHistoryAdd
(
line
);
for
(
ap
=
args
;
(
*
ap
=
strsep
(
&
line
,
"
\t
"
))
!=
NULL
;)
{
argc
=
0
;
if
(
**
ap
!=
'\0'
)
{
if
(
argc
>=
max
)
break
;
for
(
ap
=
args
;
(
*
ap
=
strsep
(
&
line
,
"
\t
"
))
!=
NULL
;)
{
if
(
strcasecmp
(
*
ap
,
"quit"
)
==
0
||
strcasecmp
(
*
ap
,
"exit"
)
==
0
)
if
(
**
ap
!=
'\0'
)
{
exit
(
0
);
if
(
argc
>=
max
)
break
;
ap
++
;
if
(
strcasecmp
(
*
ap
,
"quit"
)
==
0
||
strcasecmp
(
*
ap
,
"exit"
)
==
0
)
argc
++
;
exit
(
0
);
}
ap
++
;
argc
++
;
}
}
cliSendCommand
(
argc
,
convertToSds
(
argc
,
args
),
1
);
}
}
cliSendCommand
(
argc
,
convertToSds
(
argc
,
args
),
1
);
free
(
line
);
line
=
buffer
;
}
}
exit
(
0
);
exit
(
0
);
...
...
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