Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
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
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
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
PRGNAME
=
redis-server
...
...
@@ -34,12 +34,15 @@ ae_kqueue.o: ae_kqueue.c
ae_select.o
:
ae_select.c
anet.o
:
anet.c fmacros.h anet.h
dict.o
:
dict.c fmacros.h dict.h zmalloc.h
linenoise.o
:
linenoise.c
lzf_c.o
:
lzf_c.c lzfP.h
lzf_d.o
:
lzf_d.c lzfP.h
pqsort.o
:
pqsort.c
redis-benchmark.o
:
redis-benchmark.c fmacros.h ae.h anet.h sds.h adlist.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
\
adlist.h zmalloc.h lzf.h pqsort.h zipmap.h staticsymbols.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 @@
#include "sds.h"
#include "adlist.h"
#include "zmalloc.h"
#include "linenoise.h"
#define REDIS_CMD_INLINE 1
#define REDIS_CMD_BULK 2
...
...
@@ -451,39 +452,30 @@ static char **convertToSds(int count, char** args) {
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
()
{
int
size
=
4096
,
max
=
size
>>
1
,
argc
;
char
buffer
[
size
];
char
*
line
=
buffer
;
char
*
line
;
char
**
ap
,
*
args
[
max
];
while
(
prompt
(
line
,
size
))
{
argc
=
0
;
for
(
ap
=
args
;
(
*
ap
=
strsep
(
&
line
,
"
\t
"
))
!=
NULL
;)
{
if
(
**
ap
!=
'\0'
)
{
if
(
argc
>=
max
)
break
;
if
(
strcasecmp
(
*
ap
,
"quit"
)
==
0
||
strcasecmp
(
*
ap
,
"exit"
)
==
0
)
exit
(
0
);
ap
++
;
argc
++
;
}
while
((
line
=
linenoise
(
">> "
))
!=
NULL
)
{
if
(
line
[
0
]
!=
'\0'
)
{
linenoiseHistoryAdd
(
line
);
argc
=
0
;
for
(
ap
=
args
;
(
*
ap
=
strsep
(
&
line
,
"
\t
"
))
!=
NULL
;)
{
if
(
**
ap
!=
'\0'
)
{
if
(
argc
>=
max
)
break
;
if
(
strcasecmp
(
*
ap
,
"quit"
)
==
0
||
strcasecmp
(
*
ap
,
"exit"
)
==
0
)
exit
(
0
);
ap
++
;
argc
++
;
}
}
cliSendCommand
(
argc
,
convertToSds
(
argc
,
args
),
1
);
}
cliSendCommand
(
argc
,
convertToSds
(
argc
,
args
),
1
);
line
=
buffer
;
free
(
line
);
}
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