From d6ece900416040496c5b127da3a73adc7ade202a Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 24 Aug 2023 09:00:52 +0200 Subject: [PATCH] Add support for adding/removing files --- README.md | 8 +- cbconvert.go | 173 ++++++++++++++++++++++++++++++++++++++++++ cmd/cbconvert/main.go | 2 + go.work | 6 -- 4 files changed, 181 insertions(+), 8 deletions(-) delete mode 100644 go.work diff --git a/README.md b/README.md index 5d33308..cdb5c7c 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,13 @@ This is what it looks like in the PCManFM file manager: --cover Print cover name (default "false") --comment - Print zip comment (default "false") + Print zip comment (default "false") --comment-body - Set zip comment (default "") + Set zip comment (default "") + --file-add + Add file to archive (default "") + --file-remove + Remove file(s) from archive (glob pattern, i.e. *.xml) (default "") ``` diff --git a/cbconvert.go b/cbconvert.go index d8042b8..e705f08 100644 --- a/cbconvert.go +++ b/cbconvert.go @@ -104,6 +104,10 @@ type Options struct { Comment bool // ZIP comment body CommentBody string + // Add file + FileAdd string + // Remove file + FileRemove string // Output file Outfile string // Output directory @@ -980,6 +984,165 @@ func (c *Convertor) archiveSetComment(fileName, commentBody string) error { return nil } +// archiveFileAdd adds file to archive. +func (c *Convertor) archiveFileAdd(fileName, newFileName string) error { + zr, err := zip.OpenReader(fileName) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + defer zr.Close() + + zf, err := os.CreateTemp(os.TempDir(), "cbc") + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + tmpName := zf.Name() + defer os.Remove(tmpName) + + zw := zip.NewWriter(zf) + + for _, item := range zr.File { + if item.Name == newFileName { + continue + } + + ir, err := item.OpenRaw() + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + item := item + + it, err := zw.CreateRaw(&item.FileHeader) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + _, err = io.Copy(it, ir) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + } + + info, err := os.Stat(newFileName) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + newData, err := os.ReadFile(newFileName) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + zipInfo, err := zip.FileInfoHeader(info) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + zipInfo.Method = zip.Deflate + w, err := zw.CreateHeader(zipInfo) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + _, err = w.Write(newData) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + err = zw.Close() + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + err = zf.Close() + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + data, err := os.ReadFile(tmpName) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + err = os.WriteFile(fileName, data, 0644) + if err != nil { + return fmt.Errorf("archiveFileAdd: %w", err) + } + + return nil +} + +// archiveFileRemove removes files from archive. +func (c *Convertor) archiveFileRemove(fileName, pattern string) error { + zr, err := zip.OpenReader(fileName) + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + defer zr.Close() + + zf, err := os.CreateTemp(os.TempDir(), "cbc") + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + tmpName := zf.Name() + defer os.Remove(tmpName) + + zw := zip.NewWriter(zf) + + for _, item := range zr.File { + matched, err := filepath.Match(pattern, item.Name) + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + if matched { + continue + } + + ir, err := item.OpenRaw() + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + item := item + + it, err := zw.CreateRaw(&item.FileHeader) + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + _, err = io.Copy(it, ir) + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + } + + err = zw.Close() + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + err = zf.Close() + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + data, err := os.ReadFile(tmpName) + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + err = os.WriteFile(fileName, data, 0644) + if err != nil { + return fmt.Errorf("archiveFileRemove: %w", err) + } + + return nil +} + // coverArchive extracts cover from archive. func (c *Convertor) coverArchive(fileName string) (image.Image, error) { var images []string @@ -1560,6 +1723,16 @@ func (c *Convertor) Meta(fileName string) (any, error) { if err != nil { return nil, fmt.Errorf("%s: %w", fileName, err) } + case c.Opts.FileAdd != "": + err := c.archiveFileAdd(fileName, c.Opts.FileAdd) + if err != nil { + return nil, fmt.Errorf("%s: %w", fileName, err) + } + case c.Opts.FileRemove != "": + err := c.archiveFileRemove(fileName, c.Opts.FileRemove) + if err != nil { + return nil, fmt.Errorf("%s: %w", fileName, err) + } } return "", nil diff --git a/cmd/cbconvert/main.go b/cmd/cbconvert/main.go index f2c8e08..e95e5f1 100644 --- a/cmd/cbconvert/main.go +++ b/cmd/cbconvert/main.go @@ -194,6 +194,8 @@ func parseFlags() (cbconvert.Options, []string) { meta.BoolVar(&opts.Cover, "cover", false, "Print cover name") meta.BoolVar(&opts.Comment, "comment", false, "Print zip comment") meta.StringVar(&opts.CommentBody, "comment-body", "", "Set zip comment") + meta.StringVar(&opts.FileAdd, "file-add", "", "Add file to archive") + meta.StringVar(&opts.FileRemove, "file-remove", "", "Remove file from archive (glob pattern, i.e. *.xml)") convert.Usage = func() { _, _ = fmt.Fprintf(os.Stderr, "Usage: %s [] [file1 dir1 ... fileOrDirN]\n\n", filepath.Base(os.Args[0])) diff --git a/go.work b/go.work deleted file mode 100644 index ccd6f0a..0000000 --- a/go.work +++ /dev/null @@ -1,6 +0,0 @@ -go 1.21.0 - -use ( - . - ./cmd/cbconvert -)