You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
1.9 KiB
121 lines
1.9 KiB
package desktop |
|
|
|
import ( |
|
"log" |
|
"os" |
|
"path/filepath" |
|
"runtime" |
|
"strings" |
|
"sync" |
|
) |
|
|
|
const bufferSize = 32 * 1024 |
|
|
|
type scan struct { |
|
e [][]*Entry |
|
errs chan error |
|
in chan *scanEntry |
|
sync.Mutex |
|
sync.WaitGroup |
|
} |
|
|
|
type scanEntry struct { |
|
i int |
|
f *os.File |
|
} |
|
|
|
// Scan non-recursively scans provided directories for desktop entry files and |
|
// parses them. A slice of parsed entries is returned for each directory. |
|
func Scan(dirs []string) ([][]*Entry, error) { |
|
s := &scan{e: make([][]*Entry, len(dirs)), errs: make(chan error), in: make(chan *scanEntry)} |
|
|
|
for i := 0; i < runtime.GOMAXPROCS(-1); i++ { |
|
go scanner(s) |
|
} |
|
|
|
for i, dir := range dirs { |
|
i, dir := i, dir |
|
|
|
s.Add(1) |
|
go scanDir(i, dir, s) |
|
} |
|
|
|
done := make(chan bool, 1) |
|
go func() { |
|
s.Wait() |
|
close(s.in) |
|
|
|
done <- true |
|
}() |
|
|
|
select { |
|
case err := <-s.errs: |
|
return nil, err |
|
case <-done: |
|
return s.e, nil |
|
} |
|
} |
|
|
|
func scanner(s *scan) { |
|
var ( |
|
buf = make([]byte, bufferSize) |
|
scanEntry *scanEntry |
|
entry *Entry |
|
err error |
|
) |
|
|
|
for scanEntry = range s.in { |
|
entry, err = Parse(scanEntry.f, buf) |
|
scanEntry.f.Close() |
|
if err != nil { |
|
s.errs <- err |
|
s.Done() |
|
return |
|
} else if entry == nil { |
|
s.Done() |
|
continue |
|
} |
|
|
|
s.Lock() |
|
s.e[scanEntry.i] = append(s.e[scanEntry.i], entry) |
|
s.Unlock() |
|
|
|
s.Done() |
|
} |
|
} |
|
|
|
func scanFile(i int, dir string, e os.DirEntry, s *scan) { |
|
if e == nil || e.IsDir() || !strings.HasSuffix(strings.ToLower(e.Name()), ".desktop") { |
|
s.Done() |
|
return |
|
} |
|
|
|
f, err := os.OpenFile(filepath.Join(dir, e.Name()), os.O_RDONLY, 0644) |
|
if os.IsNotExist(err) { |
|
s.Done() |
|
return |
|
} else if err != nil { |
|
|
|
s.errs <- err |
|
s.Done() |
|
return |
|
} |
|
|
|
s.in <- &scanEntry{i: i, f: f} |
|
} |
|
|
|
func scanDir(i int, dir string, s *scan) { |
|
defer s.Done() |
|
|
|
dirEntries, err := os.ReadDir(dir) |
|
if os.IsNotExist(err) { |
|
return |
|
} else if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
for _, dirEntry := range dirEntries { |
|
s.Add(1) |
|
go scanFile(i, dir, dirEntry, s) |
|
} |
|
}
|
|
|