github.com/golang/dep@v0.5.4/hack/licenseok/main.go (about)

     1  // +build ignore
     2  
     3  // Copyright 2017 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  // Checks if all files have the license header, a lot of this is based off
     8  // https://github.com/google/addlicense.
     9  package main
    10  
    11  import (
    12  	"bytes"
    13  	"flag"
    14  	"fmt"
    15  	"io/ioutil"
    16  	"log"
    17  	"os"
    18  	"path/filepath"
    19  	"sync"
    20  	"time"
    21  )
    22  
    23  const helpText = `Usage: licenseok [flags] pattern [pattern ...]
    24  This program ensures source code files have copyright license headers
    25  by scanning directory patterns recursively.
    26  The pattern argument can be provided multiple times, and may also refer
    27  to single files.
    28  Flags:
    29  `
    30  
    31  const tmpl = `The Go Authors. All rights reserved.
    32  Use of this source code is governed by a BSD-style
    33  license that can be found in the LICENSE file.`
    34  
    35  var (
    36  	update bool
    37  )
    38  
    39  type file struct {
    40  	path string
    41  	mode os.FileMode
    42  }
    43  
    44  func init() {
    45  	flag.BoolVar(&update, "u", false, "modifies all source files in place and avoids adding a license header to any file that already has one.")
    46  
    47  	flag.Usage = func() {
    48  		fmt.Fprintln(os.Stderr, helpText)
    49  		flag.PrintDefaults()
    50  	}
    51  
    52  	flag.Parse()
    53  
    54  	if flag.NArg() == 0 {
    55  		flag.Usage()
    56  		os.Exit(1)
    57  	}
    58  }
    59  
    60  func main() {
    61  	exitStatus := 0
    62  
    63  	// process at most 1000 files in parallel
    64  	ch := make(chan *file, 1000)
    65  	done := make(chan struct{})
    66  	go func() {
    67  		var wg sync.WaitGroup
    68  		for f := range ch {
    69  			wg.Add(1)
    70  			go func(f *file) {
    71  				b, err := ioutil.ReadFile(f.path)
    72  				if err != nil {
    73  					log.Printf("%s: %v", f.path, err)
    74  					exitStatus = 1
    75  				}
    76  
    77  				if !hasLicense(b) {
    78  					if !update {
    79  						fmt.Fprintln(os.Stderr, f.path)
    80  						exitStatus = 1
    81  					} else {
    82  						fmt.Fprintln(os.Stdout, f.path)
    83  						if err := addLicense(b, f.path, f.mode); err != nil {
    84  							log.Printf("%s: %v", f.path, err)
    85  							exitStatus = 1
    86  						}
    87  					}
    88  				}
    89  
    90  				wg.Done()
    91  			}(f)
    92  		}
    93  		wg.Wait()
    94  		close(done)
    95  	}()
    96  
    97  	for _, d := range flag.Args() {
    98  		walk(ch, d)
    99  	}
   100  	close(ch)
   101  	<-done
   102  	os.Exit(exitStatus)
   103  }
   104  
   105  func walk(ch chan<- *file, start string) {
   106  	filepath.Walk(start, func(path string, fi os.FileInfo, err error) error {
   107  		if err != nil {
   108  			log.Printf("%s error: %v", path, err)
   109  			return nil
   110  		}
   111  		if fi.IsDir() {
   112  			return nil
   113  		}
   114  		ch <- &file{path, fi.Mode()}
   115  		return nil
   116  	})
   117  }
   118  
   119  func addLicense(b []byte, path string, fmode os.FileMode) error {
   120  	var lic []byte
   121  	var err error
   122  	switch filepath.Ext(path) {
   123  	default:
   124  		return nil
   125  	case ".c", ".h":
   126  		lic, err = prefix("/*", " * ", " */")
   127  	case ".js", ".css":
   128  		lic, err = prefix("/**", " * ", " */")
   129  	case ".cc", ".cpp", ".cs", ".go", ".hh", ".hpp", ".java", ".m", ".mm", ".proto", ".rs", ".scala", ".swift", ".dart":
   130  		lic, err = prefix("", "// ", "")
   131  	case ".py", ".sh":
   132  		lic, err = prefix("", "# ", "")
   133  	case ".el", ".lisp":
   134  		lic, err = prefix("", ";; ", "")
   135  	case ".erl":
   136  		lic, err = prefix("", "% ", "")
   137  	case ".hs":
   138  		lic, err = prefix("", "-- ", "")
   139  	case ".html", ".xml":
   140  		lic, err = prefix("<!--", " ", "-->")
   141  	case ".php":
   142  		lic, err = prefix("<?php", "// ", "?>")
   143  	}
   144  	if err != nil || lic == nil {
   145  		return err
   146  	}
   147  
   148  	line := hashBang(b)
   149  	if len(line) > 0 {
   150  		b = b[len(line):]
   151  		if line[len(line)-1] != '\n' {
   152  			line = append(line, '\n')
   153  		}
   154  		lic = append(line, lic...)
   155  	}
   156  	b = append(lic, b...)
   157  	return ioutil.WriteFile(path, b, fmode)
   158  }
   159  
   160  func hashBang(b []byte) []byte {
   161  	var line = make([]byte, 0, len(b))
   162  	for _, c := range b {
   163  		line = append(line, c)
   164  		if c == '\n' {
   165  			break
   166  		}
   167  	}
   168  	if bytes.HasPrefix(line, []byte("#!")) {
   169  		return line
   170  	}
   171  	return nil
   172  }
   173  
   174  func hasLicense(b []byte) bool {
   175  	n := 100
   176  	if len(b) < 100 {
   177  		n = len(b)
   178  	}
   179  	return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright"))
   180  }
   181  
   182  // prefix will execute a license template and prefix the result with top, middle and bottom.
   183  func prefix(top, mid, bot string) ([]byte, error) {
   184  	buf := bytes.NewBufferString(fmt.Sprintf("Copyright %d %s", time.Now().Year(), tmpl))
   185  	var out bytes.Buffer
   186  	if top != "" {
   187  		out.WriteString(top)
   188  		out.WriteRune('\n')
   189  	}
   190  	out.WriteString(mid)
   191  	for _, c := range buf.Bytes() {
   192  		out.WriteByte(c)
   193  		if c == '\n' {
   194  			out.WriteString(mid)
   195  		}
   196  	}
   197  	if bot != "" {
   198  		out.WriteRune('\n')
   199  		out.WriteString(bot)
   200  	}
   201  	out.Write([]byte{'\n', '\n'})
   202  	return out.Bytes(), nil
   203  }