mirror of
https://github.com/rwinkhart/go-boilerplate.git
synced 2026-08-28 04:46:41 -04:00
Add secure byte slice erase helper; add alternative input methods for Windows
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
package back
|
||||
|
||||
// EraseBytesSecurely overwrites all
|
||||
// bytes in a slice with zeros.
|
||||
func EraseBytesSecurely(input []byte) {
|
||||
for i := range input {
|
||||
input[i] = 0
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
package front
|
||||
|
||||
import (
|
||||
@@ -5,8 +7,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// Input prompts the user for input and returns the input as a string.
|
||||
@@ -17,14 +17,6 @@ func Input(prompt string) string {
|
||||
return strings.TrimRight(userInput, "\n\r ") // remove trailing newlines, carriage returns, and spaces
|
||||
}
|
||||
|
||||
// InputHidden prompts the user for input and returns the input as a byte array, hiding the input from the terminal.
|
||||
func InputHidden(prompt string) []byte {
|
||||
fmt.Print(prompt + " ")
|
||||
byteInput, _ := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Println()
|
||||
return byteInput
|
||||
}
|
||||
|
||||
// InputBinary prompts the user with a yes/no question and returns the response as a boolean.
|
||||
func InputBinary(prompt string) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
@@ -49,11 +41,3 @@ func InputInt(prompt string, min, max int) int {
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
// InputMenuGen prompts the user with a menu and returns the user's choice as an integer.
|
||||
func InputMenuGen(prompt string, options []string) int {
|
||||
for i, option := range options {
|
||||
fmt.Printf("%d. %s\n", i+1, option)
|
||||
}
|
||||
return InputInt("\n"+prompt, 1, len(options))
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
|
||||
package front
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetStdHandle = kernel32.NewProc("GetStdHandle")
|
||||
procPeekConsoleInput = kernel32.NewProc("PeekConsoleInputW")
|
||||
procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
|
||||
)
|
||||
|
||||
const (
|
||||
stdInputHandle = ^uintptr(10) - 1 // STD_INPUT_HANDLE = -10
|
||||
)
|
||||
|
||||
type inputRecord struct {
|
||||
EventType uint16
|
||||
_ [2]byte // padding
|
||||
Event [16]byte
|
||||
}
|
||||
|
||||
// pollForInput checks if input is available without blocking
|
||||
func pollForInput() bool {
|
||||
handle, _, _ := procGetStdHandle.Call(stdInputHandle)
|
||||
var numEvents uint32
|
||||
var record inputRecord
|
||||
|
||||
ret, _, _ := procPeekConsoleInput.Call(
|
||||
handle,
|
||||
uintptr(unsafe.Pointer(&record)),
|
||||
1,
|
||||
uintptr(unsafe.Pointer(&numEvents)),
|
||||
)
|
||||
|
||||
return ret != 0 && numEvents > 0
|
||||
}
|
||||
|
||||
// Input prompts the user for input and returns the input as a string.
|
||||
// Uses polling to be more responsive to interrupts on Windows.
|
||||
func Input(prompt string) string {
|
||||
fmt.Print(prompt + " ")
|
||||
|
||||
// Poll until input is available
|
||||
for !pollForInput() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
userInput, _ := reader.ReadString('\n')
|
||||
return strings.TrimRight(userInput, "\n\r ") // remove trailing newlines, carriage returns, and spaces
|
||||
}
|
||||
|
||||
// InputBinary prompts the user with a yes/no question and returns the response as a boolean.
|
||||
// Uses polling to be more responsive to interrupts on Windows.
|
||||
func InputBinary(prompt string) bool {
|
||||
fmt.Print(prompt + " (y/N) ")
|
||||
|
||||
// Poll until input is available
|
||||
for !pollForInput() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
char, _, _ := reader.ReadRune()
|
||||
if char == 'y' || char == 'Y' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InputInt prompts the user for input and returns the input as an integer.
|
||||
// A negative min/max value will disable the respective limit.
|
||||
// Uses polling to be more responsive to interrupts on Windows.
|
||||
func InputInt(prompt string, min, max int) int {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Print(prompt + " ")
|
||||
|
||||
// Poll until input is available
|
||||
for !pollForInput() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
fmt.Println()
|
||||
continue
|
||||
}
|
||||
|
||||
line = strings.TrimSpace(line)
|
||||
userInput, err := strconv.Atoi(line)
|
||||
|
||||
if err == nil && (userInput >= min || min < 0) && (userInput <= max || max < 0) {
|
||||
return userInput
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package front
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// InputMenuGen prompts the user with a menu and returns the user's choice as an integer.
|
||||
func InputMenuGen(prompt string, options []string) int {
|
||||
for i, option := range options {
|
||||
fmt.Printf("%d. %s\n", i+1, option)
|
||||
}
|
||||
return InputInt("\n"+prompt, 1, len(options))
|
||||
}
|
||||
|
||||
// InputHidden prompts the user for input and returns the input as a byte array, hiding the input from the terminal.
|
||||
func InputHidden(prompt string) []byte {
|
||||
fmt.Print(prompt + " ")
|
||||
byteInput, _ := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Println()
|
||||
return byteInput
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
module github.com/rwinkhart/go-boilerplate
|
||||
|
||||
go 1.25.5
|
||||
go 1.25.6
|
||||
|
||||
require golang.org/x/term v0.38.0
|
||||
require golang.org/x/term v0.39.0
|
||||
|
||||
require golang.org/x/sys v0.39.0 // indirect
|
||||
require golang.org/x/sys v0.40.0 // indirect
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
|
||||
Reference in New Issue
Block a user