Skip to content

Commit b391d40

Browse files
authored
Squash root archive folder (#88)
* Squash root archive folder * lint bleh * comments
1 parent d17dc12 commit b391d40

File tree

9 files changed

+86
-22
lines changed

9 files changed

+86
-22
lines changed

7z.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ func extract7z(xFile *XFile) (int64, []string, []string, error) {
7979
xFile.Debugf("Wrote archived file: %s (%d bytes), total: %d files and %d bytes", wfile, fSize, len(files), size)
8080
}
8181

82-
return size, files, sevenZip.Volumes(), nil
82+
files, err = xFile.cleanup(files)
83+
84+
return size, files, sevenZip.Volumes(), err
8385
}
8486

8587
func (x *XFile) un7zip(zipFile *sevenzip.File) (int64, string, error) {

ar.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,12 @@ func (x *XFile) unAr(reader io.Reader) (int64, []string, error) {
2828

2929
for {
3030
header, err := arReader.Next()
31+
if err != nil {
32+
if errors.Is(err, io.EOF) {
33+
break
34+
}
3135

32-
switch {
33-
case errors.Is(err, io.EOF):
34-
return size, files, nil
35-
case err != nil:
3636
return size, files, fmt.Errorf("%s: arReader.Next: %w", x.FilePath, err)
37-
case header == nil:
38-
return size, files, fmt.Errorf("%w: %s", ErrInvalidHead, x.FilePath)
3937
}
4038

4139
file := &file{
@@ -61,4 +59,8 @@ func (x *XFile) unAr(reader io.Reader) (int64, []string, error) {
6159
files = append(files, file.Path)
6260
size += fSize
6361
}
62+
63+
files, err := x.cleanup(files)
64+
65+
return size, files, err
6466
}

files.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,13 @@ type XFile struct {
112112
Password string
113113
// (RAR/7z) Archive passwords (to try multiple).
114114
Passwords []string
115+
// If the archive only has one directory in the root, then setting
116+
// this true will cause the extracted content to be moved into the
117+
// output folder, and the root folder in the archive to be removed.
118+
SquashRoot bool
115119
// Logger allows printing debug messages.
116-
log Logger
120+
log Logger
121+
moveFiles func(fromPath, toPath string, overwrite bool) ([]string, error)
117122
}
118123

119124
// Filter is the input to find compressed files.
@@ -313,6 +318,8 @@ func (x *XFile) Extract() (size int64, filesList, archiveList []string, err erro
313318
// Returns size of extracted data, list of extracted files, list of archives processed, and/or error.
314319
func ExtractFile(xFile *XFile) (size int64, filesList, archiveList []string, err error) {
315320
sName := strings.ToLower(xFile.FilePath)
321+
// just borrowing this... Has to go into an interface to avoid a cycle.
322+
xFile.moveFiles = parseConfig(&Config{Logger: xFile.log}).MoveFiles
316323

317324
for _, ext := range extension2function {
318325
if strings.HasSuffix(sName, ext.Extension) {
@@ -541,6 +548,38 @@ func (x *XFile) SetLogger(logger Logger) {
541548
x.log = logger
542549
}
543550

551+
// cleanup runs after a successful extract.
552+
// The intent it to move files into their final location.
553+
func (x *XFile) cleanup(files []string) ([]string, error) {
554+
files, err := x.squashRoot(files)
555+
if err != nil {
556+
return files, err
557+
}
558+
559+
return files, nil
560+
}
561+
562+
func (x *XFile) squashRoot(files []string) ([]string, error) {
563+
if !x.SquashRoot {
564+
return files, nil
565+
}
566+
567+
roots := map[string]bool{}
568+
//nolint:mnd
569+
for _, path := range files {
570+
// Remove the output dir suffix, then split on `/` (or `\`) and get the first item.
571+
roots[strings.SplitN(strings.TrimPrefix(path, x.OutputDir), string(filepath.Separator), 2)[0]] = true
572+
}
573+
574+
if len(roots) == 1 { // only 1 root folder...
575+
for root := range roots { // ...move it's content up a level.
576+
return x.moveFiles(filepath.Join(x.OutputDir, root), x.OutputDir, false)
577+
}
578+
}
579+
580+
return files, nil
581+
}
582+
544583
func (x *XFile) safeDirMode(current os.FileMode) os.FileMode {
545584
if current.Perm() == 0 {
546585
return x.DirMode

iso.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ func (x *XFile) uniso(isoFile *iso9660.File, parent string) (int64, []string, er
6969
files = append(files, childFiles...)
7070
}
7171

72-
return size, files, nil
72+
files, err = x.cleanup(files)
73+
74+
return size, files, err
7375
}
7476

7577
func (x *XFile) unisofile(isoFile *iso9660.File, wfile string) (int64, []string, error) {

logger.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package xtractr
2+
3+
// NoLogger gives you an empty Logger for cases when you don't want any output.
4+
func NoLogger() Logger { return &antiLogger{} } //nolint:ireturn // It's on purpose.
5+
6+
type antiLogger struct{}
7+
8+
func (*antiLogger) Printf(_ string, _ ...any) {}
9+
func (*antiLogger) Debugf(_ string, _ ...any) {}

rar.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,12 @@ func (x *XFile) unrar(rarReader *rardecode.ReadCloser) (int64, []string, error)
8181

8282
for {
8383
header, err := rarReader.Next()
84+
if err != nil {
85+
if errors.Is(err, io.EOF) {
86+
break
87+
}
8488

85-
switch {
86-
case errors.Is(err, io.EOF):
87-
return size, files, nil
88-
case err != nil:
8989
return size, files, fmt.Errorf("rarReader.Next: %w", err)
90-
case header == nil:
91-
return size, files, fmt.Errorf("%w: %s", ErrInvalidHead, x.FilePath)
9290
}
9391

9492
file := &file{
@@ -127,4 +125,8 @@ func (x *XFile) unrar(rarReader *rardecode.ReadCloser) (int64, []string, error)
127125
size += fSize
128126
x.Debugf("Wrote archived file: %s (%d bytes), total: %d files and %d bytes", file.Path, fSize, len(files), size)
129127
}
128+
129+
files, err := x.cleanup(files)
130+
131+
return size, files, err
130132
}

start.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ func parseConfig(config *Config) *Xtractr {
126126
config.Suffix = DefaultSuffix
127127
}
128128

129+
if config.Logger == nil {
130+
config.Logger = NoLogger()
131+
}
132+
129133
return &Xtractr{
130134
config: config,
131135
done: make(chan struct{}),

tar.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,12 @@ func (x *XFile) untar(reader io.Reader) (int64, []string, error) {
110110

111111
for {
112112
header, err := tarReader.Next()
113+
if err != nil {
114+
if errors.Is(err, io.EOF) {
115+
break
116+
}
113117

114-
switch {
115-
case errors.Is(err, io.EOF):
116-
return size, files, nil
117-
case err != nil:
118118
return size, files, fmt.Errorf("%s: tarReader.Next: %w", x.FilePath, err)
119-
case header == nil:
120-
return size, files, fmt.Errorf("%w: %s", ErrInvalidHead, x.FilePath)
121119
}
122120

123121
fSize, err := x.untarFile(header, tarReader)
@@ -129,6 +127,10 @@ func (x *XFile) untar(reader io.Reader) (int64, []string, error) {
129127
size += fSize
130128
x.Debugf("Wrote archived file: %s (%d bytes), total: %d files and %d bytes", header.Name, fSize, len(files), size)
131129
}
130+
131+
files, err := x.cleanup(files)
132+
133+
return size, files, err
132134
}
133135

134136
func (x *XFile) untarFile(header *tar.Header, tarReader *tar.Reader) (int64, error) {

zip.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ func ExtractZIP(xFile *XFile) (size int64, filesList []string, err error) {
3333
xFile.Debugf("Wrote archived file: %s (%d bytes), total: %d files and %d bytes", wfile, fSize, len(files), size)
3434
}
3535

36-
return size, files, nil
36+
files, err = xFile.cleanup(files)
37+
38+
return size, files, err
3739
}
3840

3941
func (x *XFile) unzip(zipFile *zip.File) (int64, string, error) {

0 commit comments

Comments
 (0)