@cryptotaxi247 / kubo / commits / 487ef33e6

core/commands: Added progress bar to 'cat'

squash! core/commands: Added progress bar to 'cat' Vendored progress bar lib

Matt Bell committed Jan 20, 2015 at 21:31 UTC 487ef33e677f7fa0ccb775a84660076d8c0ba3f3
15 files changed +798 -5
Godeps/Godeps.json
+4
@@ -68,6 +68,10 @@
68 "ImportPath": "github.com/cenkalti/backoff",
69 "Rev": "9831e1e25c874e0a0601b6dc43641071414eec7a"
70 },
71 + {
72 + "ImportPath": "github.com/cheggaaa/pb",
73 + "Rev": "e8c7cc515bfde3e267957a3b110080ceed51354e"
74 + },
75 {
76 "ImportPath": "github.com/coreos/go-semver/semver",
77 "Rev": "6fe83ccda8fb9b7549c9ab4ba47f47858bc950aa"
Godeps/_workspace/src/github.com/cheggaaa/pb/LICENSE new
+12
@@ -0,0 +1,12 @@
1 +Copyright (c) 2012, Sergey Cherepanov
2 +All rights reserved.
3 +
4 +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 +
6 +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 +
8 +* 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.
9 +
10 +* 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.
11 +
12 +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.
\ No newline at end of file
Godeps/_workspace/src/github.com/cheggaaa/pb/README.md new
+98
@@ -0,0 +1,98 @@
1 +## Terminal progress bar for Go
2 +
3 +Simple progress bar for console programms.
4 +
5 +
6 +### Installation
7 +```
8 +go get github.com/cheggaaa/pb
9 +```
10 +
11 +### Usage
12 +```Go
13 +package main
14 +
15 +import (
16 + "github.com/cheggaaa/pb"
17 + "time"
18 +)
19 +
20 +func main() {
21 + count := 100000
22 + bar := pb.StartNew(count)
23 + for i := 0; i < count; i++ {
24 + bar.Increment()
25 + time.Sleep(time.Millisecond)
26 + }
27 + bar.FinishPrint("The End!")
28 +}
29 +```
30 +Result will be like this:
31 +```
32 +> go run test.go
33 +37158 / 100000 [================>_______________________________] 37.16% 1m11s
34 +```
35 +
36 +
37 +More functions?
38 +```Go
39 +// create bar
40 +bar := pb.New(count)
41 +
42 +// refresh info every second (default 200ms)
43 +bar.SetRefreshRate(time.Second)
44 +
45 +// show percents (by default already true)
46 +bar.ShowPercent = true
47 +
48 +// show bar (by default already true)
49 +bar.ShowBar = true
50 +
51 +// no need counters
52 +bar.ShowCounters = false
53 +
54 +// show "time left"
55 +bar.ShowTimeLeft = true
56 +
57 +// show average speed
58 +bar.ShowSpeed = true
59 +
60 +// sets the width of the progress bar
61 +bar.SetWith(80)
62 +
63 +// sets the width of the progress bar, but if terminal size smaller will be ignored
64 +bar.SetMaxWith(80)
65 +
66 +// convert output to readable format (like KB, MB)
67 +bar.SetUnits(pb.U_BYTES)
68 +
69 +// and start
70 +bar.Start()
71 +```
72 +
73 +Want handle progress of io operations?
74 +```Go
75 +// create and start bar
76 +bar := pb.New(myDataLen).SetUnits(pb.U_BYTES)
77 +bar.Start()
78 +
79 +// my io.Reader
80 +r := myReader
81 +
82 +// my io.Writer
83 +w := myWriter
84 +
85 +// create multi writer
86 +writer := io.MultiWriter(w, bar)
87 +
88 +// and copy
89 +io.Copy(writer, r)
90 +
91 +// show example/copy/copy.go for advanced example
92 +
93 +```
94 +
95 +Not like the looks?
96 +```Go
97 +bar.Format("<.- >")
98 +```
Godeps/_workspace/src/github.com/cheggaaa/pb/example/copy/copy.go new
+81
@@ -0,0 +1,81 @@
1 +package main
2 +
3 +import (
4 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
5 + "os"
6 + "fmt"
7 + "io"
8 + "time"
9 + "strings"
10 + "net/http"
11 + "strconv"
12 +)
13 +
14 +func main() {
15 + // check args
16 + if len(os.Args) < 3 {
17 + printUsage()
18 + return
19 + }
20 + sourceName, destName := os.Args[1], os.Args[2]
21 +
22 + // check source
23 + var source io.Reader
24 + var sourceSize int64
25 + if strings.HasPrefix(sourceName, "http://") {
26 + // open as url
27 + resp, err := http.Get(sourceName)
28 + if err != nil {
29 + fmt.Printf("Can't get %s: %v\n", sourceName, err)
30 + return
31 + }
32 + defer resp.Body.Close()
33 + if resp.StatusCode != http.StatusOK {
34 + fmt.Printf("Server return non-200 status: %v\n", resp.Status)
35 + return
36 + }
37 + i, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
38 + sourceSize = int64(i)
39 + source = resp.Body
40 + } else {
41 + // open as file
42 + s, err := os.Open(sourceName)
43 + if err != nil {
44 + fmt.Printf("Can't open %s: %v\n", sourceName, err)
45 + return
46 + }
47 + defer s.Close()
48 + // get source size
49 + sourceStat, err := s.Stat()
50 + if err != nil {
51 + fmt.Printf("Can't stat %s: %v\n", sourceName, err)
52 + return
53 + }
54 + sourceSize = sourceStat.Size()
55 + source = s
56 + }
57 +
58 + // create dest
59 + dest, err := os.Create(destName)
60 + if err != nil {
61 + fmt.Printf("Can't create %s: %v\n", destName, err)
62 + return
63 + }
64 + defer dest.Close()
65 +
66 + // create bar
67 + bar := pb.New(int(sourceSize)).SetUnits(pb.U_BYTES).SetRefreshRate(time.Millisecond * 10)
68 + bar.ShowSpeed = true
69 + bar.Start()
70 +
71 + // create multi writer
72 + writer := io.MultiWriter(dest, bar)
73 +
74 + // and copy
75 + io.Copy(writer, source)
76 + bar.Finish()
77 +}
78 +
79 +func printUsage() {
80 + fmt.Println("copy [source file or url] [dest file]")
81 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/example/pb.go new
+30
@@ -0,0 +1,30 @@
1 +package main
2 +
3 +import (
4 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
5 + "time"
6 +)
7 +
8 +func main() {
9 + count := 5000
10 + bar := pb.New(count)
11 +
12 + // show percents (by default already true)
13 + bar.ShowPercent = true
14 +
15 + // show bar (by default already true)
16 + bar.ShowPercent = true
17 +
18 + // no need counters
19 + bar.ShowCounters = true
20 +
21 + bar.ShowTimeLeft = true
22 +
23 + // and start
24 + bar.Start()
25 + for i := 0; i < count; i++ {
26 + bar.Increment()
27 + time.Sleep(time.Millisecond)
28 + }
29 + bar.FinishPrint("The End!")
30 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/format.go new
+42
@@ -0,0 +1,42 @@
1 +package pb
2 +
3 +import (
4 + "fmt"
5 + "strconv"
6 + "strings"
7 +)
8 +
9 +const (
10 + // By default, without type handle
11 + U_NO = 0
12 + // Handle as b, Kb, Mb, etc
13 + U_BYTES = 1
14 +)
15 +
16 +// Format integer
17 +func Format(i int64, units int) string {
18 + switch units {
19 + case U_BYTES:
20 + return FormatBytes(i)
21 + }
22 + // by default just convert to string
23 + return strconv.Itoa(int(i))
24 +}
25 +
26 +// Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B
27 +func FormatBytes(i int64) (result string) {
28 + switch {
29 + case i > (1024 * 1024 * 1024 * 1024):
30 + result = fmt.Sprintf("%#.02f TB", float64(i)/1024/1024/1024/1024)
31 + case i > (1024 * 1024 * 1024):
32 + result = fmt.Sprintf("%#.02f GB", float64(i)/1024/1024/1024)
33 + case i > (1024 * 1024):
34 + result = fmt.Sprintf("%#.02f MB", float64(i)/1024/1024)
35 + case i > 1024:
36 + result = fmt.Sprintf("%#.02f KB", float64(i)/1024)
37 + default:
38 + result = fmt.Sprintf("%d B", i)
39 + }
40 + result = strings.Trim(result, " ")
41 + return
42 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/format_test.go new
+37
@@ -0,0 +1,37 @@
1 +package pb
2 +
3 +import (
4 + "fmt"
5 + "strconv"
6 + "testing"
7 +)
8 +
9 +func Test_DefaultsToInteger(t *testing.T) {
10 + value := int64(1000)
11 + expected := strconv.Itoa(int(value))
12 + actual := Format(value, -1)
13 +
14 + if actual != expected {
15 + t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual))
16 + }
17 +}
18 +
19 +func Test_CanFormatAsInteger(t *testing.T) {
20 + value := int64(1000)
21 + expected := strconv.Itoa(int(value))
22 + actual := Format(value, U_NO)
23 +
24 + if actual != expected {
25 + t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual))
26 + }
27 +}
28 +
29 +func Test_CanFormatAsBytes(t *testing.T) {
30 + value := int64(1000)
31 + expected := "1000 B"
32 + actual := Format(value, U_BYTES)
33 +
34 + if actual != expected {
35 + t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual))
36 + }
37 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/pb.go new
+341
@@ -0,0 +1,341 @@
1 +package pb
2 +
3 +import (
4 + "fmt"
5 + "io"
6 + "math"
7 + "strings"
8 + "sync/atomic"
9 + "time"
10 +)
11 +
12 +const (
13 + // Default refresh rate - 200ms
14 + DEFAULT_REFRESH_RATE = time.Millisecond * 200
15 + FORMAT = "[=>-]"
16 +)
17 +
18 +// DEPRECATED
19 +// variables for backward compatibility, from now do not work
20 +// use pb.Format and pb.SetRefreshRate
21 +var (
22 + DefaultRefreshRate = DEFAULT_REFRESH_RATE
23 + BarStart, BarEnd, Empty, Current, CurrentN string
24 +)
25 +
26 +// Create new progress bar object
27 +func New(total int) (pb *ProgressBar) {
28 + return New64(int64(total))
29 +}
30 +
31 +// Create new progress bar object uding int64 as total
32 +func New64(total int64) (pb *ProgressBar) {
33 + pb = &ProgressBar{
34 + Total: total,
35 + RefreshRate: DEFAULT_REFRESH_RATE,
36 + ShowPercent: true,
37 + ShowCounters: true,
38 + ShowBar: true,
39 + ShowTimeLeft: true,
40 + ShowFinalTime: true,
41 + ManualUpdate: false,
42 + currentValue: -1,
43 + }
44 + pb.Format(FORMAT)
45 + return
46 +}
47 +
48 +// Create new object and start
49 +func StartNew(total int) (pb *ProgressBar) {
50 + pb = New(total)
51 + pb.Start()
52 + return
53 +}
54 +
55 +// Callback for custom output
56 +// For example:
57 +// bar.Callback = func(s string) {
58 +// mySuperPrint(s)
59 +// }
60 +//
61 +type Callback func(out string)
62 +
63 +type ProgressBar struct {
64 + current int64 // current must be first member of struct (https://code.google.com/p/go/issues/detail?id=5278)
65 +
66 + Total int64
67 + RefreshRate time.Duration
68 + ShowPercent, ShowCounters bool
69 + ShowSpeed, ShowTimeLeft, ShowBar bool
70 + ShowFinalTime bool
71 + Output io.Writer
72 + Callback Callback
73 + NotPrint bool
74 + Units int
75 + Width int
76 + ForceWidth bool
77 + ManualUpdate bool
78 +
79 + isFinish bool
80 + startTime time.Time
81 + currentValue int64
82 +
83 + prefix, postfix string
84 +
85 + BarStart string
86 + BarEnd string
87 + Empty string
88 + Current string
89 + CurrentN string
90 +}
91 +
92 +// Start print
93 +func (pb *ProgressBar) Start() {
94 + pb.startTime = time.Now()
95 + if pb.Total == 0 {
96 + pb.ShowBar = false
97 + pb.ShowTimeLeft = false
98 + pb.ShowPercent = false
99 + }
100 + if !pb.ManualUpdate {
101 + go pb.writer()
102 + }
103 +}
104 +
105 +// Increment current value
106 +func (pb *ProgressBar) Increment() int {
107 + return pb.Add(1)
108 +}
109 +
110 +// Set current value
111 +func (pb *ProgressBar) Set(current int) {
112 + atomic.StoreInt64(&pb.current, int64(current))
113 +}
114 +
115 +// Add to current value
116 +func (pb *ProgressBar) Add(add int) int {
117 + return int(pb.Add64(int64(add)))
118 +}
119 +
120 +func (pb *ProgressBar) Add64(add int64) int64 {
121 + return atomic.AddInt64(&pb.current, add)
122 +}
123 +
124 +// Set prefix string
125 +func (pb *ProgressBar) Prefix(prefix string) (bar *ProgressBar) {
126 + pb.prefix = prefix
127 + return pb
128 +}
129 +
130 +// Set postfix string
131 +func (pb *ProgressBar) Postfix(postfix string) (bar *ProgressBar) {
132 + pb.postfix = postfix
133 + return pb
134 +}
135 +
136 +// Set custom format for bar
137 +// Example: bar.Format("[=>_]")
138 +func (pb *ProgressBar) Format(format string) (bar *ProgressBar) {
139 + bar = pb
140 + formatEntries := strings.Split(format, "")
141 + if len(formatEntries) != 5 {
142 + return
143 + }
144 + pb.BarStart = formatEntries[0]
145 + pb.BarEnd = formatEntries[4]
146 + pb.Empty = formatEntries[3]
147 + pb.Current = formatEntries[1]
148 + pb.CurrentN = formatEntries[2]
149 + return
150 +}
151 +
152 +// Set bar refresh rate
153 +func (pb *ProgressBar) SetRefreshRate(rate time.Duration) (bar *ProgressBar) {
154 + bar = pb
155 + pb.RefreshRate = rate
156 + return
157 +}
158 +
159 +// Set units
160 +// bar.SetUnits(U_NO) - by default
161 +// bar.SetUnits(U_BYTES) - for Mb, Kb, etc
162 +func (pb *ProgressBar) SetUnits(units int) (bar *ProgressBar) {
163 + bar = pb
164 + switch units {
165 + case U_NO, U_BYTES:
166 + pb.Units = units
167 + }
168 + return
169 +}
170 +
171 +// Set max width, if width is bigger than terminal width, will be ignored
172 +func (pb *ProgressBar) SetMaxWidth(width int) (bar *ProgressBar) {
173 + bar = pb
174 + pb.Width = width
175 + pb.ForceWidth = false
176 + return
177 +}
178 +
179 +// Set bar width
180 +func (pb *ProgressBar) SetWidth(width int) (bar *ProgressBar) {
181 + bar = pb
182 + pb.Width = width
183 + pb.ForceWidth = true
184 + return
185 +}
186 +
187 +// End print
188 +func (pb *ProgressBar) Finish() {
189 + pb.isFinish = true
190 + pb.write(atomic.LoadInt64(&pb.current))
191 + if !pb.NotPrint {
192 + fmt.Println()
193 + }
194 +}
195 +
196 +// End print and write string 'str'
197 +func (pb *ProgressBar) FinishPrint(str string) {
198 + pb.Finish()
199 + fmt.Println(str)
200 +}
201 +
202 +// implement io.Writer
203 +func (pb *ProgressBar) Write(p []byte) (n int, err error) {
204 + n = len(p)
205 + pb.Add(n)
206 + return
207 +}
208 +
209 +// implement io.Reader
210 +func (pb *ProgressBar) Read(p []byte) (n int, err error) {
211 + n = len(p)
212 + pb.Add(n)
213 + return
214 +}
215 +
216 +// Create new proxy reader over bar
217 +func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader {
218 + return &Reader{r, pb}
219 +}
220 +
221 +func (pb *ProgressBar) write(current int64) {
222 + width := pb.getWidth()
223 +
224 + var percentBox, countersBox, timeLeftBox, speedBox, barBox, end, out string
225 +
226 + // percents
227 + if pb.ShowPercent {
228 + percent := float64(current) / (float64(pb.Total) / float64(100))
229 + percentBox = fmt.Sprintf(" %#.02f %% ", percent)
230 + }
231 +
232 + // counters
233 + if pb.ShowCounters {
234 + if pb.Total > 0 {
235 + countersBox = fmt.Sprintf("%s / %s ", Format(current, pb.Units), Format(pb.Total, pb.Units))
236 + } else {
237 + countersBox = Format(current, pb.Units) + " "
238 + }
239 + }
240 +
241 + // time left
242 + fromStart := time.Now().Sub(pb.startTime)
243 + if pb.isFinish {
244 + if pb.ShowFinalTime {
245 + left := (fromStart / time.Second) * time.Second
246 + timeLeftBox = left.String()
247 + }
248 + } else if pb.ShowTimeLeft && current > 0 {
249 + perEntry := fromStart / time.Duration(current)
250 + left := time.Duration(pb.Total-current) * perEntry
251 + left = (left / time.Second) * time.Second
252 + timeLeftBox = left.String()
253 + }
254 +
255 + // speed
256 + if pb.ShowSpeed && current > 0 {
257 + fromStart := time.Now().Sub(pb.startTime)
258 + speed := float64(current) / (float64(fromStart) / float64(time.Second))
259 + speedBox = Format(int64(speed), pb.Units) + "/s "
260 + }
261 +
262 + // bar
263 + if pb.ShowBar {
264 + size := width - len(countersBox+pb.BarStart+pb.BarEnd+percentBox+timeLeftBox+speedBox+pb.prefix+pb.postfix)
265 + if size > 0 {
266 + curCount := int(math.Ceil((float64(current) / float64(pb.Total)) * float64(size)))
267 + emptCount := size - curCount
268 + barBox = pb.BarStart
269 + if emptCount < 0 {
270 + emptCount = 0
271 + }
272 + if curCount > size {
273 + curCount = size
274 + }
275 + if emptCount <= 0 {
276 + barBox += strings.Repeat(pb.Current, curCount)
277 + } else if curCount > 0 {
278 + barBox += strings.Repeat(pb.Current, curCount-1) + pb.CurrentN
279 + }
280 +
281 + barBox += strings.Repeat(pb.Empty, emptCount) + pb.BarEnd
282 + }
283 + }
284 +
285 + // check len
286 + out = pb.prefix + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix
287 + if len(out) < width {
288 + end = strings.Repeat(" ", width-len(out))
289 + }
290 +
291 + // and print!
292 + switch {
293 + case pb.Output != nil:
294 + fmt.Fprint(pb.Output, "\r"+out+end)
295 + case pb.Callback != nil:
296 + pb.Callback(out + end)
297 + case !pb.NotPrint:
298 + fmt.Print("\r" + out + end)
299 + }
300 +}
301 +
302 +func (pb *ProgressBar) getWidth() int {
303 + if pb.ForceWidth {
304 + return pb.Width
305 + }
306 +
307 + width := pb.Width
308 + termWidth, _ := terminalWidth()
309 + if width == 0 || termWidth <= width {
310 + width = termWidth
311 + }
312 +
313 + return width
314 +}
315 +
316 +// Write the current state of the progressbar
317 +func (pb *ProgressBar) Update() {
318 + c := atomic.LoadInt64(&pb.current)
319 + if c != pb.currentValue {
320 + pb.write(c)
321 + pb.currentValue = c
322 + }
323 +}
324 +
325 +// Internal loop for writing progressbar
326 +func (pb *ProgressBar) writer() {
327 + for {
328 + if pb.isFinish {
329 + break
330 + }
331 + pb.Update()
332 + time.Sleep(pb.RefreshRate)
333 + }
334 +}
335 +
336 +type window struct {
337 + Row uint16
338 + Col uint16
339 + Xpixel uint16
340 + Ypixel uint16
341 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/pb_nix.go new
+7
@@ -0,0 +1,7 @@
1 +// +build linux darwin freebsd openbsd
2 +
3 +package pb
4 +
5 +import "syscall"
6 +
7 +const sys_ioctl = syscall.SYS_IOCTL
Godeps/_workspace/src/github.com/cheggaaa/pb/pb_solaris.go new
+5
@@ -0,0 +1,5 @@
1 +// +build solaris
2 +
3 +package pb
4 +
5 +const sys_ioctl = 54
\ No newline at end of file
Godeps/_workspace/src/github.com/cheggaaa/pb/pb_test.go new
+30
@@ -0,0 +1,30 @@
1 +package pb
2 +
3 +import (
4 + "testing"
5 +)
6 +
7 +func Test_IncrementAddsOne(t *testing.T) {
8 + count := 5000
9 + bar := New(count)
10 + expected := 1
11 + actual := bar.Increment()
12 +
13 + if actual != expected {
14 + t.Errorf("Expected {%d} was {%d}", expected, actual)
15 + }
16 +}
17 +
18 +func Test_Width(t *testing.T) {
19 + count := 5000
20 + bar := New(count)
21 + width := 100
22 + bar.SetWidth(100).Callback = func(out string) {
23 + if len(out) != width {
24 + t.Errorf("Bar width expected {%d} was {%d}", len(out), width)
25 + }
26 + }
27 + bar.Start()
28 + bar.Increment()
29 + bar.Finish()
30 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/pb_win.go new
+16
@@ -0,0 +1,16 @@
1 +// +build windows
2 +
3 +package pb
4 +
5 +import (
6 + "github.com/olekukonko/ts"
7 +)
8 +
9 +func bold(str string) string {
10 + return str
11 +}
12 +
13 +func terminalWidth() (int, error) {
14 + size, err := ts.GetSize()
15 + return size.Col(), err
16 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/pb_x.go new
+46
@@ -0,0 +1,46 @@
1 +// +build linux darwin freebsd openbsd solaris
2 +
3 +package pb
4 +
5 +import (
6 + "os"
7 + "runtime"
8 + "syscall"
9 + "unsafe"
10 +)
11 +
12 +const (
13 + TIOCGWINSZ = 0x5413
14 + TIOCGWINSZ_OSX = 1074295912
15 +)
16 +
17 +var tty *os.File
18 +
19 +func init() {
20 + var err error
21 + tty, err = os.Open("/dev/tty")
22 + if err != nil {
23 + tty = os.Stdin
24 + }
25 +}
26 +
27 +func bold(str string) string {
28 + return "\033[1m" + str + "\033[0m"
29 +}
30 +
31 +func terminalWidth() (int, error) {
32 + w := new(window)
33 + tio := syscall.TIOCGWINSZ
34 + if runtime.GOOS == "darwin" {
35 + tio = TIOCGWINSZ_OSX
36 + }
37 + res, _, err := syscall.Syscall(sys_ioctl,
38 + tty.Fd(),
39 + uintptr(tio),
40 + uintptr(unsafe.Pointer(w)),
41 + )
42 + if int(res) == -1 {
43 + return 0, err
44 + }
45 + return int(w.Col), nil
46 +}
Godeps/_workspace/src/github.com/cheggaaa/pb/reader.go new
+17
@@ -0,0 +1,17 @@
1 +package pb
2 +
3 +import (
4 + "io"
5 +)
6 +
7 +// It's proxy reader, implement io.Reader
8 +type Reader struct {
9 + io.Reader
10 + bar *ProgressBar
11 +}
12 +
13 +func (r *Reader) Read(p []byte) (n int, err error) {
14 + n, err = r.Reader.Read(p)
15 + r.bar.Add(n)
16 + return
17 +}
\ No newline at end of file
core/commands/cat.go
+32 -5
@@ -2,12 +2,17 @@ package commands
2
3 import (
4 "io"
5 + "os"
6
7 cmds "github.com/jbenet/go-ipfs/commands"
8 core "github.com/jbenet/go-ipfs/core"
9 uio "github.com/jbenet/go-ipfs/unixfs/io"
10 +
11 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
12 )
13
14 +const progressBarMinSize = 1024 * 1024 * 8 // show progress bar for outputs > 8MiB
15 +
16 var CatCmd = &cmds.Command{
17 Helptext: cmds.HelpText{
18 Tagline: "Show IPFS object data",
@@ -29,29 +34,51 @@ it contains.
34
35 readers := make([]io.Reader, 0, len(req.Arguments()))
36
32 - readers, err = cat(node, req.Arguments())
37 + readers, length, err := cat(node, req.Arguments())
38 if err != nil {
39 res.SetError(err, cmds.ErrNormal)
40 return
41 }
42
43 + res.SetLength(length)
44 +
45 reader := io.MultiReader(readers...)
46 res.SetOutput(reader)
47 },
48 + PostRun: func(res cmds.Response) {
49 + if res.Length() < progressBarMinSize {
50 + return
51 + }
52 +
53 + bar := pb.New(int(res.Length())).SetUnits(pb.U_BYTES)
54 + bar.Output = os.Stderr
55 + bar.Start()
56 +
57 + reader := bar.NewProxyReader(res.Output().(io.Reader))
58 + res.SetOutput(reader)
59 + },
60 }
61
43 -func cat(node *core.IpfsNode, paths []string) ([]io.Reader, error) {
62 +func cat(node *core.IpfsNode, paths []string) ([]io.Reader, uint64, error) {
63 readers := make([]io.Reader, 0, len(paths))
64 + length := uint64(0)
65 for _, path := range paths {
66 dagnode, err := node.Resolver.ResolvePath(path)
67 if err != nil {
48 - return nil, err
68 + return nil, 0, err
69 + }
70 +
71 + nodeLength, err := dagnode.Size()
72 + if err != nil {
73 + return nil, 0, err
74 }
75 + length += nodeLength
76 +
77 read, err := uio.NewDagReader(dagnode, node.DAG)
78 if err != nil {
52 - return nil, err
79 + return nil, 0, err
80 }
81 readers = append(readers, read)
82 }
56 - return readers, nil
83 + return readers, length, nil
84 }