1package commands 2 3import ( 4 "fmt" 5 "io" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "sort" 10 "strings" 11 "time" 12 13 "git.sr.ht/~sircmpwn/aerc/lib" 14 "git.sr.ht/~sircmpwn/aerc/models" 15 "git.sr.ht/~sircmpwn/aerc/widgets" 16 "github.com/gdamore/tcell" 17 "github.com/mitchellh/go-homedir" 18) 19 20// QuickTerm is an ephemeral terminal for running a single command and quiting. 21func QuickTerm(aerc *widgets.Aerc, args []string, stdin io.Reader) (*widgets.Terminal, error) { 22 cmd := exec.Command(args[0], args[1:]...) 23 pipe, err := cmd.StdinPipe() 24 if err != nil { 25 return nil, err 26 } 27 28 term, err := widgets.NewTerminal(cmd) 29 if err != nil { 30 return nil, err 31 } 32 33 term.OnClose = func(err error) { 34 if err != nil { 35 aerc.PushError(" " + err.Error()) 36 // remove the tab on error, otherwise it gets stuck 37 aerc.RemoveTab(term) 38 } else { 39 aerc.PushStatus("Process complete, press any key to close.", 40 10*time.Second) 41 term.OnEvent = func(event tcell.Event) bool { 42 aerc.RemoveTab(term) 43 return true 44 } 45 } 46 } 47 48 term.OnStart = func() { 49 status := make(chan error, 1) 50 51 go func() { 52 _, err := io.Copy(pipe, stdin) 53 defer pipe.Close() 54 status <- err 55 }() 56 57 err := <-status 58 if err != nil { 59 aerc.PushError(" " + err.Error()) 60 } 61 } 62 63 return term, nil 64} 65 66// CompletePath provides filesystem completions given a starting path. 67func CompletePath(path string) []string { 68 if path == "" { 69 // default to cwd 70 cwd, err := os.Getwd() 71 if err != nil { 72 return nil 73 } 74 path = cwd 75 } 76 77 path, err := homedir.Expand(path) 78 if err != nil { 79 return nil 80 } 81 82 // strip trailing slashes, etc. 83 path = filepath.Clean(path) 84 85 if _, err := os.Stat(path); os.IsNotExist(err) { 86 // if the path doesn't exist, it is likely due to it being a partial path 87 // in this case, we want to return possible matches (ie /hom* should match 88 // /home) 89 matches, err := filepath.Glob(fmt.Sprintf("%s*", path)) 90 if err != nil { 91 return nil 92 } 93 94 for i, m := range matches { 95 if isDir(m) { 96 matches[i] = m + "/" 97 } 98 } 99 100 sort.Strings(matches) 101 return matches 102 } 103 104 files := listDir(path, false) 105 106 for i, f := range files { 107 f = filepath.Join(path, f) 108 if isDir(f) { 109 f += "/" 110 } 111 112 files[i] = f 113 } 114 115 sort.Strings(files) 116 return files 117} 118 119func isDir(path string) bool { 120 info, err := os.Stat(path) 121 if err != nil { 122 return false 123 } 124 125 return info.IsDir() 126} 127 128// return all filenames in a directory, optionally including hidden files 129func listDir(path string, hidden bool) []string { 130 f, err := os.Open(path) 131 if err != nil { 132 return []string{} 133 } 134 135 files, err := f.Readdirnames(-1) // read all dir names 136 if err != nil { 137 return []string{} 138 } 139 140 if hidden { 141 return files 142 } 143 144 var filtered []string 145 for _, g := range files { 146 if !strings.HasPrefix(g, ".") { 147 filtered = append(filtered, g) 148 } 149 } 150 151 return filtered 152} 153 154// MarkedOrSelected returns either all marked messages if any are marked or the 155// selected message instead 156func MarkedOrSelected(pm widgets.ProvidesMessages) ([]uint32, error) { 157 // marked has priority over the selected message 158 marked, err := pm.MarkedMessages() 159 if err != nil { 160 return nil, err 161 } 162 if len(marked) > 0 { 163 return marked, nil 164 } 165 msg, err := pm.SelectedMessage() 166 if err != nil { 167 return nil, err 168 } 169 return []uint32{msg.Uid}, nil 170} 171 172// UidsFromMessageInfos extracts a uid slice from a slice of MessageInfos 173func UidsFromMessageInfos(msgs []*models.MessageInfo) []uint32 { 174 uids := make([]uint32, len(msgs)) 175 i := 0 176 for _, msg := range msgs { 177 uids[i] = msg.Uid 178 i++ 179 } 180 return uids 181} 182 183func MsgInfoFromUids(store *lib.MessageStore, uids []uint32) ([]*models.MessageInfo, error) { 184 infos := make([]*models.MessageInfo, len(uids)) 185 for i, uid := range uids { 186 var ok bool 187 infos[i], ok = store.Messages[uid] 188 if !ok { 189 return nil, fmt.Errorf("uid not found") 190 } 191 } 192 return infos, nil 193} 194