github.com/StackExchange/blackbox/v2@v2.0.1-0.20220331193400-d84e904973ab/pkg/bbutil/filestats.go (about) 1 package bbutil 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "sort" 9 "strings" 10 "time" 11 ) 12 13 // DirExists returns true if directory exists. 14 func DirExists(path string) (bool, error) { 15 stat, err := os.Stat(path) 16 if err == nil { 17 return stat.IsDir(), nil 18 } 19 if os.IsNotExist(err) { 20 return false, nil 21 } 22 return true, err 23 } 24 25 // FileExistsOrProblem returns true if the file exists or if we can't determine its existence. 26 func FileExistsOrProblem(path string) bool { 27 _, err := os.Stat(path) 28 if err == nil { 29 return true 30 } 31 if os.IsNotExist(err) { 32 return false 33 } 34 return true 35 } 36 37 // Touch updates the timestamp of a file. 38 func Touch(name string) error { 39 var err error 40 _, err = os.Stat(name) 41 if os.IsNotExist(err) { 42 file, err := os.Create(name) 43 if err != nil { 44 return fmt.Errorf("TouchFile failed: %w", err) 45 } 46 file.Close() 47 } 48 49 currentTime := time.Now().Local() 50 return os.Chtimes(name, currentTime, currentTime) 51 } 52 53 // ReadFileLines is like ioutil.ReadFile() but returns an []string. 54 func ReadFileLines(filename string) ([]string, error) { 55 b, err := ioutil.ReadFile(filename) 56 if err != nil { 57 return nil, err 58 } 59 s := string(b) 60 s = strings.TrimSuffix(s, "\n") 61 if s == "" { 62 return []string{}, nil 63 } 64 l := strings.Split(s, "\n") 65 return l, nil 66 } 67 68 // AddLinesToSortedFile adds a line to a sorted file. 69 func AddLinesToSortedFile(filename string, newlines ...string) error { 70 lines, err := ReadFileLines(filename) 71 //fmt.Printf("DEBUG: read=%q\n", lines) 72 if err != nil { 73 return fmt.Errorf("AddLinesToSortedFile can't read %q: %w", filename, err) 74 } 75 if !sort.StringsAreSorted(lines) { 76 return fmt.Errorf("AddLinesToSortedFile: file wasn't sorted: %v", filename) 77 } 78 lines = append(lines, newlines...) 79 sort.Strings(lines) 80 contents := strings.Join(lines, "\n") + "\n" 81 //fmt.Printf("DEBUG: write=%q\n", contents) 82 err = ioutil.WriteFile(filename, []byte(contents), 0o660) 83 if err != nil { 84 return fmt.Errorf("AddLinesToSortedFile can't write %q: %w", filename, err) 85 } 86 return nil 87 } 88 89 // AddLinesToFile adds lines to the end of a file. 90 func AddLinesToFile(filename string, newlines ...string) error { 91 lines, err := ReadFileLines(filename) 92 if err != nil { 93 return fmt.Errorf("AddLinesToFile can't read %q: %w", filename, err) 94 } 95 lines = append(lines, newlines...) 96 contents := strings.Join(lines, "\n") + "\n" 97 err = ioutil.WriteFile(filename, []byte(contents), 0o660) 98 if err != nil { 99 return fmt.Errorf("AddLinesToFile can't write %q: %w", filename, err) 100 } 101 return nil 102 } 103 104 // FindDirInParent looks for target in CWD, or .., or ../.., etc. 105 func FindDirInParent(target string) (string, error) { 106 // Prevent an infinite loop by only doing "cd .." this many times 107 maxDirLevels := 30 108 relpath := "." 109 for i := 0; i < maxDirLevels; i++ { 110 // Does relpath contain our target? 111 t := filepath.Join(relpath, target) 112 //logDebug.Printf("Trying %q\n", t) 113 _, err := os.Stat(t) 114 if err == nil { 115 return t, nil 116 } 117 if !os.IsNotExist(err) { 118 return "", fmt.Errorf("stat failed FindDirInParent (%q): %w", t, err) 119 } 120 // Ok, it really wasn't found. 121 122 // If we are at the root, stop. 123 if abs, err := filepath.Abs(relpath); err == nil && abs == "/" { 124 break 125 } 126 // Try one directory up 127 relpath = filepath.Join("..", relpath) 128 } 129 return "", fmt.Errorf("Not found") 130 }