github.com/jlowellwofford/u-root@v1.0.0/cmds/wc/wc.go (about) 1 // Copyright 2013-2017 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Wc counts lines, words, runes, syntactically–invalid UTF codes. 6 // 7 // Synopsis: 8 // wc [OPTIONS...] [FILES]... 9 // 10 // Description: 11 // Wc counts lines, words, runes, syntactically–invalid UTF codes and bytes 12 // in the named files, or in the standard input if no file is named. A word 13 // is a maximal string of characters delimited by spaces, tabs or newlines. 14 // The count of runes includes invalid codes. If the optional argument is 15 // present, just the specified counts (lines, words, runes, broken UTF 16 // codes or bytes) are selected by the letters l, w, r, b, or c. Otherwise, 17 // lines, words and bytes (–lwc) are reported. 18 // 19 // Options: 20 // –l: count lines 21 // –w: count words 22 // –r: count runes 23 // –b: count broken UTF codes 24 // -c: count bytes 25 // 26 // Bugs: 27 // This wc differs from Plan 9's wc somewhat in word count (BSD's wc differs 28 // even more significantly): 29 // 30 // $ unicode 0x0-0x10ffff | 9 wc -w 31 // 2228221 32 // $ unicode 0x0-0x10ffff | gowc -w 33 // 2228198 34 // $ unicode 0x0-0x10ffff | bsdwc -w 35 // 2293628 36 // 37 // This wc differs from Plan 9's wc significantly in bad rune count: 38 // 39 // $ unicode 0x0-0x10ffff | gowc -b 40 // 6144 41 // $ unicode 0x0-0x10ffff | 9 wc -b 42 // 1966080 43 package main 44 45 import ( 46 "bufio" 47 "bytes" 48 "flag" 49 "fmt" 50 "io" 51 "os" 52 "strings" 53 "unicode/utf8" 54 ) 55 56 var lines = flag.Bool("l", false, "count lines") 57 var words = flag.Bool("w", false, "count words") 58 var runes = flag.Bool("r", false, "count runes") 59 var broken = flag.Bool("b", false, "count broken") 60 var chars = flag.Bool("c", false, "count bytes (include partial UTF)") 61 62 type cnt struct { 63 nline, nword, nrune, nbadr, nchar int64 64 } 65 66 // A modified version of utf8.Valid() 67 func invalidCount(p []byte) (n int64) { 68 i := 0 69 for i < len(p) { 70 if p[i] < utf8.RuneSelf { 71 i++ 72 } else { 73 _, size := utf8.DecodeRune(p[i:]) 74 if size == 1 { 75 // All valid runes of size 1 (those 76 // below RuneSelf) were handled above. 77 // This muse be a RuneError. 78 n++ 79 } 80 i += size 81 } 82 } 83 return 84 } 85 86 func count(in io.Reader, fname string) (c cnt) { 87 b := bufio.NewReaderSize(in, 8192) 88 89 counted := false 90 for !counted { 91 line, err := b.ReadBytes('\n') 92 if err != nil { 93 if err == io.EOF { 94 counted = true 95 } else { 96 fmt.Fprintf(os.Stderr, "error %s: %v", fname, err) 97 return cnt{} // no partial counts; should perhaps quit altogether? 98 } 99 } 100 if !counted { 101 c.nline++ 102 } 103 c.nword += int64(len(bytes.Fields(line))) 104 c.nrune += int64(utf8.RuneCount(line)) 105 c.nchar += int64(len(line)) 106 c.nbadr += invalidCount(line) 107 } 108 return 109 } 110 111 func report(c cnt, fname string) { 112 fields := []string{} 113 if *lines { 114 fields = append(fields, fmt.Sprintf("%d", c.nline)) 115 } 116 if *words { 117 fields = append(fields, fmt.Sprintf("%d", c.nword)) 118 } 119 if *runes { 120 fields = append(fields, fmt.Sprintf("%d", c.nrune)) 121 } 122 if *broken { 123 fields = append(fields, fmt.Sprintf("%d", c.nbadr)) 124 } 125 if *chars { 126 fields = append(fields, fmt.Sprintf("%d", c.nchar)) 127 } 128 if fname != "" { 129 fields = append(fields, fmt.Sprintf("%s", fname)) 130 } 131 132 fmt.Println(strings.Join(fields, " ")) 133 } 134 135 func main() { 136 var totals cnt 137 138 flag.Parse() 139 140 if !(*lines || *words || *runes || *broken || *chars) { 141 *lines, *words, *chars = true, true, true 142 } 143 144 if flag.NArg() == 0 { 145 cnt := count(os.Stdin, "") 146 report(cnt, "") 147 return 148 } 149 150 for _, v := range flag.Args() { 151 f, err := os.Open(v) 152 if err != nil { 153 fmt.Fprintf(os.Stderr, "error opening %s: %v\n", v, err) 154 os.Exit(1) 155 } 156 cnt := count(f, v) 157 totals.nline += cnt.nline 158 totals.nword += cnt.nword 159 totals.nrune += cnt.nrune 160 totals.nbadr += cnt.nbadr 161 totals.nchar += cnt.nchar 162 report(cnt, v) 163 } 164 if flag.NArg() > 1 { 165 report(totals, "total") 166 } 167 }