github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/comm/comm.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  // Perform a set comparisons over two files.
     6  //
     7  // Synopsis:
     8  //     comm [-123h] FILE1 FILE2
     9  //
    10  // Descrption:
    11  //     Comm reads file1 and file2, which are in lexicographical order, and
    12  //     produces a three column output: lines only in file1; lines only in
    13  //     file2; and lines in both files. The file name – means the standard
    14  //     input.
    15  //
    16  // Options:
    17  //     -1: suppress printing of column 1
    18  //     -2: suppress printing of column 2
    19  //     -3: suppress printing of column 3
    20  //     -h: print this help message and exit
    21  package main
    22  
    23  import (
    24  	"bufio"
    25  	"flag"
    26  	"fmt"
    27  	"log"
    28  	"os"
    29  	"strings"
    30  )
    31  
    32  const cmd = "comm [-123h] file1 file2"
    33  
    34  var (
    35  	s1   = flag.Bool("1", false, "suppress printing of column 1")
    36  	s2   = flag.Bool("2", false, "suppress printing of column 2")
    37  	s3   = flag.Bool("3", false, "suppress printing of column 3")
    38  	help = flag.Bool("h", false, "print this help message and exit")
    39  )
    40  
    41  func init() {
    42  	defUsage := flag.Usage
    43  	flag.Usage = func() {
    44  		os.Args[0] = cmd
    45  		defUsage()
    46  	}
    47  }
    48  
    49  func reader(f *os.File, c chan string) {
    50  	b := bufio.NewReader(f)
    51  
    52  	for {
    53  		s, err := b.ReadString('\n')
    54  		c <- strings.TrimRight(s, "\r\n")
    55  		if err != nil {
    56  			break
    57  		}
    58  	}
    59  	close(c)
    60  }
    61  
    62  type out struct {
    63  	s1, s2, s3 string
    64  }
    65  
    66  func outer(c1, c2 chan string, c chan out) {
    67  	s1, ok1 := <-c1
    68  	s2, ok2 := <-c2
    69  	for {
    70  		if ok1 && ok2 {
    71  			switch {
    72  			case s1 < s2:
    73  				c <- out{s1, "", ""}
    74  				s1, ok1 = <-c1
    75  			case s1 > s2:
    76  				c <- out{"", s2, ""}
    77  				s2, ok2 = <-c2
    78  			default:
    79  				c <- out{"", "", s2}
    80  				s1, ok1 = <-c1
    81  				s2, ok2 = <-c2
    82  			}
    83  		} else if ok1 {
    84  			c <- out{s1, "", ""}
    85  			s1, ok1 = <-c1
    86  		} else if ok2 {
    87  			c <- out{"", s2, ""}
    88  			s2, ok2 = <-c2
    89  		} else {
    90  			break
    91  		}
    92  	}
    93  	close(c)
    94  }
    95  
    96  func main() {
    97  	flag.Parse()
    98  	if flag.NArg() != 2 || *help {
    99  		flag.Usage()
   100  		os.Exit(1)
   101  	}
   102  
   103  	c1 := make(chan string, 100)
   104  	c2 := make(chan string, 100)
   105  	c := make(chan out, 100)
   106  
   107  	f1, err := os.Open(flag.Args()[0])
   108  	if err != nil {
   109  		log.Fatalf("Can't open %s: %v", flag.Args()[0], err)
   110  	}
   111  
   112  	f2, err := os.Open(flag.Args()[1])
   113  	if err != nil {
   114  		log.Fatalf("Can't open %s: %v", flag.Args()[1], err)
   115  	}
   116  	go reader(f1, c1)
   117  	go reader(f2, c2)
   118  	go outer(c1, c2, c)
   119  
   120  	for {
   121  		out, ok := <-c
   122  		if !ok {
   123  			break
   124  		}
   125  
   126  		line := ""
   127  		if !*s1 {
   128  			line += out.s1
   129  		}
   130  		line += "\t"
   131  		if !*s2 {
   132  			line += out.s2
   133  		}
   134  		line += "\t"
   135  		if !*s3 {
   136  			line += out.s3
   137  		}
   138  		if line != "\t\t" {
   139  			fmt.Println(strings.TrimRight(line, "\t")) // the unix comm utility does this
   140  		}
   141  	}
   142  }