Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
K
koko
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
koko
Commits
9bbcfa95
Unverified
Commit
9bbcfa95
authored
Nov 06, 2019
by
Eric_Lee
Committed by
GitHub
Nov 06, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #145 from jumpserver/dev
Dev
parents
b185b1a2
b4ed7c7a
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
232 additions
and
42 deletions
+232
-42
README.md
README.md
+1
-1
koko.mo
cmd/locale/zh_CN/LC_MESSAGES/koko.mo
+0
-0
koko.po
cmd/locale/zh_CN/LC_MESSAGES/koko.po
+3
-3
client.go
pkg/common/client.go
+2
-2
table.go
pkg/common/table.go
+0
-1
banner.go
pkg/handler/banner.go
+1
-1
pagination.go
pkg/handler/pagination.go
+1
-2
session.go
pkg/handler/session.go
+3
-6
wrappersession.go
pkg/handler/wrappersession.go
+1
-2
clients.go
pkg/httpd/clients.go
+2
-4
koko.go
pkg/koko/koko.go
+1
-1
rotate.go
pkg/logger/rotate.go
+2
-2
parser.go
pkg/proxy/parser.go
+0
-1
parsercmd.go
pkg/proxy/parsercmd.go
+3
-2
parsercmd_test.go
pkg/proxy/parsercmd_test.go
+11
-7
proxy.go
pkg/proxy/proxy.go
+0
-1
telnetconn.go
pkg/srvconn/telnetconn.go
+6
-6
parser.go
pkg/utils/parser.go
+195
-0
No files found.
README.md
View file @
9bbcfa95
# KoKo
# KoKo
koko是golang版本的的coco;重构了coco的SSH/SFTP服务和web terminal服务。
Koko 是 Go 版本的 coco;重构了 coco 的 SSH/SFTP 服务和 Web Terminal 服务
## 主要功能
## 主要功能
...
...
cmd/locale/zh_CN/LC_MESSAGES/koko.mo
View file @
9bbcfa95
No preview for this file type
cmd/locale/zh_CN/LC_MESSAGES/koko.po
View file @
9bbcfa95
...
@@ -30,17 +30,17 @@ msgstr "部分IP、主机名、备注"
...
@@ -30,17 +30,17 @@ msgstr "部分IP、主机名、备注"
#. i18n.T
#. i18n.T
#: pkg/handler/banner.go:49
#: pkg/handler/banner.go:49
msgid "to search login if unique"
msgid "to search login if unique"
msgstr "
进行
搜索登录(如果唯一)"
msgstr "搜索登录(如果唯一)"
#. i18n.T
#. i18n.T
#: pkg/handler/banner.go:50
#: pkg/handler/banner.go:50
msgid "/ + IP, Hostname, Comment"
msgid "/ + IP, Hostname, Comment"
msgstr "/ + IP,
主机名 or 备注"
msgstr "/ + IP,主机名 or 备注"
#. i18n.T
#. i18n.T
#: pkg/handler/banner.go:50
#: pkg/handler/banner.go:50
msgid "to search, such as: /192.168"
msgid "to search, such as: /192.168"
msgstr "搜索,
如:/192.168"
msgstr "搜索,如:/192.168"
#. i18n.T
#. i18n.T
#: pkg/handler/banner.go:51
#: pkg/handler/banner.go:51
...
...
pkg/common/client.go
View file @
9bbcfa95
...
@@ -37,7 +37,7 @@ type UrlParser interface {
...
@@ -37,7 +37,7 @@ type UrlParser interface {
}
}
func
NewClient
(
timeout
time
.
Duration
,
baseHost
string
)
Client
{
func
NewClient
(
timeout
time
.
Duration
,
baseHost
string
)
Client
{
headers
:=
make
(
map
[
string
]
string
,
0
)
headers
:=
make
(
map
[
string
]
string
)
client
:=
http
.
Client
{
client
:=
http
.
Client
{
Timeout
:
timeout
*
time
.
Second
,
Timeout
:
timeout
*
time
.
Second
,
}
}
...
@@ -46,7 +46,7 @@ func NewClient(timeout time.Duration, baseHost string) Client {
...
@@ -46,7 +46,7 @@ func NewClient(timeout time.Duration, baseHost string) Client {
Timeout
:
timeout
*
time
.
Second
,
Timeout
:
timeout
*
time
.
Second
,
Headers
:
headers
,
Headers
:
headers
,
http
:
client
,
http
:
client
,
cookie
:
make
(
map
[
string
]
string
,
0
),
cookie
:
make
(
map
[
string
]
string
),
}
}
}
}
...
...
pkg/common/table.go
View file @
9bbcfa95
...
@@ -26,7 +26,6 @@ type WrapperTable struct {
...
@@ -26,7 +26,6 @@ type WrapperTable struct {
paddingSize
int
paddingSize
int
bolderSize
int
bolderSize
int
fieldsSize
map
[
string
]
int
// 计算后的最终宽度
fieldsSize
map
[
string
]
int
// 计算后的最终宽度
cleanedData
[]
map
[
string
]
string
Caption
string
Caption
string
}
}
...
...
pkg/handler/banner.go
View file @
9bbcfa95
...
@@ -36,7 +36,7 @@ func (mi *MenuItem) Text() string {
...
@@ -36,7 +36,7 @@ func (mi *MenuItem) Text() string {
if
err
!=
nil
{
if
err
!=
nil
{
logger
.
Error
(
err
)
logger
.
Error
(
err
)
}
}
mi
.
showText
=
string
(
buf
.
Bytes
()
)
mi
.
showText
=
buf
.
String
(
)
return
mi
.
showText
return
mi
.
showText
}
}
...
...
pkg/handler/pagination.go
View file @
9bbcfa95
...
@@ -299,8 +299,7 @@ func (p *UserAssetPagination) displayPageAssets() {
...
@@ -299,8 +299,7 @@ func (p *UserAssetPagination) displayPageAssets() {
var
totalPage
int
var
totalPage
int
var
currentPage
int
var
currentPage
int
var
totalCount
int
var
totalCount
int
var
currentOffset
int
currentOffset
:=
p
.
offset
+
len
(
p
.
currentData
)
currentOffset
=
p
.
offset
+
len
(
p
.
currentData
)
switch
p
.
limit
{
switch
p
.
limit
{
case
0
:
case
0
:
pageSize
=
len
(
p
.
currentData
)
pageSize
=
len
(
p
.
currentData
)
...
...
pkg/handler/session.go
View file @
9bbcfa95
...
@@ -58,9 +58,6 @@ type interactiveHandler struct {
...
@@ -58,9 +58,6 @@ type interactiveHandler struct {
searchResult
[]
model
.
Asset
searchResult
[]
model
.
Asset
allAssets
[]
model
.
Asset
allAssets
[]
model
.
Asset
search
string
offset
int
limit
int
loadDataDone
chan
struct
{}
loadDataDone
chan
struct
{}
assetLoadPolicy
string
assetLoadPolicy
string
...
@@ -297,9 +294,9 @@ func (h *interactiveHandler) displayAssets(assets model.AssetList) {
...
@@ -297,9 +294,9 @@ func (h *interactiveHandler) displayAssets(assets model.AssetList) {
func
(
h
*
interactiveHandler
)
displayNodes
(
nodes
[]
model
.
Node
)
{
func
(
h
*
interactiveHandler
)
displayNodes
(
nodes
[]
model
.
Node
)
{
tree
:=
ConstructAssetNodeTree
(
nodes
)
tree
:=
ConstructAssetNodeTree
(
nodes
)
_
,
err
:
=
io
.
WriteString
(
h
.
term
,
"
\n\r
"
+
getI18nFromMap
(
"NodeHeaderTip"
))
_
,
_
=
io
.
WriteString
(
h
.
term
,
"
\n\r
"
+
getI18nFromMap
(
"NodeHeaderTip"
))
_
,
err
=
io
.
WriteString
(
h
.
term
,
tree
.
String
())
_
,
_
=
io
.
WriteString
(
h
.
term
,
tree
.
String
())
_
,
err
=
io
.
WriteString
(
h
.
term
,
getI18nFromMap
(
"NodeEndTip"
)
+
"
\n\r
"
)
_
,
err
:
=
io
.
WriteString
(
h
.
term
,
getI18nFromMap
(
"NodeEndTip"
)
+
"
\n\r
"
)
if
err
!=
nil
{
if
err
!=
nil
{
logger
.
Info
(
"displayAssetNodes err:"
,
err
)
logger
.
Info
(
"displayAssetNodes err:"
,
err
)
}
}
...
...
pkg/handler/wrappersession.go
View file @
9bbcfa95
...
@@ -61,8 +61,7 @@ func (w *WrapperSession) Read(p []byte) (int, error) {
...
@@ -61,8 +61,7 @@ func (w *WrapperSession) Read(p []byte) (int, error) {
}
}
func
(
w
*
WrapperSession
)
Close
()
error
{
func
(
w
*
WrapperSession
)
Close
()
error
{
var
err
error
err
:=
w
.
inWriter
.
Close
()
err
=
w
.
inWriter
.
Close
()
w
.
initReadPip
()
w
.
initReadPip
()
return
err
return
err
}
}
...
...
pkg/httpd/clients.go
View file @
9bbcfa95
...
@@ -66,10 +66,8 @@ func (c *Connections) GetClients(cID string) (clients []string) {
...
@@ -66,10 +66,8 @@ func (c *Connections) GetClients(cID string) (clients []string) {
}
}
func
(
c
*
Connections
)
DeleteClients
(
cID
string
)
{
func
(
c
*
Connections
)
DeleteClients
(
cID
string
)
{
if
clientIDs
:=
c
.
GetClients
(
cID
);
clientIDs
!=
nil
{
for
_
,
clientID
:=
range
c
.
GetClients
(
cID
)
{
for
_
,
clientID
:=
range
clientIDs
{
clients
.
DeleteClient
(
clientID
)
clients
.
DeleteClient
(
clientID
)
}
}
}
c
.
mu
.
Lock
()
c
.
mu
.
Lock
()
defer
c
.
mu
.
Unlock
()
defer
c
.
mu
.
Unlock
()
...
...
pkg/koko/koko.go
View file @
9bbcfa95
...
@@ -38,7 +38,7 @@ func (c *Coco) Stop() {
...
@@ -38,7 +38,7 @@ func (c *Coco) Stop() {
func
RunForever
()
{
func
RunForever
()
{
ctx
,
cancelFunc
:=
context
.
WithCancel
(
context
.
Background
())
ctx
,
cancelFunc
:=
context
.
WithCancel
(
context
.
Background
())
bootstrap
(
ctx
)
bootstrap
(
ctx
)
gracefulStop
:=
make
(
chan
os
.
Signal
)
gracefulStop
:=
make
(
chan
os
.
Signal
,
1
)
signal
.
Notify
(
gracefulStop
,
syscall
.
SIGTERM
,
syscall
.
SIGINT
,
syscall
.
SIGQUIT
)
signal
.
Notify
(
gracefulStop
,
syscall
.
SIGTERM
,
syscall
.
SIGINT
,
syscall
.
SIGQUIT
)
app
:=
&
Coco
{}
app
:=
&
Coco
{}
app
.
Start
()
app
.
Start
()
...
...
pkg/logger/rotate.go
View file @
9bbcfa95
...
@@ -46,6 +46,6 @@ func (hook *RotateFileHook) Fire(entry *logrus.Entry) (err error) {
...
@@ -46,6 +46,6 @@ func (hook *RotateFileHook) Fire(entry *logrus.Entry) (err error) {
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
hook
.
logWriter
.
Write
(
b
)
_
,
err
=
hook
.
logWriter
.
Write
(
b
)
return
nil
return
}
}
pkg/proxy/parser.go
View file @
9bbcfa95
...
@@ -123,7 +123,6 @@ func (p *Parser) parseInputState(b []byte) []byte {
...
@@ -123,7 +123,6 @@ func (p *Parser) parseInputState(b []byte) []byte {
if
bytes
.
Contains
(
b
,
charEnter
)
{
if
bytes
.
Contains
(
b
,
charEnter
)
{
// 连续输入enter key, 结算上一条可能存在的命令结果
// 连续输入enter key, 结算上一条可能存在的命令结果
p
.
sendCommandRecord
()
p
.
sendCommandRecord
()
p
.
inputState
=
false
p
.
inputState
=
false
// 用户输入了Enter,开始结算命令
// 用户输入了Enter,开始结算命令
p
.
parseCmdInput
()
p
.
parseCmdInput
()
...
...
pkg/proxy/parsercmd.go
View file @
9bbcfa95
...
@@ -13,7 +13,7 @@ import (
...
@@ -13,7 +13,7 @@ import (
var
ps1Pattern
=
regexp
.
MustCompile
(
`^\[?.*@.*\]?[\\$#]\s|mysql>\s`
)
var
ps1Pattern
=
regexp
.
MustCompile
(
`^\[?.*@.*\]?[\\$#]\s|mysql>\s`
)
func
NewCmdParser
(
sid
,
name
string
)
*
CmdParser
{
func
NewCmdParser
(
sid
,
name
string
)
*
CmdParser
{
parser
:=
CmdParser
{
id
:
sid
,
name
:
name
}
parser
:=
CmdParser
{
id
:
sid
,
name
:
name
}
parser
.
initial
()
parser
.
initial
()
return
&
parser
return
&
parser
}
}
...
@@ -102,4 +102,4 @@ func (cp *CmdParser) Parse() string {
...
@@ -102,4 +102,4 @@ func (cp *CmdParser) Parse() string {
cp
.
currentLines
=
make
([]
string
,
0
)
cp
.
currentLines
=
make
([]
string
,
0
)
cp
.
currentLength
=
0
cp
.
currentLength
=
0
return
output
return
output
}
}
\ No newline at end of file
pkg/proxy/parsercmd_test.go
View file @
9bbcfa95
package
proxy
package
proxy
import
(
import
(
"
fmt
"
"
strings
"
"testing"
"testing"
"github.com/jumpserver/koko/pkg/utils"
)
)
func
TestCmdParser_Parse
(
t
*
testing
.
T
)
{
func
TestCmdParser_Parse
(
t
*
testing
.
T
)
{
p
:=
NewCmdParser
()
var
b
=
[]
byte
(
"ifconfig
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[Konfig"
)
var
b
=
[]
byte
(
"ifconfig
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[K
\x08\x1b
[Konfig"
)
data
:=
p
.
Parse
(
b
)
data
:=
utils
.
ParseTerminalData
(
b
)
if
data
!=
"ifconfig"
{
t
.
Error
(
"data should be ifconfig but not"
)
if
strings
.
Join
(
data
,
""
)
!=
"ifconfig"
{
t
.
Error
(
"data should be ifconfig but not"
,
data
)
}
}
b
=
[]
byte
(
"ifconfig
\xe4\xbd\xa0
"
)
b
=
[]
byte
(
"ifconfig
\xe4\xbd\xa0
"
)
data
=
p
.
Parse
(
b
)
data
=
utils
.
ParseTerminalData
(
b
)
fmt
.
Println
(
"line: "
,
data
)
t
.
Log
(
"line: "
,
strings
.
Join
(
data
,
""
))
}
}
pkg/proxy/proxy.go
View file @
9bbcfa95
...
@@ -237,7 +237,6 @@ func (p *ProxyServer) sendConnectErrorMsg(err error) {
...
@@ -237,7 +237,6 @@ func (p *ProxyServer) sendConnectErrorMsg(err error) {
msg2
:=
fmt
.
Sprintf
(
"Try password: %s"
,
password
[
:
showLen
]
+
strings
.
Repeat
(
"*"
,
hiddenLen
))
msg2
:=
fmt
.
Sprintf
(
"Try password: %s"
,
password
[
:
showLen
]
+
strings
.
Repeat
(
"*"
,
hiddenLen
))
logger
.
Errorf
(
msg2
)
logger
.
Errorf
(
msg2
)
}
}
return
}
}
// Proxy 代理
// Proxy 代理
...
...
pkg/srvconn/telnetconn.go
View file @
9bbcfa95
...
@@ -27,7 +27,7 @@ const (
...
@@ -27,7 +27,7 @@ const (
SAG
=
3
SAG
=
3
ECHO
=
1
ECHO
=
1
loginRegs
=
"(?i)login:?
\\
s*$|username:?
\\
s*$|name:?
\\
s*$|用户名:?
\\
s*$|账
\\
s*号:?
\\
s*$"
loginRegs
=
"(?i)login:?
\\
s*$|username:?
\\
s*$|name:?
\\
s*$|用户名:?
\\
s*$|账
\\
s*号:?
\\
s*$
|user:?
\\
s*$
"
passwordRegs
=
"(?i)Password:?
\\
s*$|ssword:?
\\
s*$|passwd:?
\\
s*$|密
\\
s*码:?
\\
s*$"
passwordRegs
=
"(?i)Password:?
\\
s*$|ssword:?
\\
s*$|passwd:?
\\
s*$|密
\\
s*码:?
\\
s*$"
FailedRegs
=
"(?i)incorrect|failed|失败|错误"
FailedRegs
=
"(?i)incorrect|failed|失败|错误"
DefaultSuccessRegs
=
"(?i)Last
\\
s*login|success|成功|#|>|
\\
$"
DefaultSuccessRegs
=
"(?i)Last
\\
s*login|success|成功|#|>|
\\
$"
...
@@ -122,11 +122,11 @@ func (tc *ServerTelnetConnection) login(data []byte) AuthStatus {
...
@@ -122,11 +122,11 @@ func (tc *ServerTelnetConnection) login(data []byte) AuthStatus {
return
AuthFailed
return
AuthFailed
}
else
if
usernamePattern
.
Match
(
data
)
{
}
else
if
usernamePattern
.
Match
(
data
)
{
_
,
_
=
tc
.
conn
.
Write
([]
byte
(
tc
.
SystemUser
.
Username
+
"
\r\n
"
))
_
,
_
=
tc
.
conn
.
Write
([]
byte
(
tc
.
SystemUser
.
Username
+
"
\r\n
"
))
logger
.
Debug
(
"Username pattern match:
"
,
data
)
logger
.
Debug
f
(
"Username pattern match: %s
"
,
data
)
return
AuthPartial
return
AuthPartial
}
else
if
passwordPattern
.
Match
(
data
)
{
}
else
if
passwordPattern
.
Match
(
data
)
{
_
,
_
=
tc
.
conn
.
Write
([]
byte
(
tc
.
SystemUser
.
Password
+
"
\r\n
"
))
_
,
_
=
tc
.
conn
.
Write
([]
byte
(
tc
.
SystemUser
.
Password
+
"
\r\n
"
))
logger
.
Debug
(
"Password pattern
"
,
data
)
logger
.
Debug
f
(
"Password pattern: %s
"
,
data
)
return
AuthPartial
return
AuthPartial
}
else
if
successPattern
.
Match
(
data
)
{
}
else
if
successPattern
.
Match
(
data
)
{
return
AuthSuccess
return
AuthSuccess
...
@@ -148,7 +148,7 @@ func (tc *ServerTelnetConnection) Connect(h, w int, term string) (err error) {
...
@@ -148,7 +148,7 @@ func (tc *ServerTelnetConnection) Connect(h, w int, term string) (err error) {
if
asset
.
Domain
!=
""
{
if
asset
.
Domain
!=
""
{
sshConfig
:=
MakeConfig
(
tc
.
Asset
,
tc
.
SystemUser
,
tc
.
Timeout
())
sshConfig
:=
MakeConfig
(
tc
.
Asset
,
tc
.
SystemUser
,
tc
.
Timeout
())
proxyConn
,
err
=
sshConfig
.
DialProxy
()
proxyConn
,
err
=
sshConfig
.
DialProxy
()
logger
.
Errorf
(
"Proxy conn: "
,
proxyConn
)
logger
.
Errorf
(
"Proxy conn:
%p
"
,
proxyConn
)
if
err
!=
nil
{
if
err
!=
nil
{
logger
.
Error
(
"Dial proxy host error"
)
logger
.
Error
(
"Dial proxy host error"
)
return
return
...
@@ -169,7 +169,7 @@ func (tc *ServerTelnetConnection) Connect(h, w int, term string) (err error) {
...
@@ -169,7 +169,7 @@ func (tc *ServerTelnetConnection) Connect(h, w int, term string) (err error) {
return
return
}
}
if
tc
.
SystemUser
.
Password
==
""
{
if
tc
.
SystemUser
.
Password
==
""
{
info
:=
service
.
GetSystemUserAssetAuthInfo
(
tc
.
SystemUser
.
ID
,
asset
.
ID
)
info
:=
service
.
GetSystemUserAssetAuthInfo
(
tc
.
SystemUser
.
ID
,
asset
.
ID
)
tc
.
SystemUser
.
Password
=
info
.
Password
tc
.
SystemUser
.
Password
=
info
.
Password
tc
.
SystemUser
.
PrivateKey
=
info
.
PrivateKey
tc
.
SystemUser
.
PrivateKey
=
info
.
PrivateKey
...
@@ -193,7 +193,7 @@ func (tc *ServerTelnetConnection) Connect(h, w int, term string) (err error) {
...
@@ -193,7 +193,7 @@ func (tc *ServerTelnetConnection) Connect(h, w int, term string) (err error) {
case
AuthSuccess
:
case
AuthSuccess
:
return
nil
return
nil
case
AuthFailed
:
case
AuthFailed
:
return
errors
.
New
(
"
F
ailed login"
)
return
errors
.
New
(
"
f
ailed login"
)
default
:
default
:
continue
continue
}
}
...
...
pkg/utils/parser.go
0 → 100644
View file @
9bbcfa95
package
utils
import
(
"bytes"
"fmt"
"unicode/utf8"
)
func
ParseTerminalData
(
p
[]
byte
)
(
lines
[]
string
)
{
c
:=
bytes
.
NewReader
(
p
)
pasteActive
:=
false
var
line
[]
rune
var
pos
int
var
remainder
[]
byte
var
inBuf
[
256
]
byte
for
{
rest
:=
remainder
lineOk
:=
false
for
!
lineOk
{
var
key
rune
key
,
rest
=
bytesToKey
(
rest
,
pasteActive
)
if
key
==
utf8
.
RuneError
{
break
}
if
!
pasteActive
{
if
key
==
keyPasteStart
{
pasteActive
=
true
if
len
(
line
)
==
0
{
}
continue
}
}
else
if
key
==
keyPasteEnd
{
pasteActive
=
false
continue
}
switch
key
{
case
keyBackspace
:
if
pos
==
0
{
continue
}
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
case
keyAltLeft
:
// move left by a word.
pos
-=
CountToLeftWord
(
pos
,
line
)
case
keyAltRight
:
// move right by a word.
pos
+=
CountToRightWord
(
pos
,
line
)
case
keyLeft
:
if
pos
==
0
{
continue
}
pos
--
case
keyRight
:
if
pos
==
len
(
line
)
{
continue
}
pos
++
case
keyHome
:
if
pos
==
0
{
continue
}
pos
=
0
case
keyEnd
:
if
pos
==
len
(
line
)
{
continue
}
pos
=
len
(
line
)
case
keyUp
:
line
=
[]
rune
{}
pos
=
0
case
keyDown
:
line
=
[]
rune
{}
pos
=
0
case
keyEnter
:
lines
=
append
(
lines
,
string
(
line
))
line
=
line
[
:
0
]
pos
=
0
lineOk
=
true
case
keyDeleteWord
:
// Delete zero or more spaces and then one or more characters.
line
,
pos
=
EraseNPreviousChars
(
CountToLeftWord
(
pos
,
line
),
pos
,
line
)
case
keyDeleteLine
:
line
=
line
[
:
pos
]
case
keyCtrlD
:
// Erase the character under the current position.
// The EOF case when the line is empty is handled in
// readLine().
if
pos
<
len
(
line
)
{
pos
++
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
}
case
keyCtrlU
:
line
=
line
[
:
0
]
case
keyClearScreen
:
default
:
if
!
isPrintable
(
key
)
{
fmt
.
Println
(
"could not printable: "
,
[]
byte
(
string
(
key
)),
" "
,
key
)
continue
}
line
,
pos
=
AddKeyToLine
(
key
,
pos
,
line
)
}
}
if
len
(
rest
)
>
0
{
n
:=
copy
(
inBuf
[
:
],
rest
)
remainder
=
inBuf
[
:
n
]
}
else
{
remainder
=
nil
}
// remainder is a slice at the beginning of t.inBuf
// containing a partial key sequence
readBuf
:=
inBuf
[
len
(
remainder
)
:
]
var
n
int
n
,
err
:=
c
.
Read
(
readBuf
)
if
err
!=
nil
{
if
len
(
line
)
>
0
{
lines
=
append
(
lines
,
string
(
line
))
}
else
if
len
(
rest
)
>
0
{
lines
=
append
(
lines
,
string
(
rest
))
}
return
}
remainder
=
inBuf
[
:
n
+
len
(
remainder
)]
}
}
func
EraseNPreviousChars
(
n
,
cPos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
if
n
==
0
{
return
line
,
cPos
}
if
cPos
<
n
{
n
=
cPos
}
cPos
-=
n
copy
(
line
[
cPos
:
],
line
[
n
+
cPos
:
])
return
line
[
:
len
(
line
)
-
n
],
cPos
}
func
CountToLeftWord
(
currentPos
int
,
line
[]
rune
)
int
{
if
currentPos
==
0
{
return
0
}
pos
:=
currentPos
-
1
for
pos
>
0
{
if
line
[
pos
]
!=
' '
{
break
}
pos
--
}
for
pos
>
0
{
if
line
[
pos
]
==
' '
{
pos
++
break
}
pos
--
}
return
currentPos
-
pos
}
func
CountToRightWord
(
currentPos
int
,
line
[]
rune
)
int
{
pos
:=
currentPos
for
pos
<
len
(
line
)
{
if
line
[
pos
]
==
' '
{
break
}
pos
++
}
for
pos
<
len
(
line
)
{
if
line
[
pos
]
!=
' '
{
break
}
pos
++
}
return
pos
-
currentPos
}
func
AddKeyToLine
(
key
rune
,
pos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
if
len
(
line
)
==
cap
(
line
)
{
newLine
:=
make
([]
rune
,
len
(
line
),
2
*
(
1
+
len
(
line
)))
copy
(
newLine
,
line
)
line
=
newLine
}
line
=
line
[
:
len
(
line
)
+
1
]
copy
(
line
[
pos
+
1
:
],
line
[
pos
:
])
line
[
pos
]
=
key
pos
++
return
line
,
pos
}
\ No newline at end of file
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