aboutsummaryrefslogtreecommitdiff
path: root/spdxconv.go
diff options
context:
space:
mode:
Diffstat (limited to 'spdxconv.go')
-rw-r--r--spdxconv.go202
1 files changed, 202 insertions, 0 deletions
diff --git a/spdxconv.go b/spdxconv.go
new file mode 100644
index 0000000..5f28e5a
--- /dev/null
+++ b/spdxconv.go
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info>
+
+package spdxconv
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/git"
+ "git.sr.ht/~shulhan/pakakeh.go/lib/ini"
+)
+
+// SPDXConv the main type for converting files to SPDX format.
+type SPDXConv struct {
+ scm sourceCodeManagement
+
+ // dir define the directory that will processed.
+ // `name` is single file inside `dir` to be processed.
+ // If `name` is empty means process all files inside `dir`.
+ dir string
+ name string
+
+ // scmDir define the root directory for source-code management.
+ // In git, the path that contains the ".git" directory.
+ scmDir string
+
+ 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.scanFiles([]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 {
+ conv.convert(pathFile)
+ }
+ return nil
+}
+
+// New initialize new instance of SPDXConv
+func New(path string) (conv *SPDXConv, err error) {
+ var logp = `New`
+
+ path, err = filepath.Abs(path)
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ conv = &SPDXConv{}
+ var fi os.FileInfo
+ fi, err = os.Stat(path)
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ if fi.IsDir() {
+ conv.dir = path
+ } else {
+ conv.dir, conv.name = filepath.Split(path)
+ }
+
+ err = conv.loadConfig(conv.dir)
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ err = conv.scanForSCM(conv.dir)
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ return conv, nil
+}
+
+// loadConfig load the program configuration from file `spdxconv.cfg` in the
+// current directory.
+func (conv *SPDXConv) loadConfig(dir string) (err error) {
+ var logp = `loadConfig`
+ var pathcfg = filepath.Join(dir, `spdxconv.cfg`)
+ var rawcfg []byte
+ rawcfg, err = os.ReadFile(pathcfg)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return fmt.Errorf(`%s %s: %w`, logp, pathcfg, err)
+ }
+ err = ini.Unmarshal(rawcfg, &conv.cfg)
+ if err != nil {
+ return fmt.Errorf(`%s %s: %w`, logp, pathcfg, err)
+ }
+ err = conv.cfg.init()
+ if err != nil {
+ return fmt.Errorf(`%s %s: %w`, logp, pathcfg, err)
+ }
+ return nil
+}
+
+// scanForSCM scan for source-code management (SCM) from directory `dir` up
+// until the root.
+// Currently, only support git.
+func (conv *SPDXConv) scanForSCM(dir string) (err error) {
+ var scmDir string
+ var fi os.FileInfo
+ for dir != `/` {
+ scmDir = filepath.Join(dir, `.git`)
+ fi, err = os.Stat(scmDir)
+ if err != nil {
+ if os.IsNotExist(err) {
+ continue
+ }
+ return err
+ }
+ if !fi.IsDir() {
+ continue
+ }
+ conv.scmDir = dir
+ conv.scm, err = git.New(dir)
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ conv.scm = &dummySCM{}
+ return nil
+}
+
+// scanFile check if the single file can be processed or not.
+func (conv *SPDXConv) scanFile(dir, name string) (listFile []string, err error) {
+ var pathName = filepath.Join(dir, name)
+ if conv.scm.IsIgnored(pathName) {
+ return nil, nil
+ }
+ listFile = append(listFile, pathName)
+ return listFile, nil
+}
+
+// scanFiles list file to be processed in directory `dir`, recursively.
+// A file ignored by ".gitignore" file will be excluded.
+// A common ignore file or directory name likes ".git", "node_modules", and
+// "vendor"; also will be excluded.
+func (conv *SPDXConv) scanFiles(listDir []string) (listFile []string, err error) {
+ var commonIgnore = map[string]struct{}{
+ `.git`: struct{}{},
+ `node_modules`: struct{}{},
+ `vendor`: struct{}{},
+ }
+ var dir string
+ var listde []os.DirEntry
+ var de os.DirEntry
+ var ok bool
+ for len(listDir) != 0 {
+ dir = listDir[0]
+ listDir = listDir[1:]
+
+ listde, err = os.ReadDir(dir)
+ if err != nil {
+ return listFile, err
+ }
+ for _, de = range listde {
+ var name = de.Name()
+ if conv.scm.IsIgnored(name) {
+ continue
+ }
+ _, ok = commonIgnore[name]
+ if ok {
+ continue
+ }
+ var pathName = filepath.Join(dir, name)
+ if de.IsDir() {
+ listDir = append(listDir, pathName)
+ continue
+ }
+ listFile = append(listFile, pathName)
+ }
+ }
+ return listFile, nil
+}
+
+// convert the given pathFile to SPDX license format.
+func (conv *SPDXConv) convert(pathFile string) {
+
+}