-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1733ffb
Showing
3 changed files
with
231 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
GzipDate | ||
======== | ||
|
||
Simple command line app for the specific creation and handling of Gzip archives. | ||
|
||
|
||
Features | ||
-------- | ||
|
||
* Create gzip archives for each of a list of files, adding a file system safe ISO-601 datatime value to the filename when creating the archive | ||
* Extract the archived file from Gzip archives restoring their original names if available. The original name is always available for archives created by GzipDate. | ||
* Does not delete source files by default. Though they can be automatically deleted with a command line switch. | ||
* Always uses maximum compression when creating archives | ||
* 100% compatible with gzip and gunzip tools | ||
|
||
|
||
Rational | ||
-------- | ||
|
||
I like making archives with the date in the name. Especially for game saves that get auto deleted like in Roguelike type games. It's also supper annoying to me that `gunzip` doesn't use the stored filename within the archive by default. I also like my tools to be smart about if a file is already compressed or not. Gzip will give an error if you try to recompress a file that already ends in `.gz`. GzipDate just uncompresses the file instead. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/runeimp/gzipdate | ||
|
||
go 1.14 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"compress/gzip" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"path" | ||
"time" | ||
) | ||
|
||
/* | ||
* CONSTANTS | ||
*/ | ||
const ( | ||
AppName = "GzipDate" | ||
AppVersion = "1.0.0" | ||
argDeleteUsage = "Delete the source file after successful processing" | ||
argHelpUsage = "Display this help info" | ||
argVersionUsage = "Display this apps version number" | ||
) | ||
|
||
/* | ||
* DERIVED CONSTANTS | ||
*/ | ||
var ( | ||
AppLabel = fmt.Sprintf("%s v%s", AppName, AppVersion) | ||
) | ||
|
||
/* | ||
* VARIABLES | ||
*/ | ||
var ( | ||
deleteSource = false | ||
files []string | ||
showHelp = false | ||
) | ||
|
||
func main() { | ||
|
||
// Bespoke CLI Handler because flags is lame | ||
for _, arg := range os.Args[1:] { | ||
switch arg { | ||
case "-d", "-del", "-delete": | ||
deleteSource = true | ||
case "-h", "-help": | ||
helpOutput() | ||
os.Exit(0) | ||
case "-v", "-ver", "-version": | ||
fmt.Println(AppLabel) | ||
os.Exit(0) | ||
default: | ||
files = append(files, arg) | ||
} | ||
} | ||
|
||
if len(os.Args[1:]) == 0 { | ||
helpOutput() | ||
os.Exit(1) | ||
} | ||
|
||
// fmt.Printf("Delete Source File? %t\n", deleteSource) | ||
|
||
var err error | ||
|
||
if len(files) > 0 { | ||
for _, filename := range files { | ||
// j := i + 1 | ||
// fmt.Printf("%-3d %s\n", j, filename) | ||
if path.Ext(filename) == ".gz" { | ||
// fmt.Printf("File #%-3d gunzip %q\n", j, filename) | ||
content, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
err = gzipDecode(content) | ||
if err == nil && deleteSource { | ||
fmt.Printf(" Deleting source: %q\n", filename) | ||
os.Remove(filename) | ||
} | ||
} else { | ||
// fmt.Printf("File #%-3d gzip %q\n", j, filename) | ||
err = gzipEncode(filename) | ||
if err == nil && deleteSource { | ||
fmt.Printf(" Deleting source: %q\n", filename) | ||
os.Remove(filename) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
func gzipDecode(content []byte) error { | ||
newFilename := "filename.unknown" | ||
|
||
zr, err := gzip.NewReader(bytes.NewReader(content)) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
if err := zr.Close(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// fmt.Printf("Name: %s\nComment: %s\nModTime: %s\n\n", zr.Name, zr.Comment, zr.ModTime.UTC()) | ||
// if _, err := io.Copy(os.Stdout, zr); err != nil { | ||
// log.Fatal(err) | ||
// } | ||
|
||
if len(zr.Name) > 0 { | ||
newFilename = zr.Name | ||
} | ||
destination, err := os.Create(newFilename) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer destination.Close() | ||
|
||
nBytes, err := io.Copy(destination, zr) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
mtime := zr.ModTime.UTC() | ||
atime := zr.ModTime.UTC() | ||
if err := os.Chtimes(newFilename, atime, mtime); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
fmt.Printf("%d B written to %q\n", nBytes, zr.Name) | ||
|
||
return err | ||
} | ||
|
||
func gzipEncode(filename string) error { | ||
var ( | ||
destination *os.File | ||
err error | ||
file *os.File | ||
fileinfo os.FileInfo | ||
nBytes int64 | ||
) | ||
// content, err := ioutil.ReadFile(filename) | ||
file, err = os.Open(filename) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
fileinfo, err = file.Stat() | ||
content := make([]byte, fileinfo.Size()) | ||
_, err = file.Read(content) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
now := time.Now() | ||
datetime := now.Format("2006-01-02_150405") | ||
newFilename := fmt.Sprintf("%s_%s.gz", filename, datetime) | ||
// fmt.Printf("gzipEncode() | filename = %q | content length = %d B | newFilename = %q\n", filename, len(content), newFilename) | ||
|
||
var buf bytes.Buffer | ||
zw, zwError := gzip.NewWriterLevel(&buf, gzip.BestCompression) | ||
if zwError != nil { | ||
log.Fatal(zwError) | ||
} | ||
|
||
// Setting the Header fields is optional. | ||
zw.Name = filename | ||
zw.Comment = fmt.Sprintf("Compressed with %s", AppLabel) | ||
zw.ModTime = fileinfo.ModTime() | ||
|
||
if _, err := zw.Write(content); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
if err := zw.Close(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
destination, err = os.Create(newFilename) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer destination.Close() | ||
|
||
nBytes, err = buf.WriteTo(destination) | ||
fmt.Printf("Saving %d bytes from %q to %q\n", nBytes, filename, newFilename) | ||
|
||
return err | ||
} | ||
|
||
func helpOutput() { | ||
// flag.Usage() | ||
// fmt.Println("----") | ||
fmt.Printf("USAGE: gzipdate [OPTIONS] FILENAMES\n\nOPTIONS:\n") | ||
// flag.PrintDefaults() | ||
// fmt.Println() | ||
|
||
fmt.Printf(" -d | -del | -delete %s\n", argDeleteUsage) | ||
fmt.Printf(" -h | -help %s\n", argHelpUsage) | ||
fmt.Printf(" -v | -ver | -version %s\n", argVersionUsage) | ||
fmt.Println() | ||
fmt.Printf("Options may be interspersed with file names if so desired.\nThey are not position dependent.\n\n") | ||
} |