...

Source file src/code.rocketnine.space/tslocum/desktop/scan.go

Documentation: code.rocketnine.space/tslocum/desktop

     1  package desktop
     2  
     3  import (
     4  	"log"
     5  	"os"
     6  	"path/filepath"
     7  	"runtime"
     8  	"strings"
     9  	"sync"
    10  )
    11  
    12  const bufferSize = 32 * 1024
    13  
    14  type scan struct {
    15  	e    [][]*Entry
    16  	errs chan error
    17  	in   chan *scanEntry
    18  	sync.Mutex
    19  	sync.WaitGroup
    20  }
    21  
    22  type scanEntry struct {
    23  	i int
    24  	f *os.File
    25  }
    26  
    27  // Scan non-recursively scans provided directories for desktop entry files and
    28  // parses them. A slice of parsed entries is returned for each directory.
    29  func Scan(dirs []string) ([][]*Entry, error) {
    30  	s := &scan{e: make([][]*Entry, len(dirs)), errs: make(chan error), in: make(chan *scanEntry)}
    31  
    32  	for i := 0; i < runtime.GOMAXPROCS(-1); i++ {
    33  		go scanner(s)
    34  	}
    35  
    36  	for i, dir := range dirs {
    37  		i, dir := i, dir
    38  
    39  		s.Add(1)
    40  		go scanDir(i, dir, s)
    41  	}
    42  
    43  	done := make(chan bool, 1)
    44  	go func() {
    45  		s.Wait()
    46  		close(s.in)
    47  
    48  		done <- true
    49  	}()
    50  
    51  	select {
    52  	case err := <-s.errs:
    53  		return nil, err
    54  	case <-done:
    55  		return s.e, nil
    56  	}
    57  }
    58  
    59  func scanner(s *scan) {
    60  	var (
    61  		buf       = make([]byte, bufferSize)
    62  		scanEntry *scanEntry
    63  		entry     *Entry
    64  		err       error
    65  	)
    66  
    67  	for scanEntry = range s.in {
    68  		entry, err = Parse(scanEntry.f, buf)
    69  		scanEntry.f.Close()
    70  		if err != nil {
    71  			s.errs <- err
    72  			s.Done()
    73  			return
    74  		} else if entry == nil {
    75  			s.Done()
    76  			continue
    77  		}
    78  
    79  		s.Lock()
    80  		s.e[scanEntry.i] = append(s.e[scanEntry.i], entry)
    81  		s.Unlock()
    82  
    83  		s.Done()
    84  	}
    85  }
    86  
    87  func scanFile(i int, dir string, e os.DirEntry, s *scan) {
    88  	if e == nil || e.IsDir() || !strings.HasSuffix(strings.ToLower(e.Name()), ".desktop") {
    89  		s.Done()
    90  		return
    91  	}
    92  
    93  	f, err := os.OpenFile(filepath.Join(dir, e.Name()), os.O_RDONLY, 0644)
    94  	if os.IsNotExist(err) {
    95  		s.Done()
    96  		return
    97  	} else if err != nil {
    98  
    99  		s.errs <- err
   100  		s.Done()
   101  		return
   102  	}
   103  
   104  	s.in <- &scanEntry{i: i, f: f}
   105  }
   106  
   107  func scanDir(i int, dir string, s *scan) {
   108  	defer s.Done()
   109  
   110  	dirEntries, err := os.ReadDir(dir)
   111  	if os.IsNotExist(err) {
   112  		return
   113  	} else if err != nil {
   114  		log.Fatal(err)
   115  	}
   116  
   117  	for _, dirEntry := range dirEntries {
   118  		s.Add(1)
   119  		go scanFile(i, dir, dirEntry, s)
   120  	}
   121  }
   122  

View as plain text