-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
127 lines (105 loc) · 2.77 KB
/
main.go
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"errors"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"mvdan.cc/sh/shell"
)
const (
envSuffix = "_EXTRA_ARGS"
usage = `xenvflags should not be called directly, but via a symlink.
For example, shfmt is installed in /usr/local/bin/shfmt.
Setup: ln -s path/to/real/xenvflags $HOME/bin/shfmt
Make sure that $HOME/bin is placed before /usr/local/bin in PATH. Calling shfmt
will automatically apply extra arguments from SHFMT_EXTRA_ARGS environment
variable.
SHFMT_EXTRA_ARGS='-i 2' shfmt --> shfmt -i 2
`
)
var (
version = "dev"
)
func isDebug() bool {
return os.Getenv("XENVFLAGS_DEBUG") == "true"
}
func printVersion() {
if os.Getenv("XENVFLAGS_VERSION") == "true" {
fmt.Println(version)
os.Exit(0)
}
}
// isSymlink returns an error if the given file is not a symlink.
func isSymlink(file string) error {
fileInfo, err := os.Lstat(file)
if err != nil {
return err
}
if !(fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink) {
return fmt.Errorf("%s is not a symlink", file)
}
return nil
}
// findRealExecutable lookups for a `real` command in PATH with same name as
// the given symlink file.
// For example:
// - file: $HOME/bin/shfmt is a symlink to xenvflags
// - real shfmt is installed in /usr/local/bin/shfmt
// - PATH: $HOME/bin/shfmt:/usr/local/bin
// This function will return /usr/local/bin/shfmt.
func findRealExecutable(file string) (string, error) {
cmd := filepath.Base(file)
excludedDir := filepath.Dir(file)
return lookPath(cmd, excludedDir)
}
func getExtraArgs(cmdName string) ([]string, error) {
envArgs := os.Getenv(strings.ToUpper(cmdName) + envSuffix)
extraArgs, err := shell.Fields(envArgs, nil)
if err != nil {
return nil, err
}
return extraArgs, nil
}
func main() {
printVersion()
symExecutable, err := exec.LookPath(os.Args[0])
if err != nil {
log.Fatal(err)
}
if err = isSymlink(symExecutable); err != nil {
if isDebug() {
log.Print(err.Error())
}
fmt.Fprintf(os.Stderr, usage)
os.Exit(1)
}
executable, err := findRealExecutable(symExecutable)
if err != nil {
log.Fatalf("find real executable: %s", err.Error())
}
origArgs := os.Args[1:]
extraArgs, err := getExtraArgs(filepath.Base(executable))
if err != nil {
log.Fatalf("parse extra args from environment variable: %s", err.Error())
}
if isDebug() {
log.Printf("version: %s", version)
log.Printf("executable: %s", executable)
log.Printf("original arguments: %s", origArgs)
log.Printf("extra arguments from env: %s", extraArgs)
}
cmd := exec.Command(executable, append(extraArgs, origArgs...)...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
os.Exit(exitError.ExitCode())
}
log.Fatal(err.Error())
}
}