--- /dev/null
+/*
+sgodup -- File deduplication utility
+Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "time"
+
+ "github.com/dustin/go-humanize"
+)
+
+const (
+ ESC = 27
+)
+
+var LineClear = fmt.Sprintf("%c[%dA%c[2K", ESC, 2, ESC)
+
+type Progress struct {
+ fullFiles int64
+ fullSize uint64
+ fullFilesS string
+ fullSizeS string
+ suffixFiles string
+ suffixSize string
+ stop chan struct{}
+}
+
+func NewProgress(
+ fullFiles int,
+ fullSize int64,
+ files *int,
+ size *int64,
+ suffixFiles, suffixSize string,
+) Progress {
+ p := Progress{
+ int64(fullFiles),
+ uint64(fullSize),
+ humanize.Comma(int64(fullFiles)),
+ humanize.IBytes(uint64(fullSize)),
+ suffixFiles,
+ suffixSize,
+ make(chan struct{}, 0),
+ }
+ go p.Run(files, size)
+ return p
+}
+
+func (p Progress) Log(prefix string, files int64, size uint64) {
+ percentageFiles := int64(0)
+ if p.fullFiles > 0 {
+ percentageFiles = 100 * files / p.fullFiles
+ }
+ percentageSize := uint64(0)
+ if p.fullSize > 0 {
+ percentageSize = 100 * size / p.fullSize
+ }
+ now := time.Now().Format("2006/01/02 15:04:05")
+ fmt.Fprintf(
+ os.Stderr,
+ "%s%s %s / %s (%d%%) files%s\n%s %s / %s (%d%%)%s\n",
+ prefix,
+ now, humanize.Comma(files), p.fullFilesS, percentageFiles, p.suffixFiles,
+ now, humanize.IBytes(size), p.fullSizeS, percentageSize, p.suffixSize,
+ )
+}
+
+func (p Progress) Run(files *int, size *int64) {
+ p.Log("", 0, 0)
+ ticker := time.NewTicker(250 * time.Millisecond)
+ for {
+ select {
+ case <-ticker.C:
+ p.Log(LineClear, int64(*files), uint64(*size))
+ case <-p.stop:
+ ticker.Stop()
+ p.Log(LineClear, int64(*files), uint64(*size))
+ close(p.stop)
+ return
+ }
+ }
+}
+
+func (p Progress) Stop() {
+ p.stop <- struct{}{}
+ <-p.stop
+}