-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
132 lines (106 loc) · 3.14 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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"
log "github.com/sirupsen/logrus"
)
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"
)
func init() {
if err := rconfig.ParseAndValidate(&cfg); err != nil {
log.Fatalf("Unable to parse commandline options: %s", err)
}
if cfg.VersionAndExit {
fmt.Printf("scs-extract %s\n", version)
os.Exit(0)
}
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
log.WithError(err).Fatal("Unable to parse log level")
} else {
log.SetLevel(l)
}
}
func main() {
var (
archive string
extract []string
)
switch len(rconfig.Args()) {
case 1:
// No positional arguments
log.Fatal("No SCS archive given")
case 2:
archive = rconfig.Args()[1]
default:
archive = rconfig.Args()[1]
extract = rconfig.Args()[2:]
}
f, err := os.Open(archive)
if err != nil {
log.WithError(err).Fatal("Unable to open input file")
}
defer f.Close()
r, err := scs.NewReader(f, 0)
if err != nil {
log.WithError(err).Fatal("Unable to read SCS file headers")
}
log.WithField("no_files", len(r.Files)).Debug("Opened archive")
destInfo, err := os.Stat(cfg.Dest)
if err != nil {
if !os.IsNotExist(err) {
log.WithError(err).Fatal("Unable to access destination")
}
if err := os.MkdirAll(cfg.Dest, 0755); err != nil {
log.WithError(err).Fatal("Unable to create destination directory")
}
}
if destInfo != nil && !destInfo.IsDir() {
log.Fatal("Destination exists and is no directory")
}
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
}
if file.Type == scs.EntryTypeCompressedNames || file.Type == scs.EntryTypeCompressedNamesCopy ||
file.Type == scs.EntryTypeUncompressedNames || file.Type == scs.EntryTypeUncompressedNamesCopy {
// Don't care about directories, if they contain files they will be created
continue
}
if !cfg.Extract {
// Not asked to extract, do not extract
fmt.Println(file.Name)
continue
}
destPath := path.Join(cfg.Dest, file.Name)
if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
log.WithError(err).Fatal("Unable to create directory")
}
src, err := file.Open()
if err != nil {
log.WithError(err).Fatal("Unable to open file from archive")
}
dest, err := os.Create(destPath)
if err != nil {
log.WithError(err).Fatal("Unable to create destination file")
}
if _, err = io.Copy(dest, src); err != nil {
log.WithError(err).Fatal("Unable to write file contents")
}
dest.Close()
src.Close()
log.WithField("file", file.Name).Info("File extracted")
}
}