mirror of
https://github.com/FiloSottile/age.git
synced 2026-03-11 08:55:41 +00:00
cmd/age: revamp CLI with -r/-i, long options, and input arg
This commit is contained in:
parent
779b033c8b
commit
5ea3ee2f63
1 changed files with 61 additions and 26 deletions
|
|
@ -8,6 +8,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -15,61 +16,95 @@ import (
|
||||||
"github.com/FiloSottile/age/internal/age"
|
"github.com/FiloSottile/age/internal/age"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type multiFlag []string
|
||||||
|
|
||||||
|
func (f *multiFlag) String() string { return fmt.Sprint(*f) }
|
||||||
|
|
||||||
|
func (f *multiFlag) Set(value string) error {
|
||||||
|
*f = append(*f, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
|
|
||||||
decryptFlag := flag.Bool("d", false, "decrypt the input")
|
var (
|
||||||
outFlag := flag.String("o", "", "output to `FILE` (default stdout)")
|
outFlag string
|
||||||
inFlag := flag.String("i", "", "read from `FILE` (default stdin)")
|
decryptFlag, armorFlag bool
|
||||||
armorFlag := flag.Bool("a", false, "generate an armored file")
|
recipientFlags, identityFlags multiFlag
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.BoolVar(&decryptFlag, "d", false, "decrypt the input")
|
||||||
|
flag.BoolVar(&decryptFlag, "decrypt", false, "decrypt the input")
|
||||||
|
flag.StringVar(&outFlag, "o", "", "output to `FILE` (default stdout)")
|
||||||
|
flag.BoolVar(&armorFlag, "a", false, "generate an armored file")
|
||||||
|
flag.BoolVar(&armorFlag, "armor", false, "generate an armored file")
|
||||||
|
flag.Var(&recipientFlags, "r", "recipient (can be repeated)")
|
||||||
|
flag.Var(&recipientFlags, "recipient", "recipient (can be repeated)")
|
||||||
|
flag.Var(&identityFlags, "i", "identity (can be repeated)")
|
||||||
|
flag.Var(&identityFlags, "identity", "identity (can be repeated)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if flag.NArg() > 1 {
|
||||||
|
log.Printf("Error: too many arguments.")
|
||||||
|
log.Fatalf("age accepts a single optional argument for the input file.")
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case *decryptFlag:
|
case decryptFlag:
|
||||||
if *armorFlag {
|
if armorFlag {
|
||||||
log.Fatalf("Invalid flag combination")
|
log.Printf("Error: -a/--armor can't be used with -d/--decrypt.")
|
||||||
|
log.Fatalf("Note that armored files are detected automatically.")
|
||||||
|
}
|
||||||
|
if len(recipientFlags) > 0 {
|
||||||
|
log.Printf("Error: -r/--recipient can't be used with -d/--decrypt.")
|
||||||
|
log.Fatalf("Did you mean to use -i/--identity to specify a private key?")
|
||||||
}
|
}
|
||||||
default: // encrypt
|
default: // encrypt
|
||||||
|
if len(identityFlags) > 0 {
|
||||||
|
log.Printf("Error: -i/--identity can't be used in encryption mode.")
|
||||||
|
log.Fatalf("Did you forget to specify -d/--decrypt?")
|
||||||
|
}
|
||||||
|
if len(recipientFlags) == 0 {
|
||||||
|
log.Printf("Error: missing recipients.")
|
||||||
|
log.Fatalf("Did you forget to specify -r/--recipient?")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
in, out := os.Stdin, os.Stdout
|
in, out := os.Stdin, os.Stdout
|
||||||
if name := *inFlag; name != "" {
|
if name := flag.Arg(0); name != "" && name != "-" {
|
||||||
f, err := os.Open(name)
|
f, err := os.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to open input file %q: %v", name, err)
|
log.Fatalf("Error: failed to open input file %q: %v", name, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
in = f
|
in = f
|
||||||
}
|
}
|
||||||
if name := *outFlag; name != "" {
|
if name := outFlag; name != "" && name != "-" {
|
||||||
f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
|
f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to open output file %q: %v", name, err)
|
log.Fatalf("Error: failed to open output file %q: %v", name, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
out = f
|
out = f
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case *decryptFlag:
|
case decryptFlag:
|
||||||
decrypt(in, out)
|
decrypt(identityFlags, in, out)
|
||||||
default:
|
default:
|
||||||
encrypt(in, out, *armorFlag)
|
encrypt(recipientFlags, in, out, armorFlag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func encrypt(in io.Reader, out io.Writer, armor bool) {
|
func encrypt(args []string, in io.Reader, out io.Writer, armor bool) {
|
||||||
var recipients []age.Recipient
|
var recipients []age.Recipient
|
||||||
for _, arg := range flag.Args() {
|
for _, arg := range args {
|
||||||
r, err := parseRecipient(arg)
|
r, err := parseRecipient(arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
recipients = append(recipients, r)
|
recipients = append(recipients, r)
|
||||||
}
|
}
|
||||||
if len(recipients) == 0 {
|
|
||||||
log.Fatalf("Missing recipients!")
|
|
||||||
}
|
|
||||||
|
|
||||||
ageEncrypt := age.Encrypt
|
ageEncrypt := age.Encrypt
|
||||||
if armor {
|
if armor {
|
||||||
|
|
@ -77,21 +112,21 @@ func encrypt(in io.Reader, out io.Writer, armor bool) {
|
||||||
}
|
}
|
||||||
w, err := ageEncrypt(out, recipients...)
|
w, err := ageEncrypt(out, recipients...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error initializing encryption: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(w, in); err != nil {
|
if _, err := io.Copy(w, in); err != nil {
|
||||||
log.Fatalf("Error encrypting the input: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
if err := w.Close(); err != nil {
|
if err := w.Close(); err != nil {
|
||||||
log.Fatalf("Error finalizing encryption: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrypt(in io.Reader, out io.Writer) {
|
func decrypt(args []string, in io.Reader, out io.Writer) {
|
||||||
var identities []age.Identity
|
var identities []age.Identity
|
||||||
// TODO: use the default location if no arguments are provided:
|
// TODO: use the default location if no arguments are provided:
|
||||||
// os.UserConfigDir()/age/keys.txt, ~/.ssh/id_rsa, ~/.ssh/id_ed25519
|
// os.UserConfigDir()/age/keys.txt, ~/.ssh/id_rsa, ~/.ssh/id_ed25519
|
||||||
for _, name := range flag.Args() {
|
for _, name := range args {
|
||||||
ids, err := parseIdentitiesFile(name)
|
ids, err := parseIdentitiesFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
|
|
@ -101,9 +136,9 @@ func decrypt(in io.Reader, out io.Writer) {
|
||||||
|
|
||||||
r, err := age.Decrypt(in, identities...)
|
r, err := age.Decrypt(in, identities...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error initializing decryption: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(out, r); err != nil {
|
if _, err := io.Copy(out, r); err != nil {
|
||||||
log.Fatalf("Error decrypting the input: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue