github.com/xzntrc/go-enry/v2@v2.0.0-20230215091818-766cc1d65498/data/generated.go (about)

     1  package data
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  
     7  	"github.com/go-enry/go-enry/v2/regex"
     8  )
     9  
    10  // GeneratedCodeExtensions contains all extensions that belong to generated
    11  // files for sure.
    12  var GeneratedCodeExtensions = map[string]struct{}{
    13  	// XCode files
    14  	".nib":             {},
    15  	".xcworkspacedata": {},
    16  	".xcuserstate":     {},
    17  }
    18  
    19  // GeneratedCodeNameMatcher is a function that tells whether the file with the
    20  // given name is generated.
    21  type GeneratedCodeNameMatcher func(string) bool
    22  
    23  func nameMatches(pattern string) GeneratedCodeNameMatcher {
    24  	r := regex.MustCompile(pattern)
    25  	return func(name string) bool {
    26  		return r.MatchString(name)
    27  	}
    28  }
    29  
    30  func nameContains(pattern string) GeneratedCodeNameMatcher {
    31  	return func(name string) bool {
    32  		return strings.Contains(name, pattern)
    33  	}
    34  }
    35  
    36  func nameEndsWith(pattern string) GeneratedCodeNameMatcher {
    37  	return func(name string) bool {
    38  		return strings.HasSuffix(name, pattern)
    39  	}
    40  }
    41  
    42  // GeneratedCodeNameMatchers are all the matchers that check whether the code
    43  // is generated based only on the file name.
    44  var GeneratedCodeNameMatchers = []GeneratedCodeNameMatcher{
    45  	// Cocoa pods
    46  	nameMatches(`(^Pods|\/Pods)\/`),
    47  
    48  	// Carthage build
    49  	nameMatches(`(^|\/)Carthage\/Build\/`),
    50  
    51  	// NET designer file
    52  	nameMatches(`(?i)\.designer\.(cs|vb)$`),
    53  
    54  	// Generated NET specflow feature file
    55  	nameEndsWith(".feature.cs"),
    56  
    57  	// Node modules
    58  	nameContains("node_modules/"),
    59  
    60  	// Go vendor
    61  	nameMatches(`vendor\/([-0-9A-Za-z]+\.)+(com|edu|gov|in|me|net|org|fm|io)`),
    62  
    63  	// Go lock
    64  	nameEndsWith("Gopkg.lock"),
    65  	nameEndsWith("glide.lock"),
    66  
    67  	// Esy lock
    68  	nameMatches(`(^|\/)(\w+\.)?esy.lock$`),
    69  
    70  	// NPM shrinkwrap
    71  	nameEndsWith("npm-shrinkwrap.json"),
    72  
    73  	// NPM package lock
    74  	nameEndsWith("package-lock.json"),
    75  
    76  	// Yarn plugnplay
    77  	nameMatches(`(^|\/)\.pnp\..*$`),
    78  
    79  	// Godeps
    80  	nameContains("Godeps/"),
    81  
    82  	// Composer lock
    83  	nameEndsWith("composer.lock"),
    84  
    85  	// Generated by zephir
    86  	nameMatches(`.\.zep\.(?:c|h|php)$`),
    87  
    88  	// Cargo lock
    89  	nameEndsWith("Cargo.lock"),
    90  
    91  	// Pipenv lock
    92  	nameEndsWith("Pipfile.lock"),
    93  
    94  	// GraphQL relay
    95  	nameContains("__generated__/"),
    96  
    97  	// Poetry lock
    98  	nameEndsWith("poetry.lock"),
    99  }
   100  
   101  // GeneratedCodeMatcher checks whether the file with the given data is
   102  // generated code.
   103  type GeneratedCodeMatcher func(path, ext string, content []byte) bool
   104  
   105  // GeneratedCodeMatchers is the list of all generated code matchers that
   106  // rely on checking the content of the file to make the guess.
   107  var GeneratedCodeMatchers = []GeneratedCodeMatcher{
   108  	isMinifiedFile,
   109  	hasSourceMapReference,
   110  	isSourceMap,
   111  	isCompiledCoffeeScript,
   112  	isGeneratedNetDocfile,
   113  	isGeneratedJavaScriptPEGParser,
   114  	isGeneratedPostScript,
   115  	isGeneratedGo,
   116  	isGeneratedProtobufFromGo,
   117  	isGeneratedProtobuf,
   118  	isGeneratedJavaScriptProtocolBuffer,
   119  	isGeneratedApacheThrift,
   120  	isGeneratedJNIHeader,
   121  	isVCRCassette,
   122  	isCompiledCythonFile,
   123  	isGeneratedModule,
   124  	isGeneratedUnity3DMeta,
   125  	isGeneratedRacc,
   126  	isGeneratedJFlex,
   127  	isGeneratedGrammarKit,
   128  	isGeneratedRoxygen2,
   129  	isGeneratedJison,
   130  	isGeneratedGRPCCpp,
   131  	isGeneratedDart,
   132  	isGeneratedPerlPPPortHeader,
   133  	isGeneratedGameMakerStudio,
   134  	isGeneratedGimp,
   135  	isGeneratedVisualStudio6,
   136  	isGeneratedHaxe,
   137  	isGeneratedHTML,
   138  	isGeneratedJooq,
   139  }
   140  
   141  func canBeMinified(ext string) bool {
   142  	return ext == ".js" || ext == ".css"
   143  }
   144  
   145  // isMinifiedFile returns whether the file may be minified.
   146  // We consider a minified file any css or js file whose average number of chars
   147  // per line is more than 110.
   148  func isMinifiedFile(path, ext string, content []byte) bool {
   149  	if !canBeMinified(ext) {
   150  		return false
   151  	}
   152  
   153  	var chars, lines uint64
   154  	forEachLine(content, func(line []byte) {
   155  		chars += uint64(len(line))
   156  		lines++
   157  	})
   158  
   159  	if lines == 0 {
   160  		return false
   161  	}
   162  
   163  	return chars/lines > 110
   164  }
   165  
   166  var sourceMapRegex = regex.MustCompile(`^\/[*\/][\#@] source(?:Mapping)?URL|sourceURL=`)
   167  
   168  // hasSourceMapReference returns whether the file contains a reference to a
   169  // source-map file.
   170  func hasSourceMapReference(_ string, ext string, content []byte) bool {
   171  	if !canBeMinified(ext) {
   172  		return false
   173  	}
   174  
   175  	for _, line := range getLines(content, -2) {
   176  		if sourceMapRegex.Match(line) {
   177  			return true
   178  		}
   179  	}
   180  
   181  	return false
   182  }
   183  
   184  var sourceMapRegexps = []regex.EnryRegexp{
   185  	regex.MustCompile(`^{"version":\d+,`),
   186  	regex.MustCompile(`^\/\*\* Begin line maps\. \*\*\/{`),
   187  }
   188  
   189  // isSourceMap returns whether the file itself is a source map.
   190  func isSourceMap(path, _ string, content []byte) bool {
   191  	if strings.HasSuffix(path, ".js.map") || strings.HasSuffix(path, ".css.map") {
   192  		return true
   193  	}
   194  
   195  	firstLine := getFirstLine(content)
   196  	if len(firstLine) == 0 {
   197  		return false
   198  	}
   199  
   200  	for _, r := range sourceMapRegexps {
   201  		if r.Match(firstLine) {
   202  			return true
   203  		}
   204  	}
   205  
   206  	return false
   207  }
   208  
   209  func isCompiledCoffeeScript(path, ext string, content []byte) bool {
   210  	if ext != ".js" {
   211  		return false
   212  	}
   213  
   214  	firstLine := getFirstLine(content)
   215  	lastLines := getLines(content, -2)
   216  	if len(lastLines) < 2 {
   217  		return false
   218  	}
   219  
   220  	if string(firstLine) == "(function() {" &&
   221  		string(lastLines[1]) == "}).call(this);" &&
   222  		string(lastLines[0]) == "" {
   223  		score := 0
   224  
   225  		forEachLine(content, func(line []byte) {
   226  			if bytes.Contains(line, []byte("var ")) {
   227  				// Underscored temp vars are likely to be Coffee
   228  				score += 1 * countAppearancesInLine(line, "_fn", "_i", "_len", "_ref", "_results")
   229  
   230  				// bind and extend functions are very Coffee specific
   231  				score += 3 * countAppearancesInLine(line, "__bind", "__extends", "__hasProp", "__indexOf", "__slice")
   232  			}
   233  		})
   234  
   235  		// Require a score of 3. This is fairly arbitrary. Consider tweaking later.
   236  		// See: https://github.com/github/linguist/blob/master/lib/linguist/generated.rb#L176-L213
   237  		return score >= 3
   238  	}
   239  
   240  	return false
   241  }
   242  
   243  func isGeneratedNetDocfile(_, ext string, content []byte) bool {
   244  	if ext != ".xml" {
   245  		return false
   246  	}
   247  
   248  	lines := bytes.Split(content, []byte{'\n'})
   249  	if len(lines) <= 3 {
   250  		return false
   251  	}
   252  
   253  	return bytes.Contains(lines[1], []byte("<doc>")) &&
   254  		bytes.Contains(lines[2], []byte("<assembly>")) &&
   255  		bytes.Contains(lines[len(lines)-2], []byte("</doc>"))
   256  }
   257  
   258  var pegJavaScriptGeneratedRegex = regex.MustCompile(`^(?:[^\/]|\/[^\*])*\/\*(?:[^\*]|\*[^\/])*Generated by PEG.js`)
   259  
   260  func isGeneratedJavaScriptPEGParser(_, ext string, content []byte) bool {
   261  	if ext != ".js" {
   262  		return false
   263  	}
   264  
   265  	// PEG.js-generated parsers include a comment near the top  of the file
   266  	// that marks them as such.
   267  	return pegJavaScriptGeneratedRegex.Match(bytes.Join(getLines(content, 5), []byte("")))
   268  }
   269  
   270  var postScriptType1And42Regex = regex.MustCompile(`(\n|\r\n|\r)\s*(?:currentfile eexec\s+|\/sfnts\s+\[)`)
   271  
   272  var postScriptRegexes = []regex.EnryRegexp{
   273  	regex.MustCompile(`[0-9]|draw|mpage|ImageMagick|inkscape|MATLAB`),
   274  	regex.MustCompile(`PCBNEW|pnmtops|\(Unknown\)|Serif Affinity|Filterimage -tops`),
   275  }
   276  
   277  func isGeneratedPostScript(_, ext string, content []byte) bool {
   278  	if ext != ".ps" && ext != ".eps" && ext != ".pfa" {
   279  		return false
   280  	}
   281  
   282  	// Type 1 and Type 42 fonts converted to PostScript are stored as hex-encoded byte streams; these
   283  	// streams are always preceded the `eexec` operator (if Type 1), or the `/sfnts` key (if Type 42).
   284  	if postScriptType1And42Regex.Match(content) {
   285  		return true
   286  	}
   287  
   288  	// We analyze the "%%Creator:" comment, which contains the author/generator
   289  	// of the file. If there is one, it should be in one of the first few lines.
   290  	var creator []byte
   291  	for _, line := range getLines(content, 10) {
   292  		if bytes.HasPrefix(line, []byte("%%Creator: ")) {
   293  			creator = line
   294  			break
   295  		}
   296  	}
   297  
   298  	if len(creator) == 0 {
   299  		return false
   300  	}
   301  
   302  	// EAGLE doesn't include a version number when it generates PostScript.
   303  	// However, it does prepend its name to the document's "%%Title" field.
   304  	if bytes.Contains(creator, []byte("EAGLE")) {
   305  		for _, line := range getLines(content, 5) {
   306  			if bytes.HasPrefix(line, []byte("%%Title: EAGLE Drawing ")) {
   307  				return true
   308  			}
   309  		}
   310  	}
   311  
   312  	// Most generators write their version number, while human authors' or companies'
   313  	// names don't contain numbers. So look if the line contains digits. Also
   314  	// look for some special cases without version numbers.
   315  	for _, r := range postScriptRegexes {
   316  		if r.Match(creator) {
   317  			return true
   318  		}
   319  	}
   320  
   321  	return false
   322  }
   323  
   324  func isGeneratedGo(_, ext string, content []byte) bool {
   325  	if ext != ".go" {
   326  		return false
   327  	}
   328  
   329  	lines := getLines(content, 40)
   330  	if len(lines) <= 1 {
   331  		return false
   332  	}
   333  
   334  	for _, line := range lines {
   335  		if bytes.Contains(line, []byte("Code generated by")) {
   336  			return true
   337  		}
   338  	}
   339  
   340  	return false
   341  }
   342  
   343  func isGeneratedProtobufFromGo(_, ext string, content []byte) bool {
   344  	if ext != ".proto" {
   345  		return false
   346  	}
   347  	lines := getLines(content, 20)
   348  	if len(lines) <= 1 {
   349  		return false
   350  	}
   351  
   352  	for _, line := range lines {
   353  		if bytes.Contains(line, []byte("This file was autogenerated by go-to-protobuf")) {
   354  			return true
   355  		}
   356  	}
   357  
   358  	return false
   359  }
   360  
   361  var protoExtensions = map[string]struct{}{
   362  	".py":   {},
   363  	".java": {},
   364  	".h":    {},
   365  	".cc":   {},
   366  	".cpp":  {},
   367  	".m":    {},
   368  	".rb":   {},
   369  	".php":  {},
   370  }
   371  
   372  func isGeneratedProtobuf(_, ext string, content []byte) bool {
   373  	if _, ok := protoExtensions[ext]; !ok {
   374  		return false
   375  	}
   376  
   377  	lines := getLines(content, 3)
   378  	if len(lines) <= 1 {
   379  		return false
   380  	}
   381  
   382  	for _, line := range lines {
   383  		if bytes.Contains(line, []byte("Generated by the protocol buffer compiler.  DO NOT EDIT!")) {
   384  			return true
   385  		}
   386  	}
   387  
   388  	return false
   389  }
   390  
   391  func isGeneratedJavaScriptProtocolBuffer(_, ext string, content []byte) bool {
   392  	if ext != ".js" {
   393  		return false
   394  	}
   395  
   396  	lines := getLines(content, 6)
   397  	if len(lines) < 6 {
   398  		return false
   399  	}
   400  
   401  	return bytes.Contains(lines[5], []byte("GENERATED CODE -- DO NOT EDIT!"))
   402  }
   403  
   404  var apacheThriftExtensions = map[string]struct{}{
   405  	".rb":   {},
   406  	".py":   {},
   407  	".go":   {},
   408  	".js":   {},
   409  	".m":    {},
   410  	".java": {},
   411  	".h":    {},
   412  	".cc":   {},
   413  	".cpp":  {},
   414  	".php":  {},
   415  }
   416  
   417  func isGeneratedApacheThrift(_, ext string, content []byte) bool {
   418  	if _, ok := apacheThriftExtensions[ext]; !ok {
   419  		return false
   420  	}
   421  
   422  	for _, line := range getLines(content, 6) {
   423  		if bytes.Contains(line, []byte("Autogenerated by Thrift Compiler")) {
   424  			return true
   425  		}
   426  	}
   427  
   428  	return false
   429  }
   430  
   431  func isGeneratedJNIHeader(_, ext string, content []byte) bool {
   432  	if ext != ".h" {
   433  		return false
   434  	}
   435  
   436  	lines := getLines(content, 2)
   437  	if len(lines) < 2 {
   438  		return false
   439  	}
   440  
   441  	return bytes.Contains(lines[0], []byte("/* DO NOT EDIT THIS FILE - it is machine generated */")) &&
   442  		bytes.Contains(lines[1], []byte("#include <jni.h>"))
   443  }
   444  
   445  func isVCRCassette(_, ext string, content []byte) bool {
   446  	if ext != ".yml" {
   447  		return false
   448  	}
   449  
   450  	lines := getLines(content, -2)
   451  	if len(lines) < 2 {
   452  		return false
   453  	}
   454  
   455  	return bytes.Contains(lines[1], []byte("recorded_with: VCR"))
   456  }
   457  
   458  func isCompiledCythonFile(_, ext string, content []byte) bool {
   459  	if ext != ".c" && ext != ".cpp" {
   460  		return false
   461  	}
   462  
   463  	lines := getLines(content, 1)
   464  	if len(lines) < 1 {
   465  		return false
   466  	}
   467  
   468  	return bytes.Contains(lines[0], []byte("Generated by Cython"))
   469  }
   470  
   471  func isGeneratedModule(_, ext string, content []byte) bool {
   472  	if ext != ".mod" {
   473  		return false
   474  	}
   475  
   476  	lines := getLines(content, 1)
   477  	if len(lines) < 1 {
   478  		return false
   479  	}
   480  
   481  	return bytes.Contains(lines[0], []byte("PCBNEW-LibModule-V")) ||
   482  		bytes.Contains(lines[0], []byte("GFORTRAN module version '"))
   483  }
   484  
   485  func isGeneratedUnity3DMeta(_, ext string, content []byte) bool {
   486  	if ext != ".meta" {
   487  		return false
   488  	}
   489  
   490  	lines := getLines(content, 1)
   491  	if len(lines) < 1 {
   492  		return false
   493  	}
   494  
   495  	return bytes.Contains(lines[0], []byte("fileFormatVersion: "))
   496  }
   497  
   498  func isGeneratedRacc(_, ext string, content []byte) bool {
   499  	if ext != ".rb" {
   500  		return false
   501  	}
   502  
   503  	lines := getLines(content, 3)
   504  	if len(lines) < 3 {
   505  		return false
   506  	}
   507  
   508  	return bytes.HasPrefix(lines[2], []byte("# This file is automatically generated by Racc"))
   509  }
   510  
   511  func isGeneratedJFlex(_, ext string, content []byte) bool {
   512  	if ext != ".java" {
   513  		return false
   514  	}
   515  
   516  	lines := getLines(content, 1)
   517  	if len(lines) < 1 {
   518  		return false
   519  	}
   520  
   521  	return bytes.HasPrefix(lines[0], []byte("/* The following code was generated by JFlex "))
   522  }
   523  
   524  func isGeneratedGrammarKit(_, ext string, content []byte) bool {
   525  	if ext != ".java" {
   526  		return false
   527  	}
   528  
   529  	lines := getLines(content, 1)
   530  	if len(lines) < 1 {
   531  		return false
   532  	}
   533  
   534  	return bytes.Contains(lines[0], []byte("// This is a generated file. Not intended for manual editing."))
   535  }
   536  
   537  func isGeneratedRoxygen2(_, ext string, content []byte) bool {
   538  	if ext != ".rd" {
   539  		return false
   540  	}
   541  
   542  	lines := getLines(content, 1)
   543  	if len(lines) < 1 {
   544  		return false
   545  	}
   546  
   547  	return bytes.Contains(lines[0], []byte("% Generated by roxygen2: do not edit by hand"))
   548  }
   549  
   550  func isGeneratedJison(_, ext string, content []byte) bool {
   551  	if ext != ".js" {
   552  		return false
   553  	}
   554  
   555  	lines := getLines(content, 1)
   556  	if len(lines) < 1 {
   557  		return false
   558  	}
   559  
   560  	return bytes.Contains(lines[0], []byte("/* parser generated by jison ")) ||
   561  		bytes.Contains(lines[0], []byte("/* generated by jison-lex "))
   562  }
   563  
   564  func isGeneratedGRPCCpp(_, ext string, content []byte) bool {
   565  	switch ext {
   566  	case ".cpp", ".hpp", ".h", ".cc":
   567  		lines := getLines(content, 1)
   568  		if len(lines) < 1 {
   569  			return false
   570  		}
   571  
   572  		return bytes.Contains(lines[0], []byte("// Generated by the gRPC"))
   573  	default:
   574  		return false
   575  	}
   576  }
   577  
   578  var dartRegex = regex.MustCompile(`generated code\W{2,3}do not modify`)
   579  
   580  func isGeneratedDart(_, ext string, content []byte) bool {
   581  	if ext != ".dart" {
   582  		return false
   583  	}
   584  
   585  	lines := getLines(content, 1)
   586  	if len(lines) < 1 {
   587  		return false
   588  	}
   589  
   590  	return dartRegex.Match(bytes.ToLower(lines[0]))
   591  }
   592  
   593  func isGeneratedPerlPPPortHeader(name, _ string, content []byte) bool {
   594  	if !strings.HasSuffix(name, "ppport.h") {
   595  		return false
   596  	}
   597  
   598  	lines := getLines(content, 10)
   599  	if len(lines) < 10 {
   600  		return false
   601  	}
   602  
   603  	return bytes.Contains(lines[8], []byte("Automatically created by Devel::PPPort"))
   604  }
   605  
   606  var (
   607  	gameMakerStudioFirstLineRegex = regex.MustCompile(`^\d\.\d\.\d.+\|\{`)
   608  	gameMakerStudioThirdLineRegex = regex.MustCompile(`\"modelName\"\:\s*\"GM`)
   609  )
   610  
   611  func isGeneratedGameMakerStudio(_, ext string, content []byte) bool {
   612  	if ext != ".yy" && ext != ".yyp" {
   613  		return false
   614  	}
   615  
   616  	lines := getLines(content, 3)
   617  	if len(lines) < 3 {
   618  		return false
   619  	}
   620  
   621  	return gameMakerStudioThirdLineRegex.Match(lines[2]) ||
   622  		gameMakerStudioFirstLineRegex.Match(lines[0])
   623  }
   624  
   625  var gimpRegexes = []regex.EnryRegexp{
   626  	regex.MustCompile(`\/\* GIMP [a-zA-Z0-9\- ]+ C\-Source image dump \(.+?\.c\) \*\/`),
   627  	regex.MustCompile(`\/\*  GIMP header image file format \([a-zA-Z0-9\- ]+\)\: .+?\.h  \*\/`),
   628  }
   629  
   630  func isGeneratedGimp(_, ext string, content []byte) bool {
   631  	if ext != ".c" && ext != ".h" {
   632  		return false
   633  	}
   634  
   635  	lines := getLines(content, 1)
   636  	if len(lines) < 1 {
   637  		return false
   638  	}
   639  
   640  	for _, r := range gimpRegexes {
   641  		if r.Match(lines[0]) {
   642  			return true
   643  		}
   644  	}
   645  
   646  	return false
   647  }
   648  
   649  func isGeneratedVisualStudio6(_, ext string, content []byte) bool {
   650  	if ext != ".dsp" {
   651  		return false
   652  	}
   653  
   654  	for _, l := range getLines(content, 3) {
   655  		if bytes.Contains(l, []byte("# Microsoft Developer Studio Generated Build File")) {
   656  			return true
   657  		}
   658  	}
   659  
   660  	return false
   661  }
   662  
   663  var haxeExtensions = map[string]struct{}{
   664  	".js":   {},
   665  	".py":   {},
   666  	".lua":  {},
   667  	".cpp":  {},
   668  	".h":    {},
   669  	".java": {},
   670  	".cs":   {},
   671  	".php":  {},
   672  }
   673  
   674  func isGeneratedHaxe(_, ext string, content []byte) bool {
   675  	if _, ok := haxeExtensions[ext]; !ok {
   676  		return false
   677  	}
   678  
   679  	for _, l := range getLines(content, 3) {
   680  		if bytes.Contains(l, []byte("Generated by Haxe")) {
   681  			return true
   682  		}
   683  	}
   684  
   685  	return false
   686  }
   687  
   688  var (
   689  	doxygenRegex         = regex.MustCompile(`<!--\s+Generated by Doxygen\s+[.0-9]+\s*-->`)
   690  	htmlMetaRegex        = regex.MustCompile(`<meta(\s+[^>]+)>`)
   691  	htmlMetaContentRegex = regex.MustCompile(`\s+(name|content|value)\s*=\s*("[^"]+"|'[^']+'|[^\s"']+)`)
   692  	orgModeMetaRegex     = regex.MustCompile(`org\s+mode`)
   693  )
   694  
   695  func isGeneratedHTML(_, ext string, content []byte) bool {
   696  	if ext != ".html" && ext != ".htm" && ext != ".xhtml" {
   697  		return false
   698  	}
   699  
   700  	lines := getLines(content, 30)
   701  
   702  	// Pkgdown
   703  	if len(lines) >= 2 {
   704  		for _, l := range lines[:2] {
   705  			if bytes.Contains(l, []byte("<!-- Generated by pkgdown: do not edit by hand -->")) {
   706  				return true
   707  			}
   708  		}
   709  	}
   710  
   711  	// Mandoc
   712  	if len(lines) > 2 &&
   713  		bytes.HasPrefix(lines[2], []byte("<!-- This is an automatically generated file.")) {
   714  		return true
   715  	}
   716  
   717  	// Doxygen
   718  	for _, l := range lines {
   719  		if doxygenRegex.Match(l) {
   720  			return true
   721  		}
   722  	}
   723  
   724  	// HTML tag: <meta name="generator" content="" />
   725  	part := bytes.ToLower(bytes.Join(lines, []byte{' '}))
   726  	part = bytes.ReplaceAll(part, []byte{'\n'}, []byte{})
   727  	part = bytes.ReplaceAll(part, []byte{'\r'}, []byte{})
   728  	matches := htmlMetaRegex.FindAll(part, -1)
   729  	if len(matches) == 0 {
   730  		return false
   731  	}
   732  
   733  	for _, m := range matches {
   734  		var name, value, content string
   735  		ms := htmlMetaContentRegex.FindAllStringSubmatch(string(m), -1)
   736  		for _, m := range ms {
   737  			switch m[1] {
   738  			case "name":
   739  				name = m[2]
   740  			case "value":
   741  				value = m[2]
   742  			case "content":
   743  				content = m[2]
   744  			}
   745  		}
   746  
   747  		var val = value
   748  		if val == "" {
   749  			val = content
   750  		}
   751  
   752  		name = strings.Trim(name, `"'`)
   753  		val = strings.Trim(val, `"'`)
   754  
   755  		if name != "generator" || val == "" {
   756  			continue
   757  		}
   758  
   759  		if strings.Contains(val, "jlatex2html") ||
   760  			strings.Contains(val, "latex2html") ||
   761  			strings.Contains(val, "groff") ||
   762  			strings.Contains(val, "makeinfo") ||
   763  			strings.Contains(val, "texi2html") ||
   764  			strings.Contains(val, "ronn") ||
   765  			orgModeMetaRegex.MatchString(val) {
   766  			return true
   767  		}
   768  	}
   769  
   770  	return false
   771  }
   772  
   773  func isGeneratedJooq(_, ext string, content []byte) bool {
   774  	if ext != ".java" {
   775  		return false
   776  	}
   777  
   778  	for _, l := range getLines(content, 2) {
   779  		if bytes.Contains(l, []byte("This file is generated by jOOQ.")) {
   780  			return true
   781  		}
   782  	}
   783  
   784  	return false
   785  }
   786  
   787  func getFirstLine(content []byte) []byte {
   788  	lines := getLines(content, 1)
   789  	if len(lines) > 0 {
   790  		return lines[0]
   791  	}
   792  	return nil
   793  }
   794  
   795  // getLines returns up to the first n lines. A negative index will return up to
   796  // the last n lines in reverse order.
   797  func getLines(content []byte, n int) [][]byte {
   798  	var result [][]byte
   799  	if n < 0 {
   800  		for pos := len(content); pos > 0 && len(result) < -n; {
   801  			nlpos := bytes.LastIndexByte(content[:pos], '\n')
   802  			if nlpos+1 < len(content)-1 {
   803  				result = append(result, content[nlpos+1:pos])
   804  			}
   805  			pos = nlpos
   806  		}
   807  	} else {
   808  		for pos := 0; pos < len(content) && len(result) < n; {
   809  			nlpos := bytes.IndexByte(content[pos:], '\n')
   810  			if nlpos < 0 && pos < len(content) {
   811  				nlpos = len(content)
   812  			} else if nlpos >= 0 {
   813  				nlpos += pos
   814  			}
   815  
   816  			result = append(result, content[pos:nlpos])
   817  			pos = nlpos + 1
   818  		}
   819  	}
   820  
   821  	return result
   822  }
   823  
   824  func forEachLine(content []byte, cb func([]byte)) {
   825  	var pos int
   826  	for pos < len(content) {
   827  		nlpos := bytes.IndexByte(content[pos:], '\n')
   828  		if nlpos < 0 && pos < len(content) {
   829  			nlpos = len(content)
   830  		} else if nlpos >= 0 {
   831  			nlpos += pos
   832  		}
   833  
   834  		cb(content[pos:nlpos])
   835  		pos = nlpos + 1
   836  	}
   837  }
   838  
   839  func countAppearancesInLine(line []byte, targets ...string) int {
   840  	var count int
   841  	for _, t := range targets {
   842  		count += bytes.Count(line, []byte(t))
   843  	}
   844  	return count
   845  }