github.com/checksum/notify@v0.0.0-20190119234841-59aa2d88664f/util.go (about) 1 // Copyright (c) 2014-2015 The Notify Authors. All rights reserved. 2 // Use of this source code is governed by the MIT license that can be 3 // found in the LICENSE file. 4 5 package notify 6 7 import ( 8 "errors" 9 "os" 10 "path/filepath" 11 "strings" 12 ) 13 14 const all = ^Event(0) 15 const sep = string(os.PathSeparator) 16 17 var errDepth = errors.New("exceeded allowed iteration count (circular symlink?)") 18 19 func min(i, j int) int { 20 if i > j { 21 return j 22 } 23 return i 24 } 25 26 func max(i, j int) int { 27 if i < j { 28 return j 29 } 30 return i 31 } 32 33 // must panics if err is non-nil. 34 func must(err error) { 35 if err != nil { 36 panic(err) 37 } 38 } 39 40 // nonil gives first non-nil error from the given arguments. 41 func nonil(err ...error) error { 42 for _, err := range err { 43 if err != nil { 44 return err 45 } 46 } 47 return nil 48 } 49 50 func cleanpath(path string) (realpath string, isrec bool, err error) { 51 if strings.HasSuffix(path, "...") { 52 isrec = true 53 path = path[:len(path)-3] 54 } 55 if path, err = filepath.Abs(path); err != nil { 56 return "", false, err 57 } 58 if path, err = canonical(path); err != nil { 59 return "", false, err 60 } 61 return path, isrec, nil 62 } 63 64 // canonical resolves any symlink in the given path and returns it in a clean form. 65 // It expects the path to be absolute. It fails to resolve circular symlinks by 66 // maintaining a simple iteration limit. 67 func canonical(p string) (string, error) { 68 p, err := filepath.Abs(p) 69 if err != nil { 70 return "", err 71 } 72 for i, j, depth := 1, 0, 1; i < len(p); i, depth = i+1, depth+1 { 73 if depth > 128 { 74 return "", &os.PathError{Op: "canonical", Path: p, Err: errDepth} 75 } 76 if j = strings.IndexRune(p[i:], '/'); j == -1 { 77 j, i = i, len(p) 78 } else { 79 j, i = i, i+j 80 } 81 fi, err := os.Lstat(p[:i]) 82 if err != nil { 83 return "", err 84 } 85 if fi.Mode()&os.ModeSymlink == os.ModeSymlink { 86 s, err := os.Readlink(p[:i]) 87 if err != nil { 88 return "", err 89 } 90 if filepath.IsAbs(s) { 91 p = "/" + s + p[i:] 92 } else { 93 p = p[:j] + s + p[i:] 94 } 95 i = 1 // no guarantee s is canonical, start all over 96 } 97 } 98 return filepath.Clean(p), nil 99 } 100 101 func joinevents(events []Event) (e Event) { 102 if len(events) == 0 { 103 e = All 104 } else { 105 for _, event := range events { 106 e |= event 107 } 108 } 109 return 110 } 111 112 func split(s string) (string, string) { 113 if i := lastIndexSep(s); i != -1 { 114 return s[:i], s[i+1:] 115 } 116 return "", s 117 } 118 119 func base(s string) string { 120 if i := lastIndexSep(s); i != -1 { 121 return s[i+1:] 122 } 123 return s 124 } 125 126 func indexbase(root, name string) int { 127 if n, m := len(root), len(name); m >= n && name[:n] == root && 128 (n == m || name[n] == os.PathSeparator) { 129 return min(n+1, m) 130 } 131 return -1 132 } 133 134 func indexSep(s string) int { 135 for i := 0; i < len(s); i++ { 136 if s[i] == os.PathSeparator { 137 return i 138 } 139 } 140 return -1 141 } 142 143 func lastIndexSep(s string) int { 144 for i := len(s) - 1; i >= 0; i-- { 145 if s[i] == os.PathSeparator { 146 return i 147 } 148 } 149 return -1 150 }