mirror of
https://github.com/rwinkhart/go-boilerplate.git
synced 2026-09-06 00:57:16 -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
|
package front
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -5,8 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Input prompts the user for input and returns the input as a string.
|
// 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
|
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.
|
// InputBinary prompts the user with a yes/no question and returns the response as a boolean.
|
||||||
func InputBinary(prompt string) bool {
|
func InputBinary(prompt string) bool {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
@@ -49,11 +41,3 @@ func InputInt(prompt string, min, max int) int {
|
|||||||
fmt.Println()
|
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
|
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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||||
|
|||||||
Reference in New Issue
Block a user