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  }