pax_global_header00006660000000000000000000000064130041427050014506gustar00rootroot0000000000000052 comment=dd61faab99a777c652bb680e37715fe0cb549856 pb-1.0.6/000077500000000000000000000000001300414270500121135ustar00rootroot00000000000000pb-1.0.6/.travis.yml000066400000000000000000000000671300414270500142270ustar00rootroot00000000000000language: go go: - 1.4.2 sudo: false os: - linux - osx pb-1.0.6/LICENSE000066400000000000000000000027071300414270500131260ustar00rootroot00000000000000Copyright (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-1.0.6/README.md000066400000000000000000000060121300414270500133710ustar00rootroot00000000000000# Terminal progress bar for Go Simple progress bar for console programs. ## Installation ``` go get gopkg.in/cheggaaa/pb.v1 ``` ## Usage ```Go package main import ( "gopkg.in/cheggaaa/pb.v1" "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" "gopkg.in/cheggaaa/pb.v1" ) 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 141 / 1000 [===============>---------------------------------------] 14.10 % 44s Second 139 / 1000 [==============>---------------------------------------] 13.90 % 44s Third 152 / 1000 [================>--------------------------------------] 15.20 % 40s ``` pb-1.0.6/example_copy_test.go000066400000000000000000000030761300414270500161740ustar00rootroot00000000000000package pb_test import ( "fmt" "io" "net/http" "os" "strconv" "strings" "time" "gopkg.in/cheggaaa/pb.v1" ) 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-1.0.6/example_multiple_test.go000066400000000000000000000012661300414270500170540ustar00rootroot00000000000000package pb_test import ( "math/rand" "sync" "time" "gopkg.in/cheggaaa/pb.v1" ) 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-1.0.6/example_test.go000066400000000000000000000006711300414270500151400ustar00rootroot00000000000000package pb_test import ( "time" "gopkg.in/cheggaaa/pb.v1" ) 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-1.0.6/format.go000066400000000000000000000033501300414270500137330ustar00rootroot00000000000000package pb import ( "fmt" "strings" "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, Bb, Mb, ...) U_BYTES // U_DURATION units are formatted in a human readable way (3h14m15s) U_DURATION ) func Format(i int64) *formatter { return &formatter{n: i} } type formatter struct { n int64 unit Units width int perSec bool } func (f *formatter) Value(n int64) *formatter { f.n = n return f } 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_DURATION: d := time.Duration(f.n) if d > time.Hour*24 { out = fmt.Sprintf("%dd", d/24/time.Hour) d -= (d / time.Hour / 24) * (time.Hour * 24) } out = fmt.Sprintf("%s%v", out, d) default: out = fmt.Sprintf(fmt.Sprintf("%%%dd", f.width), f.n) } if f.perSec { out += "/s" } return } // Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B func formatBytes(i int64) (result string) { switch { case i > (1024 * 1024 * 1024 * 1024): result = fmt.Sprintf("%.02f TB", float64(i)/1024/1024/1024/1024) case i > (1024 * 1024 * 1024): result = fmt.Sprintf("%.02f GB", float64(i)/1024/1024/1024) case i > (1024 * 1024): result = fmt.Sprintf("%.02f MB", float64(i)/1024/1024) case i > 1024: result = fmt.Sprintf("%.02f KB", float64(i)/1024) default: result = fmt.Sprintf("%d B", i) } result = strings.Trim(result, " ") return } pb-1.0.6/format_test.go000066400000000000000000000024321300414270500147720ustar00rootroot00000000000000package pb_test import ( "fmt" "gopkg.in/cheggaaa/pb.v1" "strconv" "testing" "time" ) func Test_DefaultsToInteger(t *testing.T) { value := int64(1000) expected := strconv.Itoa(int(value)) actual := pb.Format(value).String() if actual != expected { t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual)) } } func Test_CanFormatAsInteger(t *testing.T) { value := int64(1000) expected := strconv.Itoa(int(value)) actual := pb.Format(value).To(pb.U_NO).String() if actual != expected { t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual)) } } func Test_CanFormatAsBytes(t *testing.T) { value := int64(1000) expected := "1000 B" actual := pb.Format(value).To(pb.U_BYTES).String() if actual != expected { t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual)) } } func Test_CanFormatDuration(t *testing.T) { value := 10 * time.Minute expected := "10m0s" actual := pb.Format(int64(value)).To(pb.U_DURATION).String() if actual != expected { t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual)) } } func Test_DefaultUnitsWidth(t *testing.T) { value := 10 expected := " 10" actual := pb.Format(int64(value)).Width(7).String() if actual != expected { t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual)) } } pb-1.0.6/pb.go000066400000000000000000000237401300414270500130510ustar00rootroot00000000000000// Simple console progress bars package pb import ( "fmt" "io" "math" "strings" "sync" "sync/atomic" "time" "unicode/utf8" ) // Current version const Version = "1.0.6" 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, ShowFinalTime: true, Units: U_NO, ManualUpdate: false, finish: make(chan struct{}), currentValue: -1, mu: new(sync.Mutex), } 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) Total int64 RefreshRate time.Duration ShowPercent, ShowCounters bool ShowSpeed, ShowTimeLeft, ShowBar bool ShowFinalTime 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 currentValue int64 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 = pb.current if 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.prefix = prefix return pb } // Set postfix string func (pb *ProgressBar) Postfix(postfix string) *ProgressBar { 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 len(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.current)) switch { case pb.Output != nil: fmt.Fprintln(pb.Output) case !pb.NotPrint: fmt.Println() } pb.isFinish = true }) } // 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} } func (pb *ProgressBar) write(current int64) { width := pb.GetWidth() var percentBox, countersBox, timeLeftBox, speedBox, barBox, end, out string // percents if pb.ShowPercent { var percent float64 if pb.Total > 0 { percent = float64(current) / (float64(pb.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 pb.Total > 0 { total := Format(pb.Total).To(pb.Units).Width(pb.UnitsWidth) countersBox = fmt.Sprintf(" %s / %s ", current, total) } else { countersBox = fmt.Sprintf(" %s / ? ", current) } } // time left fromStart := time.Now().Sub(pb.startTime) currentFromStart := current - pb.startValue select { case <-pb.finish: if pb.ShowFinalTime { var left time.Duration left = (fromStart / time.Second) * time.Second timeLeftBox = fmt.Sprintf(" %s", left.String()) } default: if pb.ShowTimeLeft && currentFromStart > 0 { perEntry := fromStart / time.Duration(currentFromStart) var left time.Duration if pb.Total > 0 { left = time.Duration(pb.Total-currentFromStart) * perEntry left = (left / time.Second) * time.Second } else { left = time.Duration(currentFromStart) * perEntry left = (left / time.Second) * time.Second } 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.Now().Sub(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 + timeLeftBox + speedBox + pb.prefix + pb.postfix) // bar if pb.ShowBar { size := width - barWidth if size > 0 { if pb.Total > 0 { curCount := int(math.Ceil((float64(current) / float64(pb.Total)) * float64(size))) emptCount := size - curCount barBox = pb.BarStart if emptCount < 0 { emptCount = 0 } if curCount > size { curCount = size } if emptCount <= 0 { barBox += strings.Repeat(pb.Current, curCount) } else if curCount > 0 { barBox += strings.Repeat(pb.Current, curCount-1) + pb.CurrentN } barBox += strings.Repeat(pb.Empty, emptCount) + pb.BarEnd } else { barBox = pb.BarStart pos := size - int(current)%int(size) 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 + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix if escapeAwareRuneCountInString(out) < width { end = strings.Repeat(" ", width-utf8.RuneCountInString(out)) } // and print! pb.mu.Lock() pb.lastPrint = out + end pb.mu.Unlock() switch { case pb.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) if pb.AlwaysUpdate || c != pb.currentValue { pb.write(c) pb.currentValue = c } if pb.AutoStat { if c == 0 { pb.startTime = time.Now() pb.startValue = 0 } else if c >= pb.Total && pb.isFinish != true { pb.Finish() } } } func (pb *ProgressBar) String() string { return pb.lastPrint } // Internal loop for refreshing the progressbar func (pb *ProgressBar) refresher() { for { select { case <-pb.finish: return case <-time.After(pb.RefreshRate): pb.Update() } } } type window struct { Row uint16 Col uint16 Xpixel uint16 Ypixel uint16 } pb-1.0.6/pb_appengine.go000066400000000000000000000004221300414270500150670ustar00rootroot00000000000000// +build appengine 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-1.0.6/pb_nix.go000066400000000000000000000002171300414270500137210ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd dragonfly // +build !appengine package pb import "syscall" const sysIoctl = syscall.SYS_IOCTL pb-1.0.6/pb_solaris.go000066400000000000000000000001101300414270500145670ustar00rootroot00000000000000// +build solaris // +build !appengine package pb const sysIoctl = 54 pb-1.0.6/pb_test.go000066400000000000000000000040011300414270500140750ustar00rootroot00000000000000package pb import ( "bytes" "strings" "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 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_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) } }pb-1.0.6/pb_win.go000066400000000000000000000101101300414270500137110ustar00rootroot00000000000000// +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() (quit chan int, 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 } 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-1.0.6/pb_x.go000066400000000000000000000043311300414270500133730ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd solaris dragonfly // +build !appengine package pb import ( "errors" "fmt" "os" "os/signal" "runtime" "sync" "syscall" "unsafe" ) const ( TIOCGWINSZ = 0x5413 TIOCGWINSZ_OSX = 1074295912 ) var tty *os.File var ErrPoolWasStarted = errors.New("Bar pool was started") var echoLocked bool var echoLockMutex sync.Mutex 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) { w := new(window) tio := syscall.TIOCGWINSZ if runtime.GOOS == "darwin" { tio = TIOCGWINSZ_OSX } res, _, err := syscall.Syscall(sysIoctl, tty.Fd(), uintptr(tio), uintptr(unsafe.Pointer(w)), ) if int(res) == -1 { return 0, err } return int(w.Col), nil } var oldState syscall.Termios func lockEcho() (quit chan int, err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if echoLocked { err = ErrPoolWasStarted return } echoLocked = true fd := tty.Fd() if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { err = fmt.Errorf("Can't get terminal settings: %v", e) return } newState := oldState newState.Lflag &^= syscall.ECHO newState.Lflag |= syscall.ICANON | syscall.ISIG newState.Iflag |= syscall.ICRNL if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 { err = fmt.Errorf("Can't set terminal settings: %v", e) return } quit = make(chan int, 1) go catchTerminate(quit) return } func unlockEcho() (err error) { echoLockMutex.Lock() defer echoLockMutex.Unlock() if !echoLocked { return } echoLocked = false fd := tty.Fd() if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { err = fmt.Errorf("Can't set terminal settings") } return } // listen exit signals and restore terminal state func catchTerminate(quit chan int) { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL) defer signal.Stop(sig) select { case <-quit: unlockEcho() case <-sig: unlockEcho() } } pb-1.0.6/pool.go000066400000000000000000000024471300414270500134220ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows package pb import ( "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 } type Pool struct { RefreshRate time.Duration bars []*ProgressBar quit chan int finishOnce sync.Once } // Add progress bars. func (p *Pool) Add(pbs ...*ProgressBar) { 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 quit, err := lockEcho() if err != nil { return } p.quit = make(chan int) go p.writer(quit) return } func (p *Pool) writer(finish chan int) { var first = true for { select { case <-time.After(p.RefreshRate): if p.print(first) { p.print(false) finish <- 1 return } first = false case <-p.quit: finish <- 1 return } } } // Restore terminal state and close pool func (p *Pool) Stop() error { // Wait until one final refresh has passed. time.Sleep(p.RefreshRate) p.finishOnce.Do(func() { close(p.quit) }) return unlockEcho() } pb-1.0.6/pool_win.go000066400000000000000000000010041300414270500142630ustar00rootroot00000000000000// +build windows package pb import ( "fmt" "log" ) func (p *Pool) print(first bool) bool { var out string if !first { coords, err := getCursorPos() if err != nil { log.Panic(err) } coords.Y -= int16(len(p.bars)) coords.X = 0 err = setCursorPos(coords) if err != nil { log.Panic(err) } } isFinished := true for _, bar := range p.bars { if !bar.isFinish { isFinished = false } bar.Update() out += fmt.Sprintf("\r%s\n", bar.String()) } fmt.Print(out) return isFinished } pb-1.0.6/pool_x.go000066400000000000000000000006251300414270500137450ustar00rootroot00000000000000// +build linux darwin freebsd netbsd openbsd solaris dragonfly package pb import "fmt" func (p *Pool) print(first bool) bool { var out string if !first { out = fmt.Sprintf("\033[%dA", len(p.bars)) } isFinished := true for _, bar := range p.bars { if !bar.isFinish { isFinished = false } bar.Update() out += fmt.Sprintf("\r%s\n", bar.String()) } fmt.Print(out) return isFinished } pb-1.0.6/reader.go000066400000000000000000000006201300414270500137020ustar00rootroot00000000000000package 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) { if closer, ok := r.Reader.(io.Closer); ok { return closer.Close() } return } pb-1.0.6/runecount.go000066400000000000000000000005121300414270500144620ustar00rootroot00000000000000package pb import ( "regexp" "unicode/utf8" ) // Finds the control character sequences (like colors) var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") func escapeAwareRuneCountInString(s string) int { n := utf8.RuneCountInString(s) for _, sm := range ctrlFinder.FindAllString(s, -1) { n -= len(sm) } return n } pb-1.0.6/runecount_test.go000066400000000000000000000005761300414270500155330ustar00rootroot00000000000000package 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) } } pb-1.0.6/termios_bsd.go000066400000000000000000000002731300414270500147560ustar00rootroot00000000000000// +build darwin freebsd netbsd openbsd dragonfly // +build !appengine package pb import "syscall" const ioctlReadTermios = syscall.TIOCGETA const ioctlWriteTermios = syscall.TIOCSETA pb-1.0.6/termios_nix.go000066400000000000000000000002401300414270500147760ustar00rootroot00000000000000// +build linux solaris // +build !appengine package pb const ioctlReadTermios = 0x5401 // syscall.TCGETS const ioctlWriteTermios = 0x5402 // syscall.TCSETS