-
Notifications
You must be signed in to change notification settings - Fork 84
Expand file tree
/
Copy pathconsole.go
More file actions
107 lines (95 loc) · 2.89 KB
/
console.go
File metadata and controls
107 lines (95 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
package console
import (
"bufio"
"os"
"github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
"github.com/peterh/liner"
)
type console struct {
impl *liner.State
historyFile string
prompt string
stdinRedirected bool
stdinReader *bufio.Reader
}
// NewConsole creates a sqlcmdConsole implementation that provides these features:
// - Storage of input history to a local file. History can be scrolled through using the up and down arrow keys.
// - Simple tab key completion of SQL keywords
func NewConsole(historyFile string) sqlcmd.Console {
c := &console{
impl: liner.NewLiner(),
historyFile: historyFile,
stdinRedirected: isStdinRedirected(),
}
if c.stdinRedirected {
c.stdinReader = bufio.NewReader(os.Stdin)
} else {
c.impl.SetCtrlCAborts(true)
c.impl.SetCompleter(CompleteLine)
if c.historyFile != "" {
if f, err := os.Open(historyFile); err == nil {
_, _ = c.impl.ReadHistory(f)
f.Close()
}
}
}
return c
}
// Close writes out the history data to disk and closes the console buffers
func (c *console) Close() {
if !c.stdinRedirected && c.historyFile != "" {
if f, err := os.Create(c.historyFile); err == nil {
_, _ = c.impl.WriteHistory(f)
f.Close()
}
}
if !c.stdinRedirected {
c.impl.Close()
}
}
// Readline displays the current prompt and returns a line of text entered by the user.
// It appends the returned line to the history buffer.
// If the user presses Ctrl-C the error returned is sqlcmd.ErrCtrlC
// If stdin is redirected, it reads directly from stdin without displaying prompts
func (c *console) Readline() (string, error) {
// Handle redirected stdin without displaying prompts
if c.stdinRedirected {
line, err := c.stdinReader.ReadString('\n')
if err != nil {
return "", err
}
// Trim the trailing newline
if len(line) > 0 && line[len(line)-1] == '\n' {
line = line[:len(line)-1]
// Also trim carriage return if present
if len(line) > 0 && line[len(line)-1] == '\r' {
line = line[:len(line)-1]
}
}
return line, nil
}
// Interactive terminal mode with prompts
s, err := c.impl.Prompt(c.prompt)
if err == liner.ErrPromptAborted {
return "", sqlcmd.ErrCtrlC
}
c.impl.AppendHistory(s)
return s, err
}
// ReadPassword displays the given prompt and returns the password entered by the user.
// If the user presses Ctrl-C the error returned is sqlcmd.ErrCtrlC
func (c *console) ReadPassword(prompt string) ([]byte, error) {
// Even when stdin is redirected, we need to use the prompt for passwords
// since they should not be read from the redirected input
b, err := c.impl.PasswordPrompt(prompt)
if err == liner.ErrPromptAborted {
return []byte{}, sqlcmd.ErrCtrlC
}
return []byte(b), err
}
// SetPrompt sets the prompt text shown to input the next line
func (c *console) SetPrompt(s string) {
c.prompt = s
}