aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2026-01-11 14:01:31 +0700
committerShulhan <ms@kilabit.info>2026-01-11 21:35:24 +0700
commit9bcf61fd76ed3b54bc4d9847681749ba52c6e15f (patch)
treed138e1521100694a7aadfc3f740b93ee95e5eaa9
parent187cf1cb9cc684f3860426be86e9c682aa6c849f (diff)
downloadspdxconv-9bcf61fd76ed3b54bc4d9847681749ba52c6e15f.tar.xz
all: implement apply command
The apply command read the "spdxconv.report" and apply the license and copyright as stated on each file in the report. A file that has been successfully processed will be removed from the report.
-rw-r--r--README.md9
-rw-r--r--cmd/spdxconv/main.go9
-rw-r--r--file.go354
-rw-r--r--file_test.go44
-rw-r--r--spdxconv.go97
-rw-r--r--spdxconv_test.go39
-rw-r--r--testdata/Apply/.gitignore (renamed from testdata/file/.gitignore)0
-rw-r--r--testdata/Apply_test.txt (renamed from testdata/file_test.txt)10
8 files changed, 377 insertions, 185 deletions
diff --git a/README.md b/README.md
index d6b0118..68f2302 100644
--- a/README.md
+++ b/README.md
@@ -218,8 +218,8 @@ detected comment syntax.
Binary group are non-text file, for example images (like jpg, png) or
executable files.
-For binary file, program will create new file, in the same directory, with
-the same file name as binary file plus additional suffix ".license".
+For binary file, program will create new file with the same name as binary
+file plus additional suffix ".license".
Inside those "$name.license" file, the new SPDX identifiers will be inserted
as defined in the report.
@@ -233,6 +233,11 @@ configuration and rerun the scan command for the next cycle.
The apply command read the "spdxconv.report" and apply the license and
copyright in the file as stated on each line in the report.
+Any failed operation on file will be logged to stdout.
+
+Once completed, it will write back the report file but only contains line
+that cannot be processed.
+
## References
[SPDX License List](https://spdx.org/licenses/).
diff --git a/cmd/spdxconv/main.go b/cmd/spdxconv/main.go
index c6bb931..433641c 100644
--- a/cmd/spdxconv/main.go
+++ b/cmd/spdxconv/main.go
@@ -39,6 +39,13 @@ func main() {
}
return
+ case `apply`:
+ err := spdxconv.Apply()
+ if err != nil {
+ log.Fatal(err)
+ }
+ return
+
case `help`:
usage()
os.Exit(1)
@@ -73,6 +80,8 @@ scan
apply
The apply command read the "spdxconv.report" and apply the license and
copyright as stated on each file in the report.
+ A file that has been successfully processed will be removed from the
+ report.
User then can repeat edit "spdxconv.cfg", "scan" and "apply" command multiple
times, until they satisfied with the result.`)
diff --git a/file.go b/file.go
index 735aa79..714a6c5 100644
--- a/file.go
+++ b/file.go
@@ -25,14 +25,16 @@ const (
// reLicenseID regex to detect SPDX license identifier with or without
// comment prefix.
-var reLicenseID = regexp.MustCompile(`^(//+|#+|/\*+|<!--+)?\s?SPDX-License-Identifier:.*$`)
+var reLicenseID = regexp.MustCompile(`^(//+|#+|/\*+|<!--+)?\s?SPDX-License-Identifier:\s*(.*)\s*$`)
// reCopyrightText regex to detect SPDX copyright text.
-var reCopyrightText = regexp.MustCompile(`^(//+|#+|/\*+|<!--+)?\s?SPDX-FileCopyrightText:.*$`)
+var reCopyrightText = regexp.MustCompile(`^(//+|#+|/\*+|<!--+)?\s?SPDX-FileCopyrightText:\s*(.*)\s*$`)
// REUSE-IgnoreEnd
type file struct {
+ matchLicense *configMatchLicense
+
path string
// commentPrefix used as prefix to SPDX identifier.
@@ -82,18 +84,35 @@ func newFile(path string, maxLine int) (f *file, err error) {
return f, nil
}
+ _ = f.initLines(content, maxLine)
+
+ return f, nil
+}
+
+func (f *file) initLines(content []byte, maxLine int) (err error) {
+ if content == nil {
+ content, err = os.ReadFile(f.path)
+ if err != nil {
+ return err
+ }
+ }
+
f.lines = bytes.Split(content, []byte{'\n'})
nline := len(f.lines)
if nline < maxLine*2 {
f.topLines = f.lines
- f.lines = f.lines[nline:]
+ f.lines = nil
} else {
f.topLines = f.lines[:maxLine]
f.bottomLines = f.lines[nline-maxLine:]
f.lines = f.lines[maxLine : nline-maxLine]
}
- return f, nil
+
+ if bytes.HasPrefix(f.topLines[0], []byte(`#!`)) {
+ f.hasSheBang = true
+ }
+ return nil
}
func (f *file) scan(conv *SPDXConv) {
@@ -105,13 +124,6 @@ func (f *file) scan(conv *SPDXConv) {
f.scanCopyrightText(conv)
}
-// apply the SPDX identifier to file.
-func (f *file) apply(conv *SPDXConv) {
- f.detectComment()
- f.applyLicenseID(conv)
- f.insertEmptyLine()
-}
-
func (f *file) detectComment() {
if bytes.HasPrefix(f.topLines[0], []byte(`#!`)) {
f.hasSheBang = true
@@ -137,6 +149,25 @@ func (f *file) detectComment() {
return
}
}
+ for _, line := range f.bottomLines {
+ if bytes.HasPrefix(line, []byte(`#`)) {
+ f.commentPrefix = `# `
+ return
+ }
+ if bytes.HasPrefix(line, []byte(`//`)) {
+ f.commentPrefix = `// `
+ return
+ }
+ if bytes.HasPrefix(line, []byte(`/*`)) {
+ f.commentPrefix = `// `
+ return
+ }
+ if bytes.HasPrefix(line, []byte(`<!--`)) {
+ f.commentPrefix = `<!-- `
+ f.commentSuffix = ` -->`
+ return
+ }
+ }
f.isUnknown = true
}
@@ -146,12 +177,6 @@ func (f *file) scanLicenseID(conv *SPDXConv) {
if reLicenseID.Match(line) {
f.idxLicenseID = x
f.licenseID = valExist
- if f.hasSheBang && x == 1 {
- return
- }
- if x == 0 {
- return
- }
return
}
if cml.rePattern.Match(line) {
@@ -182,12 +207,6 @@ func (f *file) scanCopyrightText(conv *SPDXConv) {
if reCopyrightText.Match(line) {
f.idxCopyrightText = x
f.copyrightText = valExist
- if f.hasSheBang && x == 1 {
- return
- }
- if x == 0 {
- return
- }
return
}
if cmc.match(string(line)) {
@@ -199,7 +218,7 @@ func (f *file) scanCopyrightText(conv *SPDXConv) {
for x, line := range f.bottomLines {
if reLicenseID.Match(line) {
f.idxCopyrightText = x - conv.cfg.MaxLineMatch
- f.licenseID = valExist
+ f.copyrightText = valExist
return
}
if cmc.match(string(line)) {
@@ -209,79 +228,230 @@ func (f *file) scanCopyrightText(conv *SPDXConv) {
}
}
}
- f.licenseID = valDefault
+ f.copyrightText = valDefault
+}
+
+// apply the SPDX identifier to file.
+func (f *file) apply(conv *SPDXConv) (err error) {
+ err = f.initLines(nil, conv.cfg.MaxLineMatch)
+ if err != nil {
+ return err
+ }
+
+ err = f.applyLicenseID(conv)
+ if err != nil {
+ return err
+ }
+
+ err = f.applyCopyrightText(conv)
+ if err != nil {
+ return err
+ }
+ f.applyDelete()
+
+ // REUSE-IgnoreStart
+ line := fmt.Sprintf("%sSPDX-License-Identifier: %s%s",
+ f.commentPrefix, f.licenseID, f.commentSuffix)
+ // REUSE-IgnoreEnd
+
+ rawline := []byte(line)
+ if f.hasSheBang {
+ f.idxLicenseID = 1
+ } else {
+ f.idxLicenseID = 0
+ }
+ f.topLines = slices.Insert(f.topLines, f.idxLicenseID, rawline)
+
+ if f.copyrightYear == valUnknown {
+ f.copyrightYear = ``
+ } else {
+ f.copyrightYear += ` `
+ }
+ // REUSE-IgnoreStart
+ line = fmt.Sprintf("%sSPDX-FileCopyrightText: %s%s%s",
+ f.commentPrefix, f.copyrightYear, f.copyrightText, f.commentSuffix)
+ // REUSE-IgnoreEnd
+
+ rawline = []byte(line)
+ f.topLines = slices.Insert(f.topLines, f.idxLicenseID+1, rawline)
+
+ f.insertEmptyLine(f.idxLicenseID + 2)
+
+ return nil
}
-// applyLicenseID check and insert the SPDX-License-Identifier.
-//
-// Its detect if SPDX-License-Identifer exist at the top or bottom of
-// the file.
-// If one found at the top, but not at the first line, or at the
-// bottom, move it to the first line, after shebang.
-func (f *file) applyLicenseID(conv *SPDXConv) {
- for _, cml := range conv.cfg.MatchLicense {
- for x, line := range f.topLines {
- if reLicenseID.Match(line) {
- f.idxLicenseID = x
- if f.hasSheBang && x == 1 {
- return
- }
- if x == 0 {
- return
- }
- f.topLines = slices.Delete(f.topLines, x, x+1)
- f.insertLicenseID(line)
- return
- }
- if cml.rePattern.Match(line) {
- f.licenseID = cml.LicenseIdentifier
- if cml.DeleteMatch {
- f.topLines = slices.Delete(f.topLines, x, x+1)
- }
- f.deleteLinePattern(f.topLines[x:], cml.reDeleteLine)
+// applyLicenseID apply the license ID based on the status of licenseID
+// (default, exist, match) and line number.
+func (f *file) applyLicenseID(conv *SPDXConv) (err error) {
+ switch f.licenseID {
+ case valDefault:
+ f.licenseID = conv.cfg.LicenseIdentifier
+ case valExist:
+ var line []byte
+ x := f.idxLicenseID
+ if f.idxLicenseID >= 0 {
+ line = f.topLines[x]
+ } else {
+ x = conv.cfg.MaxLineMatch + f.idxLicenseID
+ line = f.bottomLines[x]
+ }
+ match := reLicenseID.FindSubmatch(line)
+ if len(match) != 3 {
+ return fmt.Errorf(`%s: line %d does not contains SPDX License-Identifier`,
+ f.path, f.idxLicenseID)
+ }
+ f.licenseID = string(match[2])
+
+ // Delete the line latter after processing copyright
+ // text to prevent index mismatch.
+ if f.idxLicenseID >= 0 {
+ f.topLines[x] = nil
+ } else {
+ f.bottomLines[x] = nil
+ }
+ case valMatch:
+ var line []byte
+ x := f.idxLicenseID
+ if f.idxLicenseID >= 0 {
+ line = f.topLines[x]
+ } else {
+ x = conv.cfg.MaxLineMatch + f.idxLicenseID
+ line = f.bottomLines[x]
+ }
+ // Verify that the line actually match with one of
+ // match-license pattern.
+ for _, f.matchLicense = range conv.cfg.MatchLicense {
+ if f.matchLicense.rePattern.Match(line) {
+ f.licenseID = f.matchLicense.LicenseIdentifier
+ break
}
}
- if f.licenseID != valDefault {
- break
+ if f.matchLicense == nil {
+ return fmt.Errorf(`%s: line %d does not match with any of match-license pattern`,
+ f.path, f.idxLicenseID)
}
- for x, line := range f.bottomLines {
- if reLicenseID.Match(line) {
- f.bottomLines = slices.Delete(f.bottomLines, x, x+1)
- f.insertLicenseID(line)
- return
+
+ // Delete the line latter after processing copyright
+ // text to prevent index mismatch.
+ if f.idxLicenseID >= 0 {
+ f.topLines[x] = nil
+ } else {
+ f.bottomLines[x] = nil
+ }
+ }
+
+ return nil
+}
+
+// applyCopyrightText apply the copyright text based on the status of
+// copyright_id (default, exist, match) and line number.
+func (f *file) applyCopyrightText(conv *SPDXConv) (err error) {
+ switch f.copyrightText {
+ case valDefault:
+ f.copyrightText = conv.cfg.FileCopyrightText
+ case valExist:
+ var line []byte
+ x := f.idxCopyrightText
+ if f.idxCopyrightText >= 0 {
+ line = f.topLines[x]
+ } else {
+ x = conv.cfg.MaxLineMatch + f.idxCopyrightText
+ line = f.bottomLines[x]
+ }
+ match := reCopyrightText.FindSubmatch(line)
+ if len(match) != 3 {
+ return fmt.Errorf(`%s: line %d does not contains SPDX FileCopyrightText`,
+ f.path, f.idxLicenseID)
+ }
+ f.copyrightText = string(match[2])
+ if f.idxLicenseID >= 0 {
+ f.topLines[x] = nil
+ } else {
+ f.bottomLines[x] = nil
+ }
+ case valMatch:
+ var line []byte
+ x := f.idxCopyrightText
+ if f.idxCopyrightText >= 0 {
+ line = f.topLines[x]
+ } else {
+ x = conv.cfg.MaxLineMatch + f.idxCopyrightText
+ line = f.bottomLines[x]
+ }
+ // Verify that the line actually match with one of
+ // match-copyright pattern.
+ var cmc *configMatchCopyright
+ for _, cmc = range conv.cfg.MatchCopyright {
+ if cmc.match(string(line)) {
+ f.copyrightYear = cmc.year
+ f.copyrightText = cmc.String()
+ break
}
- if cml.rePattern.Match(line) {
- f.licenseID = cml.LicenseIdentifier
- if cml.DeleteMatch {
- f.bottomLines = slices.Delete(f.bottomLines, x, x+1)
- }
- f.deleteLinePattern(f.bottomLines[x:], cml.reDeleteLine)
+ }
+ if cmc == nil {
+ return fmt.Errorf(`%s: line %d does not match with any of match-license pattern`,
+ f.path, f.idxLicenseID)
+ }
+ if f.idxLicenseID >= 0 {
+ f.topLines[x] = nil
+ } else {
+ f.bottomLines[x] = nil
+ }
+ }
+ return nil
+}
+
+func (f *file) applyDelete() {
+ var reDeleteLine []*regexp.Regexp
+ if f.matchLicense != nil {
+ reDeleteLine = f.matchLicense.reDeleteLine
+ }
+ var lines [][]byte
+ for _, line := range f.topLines {
+ if line == nil {
+ continue
+ }
+ var found bool
+ for _, redel := range reDeleteLine {
+ if redel.Match(line) {
+ found = true
+ break
}
}
- if f.licenseID != valDefault {
- break
+ if found {
+ continue
}
+ lines = append(lines, line)
}
- if f.licenseID == valDefault {
- f.licenseID = conv.cfg.LicenseIdentifier
+ f.topLines = lines
+
+ lines = nil
+ for _, line := range f.bottomLines {
+ if line == nil {
+ continue
+ }
+ var found bool
+ for _, redel := range reDeleteLine {
+ if redel.Match(line) {
+ found = true
+ break
+ }
+ }
+ if found {
+ continue
+ }
+ lines = append(lines, line)
}
- // REUSE-IgnoreStart
- line := fmt.Sprintf("%sSPDX-License-Identifier: %s%s",
- f.commentPrefix, f.licenseID, f.commentSuffix)
- // REUSE-IgnoreEnd
- f.insertLicenseID([]byte(line))
+ f.bottomLines = lines
}
// insertEmptyLine insert empty line after SPDX identifiers or any comments after it.
-func (f *file) insertEmptyLine() {
- if f.idxLicenseID < 0 || f.commentPrefix == `` {
- // No license ID inserted.
- return
- }
+func (f *file) insertEmptyLine(start int) {
comment := []byte(f.commentPrefix)
comment = comment[:len(comment)-1] // Remove space.
- for x, line := range f.topLines[f.idxLicenseID:] {
+ for x := start; x < len(f.topLines); x++ {
+ line := f.topLines[x]
if bytes.HasPrefix(line, comment) {
continue
}
@@ -295,29 +465,6 @@ func (f *file) insertEmptyLine() {
}
}
-// insertLicenseID insert the license identifier `line` at the top of the
-// file and below the shebang "#!" if its exists.
-func (f *file) insertLicenseID(line []byte) {
- if f.hasSheBang {
- f.topLines = slices.Insert(f.topLines, 1, line)
- f.idxLicenseID = 1
- } else {
- f.topLines = slices.Insert(f.topLines, 0, line)
- f.idxLicenseID = 0
- }
-}
-
-func (f *file) deleteLinePattern(lines [][]byte, reDeleteLine []*regexp.Regexp) {
- for _, re := range reDeleteLine {
- for x, line := range lines {
- if re.Match(line) {
- lines = slices.Delete(lines, x, x+1)
- break
- }
- }
- }
-}
-
func (f *file) write() (err error) {
var finfo os.FileInfo
finfo, err = os.Stat(f.path)
@@ -328,6 +475,7 @@ func (f *file) write() (err error) {
lines := slices.Concat(f.topLines, f.lines, f.bottomLines)
content := bytes.Join(lines, []byte{'\n'})
content = bytes.TrimRight(content, "\n")
+ content = append(content, '\n')
err = os.WriteFile(f.path, content, finfo.Mode())
if err != nil {
return fmt.Errorf(`write: %w`, err)
diff --git a/file_test.go b/file_test.go
index e082d87..1b3823c 100644
--- a/file_test.go
+++ b/file_test.go
@@ -4,55 +4,11 @@
package spdxconv
import (
- "os"
"testing"
"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)
-func TestFile_all(t *testing.T) {
- // Populate the test files from `testdata/file_test.txt`.
-
- var testData *test.Data
- var err error
- testData, err = test.LoadData(`testdata/file_test.txt`)
- if err != nil {
- t.Fatal(err)
- }
- var tempDir = `testdata/file/`
- testData.ExtractInput(tempDir)
- t.Chdir(tempDir)
-
- // Inititalize the SPDXConv instance.
-
- conv, err := New(`.`)
- if err != nil {
- t.Fatal(err)
- }
-
- for input, _ := range testData.Input {
- if input == `spdxconv.cfg` {
- continue
- }
-
- f, err := newFile(input, conv.cfg.MaxLineMatch)
- if err != nil {
- t.Fatal(err)
- }
- f.apply(conv)
- err = f.write()
- if err != nil {
- t.Fatal(err)
- }
- got, err := os.ReadFile(input)
- if err != nil {
- t.Fatal(err)
- }
- test.Assert(t, input+`: after`,
- string(testData.Output[input]), string(got))
- }
-}
-
func TestFile_detectComment(t *testing.T) {
type testCase struct {
topLines [][]byte
diff --git a/spdxconv.go b/spdxconv.go
index 73b2ad3..dc0f31d 100644
--- a/spdxconv.go
+++ b/spdxconv.go
@@ -4,7 +4,9 @@
package spdxconv
import (
+ "bytes"
"fmt"
+ "log"
"os"
"path/filepath"
"slices"
@@ -17,6 +19,8 @@ import (
// ConfigFile the file name for configuration file.
const ConfigFile = `spdxconv.cfg`
+var suffixLicense = `.license`
+
// SPDXConv the main type for converting files to SPDX format.
type SPDXConv struct {
scm sourceCodeManagement
@@ -37,36 +41,6 @@ type SPDXConv struct {
cfg config
}
-// Apply the SPDX license headers to all files inside the directory `path` or
-// to single file only.
-func Apply(path string) (err error) {
- var logp = `Apply`
- var conv *SPDXConv
-
- conv, err = New(path)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
- var listFile []string
- if conv.name == `` {
- listFile, err = conv.scanDir([]string{conv.dir})
- } else {
- listFile, err = conv.scanFile(conv.dir, conv.name)
- }
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
-
- var pathFile string
- for _, pathFile = range listFile {
- err = conv.apply(pathFile)
- if err != nil {
- return fmt.Errorf(`%s: %w`, logp, err)
- }
- }
- return nil
-}
-
// Init create the configuration file [ConfigFile] in the current directory.
func Init() (err error) {
err = os.WriteFile(ConfigFile, []byte(configTemplate), 0600)
@@ -111,6 +85,68 @@ func Scan(path string) (err error) {
return nil
}
+// Apply the SPDX headers to all files listed in ReportFile].
+func Apply() (err error) {
+ var logp = `Apply`
+
+ var conv *SPDXConv
+ conv, err = New(`.`)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ var rep *report
+ rep, err = loadReport()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ var listFail []*file
+ for _, f := range rep.listRegular {
+ err = f.apply(conv)
+ if err != nil {
+ listFail = append(listFail, f)
+ log.Printf(`%s: %s`, logp, err)
+ continue
+ }
+
+ err = f.write()
+ if err != nil {
+ listFail = append(listFail, f)
+ log.Printf(`%s: %s`, logp, err)
+ continue
+ }
+ }
+ rep.listRegular = listFail
+
+ var buf bytes.Buffer
+ // REUSE-IgnoreStart
+ fmt.Fprintf(&buf, "SPDX-License-Identifier: %s\n", conv.cfg.LicenseIdentifier)
+ fmt.Fprintf(&buf, "SPDX-FileCopyrightText: %s\n", conv.cfg.FileCopyrightText)
+ // REUSE-IgnoreEnd
+
+ listFail = nil
+ for _, f := range rep.listBinary {
+ pathLicense := f.path + suffixLicense
+ err = os.WriteFile(pathLicense, buf.Bytes(), 0600)
+ if err != nil {
+ listFail = append(listFail, f)
+ log.Printf(`%s: failed to write %s`, logp, pathLicense)
+ continue
+ }
+ }
+ rep.listBinary = listFail
+
+ // Write the report back that contains only file with failed
+ // conversion.
+ err = rep.write()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ return nil
+}
+
// New initialize new instance of SPDXConv.
func New(path string) (conv *SPDXConv, err error) {
var logp = `New`
@@ -232,7 +268,6 @@ func (conv *SPDXConv) scanDir(listDir []string) (listFile []string, err error) {
ReportFile: struct{}{},
`vendor`: struct{}{},
}
- var suffixLicense = `.license`
var dir string
var listde []os.DirEntry
diff --git a/spdxconv_test.go b/spdxconv_test.go
index f002ef2..8a8e112 100644
--- a/spdxconv_test.go
+++ b/spdxconv_test.go
@@ -55,6 +55,45 @@ test.sh,match,1,unknown,match,0,# ,
test.Assert(t, `Scan: `+scanDir, exp, string(got))
}
+func TestApply(t *testing.T) {
+ // Populate the test files from `testdata/Apply_test.txt`.
+
+ var testData *test.Data
+ var err error
+ testData, err = test.LoadData(`testdata/Apply_test.txt`)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var tempDir = `testdata/Apply/`
+ testData.ExtractInput(tempDir)
+ t.Chdir(tempDir)
+
+ // Scan and Apply ...
+
+ err = Scan(`.`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = Apply()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for input, _ := range testData.Input {
+ if input == `spdxconv.cfg` {
+ continue
+ }
+
+ got, err := os.ReadFile(input)
+ if err != nil {
+ t.Fatal(err)
+ }
+ test.Assert(t, input+`: after`,
+ string(testData.Output[input]), string(got))
+ }
+}
+
func TestNew(t *testing.T) {
type testCase struct {
exp *SPDXConv
diff --git a/testdata/file/.gitignore b/testdata/Apply/.gitignore
index e9f86eb..e9f86eb 100644
--- a/testdata/file/.gitignore
+++ b/testdata/Apply/.gitignore
diff --git a/testdata/file_test.txt b/testdata/Apply_test.txt
index d61b13a..ba51ede 100644
--- a/testdata/file_test.txt
+++ b/testdata/Apply_test.txt
@@ -19,7 +19,7 @@ delete_line_pattern = "^(//+|#+)\\s*$"
delete_line_pattern = "^(//+|#+)\\s+license that(.*)$"
[match-copyright]
-pattern = "^(//+|#+)\\s+Copyright\\s+(?<year>\\d{4}),?\\s+(?<holder>.*)\\s+<*(?<email>.*)>.*$"
+pattern = "^(//+|#+)\\s+Copyright\\s+(?<year>\\d{4}),?\\s+(?<author>.*)\\s+<*(?<contact>.*)>.*$"
>>> without_spdx_license_id.txt
1
@@ -27,7 +27,6 @@ pattern = "^(//+|#+)\\s+Copyright\\s+(?<year>\\d{4}),?\\s+(?<holder>.*)\\s+<*(?<
3
<<< without_spdx_license_id.txt
-SPDX-License-Identifier: GPL-3.0-only
1
2
3
@@ -40,6 +39,7 @@ SPDX-License-Identifier: GPL-3.0-only
<<< with_spdx_at_bottom.txt
// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: M. Shulhan <ms@kilabit.info>
1
2
@@ -53,6 +53,7 @@ SPDX-License-Identifier: GPL-3.0-only
<<< with_spdx_license_id_only.txt
// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: M. Shulhan <ms@kilabit.info>
1
2
@@ -85,7 +86,7 @@ SPDX-License-Identifier: GPL-3.0-only
<<< with_match_license.txt
// SPDX-License-Identifier: BSD-3-Clause
-// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved.
+// SPDX-FileCopyrightText: 2018 Shulhan <ms@kilabit.info>
1
2
@@ -102,12 +103,11 @@ SPDX-License-Identifier: GPL-3.0-only
<<< with_match_license_bottom.txt
// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: 2018 Shulhan <ms@kilabit.info>
1
2
3
-// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved.
-
<<< END
// REUSE-IgnoreEnd