Skip to content

Commit

Permalink
add file modes
Browse files Browse the repository at this point in the history
  • Loading branch information
davidnewhall committed Jan 31, 2024
1 parent f0c103f commit ab4e3f3
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 18 deletions.
20 changes: 18 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
run:
timeout: 3m

output:
sort-results: true

linters:
enable-all: true
disable:
Expand All @@ -16,6 +22,16 @@ linters:
- nlreturn
- exhaustruct
- depguard
run:
timeout: 3m
- tagalign

issues:
# disable the default limit so we see everything
max-same-issues: 0
max-issues-per-linter: 0
exclude-rules:
# Exclude some linters from testing files.
- linters:
- goconst
- wsl
- funlen
path: '.+_test.go'
2 changes: 2 additions & 0 deletions MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Example TOML job file:
exclude_suffix = ['.iso', '.gz']
max_depth = 0
min_depth = 1
file_mode = 644
dir_mode = 755

AUTHOR
---
Expand Down
10 changes: 5 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@ import (
"golift.io/version"
)

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

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

// Extract the jobs.
for i, job := range jobs {
log.Printf("Starting Job %d of %d with %d paths, output: %s", i+1, len(jobs), len(job.Paths), job.Output)
xt.Extract(&job)
log.Printf("Starting Job %d of %d with %s", i+1, len(jobs), job)
xt.Extract(job)
}
}
40 changes: 40 additions & 0 deletions pkg/xt/filemode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package xt

import (
"fmt"
"os"
"strconv"
"strings"
)

// FileMode is used to unmarshal a unix file mode from the config file.
type FileMode os.FileMode

// UnmarshalText turns a unix file mode, wrapped in quotes or not, into a usable os.FileMode.
func (f *FileMode) UnmarshalText(text []byte) error {
str := strings.TrimSpace(strings.Trim(string(text), `"'`))

fm, err := strconv.ParseUint(str, 8, 32)
if err != nil {
return fmt.Errorf("file_mode (%s) is invalid: %w", str, err)
}

*f = FileMode(os.FileMode(fm))

return nil
}

// MarshalText satisfies an encoder.TextMarshaler interface.
func (f FileMode) MarshalText() ([]byte, error) {
return []byte(f.String()), nil
}

// String creates a unix-octal version of a file mode.
func (f FileMode) String() string {
return fmt.Sprintf("%04o", f)
}

// Mode returns the compatible os.FileMode.
func (f FileMode) Mode() os.FileMode {
return os.FileMode(f)
}
40 changes: 36 additions & 4 deletions pkg/xt/job.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package xt

import (
"fmt"

"golift.io/cnfgfile"
)

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

// ParseJobs checks for and reads more jobs in from 0 or more job files.
func ParseJobs(jobFiles []string) ([]Job, error) {
jobs := make([]Job, len(jobFiles))
func ParseJobs(jobFiles []string) ([]*Job, error) {
jobs := make([]*Job, len(jobFiles))

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

return jobs, nil
}

func (j *Job) fixModes() {
const (
defaultFileMode = 0o644
defaultDirMode = 0o755
)

if j.DirMode == 0 {
j.DirMode = defaultDirMode
}

if j.FileMode == 0 {
j.FileMode = defaultFileMode
}
}

func (j *Job) String() string {
j.fixModes()

sSfx := ""
if len(j.Paths) > 1 {
sSfx = "s"
}

return fmt.Sprintf("%d path%s, f/d-mode:%s/%s, min/max-depth: %d/%d output: %s",
len(j.Paths), sSfx, j.FileMode, j.DirMode, j.MinDepth, j.MaxDepth, j.Output)
}
16 changes: 9 additions & 7 deletions pkg/xt/xt.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ func Extract(job *Job) {
log.Println("==> No archives found in:", job.Paths)
}

job.fixModes()

total := 0
count := 0

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

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

for k, v := range xtractr.FindCompressedFiles(xtractr.Filter{
for folder, fileList := range xtractr.FindCompressedFiles(xtractr.Filter{
Path: fileName,
ExcludeSuffix: j.Exclude,
MaxDepth: int(j.MaxDepth),
MinDepth: int(j.MinDepth),
}) {
archives[k] = v
archives[folder] = fileList
}
}

Expand Down

0 comments on commit ab4e3f3

Please sign in to comment.