77 "errors"
88 "flag"
99 "fmt"
10- "io"
1110 "log"
1211 "os"
1312 "os/signal"
@@ -21,13 +20,21 @@ import (
2120
2221 "github.com/bool64/dev/version"
2322 "github.com/bool64/progress"
24- gzip "github.com/klauspost/pgzip"
2523)
2624
2725// Main is the entry point for catp CLI tool.
2826func Main (options ... func (o * Options )) error { //nolint:funlen,cyclop,gocognit,gocyclo,maintidx
2927 r := & runner {}
3028
29+ var closers []func () error
30+ defer func () {
31+ for _ , closer := range closers {
32+ if err := closer (); err != nil {
33+ log .Printf ("failed to close: %s\n " , err .Error ())
34+ }
35+ }
36+ }()
37+
3138 flag .Var (flagFunc (func (v string ) error {
3239 r .filters .addFilter (true , bytes .Split ([]byte (v ), []byte ("^" ))... )
3340
@@ -62,6 +69,17 @@ func Main(options ...func(o *Options)) error { //nolint:funlen,cyclop,gocognit,g
6269 "if filter matches, line is removed from the output (may be kept if it passed preceding -pass)\n " +
6370 "for example, you can use \" -skip quux^baz -skip fooO\" to skip lines that have (quux AND baz) OR fooO" )
6471
72+ flag .Var (flagFunc (func (v string ) error {
73+ w , closer , err := makeWriter (v )
74+ if err != nil {
75+ return err
76+ }
77+
78+ closers = append (closers , closer )
79+
80+ return r .filters .saveTo (w )
81+ }), "save-matches" , "save matches of previous filter group to file" )
82+
6583 flag .IntVar (& r .parallel , "parallel" , 1 , "number of parallel readers if multiple files are provided\n " +
6684 "lines from different files will go to output simultaneously (out of order of files, but in order of lines in each file)\n " +
6785 "use 0 for multi-threaded zst decoder (slightly faster at cost of more CPU)" )
@@ -79,8 +97,13 @@ func Main(options ...func(o *Options)) error { //nolint:funlen,cyclop,gocognit,g
7997 "files will be written to out dir with original base names\n " +
8098 "disables output flag" )
8199
100+ flag .IntVar (& r .startLine , "start-line" , 0 , "start printing lines from this line (inclusive),\n " +
101+ "default is 0 (first line), each input file is counted separately" )
102+ flag .IntVar (& r .endLine , "end-line" , 0 , "stop printing lines at this line (exclusive),\n " +
103+ "default is 0 (no limit), each input file is counted separately" )
104+
82105 flag .Usage = func () {
83- fmt .Println ("catp" , version .Module ("github.com/bool64/progress" ).Version + "," ,
106+ fmt .Println ("catp" , version .Module ("github.com/bool64/progress" ).Version + r . options . VersionLabel + "," ,
84107 version .Info ().GoVersion , strings .Join (versionExtra , " " ))
85108 fmt .Println ()
86109 fmt .Println ("catp prints contents of files to STDOUT or dir/file output, \n " +
@@ -94,20 +117,6 @@ func Main(options ...func(o *Options)) error { //nolint:funlen,cyclop,gocognit,g
94117 }
95118 flag .Parse ()
96119
97- r .filters .buildIndex ()
98-
99- if * ver {
100- fmt .Println (version .Module ("github.com/bool64/progress" ).Version )
101-
102- return nil
103- }
104-
105- if flag .NArg () == 0 {
106- flag .Usage ()
107-
108- return nil
109- }
110-
111120 if * cpuProfile != "" {
112121 startProfiling (* cpuProfile , * memProfile )
113122
@@ -122,6 +131,20 @@ func Main(options ...func(o *Options)) error { //nolint:funlen,cyclop,gocognit,g
122131 }
123132 }
124133
134+ r .filters .buildIndex ()
135+
136+ if * ver {
137+ fmt .Println (version .Module ("github.com/bool64/progress" ).Version + r .options .VersionLabel )
138+
139+ return nil
140+ }
141+
142+ if flag .NArg () == 0 {
143+ flag .Usage ()
144+
145+ return nil
146+ }
147+
125148 var files []string
126149
127150 args := flag .Args ()
@@ -158,61 +181,21 @@ func Main(options ...func(o *Options)) error { //nolint:funlen,cyclop,gocognit,g
158181 sort .Strings (files )
159182
160183 if * output != "" && r .outDir == "" {
161- fn := * output
162-
163- out , err := os .Create (fn ) //nolint:gosec
184+ w , closer , err := makeWriter (* output )
164185 if err != nil {
165- return fmt . Errorf ( "failed to create output file %s: %w" , fn , err )
186+ return err
166187 }
167188
168- r .output = out
169- compCloser := io .Closer (io .NopCloser (nil ))
170-
171- switch {
172- case strings .HasSuffix (fn , ".gz" ):
173- gw := gzip .NewWriter (r .output )
174- compCloser = gw
175-
176- r .output = gw
177- case strings .HasSuffix (fn , ".zst" ):
178- zw , err := zstdWriter (r .output )
179- if err != nil {
180- return fmt .Errorf ("zstd new writer: %w" , err )
181- }
182-
183- compCloser = zw
184-
185- r .output = zw
186- }
187-
188- w := bufio .NewWriterSize (r .output , 64 * 1024 )
189189 r .output = w
190190
191- defer func () {
192- if err := w .Flush (); err != nil {
193- log .Fatalf ("failed to flush STDOUT buffer: %s" , err )
194- }
195-
196- if err := compCloser .Close (); err != nil {
197- log .Fatalf ("failed to close compressor: %s" , err )
198- }
199-
200- if err := out .Close (); err != nil {
201- log .Fatalf ("failed to close output file %s: %s" , * output , err )
202- }
203- }()
191+ closers = append (closers , closer )
204192 } else {
205193 if isStdin {
206194 r .output = os .Stdout
207195 } else {
208196 w := bufio .NewWriterSize (os .Stdout , 64 * 1024 )
209197 r .output = w
210-
211- defer func () {
212- if err := w .Flush (); err != nil {
213- log .Fatalf ("failed to flush STDOUT buffer: %s" , err )
214- }
215- }()
198+ closers = append (closers , w .Flush )
216199 }
217200 }
218201
0 commit comments