pax_global_header00006660000000000000000000000064145544330420014516gustar00rootroot0000000000000052 comment=7667f987657572d6e2ad0c4f188c3f549916a448 pb-3.1.5/000077500000000000000000000000001455443304200121255ustar00rootroot00000000000000pb-3.1.5/.github/000077500000000000000000000000001455443304200134655ustar00rootroot00000000000000pb-3.1.5/.github/dependabot.yml000066400000000000000000000007721455443304200163230ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" # See documentation for possible values directory: "/v3" # Location of package manifests schedule: interval: "weekly" pb-3.1.5/.github/workflows/000077500000000000000000000000001455443304200155225ustar00rootroot00000000000000pb-3.1.5/.github/workflows/go.yml000066400000000000000000000005321455443304200166520ustar00rootroot00000000000000name: Go on: push: branches: [ "master" ] pull_request: branches: [ "master" ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v3 with: go-version: 1.17 - name: Test working-directory: ./v3 run: go test -v ./... pb-3.1.5/.gitignore000066400000000000000000000000071455443304200141120ustar00rootroot00000000000000.idea/ pb-3.1.5/.travis.yml000066400000000000000000000004211455443304200142330ustar00rootroot00000000000000language: go arch: - amd64 - ppc64le go: - 1.12.x - 1.15.x sudo: false os: - linux - osx before_install: - go get github.com/mattn/goveralls script: - $GOPATH/bin/goveralls -package github.com/cheggaaa/pb/v3 -repotoken QT1y5Iujb8ete6JOiE0ytKFlBDv9vheWc pb-3.1.5/LICENSE000066400000000000000000000027071455443304200131400ustar00rootroot00000000000000Copyright (c) 2012-2015, Sergey Cherepanov All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.pb-3.1.5/README.md000066400000000000000000000051711455443304200134100ustar00rootroot00000000000000# Terminal progress bar for Go [![Coverage Status](https://coveralls.io/repos/github/cheggaaa/pb/badge.svg)](https://coveralls.io/github/cheggaaa/pb) ## Installation ``` go get github.com/cheggaaa/pb/v3 ``` Documentation for v1 bar available [here](README_V1.md). ## Quick start ```Go package main import ( "time" "github.com/cheggaaa/pb/v3" ) func main() { count := 100000 // create and start new bar bar := pb.StartNew(count) // start bar from 'default' template // bar := pb.Default.Start(count) // start bar from 'simple' template // bar := pb.Simple.Start(count) // start bar from 'full' template // bar := pb.Full.Start(count) for i := 0; i < count; i++ { bar.Increment() time.Sleep(time.Millisecond) } // finish bar bar.Finish() } ``` Result will be like this: ``` > go run test.go 37158 / 100000 [---------------->_______________________________] 37.16% 916 p/s ``` ## Settings ```Go // create bar bar := pb.New(count) // refresh info every second (default 200ms) bar.SetRefreshRate(time.Second) // force set io.Writer, by default it's os.Stderr bar.SetWriter(os.Stdout) // bar will format numbers as bytes (B, KiB, MiB, etc) bar.Set(pb.Bytes, true) // bar use SI bytes prefix names (B, kB) instead of IEC (B, KiB) bar.Set(pb.SIBytesPrefix, true) // set custom bar template bar.SetTemplateString(myTemplate) // check for error after template set if err := bar.Err(); err != nil { return } // start bar bar.Start() ``` ## Progress bar for IO Operations ```Go package main import ( "crypto/rand" "io" "io/ioutil" "github.com/cheggaaa/pb/v3" ) func main() { var limit int64 = 1024 * 1024 * 500 // we will copy 500 MiB from /dev/rand to /dev/null reader := io.LimitReader(rand.Reader, limit) writer := ioutil.Discard // start new bar bar := pb.Full.Start64(limit) // create proxy reader barReader := bar.NewProxyReader(reader) // copy from proxy reader io.Copy(writer, barReader) // finish bar bar.Finish() } ``` ## Custom Progress Bar templates Rendering based on builtin [text/template](https://pkg.go.dev/text/template) package. You can use existing pb's elements or create you own. All available elements are described in the [element.go](v3/element.go) file. #### All in one example: ```Go tmpl := `{{ red "With funcs:" }} {{ bar . "<" "-" (cycle . "↖" "↗" "↘" "↙" ) "." ">"}} {{speed . | rndcolor }} {{percent .}} {{string . "my_green_string" | green}} {{string . "my_blue_string" | blue}}` // start bar based on our template bar := pb.ProgressBarTemplate(tmpl).Start64(limit) // set values for string elements bar.Set("my_green_string", "green").Set("my_blue_string", "blue") ``` pb-3.1.5/README_V1.md000066400000000000000000000060131455443304200137520ustar00rootroot00000000000000# Terminal progress bar for Go Simple progress bar for console programs. ## Installation ``` go get github.com/cheggaaa/pb ``` ## Usage ```Go package main import ( "github.com/cheggaaa/pb" "time" ) func main() { count := 100000 bar := pb.StartNew(count) for i := 0; i < count; i++ { bar.Increment() time.Sleep(time.Millisecond) } bar.FinishPrint("The End!") } ``` Result will be like this: ``` > go run test.go 37158 / 100000 [================>_______________________________] 37.16% 1m11s ``` ## Customization ```Go // create bar bar := pb.New(count) // refresh info every second (default 200ms) bar.SetRefreshRate(time.Second) // show percents (by default already true) bar.ShowPercent = true // show bar (by default already true) bar.ShowBar = true // no counters bar.ShowCounters = false // show "time left" bar.ShowTimeLeft = true // show average speed bar.ShowSpeed = true // sets the width of the progress bar bar.SetWidth(80) // sets the width of the progress bar, but if terminal size smaller will be ignored bar.SetMaxWidth(80) // convert output to readable format (like KB, MB) bar.SetUnits(pb.U_BYTES) // and start bar.Start() ``` ## Progress bar for IO Operations ```go // create and start bar bar := pb.New(myDataLen).SetUnits(pb.U_BYTES) bar.Start() // my io.Reader r := myReader // my io.Writer w := myWriter // create proxy reader reader := bar.NewProxyReader(r) // and copy from pb reader io.Copy(w, reader) ``` ```go // create and start bar bar := pb.New(myDataLen).SetUnits(pb.U_BYTES) bar.Start() // my io.Reader r := myReader // my io.Writer w := myWriter // create multi writer writer := io.MultiWriter(w, bar) // and copy io.Copy(writer, r) bar.Finish() ``` ## Custom Progress Bar Look-and-feel ```go bar.Format("<.- >") ``` ## Multiple Progress Bars (experimental and unstable) Do not print to terminal while pool is active. ```go package main import ( "math/rand" "sync" "time" "github.com/cheggaaa/pb" ) func main() { // create bars first := pb.New(200).Prefix("First ") second := pb.New(200).Prefix("Second ") third := pb.New(200).Prefix("Third ") // start pool pool, err := pb.StartPool(first, second, third) if err != nil { panic(err) } // update bars wg := new(sync.WaitGroup) for _, bar := range []*pb.ProgressBar{first, second, third} { wg.Add(1) go func(cb *pb.ProgressBar) { for n := 0; n < 200; n++ { cb.Increment() time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) } cb.Finish() wg.Done() }(bar) } wg.Wait() // close pool pool.Stop() } ``` The result will be as follows: ``` $ go run example/multiple.go First 34 / 200 [=========>---------------------------------------------] 17.00% 00m08s Second 42 / 200 [===========>------------------------------------------] 21.00% 00m06s Third 36 / 200 [=========>---------------------------------------------] 18.00% 00m08s ``` pb-3.1.5/example_copy_test.go000066400000000000000000000030751455443304200162050ustar00rootroot00000000000000package pb_test import ( "fmt" "io" "net/http" "os" "strconv" "strings" "time" "github.com/cheggaaa/pb" ) func Example_copy() { // check args if len(os.Args) < 3 { printUsage() return } sourceName, destName := os.Args[1], os.Args[2] // check source var source io.Reader var sourceSize int64 if strings.HasPrefix(sourceName, "http://") { // open as url resp, err := http.Get(sourceName) if err != nil { fmt.Printf("Can't get %s: %v\n", sourceName, err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { fmt.Printf("Server return non-200 status: %v\n", resp.Status) return } i, _ := strconv.Atoi(resp.Header.Get("Content-Length")) sourceSize = int64(i) source = resp.Body } else { // open as file s, err := os.Open(sourceName) if err != nil { fmt.Printf("Can't open %s: %v\n", sourceName, err) return } defer s.Close() // get source size sourceStat, err := s.Stat() if err != nil { fmt.Printf("Can't stat %s: %v\n", sourceName, err) return } sourceSize = sourceStat.Size() source = s } // create dest dest, err := os.Create(destName) if err != nil { fmt.Printf("Can't create %s: %v\n", destName, err) return } defer dest.Close() // create bar bar := pb.New(int(sourceSize)).SetUnits(pb.U_BYTES).SetRefreshRate(time.Millisecond * 10) bar.ShowSpeed = true bar.Start() // create proxy reader reader := bar.NewProxyReader(source) // and copy from reader io.Copy(dest, reader) bar.Finish() } func printUsage() { fmt.Println("copy [source file or url] [dest file]") } pb-3.1.5/example_multiple_test.go000066400000000000000000000012651455443304200170650ustar00rootroot00000000000000package pb_test import ( "math/rand" "sync" "time" "github.com/cheggaaa/pb" ) func Example_multiple() { // create bars first := pb.New(200).Prefix("First ") second := pb.New(200).Prefix("Second ") third := pb.New(200).Prefix("Third ") // start pool pool, err := pb.StartPool(first, second, third) if err != nil { panic(err) } // update bars wg := new(sync.WaitGroup) for _, bar := range []*pb.ProgressBar{first, second, third} { wg.Add(1) go func(cb *pb.ProgressBar) { for n := 0; n < 200; n++ { cb.Increment() time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) } cb.Finish() wg.Done() }(bar) } wg.Wait() // close pool pool.Stop() } pb-3.1.5/example_test.go000066400000000000000000000006701455443304200151510ustar00rootroot00000000000000package pb_test import ( "time" "github.com/cheggaaa/pb" ) func Example() { count := 5000 bar := pb.New(count) // show percents (by default already true) bar.ShowPercent = true // show bar (by default already true) bar.ShowBar = true bar.ShowCounters = true bar.ShowTimeLeft = true // and start bar.Start() for i := 0; i < count; i++ { bar.Increment() time.Sleep(time.Millisecond) } bar.FinishPrint("The End!") } pb-3.1.5/format.go000066400000000000000000000047631455443304200137560ustar00rootroot00000000000000package pb import ( "fmt" "time" ) type Units int const ( // U_NO are default units, they represent a simple value and are not formatted at all. U_NO Units = iota // U_BYTES units are formatted in a human readable way (B, KiB, MiB, ...) U_BYTES // U_BYTES_DEC units are like U_BYTES, but base 10 (B, KB, MB, ...) U_BYTES_DEC // U_DURATION units are formatted in a human readable way (3h14m15s) U_DURATION ) const ( KiB = 1024 MiB = 1048576 GiB = 1073741824 TiB = 1099511627776 KB = 1e3 MB = 1e6 GB = 1e9 TB = 1e12 ) func Format(i int64) *formatter { return &formatter{n: i} } type formatter struct { n int64 unit Units width int perSec bool } func (f *formatter) To(unit Units) *formatter { f.unit = unit return f } func (f *formatter) Width(width int) *formatter { f.width = width return f } func (f *formatter) PerSec() *formatter { f.perSec = true return f } func (f *formatter) String() (out string) { switch f.unit { case U_BYTES: out = formatBytes(f.n) case U_BYTES_DEC: out = formatBytesDec(f.n) case U_DURATION: out = formatDuration(f.n) default: out = fmt.Sprintf(fmt.Sprintf("%%%dd", f.width), f.n) } if f.perSec { out += "/s" } return } // Convert bytes to human readable string. Like 2 MiB, 64.2 KiB, 52 B func formatBytes(i int64) (result string) { switch { case i >= TiB: result = fmt.Sprintf("%.02f TiB", float64(i)/TiB) case i >= GiB: result = fmt.Sprintf("%.02f GiB", float64(i)/GiB) case i >= MiB: result = fmt.Sprintf("%.02f MiB", float64(i)/MiB) case i >= KiB: result = fmt.Sprintf("%.02f KiB", float64(i)/KiB) default: result = fmt.Sprintf("%d B", i) } return } // Convert bytes to base-10 human readable string. Like 2 MB, 64.2 KB, 52 B func formatBytesDec(i int64) (result string) { switch { case i >= TB: result = fmt.Sprintf("%.02f TB", float64(i)/TB) case i >= GB: result = fmt.Sprintf("%.02f GB", float64(i)/GB) case i >= MB: result = fmt.Sprintf("%.02f MB", float64(i)/MB) case i >= KB: result = fmt.Sprintf("%.02f KB", float64(i)/KB) default: result = fmt.Sprintf("%d B", i) } return } func formatDuration(n int64) (result string) { d := time.Duration(n) if d > time.Hour*24 { result = fmt.Sprintf("%dd", d/24/time.Hour) d -= (d / time.Hour / 24) * (time.Hour * 24) } if d > time.Hour { result = fmt.Sprintf("%s%dh", result, d/time.Hour) d -= d / time.Hour * time.Hour } m := d / time.Minute d -= m * time.Minute s := d / time.Second result = fmt.Sprintf("%s%02dm%02ds", result, m, s) return } pb-3.1.5/format_test.go000066400000000000000000000040241455443304200150030ustar00rootroot00000000000000package pb import ( "strconv" "testing" "time" ) func Test_DefaultsToInteger(t *testing.T) { value := int64(1000) expected := strconv.Itoa(int(value)) actual := Format(value).String() if actual != expected { t.Errorf("Expected {%s} was {%s}", expected, actual) } } func Test_CanFormatAsInteger(t *testing.T) { value := int64(1000) expected := strconv.Itoa(int(value)) actual := Format(value).To(U_NO).String() if actual != expected { t.Errorf("Expected {%s} was {%s}", expected, actual) } } func Test_CanFormatAsBytes(t *testing.T) { inputs := []struct { v int64 e string }{ {v: 1000, e: "1000 B"}, {v: 1024, e: "1.00 KiB"}, {v: 3*MiB + 140*KiB, e: "3.14 MiB"}, {v: 2 * GiB, e: "2.00 GiB"}, {v: 2048 * GiB, e: "2.00 TiB"}, } for _, input := range inputs { actual := Format(input.v).To(U_BYTES).String() if actual != input.e { t.Errorf("Expected {%s} was {%s}", input.e, actual) } } } func Test_CanFormatAsBytesDec(t *testing.T) { inputs := []struct { v int64 e string }{ {v: 999, e: "999 B"}, {v: 1024, e: "1.02 KB"}, {v: 3*MB + 140*KB, e: "3.14 MB"}, {v: 2 * GB, e: "2.00 GB"}, {v: 2048 * GB, e: "2.05 TB"}, } for _, input := range inputs { actual := Format(input.v).To(U_BYTES_DEC).String() if actual != input.e { t.Errorf("Expected {%s} was {%s}", input.e, actual) } } } func Test_CanFormatDuration(t *testing.T) { value := 10 * time.Minute expected := "10m00s" actual := Format(int64(value)).To(U_DURATION).String() if actual != expected { t.Errorf("Expected {%s} was {%s}", expected, actual) } } func Test_CanFormatLongDuration(t *testing.T) { value := 62 * time.Hour + 13 * time.Second expected := "2d14h00m13s" actual := Format(int64(value)).To(U_DURATION).String() if actual != expected { t.Errorf("Expected {%s} was {%s}", expected, actual) } } func Test_DefaultUnitsWidth(t *testing.T) { value := 10 expected := " 10" actual := Format(int64(value)).Width(7).String() if actual != expected { t.Errorf("Expected {%s} was {%s}", expected, actual) } } pb-3.1.5/go.mod000066400000000000000000000002701455443304200132320ustar00rootroot00000000000000module github.com/cheggaaa/pb require ( github.com/fatih/color v1.9.0 github.com/mattn/go-colorable v0.1.4 github.com/mattn/go-runewidth v0.0.4 golang.org/x/sys v0.1.0 ) go 1.12 pb-3.1.5/go.sum000066400000000000000000000021701455443304200132600ustar00rootroot00000000000000github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= pb-3.1.5/pb.go000066400000000000000000000273531455443304200130670ustar00rootroot00000000000000// Simple console progress bars package pb import ( "fmt" "io" "math" "strings" "sync" "sync/atomic" "time" "unicode/utf8" ) // Current version const Version = "1.0.29" const ( // Default refresh rate - 200ms DEFAULT_REFRESH_RATE = time.Millisecond * 200 FORMAT = "[=>-]" ) // DEPRECATED // variables for backward compatibility, from now do not work // use pb.Format and pb.SetRefreshRate var ( DefaultRefreshRate = DEFAULT_REFRESH_RATE BarStart, BarEnd, Empty, Current, CurrentN string ) // Create new progress bar object func New(total int) *ProgressBar { return New64(int64(total)) } // Create new progress bar object using int64 as total func New64(total int64) *ProgressBar { pb := &ProgressBar{ Total: total, RefreshRate: DEFAULT_REFRESH_RATE, ShowPercent: true, ShowCounters: true, ShowBar: true, ShowTimeLeft: true, ShowElapsedTime: false, ShowFinalTime: true, Units: U_NO, ManualUpdate: false, finish: make(chan struct{}), } return pb.Format(FORMAT) } // Create new object and start func StartNew(total int) *ProgressBar { return New(total).Start() } // Callback for custom output // For example: // bar.Callback = func(s string) { // mySuperPrint(s) // } // type Callback func(out string) type ProgressBar struct { current int64 // current must be first member of struct (https://code.google.com/p/go/issues/detail?id=5278) previous int64 Total int64 RefreshRate time.Duration ShowPercent, ShowCounters bool ShowSpeed, ShowTimeLeft, ShowBar bool ShowFinalTime, ShowElapsedTime bool HideCountersTotal bool Output io.Writer Callback Callback NotPrint bool Units Units Width int ForceWidth bool ManualUpdate bool AutoStat bool // Default width for the time box. UnitsWidth int TimeBoxWidth int finishOnce sync.Once //Guards isFinish finish chan struct{} isFinish bool startTime time.Time startValue int64 changeTime time.Time prefix, postfix string mu sync.Mutex lastPrint string BarStart string BarEnd string Empty string Current string CurrentN string AlwaysUpdate bool } // Start print func (pb *ProgressBar) Start() *ProgressBar { pb.startTime = time.Now() pb.startValue = atomic.LoadInt64(&pb.current) if atomic.LoadInt64(&pb.Total) == 0 { pb.ShowTimeLeft = false pb.ShowPercent = false pb.AutoStat = false } if !pb.ManualUpdate { pb.Update() // Initial printing of the bar before running the bar refresher. go pb.refresher() } return pb } // Increment current value func (pb *ProgressBar) Increment() int { return pb.Add(1) } // Get current value func (pb *ProgressBar) Get() int64 { c := atomic.LoadInt64(&pb.current) return c } // Set current value func (pb *ProgressBar) Set(current int) *ProgressBar { return pb.Set64(int64(current)) } // Set64 sets the current value as int64 func (pb *ProgressBar) Set64(current int64) *ProgressBar { atomic.StoreInt64(&pb.current, current) return pb } // Add to current value func (pb *ProgressBar) Add(add int) int { return int(pb.Add64(int64(add))) } func (pb *ProgressBar) Add64(add int64) int64 { return atomic.AddInt64(&pb.current, add) } // Set prefix string func (pb *ProgressBar) Prefix(prefix string) *ProgressBar { pb.mu.Lock() defer pb.mu.Unlock() pb.prefix = prefix return pb } // Set postfix string func (pb *ProgressBar) Postfix(postfix string) *ProgressBar { pb.mu.Lock() defer pb.mu.Unlock() pb.postfix = postfix return pb } // Set custom format for bar // Example: bar.Format("[=>_]") // Example: bar.Format("[\x00=\x00>\x00-\x00]") // \x00 is the delimiter func (pb *ProgressBar) Format(format string) *ProgressBar { var formatEntries []string if utf8.RuneCountInString(format) == 5 { formatEntries = strings.Split(format, "") } else { formatEntries = strings.Split(format, "\x00") } if len(formatEntries) == 5 { pb.BarStart = formatEntries[0] pb.BarEnd = formatEntries[4] pb.Empty = formatEntries[3] pb.Current = formatEntries[1] pb.CurrentN = formatEntries[2] } return pb } // Set bar refresh rate func (pb *ProgressBar) SetRefreshRate(rate time.Duration) *ProgressBar { pb.RefreshRate = rate return pb } // Set units // bar.SetUnits(U_NO) - by default // bar.SetUnits(U_BYTES) - for Mb, Kb, etc func (pb *ProgressBar) SetUnits(units Units) *ProgressBar { pb.Units = units return pb } // Set max width, if width is bigger than terminal width, will be ignored func (pb *ProgressBar) SetMaxWidth(width int) *ProgressBar { pb.Width = width pb.ForceWidth = false return pb } // Set bar width func (pb *ProgressBar) SetWidth(width int) *ProgressBar { pb.Width = width pb.ForceWidth = true return pb } // End print func (pb *ProgressBar) Finish() { //Protect multiple calls pb.finishOnce.Do(func() { close(pb.finish) pb.write(atomic.LoadInt64(&pb.Total), atomic.LoadInt64(&pb.current)) pb.mu.Lock() defer pb.mu.Unlock() switch { case pb.Output != nil: fmt.Fprintln(pb.Output) case !pb.NotPrint: fmt.Println() } pb.isFinish = true }) } // IsFinished return boolean func (pb *ProgressBar) IsFinished() bool { pb.mu.Lock() defer pb.mu.Unlock() return pb.isFinish } // End print and write string 'str' func (pb *ProgressBar) FinishPrint(str string) { pb.Finish() if pb.Output != nil { fmt.Fprintln(pb.Output, str) } else { fmt.Println(str) } } // implement io.Writer func (pb *ProgressBar) Write(p []byte) (n int, err error) { n = len(p) pb.Add(n) return } // implement io.Reader func (pb *ProgressBar) Read(p []byte) (n int, err error) { n = len(p) pb.Add(n) return } // Create new proxy reader over bar // Takes io.Reader or io.ReadCloser func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader { return &Reader{r, pb} } // Create new proxy writer over bar // Takes io.Writer or io.WriteCloser func (pb *ProgressBar) NewProxyWriter(r io.Writer) *Writer { return &Writer{r, pb} } func (pb *ProgressBar) write(total, current int64) { pb.mu.Lock() defer pb.mu.Unlock() width := pb.GetWidth() var percentBox, countersBox, timeLeftBox, timeSpentBox, speedBox, barBox, end, out string // percents if pb.ShowPercent { var percent float64 if total > 0 { percent = float64(current) / (float64(total) / float64(100)) } else { percent = float64(current) / float64(100) } percentBox = fmt.Sprintf(" %6.02f%%", percent) } // counters if pb.ShowCounters { current := Format(current).To(pb.Units).Width(pb.UnitsWidth) if total > 0 { if pb.HideCountersTotal { countersBox = fmt.Sprintf(" %s ", current) } else { totalS := Format(total).To(pb.Units).Width(pb.UnitsWidth) countersBox = fmt.Sprintf(" %s / %s ", current, totalS) } } else { if pb.HideCountersTotal { countersBox = fmt.Sprintf(" %s ", current) } else { countersBox = fmt.Sprintf(" %s / ? ", current) } } } // time left currentFromStart := current - pb.startValue fromStart := time.Since(pb.startTime) lastChangeTime := pb.changeTime fromChange := lastChangeTime.Sub(pb.startTime) if pb.ShowElapsedTime { timeSpentBox = fmt.Sprintf(" %s ", (fromStart/time.Second)*time.Second) } select { case <-pb.finish: if pb.ShowFinalTime { var left = (fromStart / time.Second) * time.Second timeLeftBox = fmt.Sprintf(" %s", left.String()) } default: if pb.ShowTimeLeft && currentFromStart > 0 { perEntry := fromChange / time.Duration(currentFromStart) var left time.Duration if total > 0 { left = time.Duration(total-current) * perEntry left -= time.Since(lastChangeTime) left = (left / time.Second) * time.Second } if left > 0 { timeLeft := Format(int64(left)).To(U_DURATION).String() timeLeftBox = fmt.Sprintf(" %s", timeLeft) } } } if len(timeLeftBox) < pb.TimeBoxWidth { timeLeftBox = fmt.Sprintf("%s%s", strings.Repeat(" ", pb.TimeBoxWidth-len(timeLeftBox)), timeLeftBox) } // speed if pb.ShowSpeed && currentFromStart > 0 { fromStart := time.Since(pb.startTime) speed := float64(currentFromStart) / (float64(fromStart) / float64(time.Second)) speedBox = " " + Format(int64(speed)).To(pb.Units).Width(pb.UnitsWidth).PerSec().String() } barWidth := escapeAwareRuneCountInString(countersBox + pb.BarStart + pb.BarEnd + percentBox + timeSpentBox + timeLeftBox + speedBox + pb.prefix + pb.postfix) // bar if pb.ShowBar { size := width - barWidth if size > 0 { if total > 0 { curSize := int(math.Ceil((float64(current) / float64(total)) * float64(size))) emptySize := size - curSize barBox = pb.BarStart if emptySize < 0 { emptySize = 0 } if curSize > size { curSize = size } cursorLen := escapeAwareRuneCountInString(pb.Current) if emptySize <= 0 { barBox += strings.Repeat(pb.Current, curSize/cursorLen) } else if curSize > 0 { cursorEndLen := escapeAwareRuneCountInString(pb.CurrentN) cursorRepetitions := (curSize - cursorEndLen) / cursorLen barBox += strings.Repeat(pb.Current, cursorRepetitions) barBox += pb.CurrentN } emptyLen := escapeAwareRuneCountInString(pb.Empty) barBox += strings.Repeat(pb.Empty, emptySize/emptyLen) barBox += pb.BarEnd } else { pos := size - int(current)%int(size) barBox = pb.BarStart if pos-1 > 0 { barBox += strings.Repeat(pb.Empty, pos-1) } barBox += pb.Current if size-pos-1 > 0 { barBox += strings.Repeat(pb.Empty, size-pos-1) } barBox += pb.BarEnd } } } // check len out = pb.prefix + timeSpentBox + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix if cl := escapeAwareRuneCountInString(out); cl < width { end = strings.Repeat(" ", width-cl) } // and print! pb.lastPrint = out + end isFinish := pb.isFinish switch { case isFinish: return case pb.Output != nil: fmt.Fprint(pb.Output, "\r"+out+end) case pb.Callback != nil: pb.Callback(out + end) case !pb.NotPrint: fmt.Print("\r" + out + end) } } // GetTerminalWidth - returns terminal width for all platforms. func GetTerminalWidth() (int, error) { return terminalWidth() } func (pb *ProgressBar) GetWidth() int { if pb.ForceWidth { return pb.Width } width := pb.Width termWidth, _ := terminalWidth() if width == 0 || termWidth <= width { width = termWidth } return width } // Write the current state of the progressbar func (pb *ProgressBar) Update() { c := atomic.LoadInt64(&pb.current) p := atomic.LoadInt64(&pb.previous) t := atomic.LoadInt64(&pb.Total) if p != c { pb.mu.Lock() pb.changeTime = time.Now() pb.mu.Unlock() atomic.StoreInt64(&pb.previous, c) } pb.write(t, c) if pb.AutoStat { if c == 0 { pb.startTime = time.Now() pb.startValue = 0 } else if c >= t && !pb.isFinish{ pb.Finish() } } } // String return the last bar print func (pb *ProgressBar) String() string { pb.mu.Lock() defer pb.mu.Unlock() return pb.lastPrint } // SetTotal atomically sets new total count func (pb *ProgressBar) SetTotal(total int) *ProgressBar { return pb.SetTotal64(int64(total)) } // SetTotal64 atomically sets new total count func (pb *ProgressBar) SetTotal64(total int64) *ProgressBar { atomic.StoreInt64(&pb.Total, total) return pb } // Reset bar and set new total count // Does effect only on finished bar func (pb *ProgressBar) Reset(total int) *ProgressBar { pb.mu.Lock() defer pb.mu.Unlock() if pb.isFinish { pb.SetTotal(total).Set(0) atomic.StoreInt64(&pb.previous, 0) } return pb } // Internal loop for refreshing the progressbar func (pb *ProgressBar) refresher() { for { select { case <-pb.finish: return case <-time.After(pb.RefreshRate): pb.Update() } } } pb-3.1.5/pb_appengine.go000066400000000000000000000004251455443304200151040ustar00rootroot00000000000000// +build appengine js package pb import "errors" // terminalWidth returns width of the terminal, which is not supported // and should always failed on appengine classic which is a sandboxed PaaS. func terminalWidth() (int, error) { return 0, errors.New("Not supported") } pb-3.1.5/pb_plan9.go000066400000000000000000000023661455443304200141670ustar00rootroot00000000000000package pb import ( "errors" "os" "os/signal" "sync" "syscall" ) var ErrPoolWasStarted = errors.New("Bar pool was started") var ( echoLockMutex sync.Mutex consctl *os.File ) // terminalWidth returns width of the terminal. func terminalWidth() (int, error) { return 0, errors.New("Not Supported") } func lockEcho() (shutdownCh chan struct{}, err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if consctl != nil { return nil, ErrPoolWasStarted } consctl, err = os.OpenFile("/dev/consctl", os.O_WRONLY, 0) if err != nil { return nil, err } _, err = consctl.WriteString("rawon") if err != nil { consctl.Close() consctl = nil return nil, err } shutdownCh = make(chan struct{}) go catchTerminate(shutdownCh) return } func unlockEcho() error { echoLockMutex.Lock() defer echoLockMutex.Unlock() if consctl == nil { return nil } if err := consctl.Close(); err != nil { return err } consctl = nil return nil } // listen exit signals and restore terminal state func catchTerminate(shutdownCh chan struct{}) { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGKILL) defer signal.Stop(sig) select { case <-shutdownCh: unlockEcho() case <-sig: unlockEcho() } } pb-3.1.5/pb_test.go000066400000000000000000000060461455443304200141220ustar00rootroot00000000000000package pb import ( "bytes" "strings" "sync" "testing" "time" "github.com/fatih/color" "github.com/mattn/go-colorable" ) func Test_IncrementAddsOne(t *testing.T) { count := 5000 bar := New(count) expected := 1 actual := bar.Increment() if actual != expected { t.Errorf("Expected {%d} was {%d}", expected, actual) } } func Test_Width(t *testing.T) { count := 5000 bar := New(count) width := 100 bar.SetWidth(100).Callback = func(out string) { if len(out) != width { t.Errorf("Bar width expected {%d} was {%d}", len(out), width) } } bar.Start() bar.Increment() bar.Finish() } func Test_MultipleFinish(t *testing.T) { bar := New(5000) bar.Add(2000) bar.Finish() bar.Finish() } func TestWriteRace(t *testing.T) { outBuffer := &bytes.Buffer{} totalCount := 20 bar := New(totalCount) bar.Output = outBuffer bar.Start() var wg sync.WaitGroup for i := 0; i < totalCount; i++ { wg.Add(1) go func() { bar.Increment() time.Sleep(250 * time.Millisecond) wg.Done() }() } wg.Wait() bar.Finish() } func Test_Format(t *testing.T) { bar := New(5000).Format(strings.Join([]string{ color.GreenString("["), color.New(color.BgGreen).SprintFunc()("o"), color.New(color.BgHiGreen).SprintFunc()("o"), color.New(color.BgRed).SprintFunc()("o"), color.GreenString("]"), }, "\x00")) w := colorable.NewColorableStdout() bar.Callback = func(out string) { w.Write([]byte(out)) } bar.Add(2000) bar.Finish() bar.Finish() } func Test_MultiCharacter(t *testing.T) { bar := New(5).Format(strings.Join([]string{"[[[", "---", ">>", "....", "]]"}, "\x00")) bar.Start() for i := 0; i < 5; i++ { time.Sleep(500 * time.Millisecond) bar.Increment() } time.Sleep(500 * time.Millisecond) bar.Finish() } func Test_AutoStat(t *testing.T) { bar := New(5) bar.AutoStat = true bar.Start() time.Sleep(2 * time.Second) //real start work for i := 0; i < 5; i++ { time.Sleep(500 * time.Millisecond) bar.Increment() } //real finish work time.Sleep(2 * time.Second) bar.Finish() } func Test_Finish_PrintNewline(t *testing.T) { bar := New(5) buf := &bytes.Buffer{} bar.Output = buf bar.Finish() expected := "\n" actual := buf.String() //Finish should write newline to bar.Output if !strings.HasSuffix(actual, expected) { t.Errorf("Expected %q to have suffix %q", expected, actual) } } func Test_FinishPrint(t *testing.T) { bar := New(5) buf := &bytes.Buffer{} bar.Output = buf bar.FinishPrint("foo") expected := "foo\n" actual := buf.String() //FinishPrint should write to bar.Output if !strings.HasSuffix(actual, expected) { t.Errorf("Expected %q to have suffix %q", expected, actual) } } func Test_Reset(t *testing.T) { bar := StartNew(5) for i := 0; i < 5; i++ { bar.Increment() } if actual := bar.Get(); actual != 5 { t.Errorf("Expected: %d; actual: %d", 5, actual) } bar.Finish() bar.Reset(10).Start() defer bar.Finish() if actual := bar.Get(); actual != 0 { t.Errorf("Expected: %d; actual: %d", 0, actual) } if actual := bar.Total; actual != 10 { t.Errorf("Expected: %d; actual: %d", 10, actual) } } pb-3.1.5/pb_win.go000066400000000000000000000101661455443304200137360ustar00rootroot00000000000000// +build windows package pb import ( "errors" "fmt" "os" "sync" "syscall" "unsafe" ) var tty = os.Stdin var ( kernel32 = syscall.NewLazyDLL("kernel32.dll") // GetConsoleScreenBufferInfo retrieves information about the // specified console screen buffer. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") // GetConsoleMode retrieves the current input mode of a console's // input buffer or the current output mode of a console screen buffer. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx getConsoleMode = kernel32.NewProc("GetConsoleMode") // SetConsoleMode sets the input mode of a console's input buffer // or the output mode of a console screen buffer. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx setConsoleMode = kernel32.NewProc("SetConsoleMode") // SetConsoleCursorPosition sets the cursor position in the // specified console screen buffer. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") ) type ( // Defines the coordinates of the upper left and lower right corners // of a rectangle. // See // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx smallRect struct { Left, Top, Right, Bottom int16 } // Defines the coordinates of a character cell in a console screen // buffer. The origin of the coordinate system (0,0) is at the top, left cell // of the buffer. // See // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx coordinates struct { X, Y int16 } word int16 // Contains information about a console screen buffer. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx consoleScreenBufferInfo struct { dwSize coordinates dwCursorPosition coordinates wAttributes word srWindow smallRect dwMaximumWindowSize coordinates } ) // terminalWidth returns width of the terminal. func terminalWidth() (width int, err error) { var info consoleScreenBufferInfo _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) if e != 0 { return 0, error(e) } return int(info.dwSize.X) - 1, nil } func getCursorPos() (pos coordinates, err error) { var info consoleScreenBufferInfo _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) if e != 0 { return info.dwCursorPosition, error(e) } return info.dwCursorPosition, nil } func setCursorPos(pos coordinates) error { _, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0) if e != 0 { return error(e) } return nil } var ErrPoolWasStarted = errors.New("Bar pool was started") var echoLocked bool var echoLockMutex sync.Mutex var oldState word func lockEcho() (shutdownCh chan struct{}, err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if echoLocked { err = ErrPoolWasStarted return } echoLocked = true if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 { err = fmt.Errorf("Can't get terminal settings: %v", e) return } newState := oldState const ENABLE_ECHO_INPUT = 0x0004 const ENABLE_LINE_INPUT = 0x0002 newState = newState & (^(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)) if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 { err = fmt.Errorf("Can't set terminal settings: %v", e) return } shutdownCh = make(chan struct{}) return } func unlockEcho() (err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if !echoLocked { return } echoLocked = false if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 { err = fmt.Errorf("Can't set terminal settings") } return } pb-3.1.5/pb_x.go000066400000000000000000000045051455443304200134100ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd solaris dragonfly aix zos // +build !appengine !js package pb import ( "errors" "fmt" "os" "os/signal" "sync" "syscall" "golang.org/x/sys/unix" ) var ErrPoolWasStarted = errors.New("Bar pool was started") var ( echoLockMutex sync.Mutex origTermStatePtr *unix.Termios tty *os.File istty bool ) func init() { echoLockMutex.Lock() defer echoLockMutex.Unlock() var err error tty, err = os.Open("/dev/tty") istty = true if err != nil { tty = os.Stdin istty = false } } // terminalWidth returns width of the terminal. func terminalWidth() (int, error) { if !istty { return 0, errors.New("Not Supported") } echoLockMutex.Lock() defer echoLockMutex.Unlock() fd := int(tty.Fd()) ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) if err != nil { return 0, err } return int(ws.Col), nil } func lockEcho() (shutdownCh chan struct{}, err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if istty { if origTermStatePtr != nil { return shutdownCh, ErrPoolWasStarted } fd := int(tty.Fd()) origTermStatePtr, err = unix.IoctlGetTermios(fd, ioctlReadTermios) if err != nil { return nil, fmt.Errorf("Can't get terminal settings: %v", err) } oldTermios := *origTermStatePtr newTermios := oldTermios newTermios.Lflag &^= syscall.ECHO newTermios.Lflag |= syscall.ICANON | syscall.ISIG newTermios.Iflag |= syscall.ICRNL if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newTermios); err != nil { return nil, fmt.Errorf("Can't set terminal settings: %v", err) } } shutdownCh = make(chan struct{}) go catchTerminate(shutdownCh) return } func unlockEcho() error { echoLockMutex.Lock() defer echoLockMutex.Unlock() if istty { if origTermStatePtr == nil { return nil } fd := int(tty.Fd()) if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, origTermStatePtr); err != nil { return fmt.Errorf("Can't set terminal settings: %v", err) } } origTermStatePtr = nil return nil } // listen exit signals and restore terminal state func catchTerminate(shutdownCh chan struct{}) { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL) defer signal.Stop(sig) select { case <-shutdownCh: unlockEcho() case <-sig: unlockEcho() } } pb-3.1.5/pool.go000066400000000000000000000034151455443304200134300ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows plan9 aix zos package pb import ( "io" "sync" "time" ) // Create and start new pool with given bars // You need call pool.Stop() after work func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) { pool = new(Pool) if err = pool.Start(); err != nil { return } pool.Add(pbs...) return } // NewPool initialises a pool with progress bars, but // doesn't start it. You need to call Start manually func NewPool(pbs ...*ProgressBar) (pool *Pool) { pool = new(Pool) pool.Add(pbs...) return } type Pool struct { Output io.Writer RefreshRate time.Duration bars []*ProgressBar lastBarsCount int shutdownCh chan struct{} workerCh chan struct{} m sync.Mutex finishOnce sync.Once } // Add progress bars. func (p *Pool) Add(pbs ...*ProgressBar) { p.m.Lock() defer p.m.Unlock() for _, bar := range pbs { bar.ManualUpdate = true bar.NotPrint = true bar.Start() p.bars = append(p.bars, bar) } } func (p *Pool) Start() (err error) { p.RefreshRate = DefaultRefreshRate p.shutdownCh, err = lockEcho() if err != nil { return } p.workerCh = make(chan struct{}) go p.writer() return } func (p *Pool) writer() { var first = true defer func() { if !first { p.print(false) } else { p.print(true) p.print(false) } close(p.workerCh) }() for { select { case <-time.After(p.RefreshRate): if p.print(first) { p.print(false) return } first = false case <-p.shutdownCh: return } } } // Stop Restore terminal state and close pool func (p *Pool) Stop() error { p.finishOnce.Do(func() { if p.shutdownCh != nil { close(p.shutdownCh) } }) // Wait for the worker to complete <-p.workerCh return unlockEcho() } pb-3.1.5/pool_win.go000066400000000000000000000012631455443304200143040ustar00rootroot00000000000000// +build windows package pb import ( "fmt" "log" ) func (p *Pool) print(first bool) bool { p.m.Lock() defer p.m.Unlock() var out string if !first { coords, err := getCursorPos() if err != nil { log.Panic(err) } coords.Y -= int16(p.lastBarsCount) if coords.Y < 0 { coords.Y = 0 } coords.X = 0 err = setCursorPos(coords) if err != nil { log.Panic(err) } } isFinished := true for _, bar := range p.bars { if !bar.IsFinished() { isFinished = false } bar.Update() out += fmt.Sprintf("\r%s\n", bar.String()) } if p.Output != nil { fmt.Fprint(p.Output, out) } else { fmt.Print(out) } p.lastBarsCount = len(p.bars) return isFinished } pb-3.1.5/pool_x.go000066400000000000000000000010521455443304200137520ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd solaris dragonfly plan9 aix zos package pb import "fmt" func (p *Pool) print(first bool) bool { p.m.Lock() defer p.m.Unlock() var out string if !first { out = fmt.Sprintf("\033[%dA", p.lastBarsCount) } isFinished := true for _, bar := range p.bars { if !bar.IsFinished() { isFinished = false } bar.Update() out += fmt.Sprintf("\r%s\n", bar.String()) } if p.Output != nil { fmt.Fprint(p.Output, out) } else { fmt.Print(out) } p.lastBarsCount = len(p.bars) return isFinished } pb-3.1.5/reader.go000066400000000000000000000006401455443304200137160ustar00rootroot00000000000000package pb import ( "io" ) // It's proxy reader, implement io.Reader type Reader struct { io.Reader bar *ProgressBar } func (r *Reader) Read(p []byte) (n int, err error) { n, err = r.Reader.Read(p) r.bar.Add(n) return } // Close the reader when it implements io.Closer func (r *Reader) Close() (err error) { r.bar.Finish() if closer, ok := r.Reader.(io.Closer); ok { return closer.Close() } return } pb-3.1.5/runecount.go000066400000000000000000000005541455443304200145020ustar00rootroot00000000000000package pb import ( "github.com/mattn/go-runewidth" "regexp" ) // Finds the control character sequences (like colors) var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") func escapeAwareRuneCountInString(s string) int { n := runewidth.StringWidth(s) for _, sm := range ctrlFinder.FindAllString(s, -1) { n -= runewidth.StringWidth(sm) } return n } pb-3.1.5/runecount_test.go000066400000000000000000000007741455443304200155450ustar00rootroot00000000000000package pb import "testing" func Test_RuneCount(t *testing.T) { s := string([]byte{ 27, 91, 51, 49, 109, // {Red} 72, 101, 108, 108, 111, // Hello 44, 32, // , 112, 108, 97, 121, 103, 114, 111, 117, 110, 100, // Playground 27, 91, 48, 109, // {Reset} }) if e, l := 17, escapeAwareRuneCountInString(s); l != e { t.Errorf("Invalid length %d, expected %d", l, e) } s = "進捗 " if e, l := 5, escapeAwareRuneCountInString(s); l != e { t.Errorf("Invalid length %d, expected %d", l, e) } } pb-3.1.5/termios_bsd.go000066400000000000000000000002731455443304200147700ustar00rootroot00000000000000// +build darwin freebsd netbsd openbsd dragonfly // +build !appengine package pb import "syscall" const ioctlReadTermios = syscall.TIOCGETA const ioctlWriteTermios = syscall.TIOCSETA pb-3.1.5/termios_sysv.go000066400000000000000000000005151455443304200152230ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build linux solaris aix zos // +build !appengine package pb import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS const ioctlWriteTermios = unix.TCSETS pb-3.1.5/v3/000077500000000000000000000000001455443304200124555ustar00rootroot00000000000000pb-3.1.5/v3/LICENSE000066400000000000000000000027071455443304200134700ustar00rootroot00000000000000Copyright (c) 2012-2015, Sergey Cherepanov All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.pb-3.1.5/v3/element.go000066400000000000000000000221271455443304200144410ustar00rootroot00000000000000package pb import ( "bytes" "fmt" "math" "strings" "sync" "time" ) const ( adElPlaceholder = "%_ad_el_%" adElPlaceholderLen = len(adElPlaceholder) ) var ( defaultBarEls = [5]string{"[", "-", ">", "_", "]"} ) // Element is an interface for bar elements type Element interface { ProgressElement(state *State, args ...string) string } // ElementFunc type implements Element interface and created for simplify elements type ElementFunc func(state *State, args ...string) string // ProgressElement just call self func func (e ElementFunc) ProgressElement(state *State, args ...string) string { return e(state, args...) } var elementsM sync.Mutex var elements = map[string]Element{ "percent": ElementPercent, "counters": ElementCounters, "bar": adaptiveWrap(ElementBar), "speed": ElementSpeed, "rtime": ElementRemainingTime, "etime": ElementElapsedTime, "string": ElementString, "cycle": ElementCycle, } // RegisterElement give you a chance to use custom elements func RegisterElement(name string, el Element, adaptive bool) { if adaptive { el = adaptiveWrap(el) } elementsM.Lock() elements[name] = el elementsM.Unlock() } type argsHelper []string func (args argsHelper) getOr(n int, value string) string { if len(args) > n { return args[n] } return value } func (args argsHelper) getNotEmptyOr(n int, value string) (v string) { if v = args.getOr(n, value); v == "" { return value } return } func adaptiveWrap(el Element) Element { return ElementFunc(func(state *State, args ...string) string { state.recalc = append(state.recalc, ElementFunc(func(s *State, _ ...string) (result string) { s.adaptive = true result = el.ProgressElement(s, args...) s.adaptive = false return })) return adElPlaceholder }) } // ElementPercent shows current percent of progress. // Optionally can take one or two string arguments. // First string will be used as value for format float64, default is "%.02f%%". // Second string will be used when percent can't be calculated, default is "?%" // In template use as follows: {{percent .}} or {{percent . "%.03f%%"}} or {{percent . "%.03f%%" "?"}} var ElementPercent ElementFunc = func(state *State, args ...string) string { argsh := argsHelper(args) if state.Total() > 0 { return fmt.Sprintf( argsh.getNotEmptyOr(0, "%.02f%%"), float64(state.Value())/(float64(state.Total())/float64(100)), ) } return argsh.getOr(1, "?%") } // ElementCounters shows current and total values. // Optionally can take one or two string arguments. // First string will be used as format value when Total is present (>0). Default is "%s / %s" // Second string will be used when total <= 0. Default is "%[1]s" // In template use as follows: {{counters .}} or {{counters . "%s/%s"}} or {{counters . "%s/%s" "%s/?"}} var ElementCounters ElementFunc = func(state *State, args ...string) string { var f string if state.Total() > 0 { f = argsHelper(args).getNotEmptyOr(0, "%s / %s") } else { f = argsHelper(args).getNotEmptyOr(1, "%[1]s") } return fmt.Sprintf(f, state.Format(state.Value()), state.Format(state.Total())) } type elementKey int const ( barObj elementKey = iota speedObj cycleObj ) type bar struct { eb [5][]byte // elements in bytes cc [5]int // cell counts buf *bytes.Buffer } func (p *bar) write(state *State, eln, width int) int { repeat := width / p.cc[eln] remainder := width % p.cc[eln] for i := 0; i < repeat; i++ { p.buf.Write(p.eb[eln]) } if remainder > 0 { StripStringToBuffer(string(p.eb[eln]), remainder, p.buf) } return width } func getProgressObj(state *State, args ...string) (p *bar) { var ok bool if p, ok = state.Get(barObj).(*bar); !ok { p = &bar{ buf: bytes.NewBuffer(nil), } state.Set(barObj, p) } argsH := argsHelper(args) for i := range p.eb { arg := argsH.getNotEmptyOr(i, defaultBarEls[i]) if string(p.eb[i]) != arg { p.cc[i] = CellCount(arg) p.eb[i] = []byte(arg) if p.cc[i] == 0 { p.cc[i] = 1 p.eb[i] = []byte(" ") } } } return } // ElementBar make progress bar view [-->__] // Optionally can take up to 5 string arguments. Defaults is "[", "-", ">", "_", "]" // In template use as follows: {{bar . }} or {{bar . "<" "oOo" "|" "~" ">"}} // Color args: {{bar . (red "[") (green "-") ... var ElementBar ElementFunc = func(state *State, args ...string) string { // init var p = getProgressObj(state, args...) total, value := state.Total(), state.Value() if total < 0 { total = -total } if value < 0 { value = -value } // check for overflow if total != 0 && value > total { total = value } p.buf.Reset() var widthLeft = state.AdaptiveElWidth() if widthLeft <= 0 || !state.IsAdaptiveWidth() { widthLeft = 30 } // write left border if p.cc[0] < widthLeft { widthLeft -= p.write(state, 0, p.cc[0]) } else { p.write(state, 0, widthLeft) return p.buf.String() } // check right border size if p.cc[4] < widthLeft { // write later widthLeft -= p.cc[4] } else { p.write(state, 4, widthLeft) return p.buf.String() } var curCount int if total > 0 { // calculate count of currenct space curCount = int(math.Ceil((float64(value) / float64(total)) * float64(widthLeft))) } // write bar if total == value && state.IsFinished() { widthLeft -= p.write(state, 1, curCount) } else if toWrite := curCount - p.cc[2]; toWrite > 0 { widthLeft -= p.write(state, 1, toWrite) widthLeft -= p.write(state, 2, p.cc[2]) } else if curCount > 0 { widthLeft -= p.write(state, 2, curCount) } if widthLeft > 0 { widthLeft -= p.write(state, 3, widthLeft) } // write right border p.write(state, 4, p.cc[4]) // cut result and return string return p.buf.String() } func elapsedTime(state *State) string { elapsed := state.Time().Sub(state.StartTime()) var precision time.Duration var ok bool if precision, ok = state.Get(TimeRound).(time.Duration); !ok { // default behavior: round to nearest .1s when elapsed < 10s // // we compare with 9.95s as opposed to 10s to avoid an annoying // interaction with the fixed precision display code below, // where 9.9s would be rounded to 10s but printed as 10.0s, and // then 10.0s would be rounded to 10s and printed as 10s if elapsed < 9950*time.Millisecond { precision = 100 * time.Millisecond } else { precision = time.Second } } rounded := elapsed.Round(precision) if precision < time.Second && rounded >= time.Second { // special handling to ensure string is shown with the given // precision, with trailing zeros after the decimal point if // necessary reference := (2*time.Second - time.Nanosecond).Truncate(precision).String() // reference looks like "1.9[...]9s", telling us how many // decimal digits we need neededDecimals := len(reference) - 3 s := rounded.String() dotIndex := strings.LastIndex(s, ".") if dotIndex != -1 { // s has the form "[stuff].[decimals]s" decimals := len(s) - dotIndex - 2 extraZeros := neededDecimals - decimals return fmt.Sprintf("%s%ss", s[:len(s)-1], strings.Repeat("0", extraZeros)) } else { // s has the form "[stuff]s" return fmt.Sprintf("%s.%ss", s[:len(s)-1], strings.Repeat("0", neededDecimals)) } } else { return rounded.String() } } // ElementRemainingTime calculates remaining time based on speed (EWMA) // Optionally can take one or two string arguments. // First string will be used as value for format time duration string, default is "%s". // Second string will be used when bar finished and value indicates elapsed time, default is "%s" // Third string will be used when value not available, default is "?" // In template use as follows: {{rtime .}} or {{rtime . "%s remain"}} or {{rtime . "%s remain" "%s total" "???"}} var ElementRemainingTime ElementFunc = func(state *State, args ...string) string { if state.IsFinished() { return fmt.Sprintf(argsHelper(args).getOr(1, "%s"), elapsedTime(state)) } sp := getSpeedObj(state).value(state) if sp > 0 { remain := float64(state.Total() - state.Value()) remainDur := time.Duration(remain/sp) * time.Second return fmt.Sprintf(argsHelper(args).getOr(0, "%s"), remainDur) } return argsHelper(args).getOr(2, "?") } // ElementElapsedTime shows elapsed time // Optionally can take one argument - it's format for time string. // In template use as follows: {{etime .}} or {{etime . "%s elapsed"}} var ElementElapsedTime ElementFunc = func(state *State, args ...string) string { return fmt.Sprintf(argsHelper(args).getOr(0, "%s"), elapsedTime(state)) } // ElementString get value from bar by given key and print them // bar.Set("myKey", "string to print") // In template use as follows: {{string . "myKey"}} var ElementString ElementFunc = func(state *State, args ...string) string { if len(args) == 0 { return "" } v := state.Get(args[0]) if v == nil { return "" } return fmt.Sprint(v) } // ElementCycle return next argument for every call // In template use as follows: {{cycle . "1" "2" "3"}} // Or mix width other elements: {{ bar . "" "" (cycle . "↖" "↗" "↘" "↙" )}} var ElementCycle ElementFunc = func(state *State, args ...string) string { if len(args) == 0 { return "" } n, _ := state.Get(cycleObj).(int) if n >= len(args) { n = 0 } state.Set(cycleObj, n+1) return args[n] } pb-3.1.5/v3/element_test.go000066400000000000000000000237471455443304200155110ustar00rootroot00000000000000package pb import ( "fmt" "strings" "testing" "time" "github.com/fatih/color" ) func testState(total, value int64, maxWidth int, bools ...bool) (s *State) { s = &State{ total: total, current: value, adaptiveElWidth: maxWidth, ProgressBar: new(ProgressBar), } if len(bools) > 0 { s.Set(Bytes, bools[0]) } if len(bools) > 1 && bools[1] { s.adaptive = true } return } func testElementBarString(t *testing.T, state *State, el Element, want string, args ...string) { if state.ProgressBar == nil { state.ProgressBar = new(ProgressBar) } res := el.ProgressElement(state, args...) if res != want { t.Errorf("Unexpected result: '%s'; want: '%s'", res, want) } if state.IsAdaptiveWidth() && state.AdaptiveElWidth() != CellCount(res) { t.Errorf("Unepected width: %d; want: %d", CellCount(res), state.AdaptiveElWidth()) } } func TestElementPercent(t *testing.T) { testElementBarString(t, testState(100, 50, 0), ElementPercent, "50.00%") testElementBarString(t, testState(100, 50, 0), ElementPercent, "50 percent", "%v percent") testElementBarString(t, testState(0, 50, 0), ElementPercent, "?%") testElementBarString(t, testState(0, 50, 0), ElementPercent, "unkn", "%v%%", "unkn") } func TestElementCounters(t *testing.T) { testElementBarString(t, testState(100, 50, 0), ElementCounters, "50 / 100") testElementBarString(t, testState(100, 50, 0), ElementCounters, "50 of 100", "%s of %s") testElementBarString(t, testState(100, 50, 0, true), ElementCounters, "50 B of 100 B", "%s of %s") testElementBarString(t, testState(100, 50, 0, true), ElementCounters, "50 B / 100 B") testElementBarString(t, testState(0, 50, 0, true), ElementCounters, "50 B") testElementBarString(t, testState(0, 50, 0, true), ElementCounters, "50 B / ?", "", "%[1]s / ?") } func TestElementBar(t *testing.T) { // short testElementBarString(t, testState(100, 50, 1, false, true), ElementBar, "[") testElementBarString(t, testState(100, 50, 2, false, true), ElementBar, "[]") testElementBarString(t, testState(100, 50, 3, false, true), ElementBar, "[>]") testElementBarString(t, testState(100, 50, 4, false, true), ElementBar, "[>_]") testElementBarString(t, testState(100, 50, 5, false, true), ElementBar, "[->_]") // middle testElementBarString(t, testState(100, 50, 10, false, true), ElementBar, "[--->____]") testElementBarString(t, testState(100, 50, 10, false, true), ElementBar, "<--->____>", "<", "", "", "", ">") // finished st := testState(100, 100, 10, false, true) st.finished = true testElementBarString(t, st, ElementBar, "[--------]") // empty color st = testState(100, 50, 10, false, true) st.Set(Terminal, true) color.NoColor = false testElementBarString(t, st, ElementBar, " --->____]", color.RedString("%s", "")) // empty testElementBarString(t, testState(0, 50, 10, false, true), ElementBar, "[________]") // full testElementBarString(t, testState(20, 20, 10, false, true), ElementBar, "[------->]") // everflow testElementBarString(t, testState(20, 50, 10, false, true), ElementBar, "[------->]") // small width testElementBarString(t, testState(20, 50, 2, false, true), ElementBar, "[]") testElementBarString(t, testState(20, 50, 1, false, true), ElementBar, "[") // negative counters testElementBarString(t, testState(-50, -150, 10, false, true), ElementBar, "[------->]") testElementBarString(t, testState(-150, -50, 10, false, true), ElementBar, "[-->_____]") testElementBarString(t, testState(50, -150, 10, false, true), ElementBar, "[------->]") testElementBarString(t, testState(-50, 150, 10, false, true), ElementBar, "[------->]") // long entities / unicode f1 := []string{"進捗|", "многобайт", "active", "пусто", "|end"} testElementBarString(t, testState(100, 50, 1, false, true), ElementBar, " ", f1...) testElementBarString(t, testState(100, 50, 3, false, true), ElementBar, "進 ", f1...) testElementBarString(t, testState(100, 50, 4, false, true), ElementBar, "進捗", f1...) testElementBarString(t, testState(100, 50, 29, false, true), ElementBar, "進捗|многactiveпустопусто|end", f1...) testElementBarString(t, testState(100, 50, 11, false, true), ElementBar, "進捗|aп|end", f1...) // unicode f2 := []string{"⚑", ".", ">", "⟞", "⚐"} testElementBarString(t, testState(100, 50, 8, false, true), ElementBar, "⚑..>⟞⟞⟞⚐", f2...) // no adaptive testElementBarString(t, testState(0, 50, 10), ElementBar, "[____________________________]") var formats = [][]string{ []string{}, f1, f2, } // all widths / extreme values // check for panic and correct width for _, f := range formats { for tt := int64(-2); tt < 12; tt++ { for v := int64(-2); v < 12; v++ { state := testState(tt, v, 0, false, true) for w := -2; w < 20; w++ { state.adaptiveElWidth = w res := ElementBar(state, f...) var we = w if we <= 0 { we = 30 } if CellCount(res) != we { t.Errorf("Unexpected len(%d): '%s'", we, res) } } } } } } func TestElementSpeed(t *testing.T) { var state = testState(1000, 0, 0, false) state.time = time.Now() for i := int64(0); i < 10; i++ { state.id = uint64(i) + 1 state.current += 42 state.time = state.time.Add(time.Second) state.finished = i == 9 if state.finished { state.current += 100 } r := ElementSpeed(state) r2 := ElementSpeed(state) if r != r2 { t.Errorf("Must be the same: '%s' vs '%s'", r, r2) } if i < 1 { // do not calc first result if w := "? p/s"; r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } } else if state.finished { if w := "58 p/s"; r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } state.time = state.time.Add(-time.Hour) r = ElementSpeed(state) if w := "? p/s"; r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } } else { if w := "42 p/s"; r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } } } } func TestElementRemainingTime(t *testing.T) { var state = testState(100, 0, 0, false) state.time = time.Now() state.startTime = state.time for i := int64(0); i < 10; i++ { state.id = uint64(i) + 1 state.time = state.time.Add(time.Second) state.finished = i == 9 r := ElementRemainingTime(state) if i < 1 { // do not calc first two results if w := "?"; r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } } else if state.finished { // final elapsed time if w := "10s"; r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } } else { w := fmt.Sprintf("%ds", 10-i) if r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } } state.current += 10 } } func TestElementElapsedTime(t *testing.T) { t.Run("default behavior", func(t *testing.T) { var state = testState(1000, 0, 0, false) state.startTime = time.Now() state.time = state.startTime for i := int64(0); i <= 12; i++ { r := ElementElapsedTime(state) w := fmt.Sprintf("%d.0s", i) if i == 0 || i >= 10 { w = fmt.Sprintf("%ds", i) } if r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } state.time = state.time.Add(time.Second) } }) t.Run("with round set", func(t *testing.T) { var state = testState(1000, 0, 0, false) state.Set(TimeRound, time.Second) state.startTime = time.Now() state.time = state.startTime for i := int64(0); i <= 10; i++ { r := ElementElapsedTime(state) w := fmt.Sprintf("%ds", i) if r != w { t.Errorf("Unexpected result[%d]: '%s' vs '%s'", i, r, w) } state.time = state.time.Add(time.Second) } }) } func TestElementString(t *testing.T) { var state = testState(0, 0, 0, false) testElementBarString(t, state, ElementString, "", "myKey") state.Set("myKey", "my value") testElementBarString(t, state, ElementString, "my value", "myKey") state.Set("myKey", "my value1") testElementBarString(t, state, ElementString, "my value1", "myKey") testElementBarString(t, state, ElementString, "") } func TestElementCycle(t *testing.T) { var state = testState(0, 0, 0, false) testElementBarString(t, state, ElementCycle, "") testElementBarString(t, state, ElementCycle, "1", "1", "2", "3") testElementBarString(t, state, ElementCycle, "2", "1", "2", "3") testElementBarString(t, state, ElementCycle, "3", "1", "2", "3") testElementBarString(t, state, ElementCycle, "1", "1", "2", "3") testElementBarString(t, state, ElementCycle, "2", "1", "2") testElementBarString(t, state, ElementCycle, "1", "1", "2") } func TestAdaptiveWrap(t *testing.T) { var state = testState(0, 0, 0, false) state.id = 1 state.Set("myKey", "my value") el := adaptiveWrap(ElementString) testElementBarString(t, state, el, adElPlaceholder, "myKey") if v := state.recalc[0].ProgressElement(state); v != "my value" { t.Errorf("Unexpected result: %s", v) } state.id = 2 testElementBarString(t, state, el, adElPlaceholder, "myKey1") state.Set("myKey", "my value1") if v := state.recalc[0].ProgressElement(state); v != "my value1" { t.Errorf("Unexpected result: %s", v) } } func TestRegisterElement(t *testing.T) { var testEl ElementFunc = func(state *State, args ...string) string { return strings.Repeat("*", state.AdaptiveElWidth()) } RegisterElement("testEl", testEl, true) result := ProgressBarTemplate(`{{testEl . }}`).New(0).SetWidth(5).String() if result != "*****" { t.Errorf("Unexpected result: '%v'", result) } } func BenchmarkBar(b *testing.B) { var formats = map[string][]string{ "simple": []string{".", ".", ".", ".", "."}, "unicode": []string{"⚑", "⚒", "⚟", "⟞", "⚐"}, "color": []string{color.RedString("%s", "."), color.RedString("%s", "."), color.RedString("%s", "."), color.RedString("%s", "."), color.RedString("%s", ".")}, "long": []string{"..", "..", "..", "..", ".."}, "longunicode": []string{"⚑⚑", "⚒⚒", "⚟⚟", "⟞⟞", "⚐⚐"}, } for name, args := range formats { state := testState(100, 50, 100, false, true) b.Run(name, func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { ElementBar(state, args...) } }) } } pb-3.1.5/v3/go.mod000066400000000000000000000005121455443304200135610ustar00rootroot00000000000000module github.com/cheggaaa/pb/v3 require ( github.com/VividCortex/ewma v1.2.0 github.com/fatih/color v1.15.0 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-runewidth v0.0.15 ) require ( github.com/rivo/uniseg v0.2.0 // indirect golang.org/x/sys v0.6.0 // indirect ) go 1.17 pb-3.1.5/v3/go.sum000066400000000000000000000026761455443304200136230ustar00rootroot00000000000000github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= pb-3.1.5/v3/io.go000066400000000000000000000020111455443304200134050ustar00rootroot00000000000000package pb import ( "io" ) // Reader it's a wrapper for given reader, but with progress handle type Reader struct { io.Reader bar *ProgressBar } // Read reads bytes from wrapped reader and add amount of bytes to progress bar func (r *Reader) Read(p []byte) (n int, err error) { n, err = r.Reader.Read(p) r.bar.Add(n) return } // Close the wrapped reader when it implements io.Closer func (r *Reader) Close() (err error) { r.bar.Finish() if closer, ok := r.Reader.(io.Closer); ok { return closer.Close() } return } // Writer it's a wrapper for given writer, but with progress handle type Writer struct { io.Writer bar *ProgressBar } // Write writes bytes to wrapped writer and add amount of bytes to progress bar func (r *Writer) Write(p []byte) (n int, err error) { n, err = r.Writer.Write(p) r.bar.Add(n) return } // Close the wrapped reader when it implements io.Closer func (r *Writer) Close() (err error) { r.bar.Finish() if closer, ok := r.Writer.(io.Closer); ok { return closer.Close() } return } pb-3.1.5/v3/io_test.go000066400000000000000000000044221455443304200144540ustar00rootroot00000000000000package pb import ( "testing" ) func TestPBProxyReader(t *testing.T) { bar := new(ProgressBar) if bar.GetBool(Bytes) { t.Errorf("By default bytes must be false") } testReader := new(testReaderWriterCloser) proxyReader := bar.NewProxyReader(testReader) if !bar.GetBool(Bytes) { t.Errorf("Bytes must be true after call NewProxyReader") } for i := 0; i < 10; i++ { buf := make([]byte, 10) n, e := proxyReader.Read(buf) if e != nil { t.Errorf("Proxy reader return err: %v", e) } if n != len(buf) { t.Errorf("Proxy reader return unexpected N: %d (wand %d)", n, len(buf)) } for _, b := range buf { if b != 'f' { t.Errorf("Unexpected read value: %v (want %v)", b, 'f') } } if want := int64((i + 1) * len(buf)); bar.Current() != want { t.Errorf("Unexpected bar current value: %d (want %d)", bar.Current(), want) } } proxyReader.Close() if !testReader.closed { t.Errorf("Reader must be closed after call ProxyReader.Close") } proxyReader.Reader = nil proxyReader.Close() } func TestPBProxyWriter(t *testing.T) { bar := new(ProgressBar) if bar.GetBool(Bytes) { t.Errorf("By default bytes must be false") } testWriter := new(testReaderWriterCloser) proxyReader := bar.NewProxyWriter(testWriter) if !bar.GetBool(Bytes) { t.Errorf("Bytes must be true after call NewProxyReader") } for i := 0; i < 10; i++ { buf := make([]byte, 10) n, e := proxyReader.Write(buf) if e != nil { t.Errorf("Proxy reader return err: %v", e) } if n != len(buf) { t.Errorf("Proxy reader return unexpected N: %d (wand %d)", n, len(buf)) } if want := int64((i + 1) * len(buf)); bar.Current() != want { t.Errorf("Unexpected bar current value: %d (want %d)", bar.Current(), want) } } proxyReader.Close() if !testWriter.closed { t.Errorf("Reader must be closed after call ProxyReader.Close") } proxyReader.Writer = nil proxyReader.Close() } type testReaderWriterCloser struct { closed bool data []byte } func (tr *testReaderWriterCloser) Read(p []byte) (n int, err error) { for i := range p { p[i] = 'f' } return len(p), nil } func (tr *testReaderWriterCloser) Write(p []byte) (n int, err error) { tr.data = append(tr.data, p...) return len(p), nil } func (tr *testReaderWriterCloser) Close() (err error) { tr.closed = true return } pb-3.1.5/v3/pb.go000066400000000000000000000321751455443304200134150ustar00rootroot00000000000000package pb import ( "bytes" "fmt" "io" "os" "strconv" "strings" "sync" "sync/atomic" "text/template" "time" "github.com/fatih/color" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/cheggaaa/pb/v3/termutil" ) // Version of ProgressBar library const Version = "3.0.8" type key int const ( // Bytes means we're working with byte sizes. Numbers will print as Kb, Mb, etc // bar.Set(pb.Bytes, true) Bytes key = 1 << iota // Use SI bytes prefix names (kB, MB, etc) instead of IEC prefix names (KiB, MiB, etc) SIBytesPrefix // Terminal means we're will print to terminal and can use ascii sequences // Also we're will try to use terminal width Terminal // Static means progress bar will not update automaticly Static // ReturnSymbol - by default in terminal mode it's '\r' ReturnSymbol // Color by default is true when output is tty, but you can set to false for disabling colors Color // Hide the progress bar when finished, rather than leaving it up. By default it's false. CleanOnFinish // Round elapsed time to this precision. Defaults to time.Second. TimeRound ) const ( defaultBarWidth = 100 defaultRefreshRate = time.Millisecond * 200 ) // New creates new ProgressBar object func New(total int) *ProgressBar { return New64(int64(total)) } // New64 creates new ProgressBar object using int64 as total func New64(total int64) *ProgressBar { pb := new(ProgressBar) return pb.SetTotal(total) } // StartNew starts new ProgressBar with Default template func StartNew(total int) *ProgressBar { return New(total).Start() } // Start64 starts new ProgressBar with Default template. Using int64 as total. func Start64(total int64) *ProgressBar { return New64(total).Start() } var ( terminalWidth = termutil.TerminalWidth isTerminal = isatty.IsTerminal isCygwinTerminal = isatty.IsCygwinTerminal ) // ProgressBar is the main object of bar type ProgressBar struct { current, total int64 width int maxWidth int mu sync.RWMutex rm sync.Mutex vars map[interface{}]interface{} elements map[string]Element output io.Writer coutput io.Writer nocoutput io.Writer startTime time.Time refreshRate time.Duration tmpl *template.Template state *State buf *bytes.Buffer ticker *time.Ticker finish chan struct{} finished bool configured bool err error } func (pb *ProgressBar) configure() { if pb.configured { return } pb.configured = true if pb.vars == nil { pb.vars = make(map[interface{}]interface{}) } if pb.output == nil { pb.output = os.Stderr } if pb.tmpl == nil { pb.tmpl, pb.err = getTemplate(string(Default)) if pb.err != nil { return } } if pb.vars[Terminal] == nil { if f, ok := pb.output.(*os.File); ok { if isTerminal(f.Fd()) || isCygwinTerminal(f.Fd()) { pb.vars[Terminal] = true } } } if pb.vars[ReturnSymbol] == nil { if tm, ok := pb.vars[Terminal].(bool); ok && tm { pb.vars[ReturnSymbol] = "\r" } } if pb.vars[Color] == nil { if tm, ok := pb.vars[Terminal].(bool); ok && tm { pb.vars[Color] = true } } if pb.refreshRate == 0 { pb.refreshRate = defaultRefreshRate } if pb.vars[CleanOnFinish] == nil { pb.vars[CleanOnFinish] = false } if f, ok := pb.output.(*os.File); ok { pb.coutput = colorable.NewColorable(f) } else { pb.coutput = pb.output } pb.nocoutput = colorable.NewNonColorable(pb.output) } // Start starts the bar func (pb *ProgressBar) Start() *ProgressBar { pb.mu.Lock() defer pb.mu.Unlock() if pb.finish != nil { return pb } pb.configure() pb.finished = false pb.state = nil pb.startTime = time.Now() if st, ok := pb.vars[Static].(bool); ok && st { return pb } pb.finish = make(chan struct{}) pb.ticker = time.NewTicker(pb.refreshRate) go pb.writer(pb.finish) return pb } func (pb *ProgressBar) writer(finish chan struct{}) { for { select { case <-pb.ticker.C: pb.write(false) case <-finish: pb.ticker.Stop() pb.write(true) finish <- struct{}{} return } } } // Write performs write to the output func (pb *ProgressBar) Write() *ProgressBar { pb.mu.RLock() finished := pb.finished pb.mu.RUnlock() pb.write(finished) return pb } func (pb *ProgressBar) write(finish bool) { result, width := pb.render() if pb.Err() != nil { return } if pb.GetBool(Terminal) { if r := (width - CellCount(result)); r > 0 { result += strings.Repeat(" ", r) } } if ret, ok := pb.Get(ReturnSymbol).(string); ok { result = ret + result if finish && ret == "\r" { if pb.GetBool(CleanOnFinish) { // "Wipe out" progress bar by overwriting one line with blanks result = "\r" + color.New(color.Reset).Sprintf(strings.Repeat(" ", width)) + "\r" } else { result += "\n" } } } if pb.GetBool(Color) { pb.coutput.Write([]byte(result)) } else { pb.nocoutput.Write([]byte(result)) } } // Total return current total bar value func (pb *ProgressBar) Total() int64 { return atomic.LoadInt64(&pb.total) } // SetTotal sets the total bar value func (pb *ProgressBar) SetTotal(value int64) *ProgressBar { atomic.StoreInt64(&pb.total, value) return pb } // AddTotal adds to the total bar value func (pb *ProgressBar) AddTotal(value int64) *ProgressBar { atomic.AddInt64(&pb.total, value) return pb } // SetCurrent sets the current bar value func (pb *ProgressBar) SetCurrent(value int64) *ProgressBar { atomic.StoreInt64(&pb.current, value) return pb } // Current return current bar value func (pb *ProgressBar) Current() int64 { return atomic.LoadInt64(&pb.current) } // Add adding given int64 value to bar value func (pb *ProgressBar) Add64(value int64) *ProgressBar { atomic.AddInt64(&pb.current, value) return pb } // Add adding given int value to bar value func (pb *ProgressBar) Add(value int) *ProgressBar { return pb.Add64(int64(value)) } // Increment atomically increments the progress func (pb *ProgressBar) Increment() *ProgressBar { return pb.Add64(1) } // Set sets any value by any key func (pb *ProgressBar) Set(key, value interface{}) *ProgressBar { pb.mu.Lock() defer pb.mu.Unlock() if pb.vars == nil { pb.vars = make(map[interface{}]interface{}) } pb.vars[key] = value return pb } // Get return value by key func (pb *ProgressBar) Get(key interface{}) interface{} { pb.mu.RLock() defer pb.mu.RUnlock() if pb.vars == nil { return nil } return pb.vars[key] } // GetBool return value by key and try to convert there to boolean // If value doesn't set or not boolean - return false func (pb *ProgressBar) GetBool(key interface{}) bool { if v, ok := pb.Get(key).(bool); ok { return v } return false } // SetWidth sets the bar width // When given value <= 0 would be using the terminal width (if possible) or default value. func (pb *ProgressBar) SetWidth(width int) *ProgressBar { pb.mu.Lock() pb.width = width pb.mu.Unlock() return pb } // SetMaxWidth sets the bar maximum width // When given value <= 0 would be using the terminal width (if possible) or default value. func (pb *ProgressBar) SetMaxWidth(maxWidth int) *ProgressBar { pb.mu.Lock() pb.maxWidth = maxWidth pb.mu.Unlock() return pb } // Width return the bar width // It's current terminal width or settled over 'SetWidth' value. func (pb *ProgressBar) Width() (width int) { defer func() { if r := recover(); r != nil { width = defaultBarWidth } }() pb.mu.RLock() width = pb.width maxWidth := pb.maxWidth pb.mu.RUnlock() if width <= 0 { var err error if width, err = terminalWidth(); err != nil { return defaultBarWidth } } if maxWidth > 0 && width > maxWidth { width = maxWidth } return } func (pb *ProgressBar) SetRefreshRate(dur time.Duration) *ProgressBar { pb.mu.Lock() if dur > 0 { pb.refreshRate = dur } pb.mu.Unlock() return pb } // SetWriter sets the io.Writer. Bar will write in this writer // By default this is os.Stderr func (pb *ProgressBar) SetWriter(w io.Writer) *ProgressBar { pb.mu.Lock() pb.output = w pb.configured = false pb.configure() pb.mu.Unlock() return pb } // StartTime return the time when bar started func (pb *ProgressBar) StartTime() time.Time { pb.mu.RLock() defer pb.mu.RUnlock() return pb.startTime } // Format convert int64 to string according to the current settings func (pb *ProgressBar) Format(v int64) string { if pb.GetBool(Bytes) { return formatBytes(v, pb.GetBool(SIBytesPrefix)) } return strconv.FormatInt(v, 10) } // Finish stops the bar func (pb *ProgressBar) Finish() *ProgressBar { pb.mu.Lock() if pb.finished { pb.mu.Unlock() return pb } finishChan := pb.finish pb.finished = true pb.mu.Unlock() if finishChan != nil { finishChan <- struct{}{} <-finishChan pb.mu.Lock() pb.finish = nil pb.mu.Unlock() } return pb } // IsStarted indicates progress bar state func (pb *ProgressBar) IsStarted() bool { pb.mu.RLock() defer pb.mu.RUnlock() return pb.finish != nil } // IsFinished indicates progress bar is finished func (pb *ProgressBar) IsFinished() bool { pb.mu.RLock() defer pb.mu.RUnlock() return pb.finished } // SetTemplateString sets ProgressBar tempate string and parse it func (pb *ProgressBar) SetTemplateString(tmpl string) *ProgressBar { pb.mu.Lock() defer pb.mu.Unlock() pb.tmpl, pb.err = getTemplate(tmpl) return pb } // SetTemplateString sets ProgressBarTempate and parse it func (pb *ProgressBar) SetTemplate(tmpl ProgressBarTemplate) *ProgressBar { return pb.SetTemplateString(string(tmpl)) } // NewProxyReader creates a wrapper for given reader, but with progress handle // Takes io.Reader or io.ReadCloser // Also, it automatically switches progress bar to handle units as bytes func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader { pb.Set(Bytes, true) return &Reader{r, pb} } // NewProxyWriter creates a wrapper for given writer, but with progress handle // Takes io.Writer or io.WriteCloser // Also, it automatically switches progress bar to handle units as bytes func (pb *ProgressBar) NewProxyWriter(r io.Writer) *Writer { pb.Set(Bytes, true) return &Writer{r, pb} } func (pb *ProgressBar) render() (result string, width int) { defer func() { if r := recover(); r != nil { pb.SetErr(fmt.Errorf("render panic: %v", r)) } }() pb.rm.Lock() defer pb.rm.Unlock() pb.mu.Lock() pb.configure() if pb.state == nil { pb.state = &State{ProgressBar: pb} pb.buf = bytes.NewBuffer(nil) } if pb.startTime.IsZero() { pb.startTime = time.Now() } pb.state.id++ pb.state.finished = pb.finished pb.state.time = time.Now() pb.mu.Unlock() pb.state.width = pb.Width() width = pb.state.width pb.state.total = pb.Total() pb.state.current = pb.Current() pb.buf.Reset() if e := pb.tmpl.Execute(pb.buf, pb.state); e != nil { pb.SetErr(e) return "", 0 } result = pb.buf.String() aec := len(pb.state.recalc) if aec == 0 { // no adaptive elements return } staticWidth := CellCount(result) - (aec * adElPlaceholderLen) if pb.state.Width()-staticWidth <= 0 { result = strings.Replace(result, adElPlaceholder, "", -1) result = StripString(result, pb.state.Width()) } else { pb.state.adaptiveElWidth = (width - staticWidth) / aec for _, el := range pb.state.recalc { result = strings.Replace(result, adElPlaceholder, el.ProgressElement(pb.state), 1) } } pb.state.recalc = pb.state.recalc[:0] return } // SetErr sets error to the ProgressBar // Error will be available over Err() func (pb *ProgressBar) SetErr(err error) *ProgressBar { pb.mu.Lock() pb.err = err pb.mu.Unlock() return pb } // Err return possible error // When all ok - will be nil // May contain template.Execute errors func (pb *ProgressBar) Err() error { pb.mu.RLock() defer pb.mu.RUnlock() return pb.err } // String return currrent string representation of ProgressBar func (pb *ProgressBar) String() string { res, _ := pb.render() return res } // ProgressElement implements Element interface func (pb *ProgressBar) ProgressElement(s *State, args ...string) string { if s.IsAdaptiveWidth() { pb.SetWidth(s.AdaptiveElWidth()) } return pb.String() } // State represents the current state of bar // Need for bar elements type State struct { *ProgressBar id uint64 total, current int64 width, adaptiveElWidth int finished, adaptive bool time time.Time recalc []Element } // Id it's the current state identifier // - incremental // - starts with 1 // - resets after finish/start func (s *State) Id() uint64 { return s.id } // Total it's bar int64 total func (s *State) Total() int64 { return s.total } // Value it's current value func (s *State) Value() int64 { return s.current } // Width of bar func (s *State) Width() int { return s.width } // AdaptiveElWidth - adaptive elements must return string with given cell count (when AdaptiveElWidth > 0) func (s *State) AdaptiveElWidth() int { return s.adaptiveElWidth } // IsAdaptiveWidth returns true when element must be shown as adaptive func (s *State) IsAdaptiveWidth() bool { return s.adaptive } // IsFinished return true when bar is finished func (s *State) IsFinished() bool { return s.finished } // IsFirst return true only in first render func (s *State) IsFirst() bool { return s.id == 1 } // Time when state was created func (s *State) Time() time.Time { return s.time } pb-3.1.5/v3/pb_test.go000066400000000000000000000144261455443304200144530ustar00rootroot00000000000000package pb import ( "bytes" "errors" "fmt" "strings" "testing" "time" "github.com/fatih/color" ) func TestPBBasic(t *testing.T) { bar := new(ProgressBar) var a, e int64 if a, e = bar.Total(), 0; a != e { t.Errorf("Unexpected total: actual: %v; expected: %v", a, e) } if a, e = bar.Current(), 0; a != e { t.Errorf("Unexpected current: actual: %v; expected: %v", a, e) } bar.SetCurrent(10).SetTotal(20) if a, e = bar.Total(), 20; a != e { t.Errorf("Unexpected total: actual: %v; expected: %v", a, e) } if a, e = bar.Current(), 10; a != e { t.Errorf("Unexpected current: actual: %v; expected: %v", a, e) } bar.Add(5) if a, e = bar.Current(), 15; a != e { t.Errorf("Unexpected current: actual: %v; expected: %v", a, e) } bar.Increment() if a, e = bar.Current(), 16; a != e { t.Errorf("Unexpected current: actual: %v; expected: %v", a, e) } } func TestPBWidth(t *testing.T) { terminalWidth = func() (int, error) { return 50, nil } // terminal width bar := new(ProgressBar) if a, e := bar.Width(), 50; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } // terminal width error terminalWidth = func() (int, error) { return 0, errors.New("test error") } if a, e := bar.Width(), defaultBarWidth; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } // terminal width panic terminalWidth = func() (int, error) { panic("test") return 0, nil } if a, e := bar.Width(), defaultBarWidth; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } // set negative terminal width bar.SetWidth(-42) if a, e := bar.Width(), defaultBarWidth; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } // set terminal width bar.SetWidth(42) if a, e := bar.Width(), 42; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } } func TestPBMaxWidth(t *testing.T) { terminalWidth = func() (int, error) { return 50, nil } // terminal width bar := new(ProgressBar) if a, e := bar.Width(), 50; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } bar.SetMaxWidth(55) if a, e := bar.Width(), 50; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } bar.SetMaxWidth(38) if a, e := bar.Width(), 38; a != e { t.Errorf("Unexpected width: actual: %v; expected: %v", a, e) } } func TestAddTotal(t *testing.T) { bar := new(ProgressBar) bar.SetTotal(0) bar.AddTotal(50) got := bar.Total() if got != 50 { t.Errorf("bar.Total() = %v, want %v", got, 50) } bar.AddTotal(-10) got = bar.Total() if got != 40 { t.Errorf("bar.Total() = %v, want %v", got, 40) } } func TestPBTemplate(t *testing.T) { bar := new(ProgressBar) result := bar.SetTotal(100).SetCurrent(50).SetWidth(40).String() expected := "50 / 100 [------->________] 50.00% ? p/s" if result != expected { t.Errorf("Unexpected result: (actual/expected)\n%s\n%s", result, expected) } // check strip result = bar.SetWidth(8).String() expected = "50 / 100" if result != expected { t.Errorf("Unexpected result: (actual/expected)\n%s\n%s", result, expected) } // invalid template for _, invalidTemplate := range []string{ `{{invalid template`, `{{speed}}`, } { bar.SetTemplateString(invalidTemplate) result = bar.String() expected = "" if result != expected { t.Errorf("Unexpected result: (actual/expected)\n%s\n%s", result, expected) } if err := bar.Err(); err == nil { t.Errorf("Must be error") } } // simple template without adaptive elemnts bar.SetTemplateString(`{{counters . }}`) result = bar.String() expected = "50 / 100" if result != expected { t.Errorf("Unexpected result: (actual/expected)\n%s\n%s", result, expected) } } func TestPBStartFinish(t *testing.T) { bar := ProgressBarTemplate(`{{counters . }}`).New(0) for i := int64(0); i < 2; i++ { if bar.IsStarted() { t.Error("Must be false") } var buf = bytes.NewBuffer(nil) bar.SetTotal(100). SetCurrent(int64(i)). SetWidth(7). Set(Terminal, true). SetWriter(buf). SetRefreshRate(time.Millisecond * 20). Start() if !bar.IsStarted() { t.Error("Must be true") } time.Sleep(time.Millisecond * 100) bar.Finish() if buf.Len() == 0 { t.Error("no writes") } var resultsString = strings.TrimPrefix(buf.String(), "\r") if !strings.HasSuffix(resultsString, "\n") { t.Error("No end \\n symb") } else { resultsString = resultsString[:len(resultsString)-1] } var results = strings.Split(resultsString, "\r") if len(results) < 3 { t.Errorf("Unexpected writes count: %v", len(results)) } exp := fmt.Sprintf("%d / 100", i) for i, res := range results { if res != exp { t.Errorf("Unexpected result[%d]: '%v'", i, res) } } // test second finish call bar.Finish() } } func TestPBFlags(t *testing.T) { // Static color.NoColor = false buf := bytes.NewBuffer(nil) bar := ProgressBarTemplate(`{{counters . | red}}`).New(100) bar.Set(Static, true).SetCurrent(50).SetWidth(10).SetWriter(buf).Start() if bar.IsStarted() { t.Error("Must be false") } bar.Write() result := buf.String() expected := "50 / 100" if result != expected { t.Errorf("Unexpected result: (actual/expected)\n'%s'\n'%s'", result, expected) } if !bar.state.IsFirst() { t.Error("must be true") } // Color bar.Set(Color, true) buf.Reset() bar.Write() result = buf.String() expected = color.RedString("50 / 100") if result != expected { t.Errorf("Unexpected result: (actual/expected)\n'%s'\n'%s'", result, expected) } if bar.state.IsFirst() { t.Error("must be false") } // Terminal bar.Set(Terminal, true).SetWriter(buf) buf.Reset() bar.Write() result = buf.String() expected = "\r" + color.RedString("50 / 100") + " " if result != expected { t.Errorf("Unexpected result: (actual/expected)\n'%s'\n'%s'", result, expected) } } func BenchmarkRender(b *testing.B) { var formats = []string{ string(Simple), string(Default), string(Full), `{{string . "prefix" | red}}{{counters . | green}} {{bar . | yellow}} {{percent . | cyan}} {{speed . | cyan}}{{string . "suffix" | cyan}}`, } var names = []string{ "Simple", "Default", "Full", "Color", } for i, tmpl := range formats { bar := new(ProgressBar) bar.SetTemplateString(tmpl).SetWidth(100) b.Run(names[i], func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { bar.String() } }) } } pb-3.1.5/v3/pool.go000066400000000000000000000035001455443304200137530ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows plan9 aix package pb import ( "io" "sync" "time" "github.com/cheggaaa/pb/v3/termutil" ) // Create and start new pool with given bars // You need call pool.Stop() after work func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) { pool = new(Pool) if err = pool.Start(); err != nil { return } pool.Add(pbs...) return } // NewPool initialises a pool with progress bars, but // doesn't start it. You need to call Start manually func NewPool(pbs ...*ProgressBar) (pool *Pool) { pool = new(Pool) pool.Add(pbs...) return } type Pool struct { Output io.Writer RefreshRate time.Duration bars []*ProgressBar lastBarsCount int shutdownCh chan struct{} workerCh chan struct{} m sync.Mutex finishOnce sync.Once } // Add progress bars. func (p *Pool) Add(pbs ...*ProgressBar) { p.m.Lock() defer p.m.Unlock() for _, bar := range pbs { bar.Set(Static, true) bar.Start() p.bars = append(p.bars, bar) } } func (p *Pool) Start() (err error) { p.RefreshRate = defaultRefreshRate p.shutdownCh, err = termutil.RawModeOn() if err != nil { return } p.workerCh = make(chan struct{}) go p.writer() return } func (p *Pool) writer() { var first = true defer func() { if first == false { p.print(false) } else { p.print(true) p.print(false) } close(p.workerCh) }() for { select { case <-time.After(p.RefreshRate): if p.print(first) { p.print(false) return } first = false case <-p.shutdownCh: return } } } // Restore terminal state and close pool func (p *Pool) Stop() error { p.finishOnce.Do(func() { if p.shutdownCh != nil { close(p.shutdownCh) } }) // Wait for the worker to complete select { case <-p.workerCh: } return termutil.RawModeOff() } pb-3.1.5/v3/pool_win.go000066400000000000000000000016661455443304200146430ustar00rootroot00000000000000//go:build windows // +build windows package pb import ( "fmt" "log" "strings" "github.com/cheggaaa/pb/v3/termutil" ) func (p *Pool) print(first bool) bool { p.m.Lock() defer p.m.Unlock() var out string if !first { coords, err := termutil.GetCursorPos() if err != nil { log.Panic(err) } coords.Y -= int16(p.lastBarsCount) if coords.Y < 0 { coords.Y = 0 } coords.X = 0 err = termutil.SetCursorPos(coords) if err != nil { log.Panic(err) } } cols, err := termutil.TerminalWidth() if err != nil { cols = defaultBarWidth } isFinished := true for _, bar := range p.bars { if !bar.IsFinished() { isFinished = false } result := bar.String() if r := cols - CellCount(result); r > 0 { result += strings.Repeat(" ", r) } out += fmt.Sprintf("\r%s\n", result) } if p.Output != nil { fmt.Fprint(p.Output, out) } else { fmt.Print(out) } p.lastBarsCount = len(p.bars) return isFinished } pb-3.1.5/v3/pool_x.go000066400000000000000000000020411455443304200143010ustar00rootroot00000000000000//go:build linux || darwin || freebsd || netbsd || openbsd || solaris || dragonfly || plan9 || aix // +build linux darwin freebsd netbsd openbsd solaris dragonfly plan9 aix package pb import ( "fmt" "os" "strings" "github.com/cheggaaa/pb/v3/termutil" ) func (p *Pool) print(first bool) bool { p.m.Lock() defer p.m.Unlock() var out string if !first { out = fmt.Sprintf("\033[%dA", p.lastBarsCount) } isFinished := true bars := p.bars rows, cols, err := termutil.TerminalSize() if err != nil { cols = defaultBarWidth } if rows > 0 && len(bars) > rows { // we need to hide bars that overflow terminal height bars = bars[len(bars)-rows:] } for _, bar := range bars { if !bar.IsFinished() { isFinished = false } bar.SetWidth(cols) result := bar.String() if r := cols - CellCount(result); r > 0 { result += strings.Repeat(" ", r) } out += fmt.Sprintf("\r%s\n", result) } if p.Output != nil { fmt.Fprint(p.Output, out) } else { fmt.Fprint(os.Stderr, out) } p.lastBarsCount = len(bars) return isFinished } pb-3.1.5/v3/preset.go000066400000000000000000000015731455443304200143140ustar00rootroot00000000000000package pb var ( // Full - preset with all default available elements // Example: 'Prefix 20/100 [-->______] 20% 1 p/s ETA 1m Suffix' Full ProgressBarTemplate = `{{with string . "prefix"}}{{.}} {{end}}{{counters . }} {{bar . }} {{percent . }} {{speed . }} {{rtime . "ETA %s"}}{{with string . "suffix"}} {{.}}{{end}}` // Default - preset like Full but without elapsed time // Example: 'Prefix 20/100 [-->______] 20% 1 p/s Suffix' Default ProgressBarTemplate = `{{with string . "prefix"}}{{.}} {{end}}{{counters . }} {{bar . }} {{percent . }} {{speed . }}{{with string . "suffix"}} {{.}}{{end}}` // Simple - preset without speed and any timers. Only counters, bar and percents // Example: 'Prefix 20/100 [-->______] 20% Suffix' Simple ProgressBarTemplate = `{{with string . "prefix"}}{{.}} {{end}}{{counters . }} {{bar . }} {{percent . }}{{with string . "suffix"}} {{.}}{{end}}` ) pb-3.1.5/v3/preset_test.go000066400000000000000000000010421455443304200153420ustar00rootroot00000000000000package pb import ( "strings" "testing" ) func TestPreset(t *testing.T) { prefix := "Prefix" suffix := "Suffix" for _, preset := range []ProgressBarTemplate{Full, Default, Simple} { bar := preset.New(100). SetCurrent(20). Set("prefix", prefix). Set("suffix", suffix). SetWidth(50) // initialize the internal state _, _ = bar.render() s := bar.String() if !strings.HasPrefix(s, prefix+" ") { t.Error("prefix not found:", s) } if !strings.HasSuffix(s, " "+suffix) { t.Error("suffix not found:", s) } } } pb-3.1.5/v3/speed.go000066400000000000000000000040301455443304200141010ustar00rootroot00000000000000package pb import ( "fmt" "math" "time" "github.com/VividCortex/ewma" ) var speedAddLimit = time.Second / 2 type speed struct { ewma ewma.MovingAverage lastStateId uint64 prevValue, startValue int64 prevTime, startTime time.Time } func (s *speed) value(state *State) float64 { if s.ewma == nil { s.ewma = ewma.NewMovingAverage() } if state.IsFirst() || state.Id() < s.lastStateId { s.reset(state) return 0 } if state.Id() == s.lastStateId { return s.ewma.Value() } if state.IsFinished() { return s.absValue(state) } dur := state.Time().Sub(s.prevTime) if dur < speedAddLimit { return s.ewma.Value() } diff := math.Abs(float64(state.Value() - s.prevValue)) lastSpeed := diff / dur.Seconds() s.prevTime = state.Time() s.prevValue = state.Value() s.lastStateId = state.Id() s.ewma.Add(lastSpeed) return s.ewma.Value() } func (s *speed) reset(state *State) { s.lastStateId = state.Id() s.startTime = state.Time() s.prevTime = state.Time() s.startValue = state.Value() s.prevValue = state.Value() s.ewma = ewma.NewMovingAverage() } func (s *speed) absValue(state *State) float64 { if dur := state.Time().Sub(s.startTime); dur > 0 { return float64(state.Value()) / dur.Seconds() } return 0 } func getSpeedObj(state *State) (s *speed) { if sObj, ok := state.Get(speedObj).(*speed); ok { return sObj } s = new(speed) state.Set(speedObj, s) return } // ElementSpeed calculates current speed by EWMA // Optionally can take one or two string arguments. // First string will be used as value for format speed, default is "%s p/s". // Second string will be used when speed not available, default is "? p/s" // In template use as follows: {{speed .}} or {{speed . "%s per second"}} or {{speed . "%s ps" "..."} var ElementSpeed ElementFunc = func(state *State, args ...string) string { sp := getSpeedObj(state).value(state) if sp == 0 { return argsHelper(args).getNotEmptyOr(1, "? p/s") } return fmt.Sprintf(argsHelper(args).getNotEmptyOr(0, "%s p/s"), state.Format(int64(round(sp)))) } pb-3.1.5/v3/template.go000066400000000000000000000043031455443304200146170ustar00rootroot00000000000000package pb import ( "math/rand" "sync" "text/template" "github.com/fatih/color" ) // ProgressBarTemplate that template string type ProgressBarTemplate string // New creates new bar from template func (pbt ProgressBarTemplate) New(total int) *ProgressBar { return New(total).SetTemplate(pbt) } // Start64 create and start new bar with given int64 total value func (pbt ProgressBarTemplate) Start64(total int64) *ProgressBar { return New64(total).SetTemplate(pbt).Start() } // Start create and start new bar with given int total value func (pbt ProgressBarTemplate) Start(total int) *ProgressBar { return pbt.Start64(int64(total)) } var templateCacheMu sync.Mutex var templateCache = make(map[string]*template.Template) var defaultTemplateFuncs = template.FuncMap{ // colors "black": color.New(color.FgBlack).SprintFunc(), "red": color.New(color.FgRed).SprintFunc(), "green": color.New(color.FgGreen).SprintFunc(), "yellow": color.New(color.FgYellow).SprintFunc(), "blue": color.New(color.FgBlue).SprintFunc(), "magenta": color.New(color.FgMagenta).SprintFunc(), "cyan": color.New(color.FgCyan).SprintFunc(), "white": color.New(color.FgWhite).SprintFunc(), "resetcolor": color.New(color.Reset).SprintFunc(), "rndcolor": rndcolor, "rnd": rnd, } func getTemplate(tmpl string) (t *template.Template, err error) { templateCacheMu.Lock() defer templateCacheMu.Unlock() t = templateCache[tmpl] if t != nil { // found in cache return } t = template.New("") fillTemplateFuncs(t) _, err = t.Parse(tmpl) if err != nil { t = nil return } templateCache[tmpl] = t return } func fillTemplateFuncs(t *template.Template) { t.Funcs(defaultTemplateFuncs) emf := make(template.FuncMap) elementsM.Lock() for k, v := range elements { element := v emf[k] = func(state *State, args ...string) string { return element.ProgressElement(state, args...) } } elementsM.Unlock() t.Funcs(emf) return } func rndcolor(s string) string { c := rand.Intn(int(color.FgWhite-color.FgBlack)) + int(color.FgBlack) return color.New(color.Attribute(c)).Sprint(s) } func rnd(args ...string) string { if len(args) == 0 { return "" } return args[rand.Intn(len(args))] } pb-3.1.5/v3/template_test.go000066400000000000000000000022531455443304200156600ustar00rootroot00000000000000package pb import ( "bytes" "testing" ) func TestProgressBarTemplate(t *testing.T) { // test New bar := ProgressBarTemplate(`{{counters . }}`).New(0) result := bar.String() expected := "0" if result != expected { t.Errorf("Unexpected result: (actual/expected)\n%s\n%s", result, expected) } if bar.IsStarted() { t.Error("Must be false") } // test Start bar = ProgressBarTemplate(`{{counters . }}`).Start(42).SetWriter(bytes.NewBuffer(nil)) result = bar.String() expected = "0 / 42" if result != expected { t.Errorf("Unexpected result: (actual/expected)\n%s\n%s", result, expected) } if !bar.IsStarted() { t.Error("Must be true") } } func TestTemplateFuncs(t *testing.T) { var results = make(map[string]int) for i := 0; i < 100; i++ { r := rndcolor("s") results[r] = results[r] + 1 } if len(results) < 6 { t.Errorf("Unexpected rndcolor results count: %v", len(results)) } results = make(map[string]int) for i := 0; i < 100; i++ { r := rnd("1", "2", "3") results[r] = results[r] + 1 } if len(results) != 3 { t.Errorf("Unexpected rnd results count: %v", len(results)) } if r := rnd(); r != "" { t.Errorf("Unexpected rnd result: '%v'", r) } } pb-3.1.5/v3/termutil/000077500000000000000000000000001455443304200143225ustar00rootroot00000000000000pb-3.1.5/v3/termutil/term.go000066400000000000000000000024531455443304200156240ustar00rootroot00000000000000package termutil import ( "errors" "os" "os/signal" "sync" ) var echoLocked bool var echoLockMutex sync.Mutex var errLocked = errors.New("terminal locked") var autoTerminate = true // AutoTerminate enables or disables automatic terminate signal catching. // It's needed to restore the terminal state after the pool was used. // By default, it's enabled. func AutoTerminate(enable bool) { echoLockMutex.Lock() defer echoLockMutex.Unlock() autoTerminate = enable } // RawModeOn switches terminal to raw mode func RawModeOn() (quit chan struct{}, err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if echoLocked { err = errLocked return } if err = lockEcho(); err != nil { return } echoLocked = true quit = make(chan struct{}, 1) go catchTerminate(quit) return } // RawModeOff restore previous terminal state func RawModeOff() (err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if !echoLocked { return } if err = unlockEcho(); err != nil { return } echoLocked = false return } // listen exit signals and restore terminal state func catchTerminate(quit chan struct{}) { sig := make(chan os.Signal, 1) if autoTerminate { signal.Notify(sig, unlockSignals...) defer signal.Stop(sig) } select { case <-quit: RawModeOff() case <-sig: RawModeOff() } } pb-3.1.5/v3/termutil/term_appengine.go000066400000000000000000000004551455443304200176520ustar00rootroot00000000000000//go:build appengine // +build appengine package termutil import "errors" // terminalWidth returns width of the terminal, which is not supported // and should always failed on appengine classic which is a sandboxed PaaS. func TerminalWidth() (int, error) { return 0, errors.New("Not supported") } pb-3.1.5/v3/termutil/term_bsd.go000066400000000000000000000004201455443304200164440ustar00rootroot00000000000000//go:build (darwin || freebsd || netbsd || openbsd || dragonfly) && !appengine // +build darwin freebsd netbsd openbsd dragonfly // +build !appengine package termutil import "syscall" const ioctlReadTermios = syscall.TIOCGETA const ioctlWriteTermios = syscall.TIOCSETA pb-3.1.5/v3/termutil/term_linux.go000066400000000000000000000002631455443304200170400ustar00rootroot00000000000000//go:build linux && !appengine // +build linux,!appengine package termutil const ioctlReadTermios = 0x5401 // syscall.TCGETS const ioctlWriteTermios = 0x5402 // syscall.TCSETS pb-3.1.5/v3/termutil/term_nix.go000066400000000000000000000003551455443304200165010ustar00rootroot00000000000000//go:build (linux || darwin || freebsd || netbsd || openbsd || dragonfly) && !appengine // +build linux darwin freebsd netbsd openbsd dragonfly // +build !appengine package termutil import "syscall" const sysIoctl = syscall.SYS_IOCTL pb-3.1.5/v3/termutil/term_plan9.go000066400000000000000000000014441455443304200167260ustar00rootroot00000000000000package termutil import ( "errors" "os" "syscall" ) var ( consctl *os.File // Plan 9 doesn't have syscall.SIGQUIT unlockSignals = []os.Signal{ os.Interrupt, syscall.SIGTERM, syscall.SIGKILL, } ) // TerminalWidth returns width of the terminal. func TerminalWidth() (int, error) { return 0, errors.New("Not supported") } func lockEcho() error { if consctl != nil { return errors.New("consctl already open") } var err error consctl, err = os.OpenFile("/dev/consctl", os.O_WRONLY, 0) if err != nil { return err } _, err = consctl.WriteString("rawon") if err != nil { consctl.Close() consctl = nil return err } return nil } func unlockEcho() error { if consctl == nil { return nil } if err := consctl.Close(); err != nil { return err } consctl = nil return nil } pb-3.1.5/v3/termutil/term_solaris.go000066400000000000000000000003131455443304200173510ustar00rootroot00000000000000//go:build solaris && !appengine // +build solaris,!appengine package termutil const ioctlReadTermios = 0x5401 // syscall.TCGETS const ioctlWriteTermios = 0x5402 // syscall.TCSETS const sysIoctl = 54 pb-3.1.5/v3/termutil/term_win.go000066400000000000000000000106251455443304200165010ustar00rootroot00000000000000//go:build windows // +build windows package termutil import ( "fmt" "os" "os/exec" "strconv" "syscall" "unsafe" ) var ( tty = os.Stdin unlockSignals = []os.Signal{ os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL, } ) var ( kernel32 = syscall.NewLazyDLL("kernel32.dll") // GetConsoleScreenBufferInfo retrieves information about the // specified console screen buffer. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") // GetConsoleMode retrieves the current input mode of a console's // input buffer or the current output mode of a console screen buffer. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx getConsoleMode = kernel32.NewProc("GetConsoleMode") // SetConsoleMode sets the input mode of a console's input buffer // or the output mode of a console screen buffer. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx setConsoleMode = kernel32.NewProc("SetConsoleMode") // SetConsoleCursorPosition sets the cursor position in the // specified console screen buffer. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") mingw = isMingw() ) type ( // Defines the coordinates of the upper left and lower right corners // of a rectangle. // See // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx smallRect struct { Left, Top, Right, Bottom int16 } // Defines the coordinates of a character cell in a console screen // buffer. The origin of the coordinate system (0,0) is at the top, left cell // of the buffer. // See // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx coordinates struct { X, Y int16 } word int16 // Contains information about a console screen buffer. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx consoleScreenBufferInfo struct { dwSize coordinates dwCursorPosition coordinates wAttributes word srWindow smallRect dwMaximumWindowSize coordinates } ) // TerminalWidth returns width of the terminal. func TerminalWidth() (width int, err error) { if mingw { return termWidthTPut() } return termWidthCmd() } func termWidthCmd() (width int, err error) { var info consoleScreenBufferInfo _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) if e != 0 { return 0, error(e) } return int(info.dwSize.X) - 1, nil } func isMingw() bool { return os.Getenv("MINGW_PREFIX") != "" || os.Getenv("MSYSTEM") == "MINGW64" } func termWidthTPut() (width int, err error) { // TODO: maybe anybody knows a better way to get it on mintty... var res []byte cmd := exec.Command("tput", "cols") cmd.Stdin = os.Stdin if res, err = cmd.CombinedOutput(); err != nil { return 0, fmt.Errorf("%s: %v", string(res), err) } if len(res) > 1 { res = res[:len(res)-1] } return strconv.Atoi(string(res)) } func GetCursorPos() (pos coordinates, err error) { var info consoleScreenBufferInfo _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) if e != 0 { return info.dwCursorPosition, error(e) } return info.dwCursorPosition, nil } func SetCursorPos(pos coordinates) error { _, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0) if e != 0 { return error(e) } return nil } var oldState word func lockEcho() (err error) { if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 { err = fmt.Errorf("Can't get terminal settings: %v", e) return } newState := oldState const ENABLE_LINE_INPUT = 0x0002 newState = newState & (^ENABLE_LINE_INPUT) if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 { err = fmt.Errorf("Can't set terminal settings: %v", e) return } return } func unlockEcho() (err error) { if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 { err = fmt.Errorf("Can't set terminal settings") } return } pb-3.1.5/v3/termutil/term_x.go000066400000000000000000000035351455443304200161550ustar00rootroot00000000000000//go:build (linux || darwin || freebsd || netbsd || openbsd || solaris || dragonfly) && !appengine // +build linux darwin freebsd netbsd openbsd solaris dragonfly // +build !appengine package termutil import ( "fmt" "os" "syscall" "unsafe" ) var ( tty *os.File unlockSignals = []os.Signal{ os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL, } oldState syscall.Termios ) type window struct { Row uint16 Col uint16 Xpixel uint16 Ypixel uint16 } func init() { var err error tty, err = os.Open("/dev/tty") if err != nil { tty = os.Stdin } } // TerminalWidth returns width of the terminal. func TerminalWidth() (int, error) { _, c, err := TerminalSize() return c, err } // TerminalSize returns size of the terminal. func TerminalSize() (rows, cols int, err error) { w := new(window) res, _, err := syscall.Syscall(sysIoctl, tty.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(w)), ) if int(res) == -1 { return 0, 0, err } return int(w.Row), int(w.Col), nil } func lockEcho() error { fd := tty.Fd() if _, _, err := syscall.Syscall(sysIoctl, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&oldState))); err != 0 { return fmt.Errorf("error when puts the terminal connected to the given file descriptor: %w", err) } newState := oldState newState.Lflag &^= syscall.ECHO newState.Lflag |= syscall.ICANON | syscall.ISIG newState.Iflag |= syscall.ICRNL if _, _, e := syscall.Syscall(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&newState))); e != 0 { return fmt.Errorf("error update terminal settings: %w", e) } return nil } func unlockEcho() error { fd := tty.Fd() if _, _, err := syscall.Syscall(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState))); err != 0 { return fmt.Errorf("error restores the terminal connected to the given file descriptor: %w", err) } return nil } pb-3.1.5/v3/util.go000066400000000000000000000044461455443304200137710ustar00rootroot00000000000000package pb import ( "bytes" "fmt" "github.com/mattn/go-runewidth" "math" "regexp" //"unicode/utf8" ) const ( _KiB = 1024 _MiB = 1048576 _GiB = 1073741824 _TiB = 1099511627776 _kB = 1e3 _MB = 1e6 _GB = 1e9 _TB = 1e12 ) var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9;]+\x6d") func CellCount(s string) int { n := runewidth.StringWidth(s) for _, sm := range ctrlFinder.FindAllString(s, -1) { n -= runewidth.StringWidth(sm) } return n } func StripString(s string, w int) string { l := CellCount(s) if l <= w { return s } var buf = bytes.NewBuffer(make([]byte, 0, len(s))) StripStringToBuffer(s, w, buf) return buf.String() } func StripStringToBuffer(s string, w int, buf *bytes.Buffer) { var seqs = ctrlFinder.FindAllStringIndex(s, -1) var maxWidthReached bool mainloop: for i, r := range s { for _, seq := range seqs { if i >= seq[0] && i < seq[1] { buf.WriteRune(r) continue mainloop } } if rw := CellCount(string(r)); rw <= w && !maxWidthReached { w -= rw buf.WriteRune(r) } else { maxWidthReached = true } } for w > 0 { buf.WriteByte(' ') w-- } return } func round(val float64) (newVal float64) { roundOn := 0.5 places := 0 var round float64 pow := math.Pow(10, float64(places)) digit := pow * val _, div := math.Modf(digit) if div >= roundOn { round = math.Ceil(digit) } else { round = math.Floor(digit) } newVal = round / pow return } // Convert bytes to human readable string. Like a 2 MiB, 64.2 KiB, or 2 MB, 64.2 kB // if useSIPrefix is set to true func formatBytes(i int64, useSIPrefix bool) (result string) { if !useSIPrefix { switch { case i >= _TiB: result = fmt.Sprintf("%.02f TiB", float64(i)/_TiB) case i >= _GiB: result = fmt.Sprintf("%.02f GiB", float64(i)/_GiB) case i >= _MiB: result = fmt.Sprintf("%.02f MiB", float64(i)/_MiB) case i >= _KiB: result = fmt.Sprintf("%.02f KiB", float64(i)/_KiB) default: result = fmt.Sprintf("%d B", i) } } else { switch { case i >= _TB: result = fmt.Sprintf("%.02f TB", float64(i)/_TB) case i >= _GB: result = fmt.Sprintf("%.02f GB", float64(i)/_GB) case i >= _MB: result = fmt.Sprintf("%.02f MB", float64(i)/_MB) case i >= _kB: result = fmt.Sprintf("%.02f kB", float64(i)/_kB) default: result = fmt.Sprintf("%d B", i) } } return } pb-3.1.5/v3/util_test.go000066400000000000000000000035141455443304200150230ustar00rootroot00000000000000package pb import ( "github.com/fatih/color" "testing" ) var testColorString = color.RedString("red") + color.GreenString("hello") + "simple" + color.WhiteString("進捗") func TestUtilCellCount(t *testing.T) { if e, l := 18, CellCount(testColorString); l != e { t.Errorf("Invalid length %d, expected %d", l, e) } } func TestUtilStripString(t *testing.T) { if r, e := StripString("12345", 4), "1234"; r != e { t.Errorf("Invalid result '%s', expected '%s'", r, e) } if r, e := StripString("12345", 5), "12345"; r != e { t.Errorf("Invalid result '%s', expected '%s'", r, e) } if r, e := StripString("12345", 10), "12345"; r != e { t.Errorf("Invalid result '%s', expected '%s'", r, e) } s := color.RedString("1") + "23" e := color.RedString("1") + "2" if r := StripString(s, 2); r != e { t.Errorf("Invalid result '%s', expected '%s'", r, e) } return } func TestUtilRound(t *testing.T) { if v := round(4.4); v != 4 { t.Errorf("Unexpected result: %v", v) } if v := round(4.501); v != 5 { t.Errorf("Unexpected result: %v", v) } } func TestUtilFormatBytes(t *testing.T) { inputs := []struct { v int64 s bool e string }{ {v: 1000, s: false, e: "1000 B"}, {v: 1024, s: false, e: "1.00 KiB"}, {v: 3*_MiB + 140*_KiB, s: false, e: "3.14 MiB"}, {v: 2 * _GiB, s: false, e: "2.00 GiB"}, {v: 2048 * _GiB, s: false, e: "2.00 TiB"}, {v: 999, s: true, e: "999 B"}, {v: 1024, s: true, e: "1.02 kB"}, {v: 3*_MB + 140*_kB, s: true, e: "3.14 MB"}, {v: 2 * _GB, s: true, e: "2.00 GB"}, {v: 2048 * _GB, s: true, e: "2.05 TB"}, } for _, input := range inputs { actual := formatBytes(input.v, input.s) if actual != input.e { t.Errorf("Expected {%s} was {%s}", input.e, actual) } } } func BenchmarkUtilsCellCount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { CellCount(testColorString) } } pb-3.1.5/writer.go000066400000000000000000000006421455443304200137720ustar00rootroot00000000000000package pb import ( "io" ) // It's proxy Writer, implement io.Writer type Writer struct { io.Writer bar *ProgressBar } func (r *Writer) Write(p []byte) (n int, err error) { n, err = r.Writer.Write(p) r.bar.Add(n) return } // Close the reader when it implements io.Closer func (r *Writer) Close() (err error) { r.bar.Finish() if closer, ok := r.Writer.(io.Closer); ok { return closer.Close() } return }