/
file.go
151 lines (133 loc) · 3.89 KB
/
file.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright 2017-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gzip
import (
"fmt"
"os"
"strings"
"github.com/u-root/u-root/pkg/uio"
)
// File is a file path to be compressed or decompressed.
type File struct {
Path string
Options *Options
}
// outputPath removes the path suffix on decompress and adds it on compress.
// In the case of when options stdout or test are enabled it returns the path
// as is.
func (f *File) outputPath() string {
if f.Options.Stdout || f.Options.Test {
return f.Path
} else if f.Options.Decompress {
return f.Path[:len(f.Path)-len(f.Options.Suffix)]
}
return f.Path + f.Options.Suffix
}
// CheckPath validates the input file path. Checks on compression
// if the path has the correct suffix, and on decompression checks
// that it doesn't have the suffix. Allows override by force option.
// Skip if the input is a Stdin.
func (f *File) CheckPath() error {
if f.Options.Stdin {
return nil
}
_, err := os.Stat(f.Path)
if os.IsNotExist(err) {
return fmt.Errorf("skipping, %s does not exist", f.Path)
} else if os.IsPermission(err) {
return fmt.Errorf("skipping, %s permission denied", f.Path)
}
if !f.Options.Force {
if f.Options.Decompress {
if !strings.HasSuffix(f.Path, f.Options.Suffix) {
return fmt.Errorf("skipping, %s does not have %s suffix", f.Path, f.Options.Suffix)
}
} else {
if strings.HasSuffix(f.Path, f.Options.Suffix) {
return fmt.Errorf("skipping, %s already has %s suffix", f.Path, f.Options.Suffix)
}
}
}
return nil
}
// CheckOutputPath checks if output is attempting to write binary to stdout if
// stdout is a device. Also checks if output path already exists. Allow
// override via force option.
func (f *File) CheckOutputPath() error {
_, err := os.Stat(f.outputPath())
if !os.IsNotExist(err) && !f.Options.Stdout && !f.Options.Test && !f.Options.Force {
return fmt.Errorf("skipping, %s already exist", f.outputPath())
} else if os.IsPermission(err) {
return fmt.Errorf("skipping, %s permission denied", f.outputPath())
}
return nil
}
// CheckOutputStdout checks if output is attempting to write binary to stdout
// if stdout is a device.
func (f *File) CheckOutputStdout() error {
if f.Options.Stdout {
stat, _ := os.Stdout.Stat()
if !f.Options.Decompress && !f.Options.Force && (stat.Mode()&os.ModeDevice) != 0 {
return fmt.Errorf("fatal, trying to write compressed data to a terminal/device (use -f to force)")
}
}
return nil
}
// Cleanup removes input file. Overrided with keep option. Skipped if
// stdout or test option is true.
func (f *File) Cleanup() error {
if !f.Options.Keep && !f.Options.Stdout && !f.Options.Test {
return os.Remove(f.Path)
}
return nil
}
// Process either compresses or decompressed the input file based on
// the associated file.options.
func (f *File) Process() error {
var i *os.File
var err error
if f.Options.Stdin {
i = os.Stdin
} else {
i, err = os.Open(f.Path)
if err != nil {
return err
}
defer i.Close()
}
// Use the uio.WriteNameCloser interface so both *os.File and
// uio.WriteNameClose can be assigned to var o without any type casting below.
var o uio.WriteNameCloser
if f.Options.Test {
o = uio.Discard
} else if f.Options.Stdout {
o = os.Stdout
} else {
if o, err = os.Create(f.outputPath()); err != nil {
return err
}
}
if f.Options.Verbose && !f.Options.Quiet {
fmt.Fprintf(os.Stderr, "%s to %s\n", i.Name(), o.Name())
}
if f.Options.Decompress {
if err := Decompress(i, o, f.Options.Blocksize, f.Options.Processes); err != nil {
if !f.Options.Stdout {
o.Close()
}
return err
}
} else {
if err := Compress(i, o, f.Options.Level, f.Options.Blocksize, f.Options.Processes); err != nil {
if !f.Options.Stdout {
o.Close()
}
return err
}
}
if f.Options.Stdout {
return nil
}
return o.Close()
}