github.com/DiversionCompany/notify@v0.9.9/node.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  )
    12  
    13  var errSkip = errors.New("notify: skip")
    14  
    15  type walkPathFunc func(nd node, isbase bool) error
    16  
    17  type walkFunc func(node) error
    18  
    19  func errnotexist(name string) error {
    20  	return &os.PathError{
    21  		Op:   "Node",
    22  		Path: name,
    23  		Err:  os.ErrNotExist,
    24  	}
    25  }
    26  
    27  type node struct {
    28  	Name  string
    29  	Watch watchpoint
    30  	Child map[string]node
    31  }
    32  
    33  func newnode(name string) node {
    34  	return node{
    35  		Name:  name,
    36  		Watch: make(watchpoint),
    37  		Child: make(map[string]node),
    38  	}
    39  }
    40  
    41  func (nd node) addchild(name, base string) node {
    42  	child, ok := nd.Child[base]
    43  	if !ok {
    44  		child = newnode(name)
    45  		nd.Child[base] = child
    46  	}
    47  	return child
    48  }
    49  
    50  func (nd node) Add(name string) node {
    51  	i := indexrel(nd.Name, name)
    52  	if i == -1 {
    53  		return node{}
    54  	}
    55  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
    56  		nd = nd.addchild(name[:i+j], name[i:i+j])
    57  		i += j + 1
    58  	}
    59  	return nd.addchild(name, name[i:])
    60  }
    61  
    62  func (nd node) AddDir(fn walkFunc, doNotWatch DoNotWatchFn) error {
    63  	stack := []node{nd}
    64  Traverse:
    65  	for n := len(stack); n != 0; n = len(stack) {
    66  		nd, stack = stack[n-1], stack[:n-1]
    67  		switch err := fn(nd); err {
    68  		case nil:
    69  		case errSkip:
    70  			continue Traverse
    71  		default:
    72  			return &os.PathError{
    73  				Op:   "error while traversing",
    74  				Path: nd.Name,
    75  				Err:  err,
    76  			}
    77  		}
    78  		// TODO(rjeczalik): tolerate open failures - add failed names to
    79  		// AddDirError and notify users which names are not added to the tree.
    80  		f, err := os.Open(nd.Name)
    81  		if err != nil {
    82  			return err
    83  		}
    84  		names, err := f.Readdirnames(-1)
    85  		f.Close()
    86  		if err != nil {
    87  			return err
    88  		}
    89  		for _, name := range names {
    90  			name = filepath.Join(nd.Name, name)
    91  			if doNotWatch != nil && doNotWatch(name) {
    92  				continue
    93  			}
    94  			fi, err := os.Lstat(name)
    95  			if err != nil {
    96  				return err
    97  			}
    98  			if fi.Mode()&(os.ModeSymlink|os.ModeDir) == os.ModeDir {
    99  				stack = append(stack, nd.addchild(name, name[len(nd.Name)+1:]))
   100  			}
   101  		}
   102  	}
   103  	return nil
   104  }
   105  
   106  func (nd node) Get(name string) (node, error) {
   107  	i := indexrel(nd.Name, name)
   108  	if i == -1 {
   109  		return node{}, errnotexist(name)
   110  	}
   111  	ok := false
   112  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
   113  		if nd, ok = nd.Child[name[i:i+j]]; !ok {
   114  			return node{}, errnotexist(name)
   115  		}
   116  		i += j + 1
   117  	}
   118  	if nd, ok = nd.Child[name[i:]]; !ok {
   119  		return node{}, errnotexist(name)
   120  	}
   121  	return nd, nil
   122  }
   123  
   124  func (nd node) Del(name string) error {
   125  	i := indexrel(nd.Name, name)
   126  	if i == -1 {
   127  		return errnotexist(name)
   128  	}
   129  	stack := []node{nd}
   130  	ok := false
   131  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
   132  		if nd, ok = nd.Child[name[i:i+j]]; !ok {
   133  			return errnotexist(name[:i+j])
   134  		}
   135  		stack = append(stack, nd)
   136  		i += j + 1
   137  	}
   138  	if _, ok = nd.Child[name[i:]]; !ok {
   139  		return errnotexist(name)
   140  	}
   141  	delete(nd.Child, name[i:])
   142  	for name, i = name[i:], len(stack); i != 0; name, i = base(nd.Name), i-1 {
   143  		nd = stack[i-1]
   144  		if nd := nd.Child[name]; len(nd.Watch) > 1 || len(nd.Child) != 0 {
   145  			break
   146  		} else {
   147  			nd.Child = nil
   148  			nd.Watch = nil
   149  		}
   150  		delete(nd.Child, name)
   151  	}
   152  	return nil
   153  }
   154  
   155  func (nd node) Walk(fn walkFunc, doNotWatch DoNotWatchFn) error {
   156  	stack := []node{nd}
   157  Traverse:
   158  	for n := len(stack); n != 0; n = len(stack) {
   159  		nd, stack = stack[n-1], stack[:n-1]
   160  		switch err := fn(nd); err {
   161  		case nil:
   162  		case errSkip:
   163  			continue Traverse
   164  		default:
   165  			return err
   166  		}
   167  		for name, nd := range nd.Child {
   168  			if name == "" {
   169  				// Node storing inactive watchpoints has empty name, skip it
   170  				// form traversing. Root node has also an empty name, but it
   171  				// never has a parent node.
   172  				continue
   173  			}
   174  			if doNotWatch != nil && doNotWatch(nd.Name) {
   175  				continue
   176  			}
   177  			stack = append(stack, nd)
   178  		}
   179  	}
   180  	return nil
   181  }
   182  
   183  func (nd node) WalkPath(name string, fn walkPathFunc) error {
   184  	i := indexrel(nd.Name, name)
   185  	if i == -1 {
   186  		return errnotexist(name)
   187  	}
   188  	ok := false
   189  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
   190  		switch err := fn(nd, false); err {
   191  		case nil:
   192  		case errSkip:
   193  			return nil
   194  		default:
   195  			return err
   196  		}
   197  		if nd, ok = nd.Child[name[i:i+j]]; !ok {
   198  			return errnotexist(name[:i+j])
   199  		}
   200  		i += j + 1
   201  	}
   202  	switch err := fn(nd, false); err {
   203  	case nil:
   204  	case errSkip:
   205  		return nil
   206  	default:
   207  		return err
   208  	}
   209  	if nd, ok = nd.Child[name[i:]]; !ok {
   210  		return errnotexist(name)
   211  	}
   212  	switch err := fn(nd, true); err {
   213  	case nil, errSkip:
   214  		return nil
   215  	default:
   216  		return err
   217  	}
   218  }
   219  
   220  type root struct {
   221  	nd node
   222  }
   223  
   224  func (r root) addroot(name string) node {
   225  	if vol := filepath.VolumeName(name); vol != "" {
   226  		root, ok := r.nd.Child[vol]
   227  		if !ok {
   228  			root = r.nd.addchild(vol, vol)
   229  		}
   230  		return root
   231  	}
   232  	return r.nd
   233  }
   234  
   235  func (r root) root(name string) (node, error) {
   236  	if vol := filepath.VolumeName(name); vol != "" {
   237  		nd, ok := r.nd.Child[vol]
   238  		if !ok {
   239  			return node{}, errnotexist(name)
   240  		}
   241  		return nd, nil
   242  	}
   243  	return r.nd, nil
   244  }
   245  
   246  func (r root) Add(name string) node {
   247  	return r.addroot(name).Add(name)
   248  }
   249  
   250  func (r root) AddDir(dir string, fn walkFunc, doNotWatch DoNotWatchFn) error {
   251  	return r.Add(dir).AddDir(fn, doNotWatch)
   252  }
   253  
   254  func (r root) Del(name string) error {
   255  	nd, err := r.root(name)
   256  	if err != nil {
   257  		return err
   258  	}
   259  	return nd.Del(name)
   260  }
   261  
   262  func (r root) Get(name string) (node, error) {
   263  	nd, err := r.root(name)
   264  	if err != nil {
   265  		return node{}, err
   266  	}
   267  	if nd.Name != name {
   268  		if nd, err = nd.Get(name); err != nil {
   269  			return node{}, err
   270  		}
   271  	}
   272  	return nd, nil
   273  }
   274  
   275  func (r root) Walk(name string, fn walkFunc, doNotWatch DoNotWatchFn) error {
   276  	nd, err := r.Get(name)
   277  	if err != nil {
   278  		return err
   279  	}
   280  	return nd.Walk(fn, doNotWatch)
   281  }
   282  
   283  func (r root) WalkPath(name string, fn walkPathFunc) error {
   284  	nd, err := r.root(name)
   285  	if err != nil {
   286  		return err
   287  	}
   288  	return nd.WalkPath(name, fn)
   289  }