Skip to content

Commit ab4e3f3

Browse files
committed
add file modes
1 parent f0c103f commit ab4e3f3

File tree

6 files changed

+110
-18
lines changed

6 files changed

+110
-18
lines changed

.golangci.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
run:
2+
timeout: 3m
3+
4+
output:
5+
sort-results: true
6+
17
linters:
28
enable-all: true
39
disable:
@@ -16,6 +22,16 @@ linters:
1622
- nlreturn
1723
- exhaustruct
1824
- depguard
19-
run:
20-
timeout: 3m
25+
- tagalign
2126

27+
issues:
28+
# disable the default limit so we see everything
29+
max-same-issues: 0
30+
max-issues-per-linter: 0
31+
exclude-rules:
32+
# Exclude some linters from testing files.
33+
- linters:
34+
- goconst
35+
- wsl
36+
- funlen
37+
path: '.+_test.go'

MANUAL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ Example TOML job file:
5959
exclude_suffix = ['.iso', '.gz']
6060
max_depth = 0
6161
min_depth = 1
62+
file_mode = 644
63+
dir_mode = 755
6264

6365
AUTHOR
6466
---

main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ import (
1111
"golift.io/version"
1212
)
1313

14-
func parseFlags(pwd string) (xt.Job, *flags) {
14+
func parseFlags(pwd string) (*xt.Job, *flags) {
1515
flag.Usage = func() {
1616
// XXX: Write more "help" info here.
1717
fmt.Println("If you pass a directory, this app will extract every archive in it.")
1818
fmt.Printf("Usage: %s [-v] [--output <path>] <path> [paths...]\n", os.Args[0])
1919
flag.PrintDefaults()
2020
os.Exit(0)
2121
}
22-
job := xt.Job{}
22+
job := &xt.Job{}
2323
flags := &flags{}
2424

2525
flag.BoolVarP(&flags.PrintVer, "version", "v", false, "Print application version and exit")
2626
// These cli options create 1 job. Using job files creates N jobs.
2727
flag.StringVarP(&job.Output, "output", "o", pwd, "Output directory, default is current directory")
2828
flag.UintVarP(&job.MaxDepth, "max-depth", "d", 0, "Maximum folder depth to recursively search for archives.")
2929
flag.UintVarP(&job.MinDepth, "min-depth", "m", 0, "Minimum folder depth to recursively search for archives.")
30-
//flag.UintVarP(&job.Recurse, "recurse", "r", 0, "Extract archives inside archives, up to this depth.")
30+
// flag.UintVarP(&job.Recurse, "recurse", "r", 0, "Extract archives inside archives, up to this depth.")
3131
flag.StringSliceVarP(&job.Passwords, "password", "P", nil, "Attempt these passwords for rar and 7zip archives.")
3232
flag.StringSliceVarP(&flags.JobFiles, "job-file", "j", nil, "Read additional extraction jobs from these files.")
3333
// Preserve paths?
@@ -77,7 +77,7 @@ func main() {
7777

7878
// Extract the jobs.
7979
for i, job := range jobs {
80-
log.Printf("Starting Job %d of %d with %d paths, output: %s", i+1, len(jobs), len(job.Paths), job.Output)
81-
xt.Extract(&job)
80+
log.Printf("Starting Job %d of %d with %s", i+1, len(jobs), job)
81+
xt.Extract(job)
8282
}
8383
}

pkg/xt/filemode.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package xt
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strconv"
7+
"strings"
8+
)
9+
10+
// FileMode is used to unmarshal a unix file mode from the config file.
11+
type FileMode os.FileMode
12+
13+
// UnmarshalText turns a unix file mode, wrapped in quotes or not, into a usable os.FileMode.
14+
func (f *FileMode) UnmarshalText(text []byte) error {
15+
str := strings.TrimSpace(strings.Trim(string(text), `"'`))
16+
17+
fm, err := strconv.ParseUint(str, 8, 32)
18+
if err != nil {
19+
return fmt.Errorf("file_mode (%s) is invalid: %w", str, err)
20+
}
21+
22+
*f = FileMode(os.FileMode(fm))
23+
24+
return nil
25+
}
26+
27+
// MarshalText satisfies an encoder.TextMarshaler interface.
28+
func (f FileMode) MarshalText() ([]byte, error) {
29+
return []byte(f.String()), nil
30+
}
31+
32+
// String creates a unix-octal version of a file mode.
33+
func (f FileMode) String() string {
34+
return fmt.Sprintf("%04o", f)
35+
}
36+
37+
// Mode returns the compatible os.FileMode.
38+
func (f FileMode) Mode() os.FileMode {
39+
return os.FileMode(f)
40+
}

pkg/xt/job.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package xt
22

33
import (
4+
"fmt"
5+
46
"golift.io/cnfgfile"
57
)
68

@@ -12,20 +14,50 @@ type Job struct {
1214
Exclude []string `json:"excludeSuffix" yaml:"excludeSuffix" xml:"exclude_suffix" toml:"exclude_suffix"`
1315
MaxDepth uint `json:"maxDepth" yaml:"maxDepth" xml:"max_depth" toml:"max_depth"`
1416
MinDepth uint `json:"minDepth" yaml:"minDepth" xml:"min_depth" toml:"min_depth"`
17+
DirMode FileMode `json:"dirMode" yaml:"dirMode" xml:"dir_mode" toml:"dir_mode"`
18+
FileMode FileMode `json:"fileMode" yaml:"fileMode" xml:"file_mode" toml:"file_mode"`
1519
}
1620

1721
// ParseJobs checks for and reads more jobs in from 0 or more job files.
18-
func ParseJobs(jobFiles []string) ([]Job, error) {
19-
jobs := make([]Job, len(jobFiles))
22+
func ParseJobs(jobFiles []string) ([]*Job, error) {
23+
jobs := make([]*Job, len(jobFiles))
2024

2125
for idx, jobFile := range jobFiles {
26+
jobs[idx] = &Job{}
2227
// This library simply parses xml, json, toml, and yaml into a data struct.
2328
// It parses based on file name extension, toml is default. Supports compression.
24-
err := cnfgfile.Unmarshal(&jobs[idx], jobFile)
29+
err := cnfgfile.Unmarshal(jobs[idx], jobFile)
2530
if err != nil {
26-
return nil, err
31+
return nil, fmt.Errorf("bad job file: %w", err)
2732
}
2833
}
2934

3035
return jobs, nil
3136
}
37+
38+
func (j *Job) fixModes() {
39+
const (
40+
defaultFileMode = 0o644
41+
defaultDirMode = 0o755
42+
)
43+
44+
if j.DirMode == 0 {
45+
j.DirMode = defaultDirMode
46+
}
47+
48+
if j.FileMode == 0 {
49+
j.FileMode = defaultFileMode
50+
}
51+
}
52+
53+
func (j *Job) String() string {
54+
j.fixModes()
55+
56+
sSfx := ""
57+
if len(j.Paths) > 1 {
58+
sSfx = "s"
59+
}
60+
61+
return fmt.Sprintf("%d path%s, f/d-mode:%s/%s, min/max-depth: %d/%d output: %s",
62+
len(j.Paths), sSfx, j.FileMode, j.DirMode, j.MinDepth, j.MaxDepth, j.Output)
63+
}

pkg/xt/xt.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ func Extract(job *Job) {
1515
log.Println("==> No archives found in:", job.Paths)
1616
}
1717

18+
job.fixModes()
19+
1820
total := 0
1921
count := 0
2022

@@ -30,11 +32,11 @@ func Extract(job *Job) {
3032
start := time.Now()
3133

3234
size, files, _, err := xtractr.ExtractFile(&xtractr.XFile{
33-
FilePath: fileName, // Path to archive being extracted.
34-
OutputDir: job.Output, // Folder to extract archive into.
35-
FileMode: 0o644, //nolint:gomnd // Write files with this mode.
36-
DirMode: 0o755, //nolint:gomnd // Write folders with this mode.
37-
Passwords: job.Passwords, // (RAR/7zip) Archive password(s).
35+
FilePath: fileName, // Path to archive being extracted.
36+
OutputDir: job.Output, // Folder to extract archive into.
37+
FileMode: job.FileMode.Mode(), // Write files with this mode.
38+
DirMode: job.DirMode.Mode(), // Write folders with this mode.
39+
Passwords: job.Passwords, // (RAR/7zip) Archive password(s).
3840
})
3941
if err != nil {
4042
log.Printf("[ERROR] Archive: %s: %v", fileName, err)
@@ -63,13 +65,13 @@ func (j *Job) getArchives() map[string][]string {
6365
continue
6466
}
6567

66-
for k, v := range xtractr.FindCompressedFiles(xtractr.Filter{
68+
for folder, fileList := range xtractr.FindCompressedFiles(xtractr.Filter{
6769
Path: fileName,
6870
ExcludeSuffix: j.Exclude,
6971
MaxDepth: int(j.MaxDepth),
7072
MinDepth: int(j.MinDepth),
7173
}) {
72-
archives[k] = v
74+
archives[folder] = fileList
7375
}
7476
}
7577

0 commit comments

Comments
 (0)