gitlab.com/apertussolutions/u-root@v7.0.0+incompatible/cmds/core/cmp/cmp.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 // cmp compares two files and prints a message if their contents differ. 6 // 7 // Synopsis: 8 // cmp [–lLs] FILE1 FILE2 [OFFSET1 [OFFSET2]] 9 // 10 // Description: 11 // If offsets are given, comparison starts at the designated byte position 12 // of the corresponding file. 13 // 14 // Offsets that begin with 0x are hexadecimal; with 0, octal; with anything 15 // else, decimal. 16 // 17 // Options: 18 // –l: Print the byte number (decimal) and the differing bytes (octal) for 19 // each difference. 20 // –L: Print the line number of the first differing byte. 21 // –s: Print nothing for differing files, but set the exit status. 22 package main 23 24 import ( 25 "bufio" 26 "flag" 27 "fmt" 28 "io" 29 "log" 30 "os" 31 32 "github.com/rck/unit" 33 ) 34 35 var ( 36 long = flag.Bool("l", false, "print the byte number (decimal) and the differing bytes (hexadecimal) for each difference") 37 line = flag.Bool("L", false, "print the line number of the first differing byte") 38 silent = flag.Bool("s", false, "print nothing for differing files, but set the exit status") 39 ) 40 41 func emit(rs io.ReadSeeker, c chan byte, offset int64) error { 42 if offset > 0 { 43 if _, err := rs.Seek(offset, 0); err != nil { 44 log.Fatalf("%v", err) 45 } 46 } 47 48 b := bufio.NewReader(rs) 49 for { 50 b, err := b.ReadByte() 51 if err != nil { 52 close(c) 53 return err 54 } 55 c <- b 56 } 57 } 58 59 func openFile(name string) (*os.File, error) { 60 var f *os.File 61 var err error 62 63 if name == "-" { 64 f = os.Stdin 65 } else { 66 f, err = os.Open(name) 67 } 68 69 return f, err 70 } 71 72 // cmp is defined to fail with exit code 2 73 func failf(format string, a ...interface{}) { 74 fmt.Fprintf(os.Stderr, format, a...) 75 os.Exit(2) 76 } 77 78 func main() { 79 flag.Parse() 80 var offset [2]int64 81 var f *os.File 82 var err error 83 84 fnames := flag.Args() 85 86 cmpUnits := unit.DefaultUnits 87 88 off, err := unit.NewUnit(cmpUnits) 89 if err != nil { 90 failf("Could not create unit based on mapping: %v\n", err) 91 } 92 93 var v *unit.Value 94 switch len(fnames) { 95 case 2: 96 case 3: 97 if v, err = off.ValueFromString(fnames[2]); err != nil { 98 failf("bad offset1: %s: %v\n", fnames[2], err) 99 } 100 offset[0] = v.Value 101 case 4: 102 if v, err = off.ValueFromString(fnames[2]); err != nil { 103 failf("bad offset1: %s: %v\n", fnames[2], err) 104 } 105 offset[0] = v.Value 106 107 if v, err = off.ValueFromString(fnames[3]); err != nil { 108 failf("bad offset2: %s: %v\n", fnames[3], err) 109 } 110 offset[1] = v.Value 111 default: 112 failf("expected two filenames (and one to two optional offsets), got %d", len(fnames)) 113 } 114 115 c := make([]chan byte, 2) 116 117 for i := 0; i < 2; i++ { 118 if f, err = openFile(fnames[i]); err != nil { 119 failf("Failed to open %s: %v", fnames[i], err) 120 } 121 c[i] = make(chan byte, 8192) 122 go emit(f, c[i], offset[i]) 123 } 124 125 lineno, charno := int64(1), int64(1) 126 var b1, b2 byte 127 for { 128 b1 = <-c[0] 129 b2 = <-c[1] 130 131 if b1 != b2 { 132 if *silent { 133 os.Exit(1) 134 } 135 if *line { 136 fmt.Fprintf(os.Stderr, "%s %s differ: char %d line %d\n", fnames[0], fnames[1], charno, lineno) 137 os.Exit(1) 138 } 139 if *long { 140 if b1 == '\u0000' { 141 fmt.Fprintf(os.Stderr, "EOF on %s\n", fnames[0]) 142 os.Exit(1) 143 } 144 if b2 == '\u0000' { 145 fmt.Fprintf(os.Stderr, "EOF on %s\n", fnames[1]) 146 os.Exit(1) 147 } 148 fmt.Fprintf(os.Stderr, "%8d %#.2o %#.2o\n", charno, b1, b2) 149 goto skip 150 } 151 fmt.Fprintf(os.Stderr, "%s %s differ: char %d\n", fnames[0], fnames[1], charno) 152 os.Exit(1) 153 } 154 skip: 155 charno++ 156 if b1 == '\n' { 157 lineno++ 158 } 159 if b1 == '\u0000' && b2 == '\u0000' { 160 os.Exit(0) 161 } 162 } 163 }