File: main.go

package info (click to toggle)
golang-github-charlievieth-fastwalk 1.0.14-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 372 kB
  • sloc: makefile: 80; sh: 35; asm: 13
file content (134 lines) | stat: -rw-r--r-- 3,334 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// fwwc is a an example program that recursively walks directories and
// prints the number of lines in each file it encounters.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path/filepath"

	"github.com/charlievieth/fastwalk"
)

var newLine = []byte{'\n'}

// countLinesInFile returns the number of newlines ('\n') in file name.
func countLinesInFile(name string) (int64, error) {
	f, err := os.Open(name)
	if err != nil {
		return 0, err
	}
	defer f.Close()

	buf := make([]byte, 16*1024)
	var lines int64
	for {
		n, e := f.Read(buf)
		if n > 0 {
			lines += int64(bytes.Count(buf[:n], newLine))
		}
		if e != nil {
			if e != io.EOF {
				err = e
			}
			break
		}
	}
	return lines, err
}

func LineCount(root string, followLinks bool) error {
	countLinesWalkFn := func(path string, d fs.DirEntry, err error) error {
		// We wrap this with fastwalk.IgnorePermissionErrors so we know the
		// error is not a permission error (common when walking outside a users
		// home directory) and is likely something worse so we should return it
		// and abort the walk.
		//
		// A common error here is "too many open files", which can occur if the
		// walkFn opens, but does not close, files.
		if err != nil {
			return err
		}

		// If the entry is a symbolic link get the type of file that
		// it references.
		typ := d.Type()
		if typ&fs.ModeSymlink != 0 {
			if fi, err := fastwalk.StatDirEntry(path, d); err == nil {
				typ = fi.Mode().Type()
			}
		}

		// Skip dot (".") files (but allow "." / PWD as the path)
		if path != "." && typ.IsDir() {
			name := d.Name()
			if name == "" || name[0] == '.' || name[0] == '_' {
				return fastwalk.SkipDir
			}
			return nil
		}
		if typ.IsRegular() {
			lines, err := countLinesInFile(path)
			if err == nil {
				fmt.Printf("%8d %s\n", lines, path)
			} else {
				// Print but do not return the error.
				fmt.Fprintf(os.Stderr, "%s: %s\n", path, err)
			}
		}
		return nil
	}

	// Ignore permission errors traversing directories.
	//
	// Note: this only ignores permission errors when traversing directories.
	// Permission errors may still be encountered when accessing files.
	walkFn := fastwalk.IgnorePermissionErrors(countLinesWalkFn)

	conf := fastwalk.Config{
		// Safely follow symbolic links. This can also be achieved by
		// wrapping walkFn with fastwalk.FollowSymlinks().
		Follow: followLinks,

		// If NumWorkers is ≤ 0 the default is used, which is sufficient
		// for most use cases.
	}
	// Note: Walk can also be called with a nil Config, in which case
	// fastwalk.DefaultConfig is used.
	if err := fastwalk.Walk(&conf, root, walkFn); err != nil {
		return fmt.Errorf("walking directory %s: %w", root, err)
	}
	return nil
}

const UsageMsg = `Usage: %[1]s [-L] [PATH...]:

%[1]s prints the number of lines in each file it finds,
ignoring directories that start with '.' or '_'.

`

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stdout, UsageMsg, filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}
	followLinks := flag.Bool("L", false, "Follow symbolic links")
	flag.Parse()

	args := flag.Args()
	if len(args) == 0 {
		args = append(args, ".")
	}
	for _, root := range args {
		// fmt.Println("ROOT:", root)
		if err := LineCount(root, *followLinks); err != nil {
			fmt.Fprintln(os.Stderr, "error:", err)
			os.Exit(1)
		}
	}
}