You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

generated.go 18KB

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