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
|
package fsutil
import (
"errors"
"fmt"
"io/fs"
"os"
"os/user"
"path/filepath"
"strings"
)
// ResolveRootPath expands and validates the supplied root path. When the
// caller did not specify a value, defaultValue is used and created on demand.
func ResolveRootPath(input, defaultValue string) (string, error) {
value, isDefault := normalizeRootInput(input, defaultValue)
expanded, err := expandPath(value)
if err != nil {
return "", fmt.Errorf("cannot expand root path %q: %w", value, err)
}
abs, err := filepath.Abs(expanded)
if err != nil {
return "", fmt.Errorf("cannot resolve root path %q: %w", expanded, err)
}
info, err := ensureRootExists(abs, isDefault)
if err != nil {
return "", err
}
if !info.IsDir() && !info.Mode().IsRegular() {
return "", fmt.Errorf("root path %q is not a file or directory", abs)
}
return abs, nil
}
func normalizeRootInput(input, defaultValue string) (value string, isDefault bool) {
trimmed := strings.TrimSpace(input)
if trimmed == "" {
return defaultValue, true
}
return trimmed, false
}
func ensureRootExists(path string, allowCreate bool) (fs.FileInfo, error) {
info, err := os.Stat(path)
if err == nil {
return info, nil
}
if !errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("cannot access root path %q: %w", path, err)
}
if !allowCreate {
return nil, fmt.Errorf("root path does not exist: %s", path)
}
if mkErr := os.MkdirAll(path, 0o755); mkErr != nil {
return nil, fmt.Errorf("cannot create default directory %q: %w", path, mkErr)
}
info, err = os.Stat(path)
if err != nil {
return nil, fmt.Errorf("cannot stat default directory %q: %w", path, err)
}
return info, nil
}
func expandPath(p string) (string, error) {
if p == "" || p[0] != '~' {
return p, nil
}
if len(p) == 1 {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return home, nil
}
if p[1] == '/' {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, p[2:]), nil
}
username, rest := splitUserPath(p)
usr, err := user.Lookup(username)
if err != nil {
return "", err
}
if rest == "" {
return usr.HomeDir, nil
}
return filepath.Join(usr.HomeDir, rest), nil
}
func splitUserPath(p string) (string, string) {
sep := strings.IndexRune(p, '/')
if sep == -1 {
return p[1:], ""
}
return p[1:sep], p[sep:]
}
|