2019-10-23 18:13:12 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
|
|
|
|
"github.com/Luzifer/go_helpers/v2/str"
|
|
|
|
"github.com/Luzifer/rconfig/v2"
|
|
|
|
"github.com/Luzifer/scs-extract/scs"
|
2024-11-25 15:01:02 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-10-23 18:13:12 +00:00
|
|
|
)
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
const dirPermissions = 0x750
|
|
|
|
|
2019-10-23 18:13:12 +00:00
|
|
|
var (
|
|
|
|
cfg = struct {
|
|
|
|
Dest string `flag:"dest,d" default:"." description:"Path prefix to use to extract files to"`
|
|
|
|
Extract bool `flag:"extract,x" default:"false" description:"Extract files (if not given files are just listed)"`
|
|
|
|
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
|
|
|
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
|
|
|
}{}
|
|
|
|
|
|
|
|
version = "dev"
|
|
|
|
)
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
func initApp() (err error) {
|
|
|
|
if err = rconfig.ParseAndValidate(&cfg); err != nil {
|
|
|
|
return fmt.Errorf("parsing CLI options: %w", err)
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
l, err := logrus.ParseLevel(cfg.LogLevel)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("parsing log-level: %w", err)
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.SetLevel(l)
|
2019-10-23 18:13:12 +00:00
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
return nil
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
//nolint:gocyclo // simple loop routine, fine to understand
|
2019-10-23 18:13:12 +00:00
|
|
|
func main() {
|
2024-11-25 15:01:02 +00:00
|
|
|
var err error
|
|
|
|
if err = initApp(); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("initializing app")
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.VersionAndExit {
|
|
|
|
fmt.Printf("scs-extract %s\n", version) //nolint:forbidigo
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2019-10-23 18:13:12 +00:00
|
|
|
var (
|
|
|
|
archive string
|
|
|
|
extract []string
|
|
|
|
)
|
|
|
|
|
|
|
|
switch len(rconfig.Args()) {
|
|
|
|
case 1:
|
|
|
|
// No positional arguments
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.Fatal("no SCS archive given")
|
2019-10-23 18:13:12 +00:00
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
case 2: //nolint:mnd
|
2019-10-23 18:13:12 +00:00
|
|
|
archive = rconfig.Args()[1]
|
|
|
|
|
|
|
|
default:
|
|
|
|
archive = rconfig.Args()[1]
|
|
|
|
extract = rconfig.Args()[2:]
|
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
f, err := os.Open(archive) //#nosec:G304 // Intended to open arbitrary files
|
2019-10-23 18:13:12 +00:00
|
|
|
if err != nil {
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithError(err).Fatal("opening input file")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
2024-11-25 15:01:02 +00:00
|
|
|
defer f.Close() //nolint:errcheck // will be closed by program exit
|
2019-10-23 18:13:12 +00:00
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
r, err := scs.NewReader(f)
|
2019-10-23 18:13:12 +00:00
|
|
|
if err != nil {
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithError(err).Fatal("reading SCS file headers")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithField("no_files", len(r.Files)).Debug("opened archive")
|
2019-10-23 18:13:12 +00:00
|
|
|
|
|
|
|
destInfo, err := os.Stat(cfg.Dest)
|
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithError(err).Fatal("accessing destination")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
if err := os.MkdirAll(cfg.Dest, dirPermissions); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("creating destination directory")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if destInfo != nil && !destInfo.IsDir() {
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.Fatal("destination exists and is no directory")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range r.Files {
|
|
|
|
if !str.StringInSlice(file.Name, extract) && len(extract) > 0 {
|
|
|
|
// Files to extract are given but this is not mentioned
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
if file.IsDirectory {
|
2019-10-23 18:13:12 +00:00
|
|
|
// Don't care about directories, if they contain files they will be created
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !cfg.Extract {
|
|
|
|
// Not asked to extract, do not extract
|
2024-11-25 15:01:02 +00:00
|
|
|
fmt.Println(file.Name) //nolint:forbidigo // Intended to print file list
|
2019-10-23 18:13:12 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
destPath := path.Join(cfg.Dest, file.Name)
|
2024-11-25 15:01:02 +00:00
|
|
|
if err := os.MkdirAll(path.Dir(destPath), dirPermissions); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("creating directory")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
src, err := file.Open()
|
|
|
|
if err != nil {
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithError(err).Fatal("opening file from archive")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
dest, err := os.Create(destPath) //#nosec:G304 // Intended to create files at given location
|
2019-10-23 18:13:12 +00:00
|
|
|
if err != nil {
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithError(err).Fatal("creating destination file")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = io.Copy(dest, src); err != nil {
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithError(err).WithField("name", file.Name).Fatal("Unable to write file contents")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
dest.Close() //nolint:errcheck,gosec,revive // Will be closed by program exit
|
|
|
|
src.Close() //nolint:errcheck,gosec // Will be closed by program exit
|
2019-10-23 18:13:12 +00:00
|
|
|
|
2024-11-25 15:01:02 +00:00
|
|
|
logrus.WithField("file", file.Name).Info("File extracted")
|
2019-10-23 18:13:12 +00:00
|
|
|
}
|
|
|
|
}
|