1 /*
2 Copyright (c) 2013 Blake Smith <blakesmith0@gmail.com>
3 4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22 package ar
23 24 import (
25 "io"
26 "io/ioutil"
27 "os"
28 "strconv"
29 "time"
30 )
31 32 // Provides read access to an ar archive.
33 // Call next to skip files
34 //
35 // Example:
36 // reader := NewReader(f)
37 // var buf bytes.Buffer
38 // for {
39 // _, err := reader.Next()
40 // if err == io.EOF {
41 // break
42 // }
43 // if err != nil {
44 // t.Errorf(err.Error())
45 // }
46 // io.Copy(&buf, reader)
47 // }
48 49 type Reader struct {
50 r io.Reader
51 nb int64
52 pad int64
53 }
54 55 // Copies read data to r. Strips the global ar header.
56 func NewReader(r io.Reader) *Reader {
57 io.CopyN(ioutil.Discard, r, 8) // Discard global header
58 59 return &Reader{r: r}
60 }
61 62 func (rd *Reader) string(b []byte) string {
63 i := len(b)-1
64 for i > 0 && b[i] == 32 {
65 i--
66 }
67 68 return string(b[0:i+1])
69 }
70 71 func (rd *Reader) numeric(b []byte) int64 {
72 i := len(b)-1
73 for i > 0 && b[i] == 32 {
74 i--
75 }
76 77 n, _ := strconv.ParseInt(string(b[0:i+1]), 10, 64)
78 79 return n
80 }
81 82 func (rd *Reader) octal(b []byte) int64 {
83 i := len(b)-1
84 for i > 0 && b[i] == 32 {
85 i--
86 }
87 88 n, _ := strconv.ParseInt(string(b[3:i+1]), 8, 64)
89 90 return n
91 }
92 93 func (rd *Reader) skipUnread() error {
94 skip := rd.nb + rd.pad
95 rd.nb, rd.pad = 0, 0
96 if seeker, ok := rd.r.(io.Seeker); ok {
97 _, err := seeker.Seek(skip, os.SEEK_CUR)
98 return err
99 }
100 101 _, err := io.CopyN(ioutil.Discard, rd.r, skip)
102 return err
103 }
104 105 func (rd *Reader) readHeader() (*Header, error) {
106 headerBuf := make([]byte, HEADER_BYTE_SIZE)
107 if _, err := io.ReadFull(rd.r, headerBuf); err != nil {
108 return nil, err
109 }
110 111 header := new(Header)
112 s := slicer(headerBuf)
113 114 header.Name = rd.string(s.next(16))
115 header.ModTime = time.Unix(rd.numeric(s.next(12)), 0)
116 header.Uid = int(rd.numeric(s.next(6)))
117 header.Gid = int(rd.numeric(s.next(6)))
118 header.Mode = rd.octal(s.next(8))
119 header.Size = rd.numeric(s.next(10))
120 121 rd.nb = int64(header.Size)
122 if header.Size%2 == 1 {
123 rd.pad = 1
124 } else {
125 rd.pad = 0
126 }
127 128 return header, nil
129 }
130 131 // Call Next() to skip to the next file in the archive file.
132 // Returns a Header which contains the metadata about the
133 // file in the archive.
134 func (rd *Reader) Next() (*Header, error) {
135 err := rd.skipUnread()
136 if err != nil {
137 return nil, err
138 }
139 140 return rd.readHeader()
141 }
142 143 // Read data from the current entry in the archive.
144 func (rd *Reader) Read(b []byte) (n int, err error) {
145 if rd.nb == 0 {
146 return 0, io.EOF
147 }
148 if int64(len(b)) > rd.nb {
149 b = b[0:rd.nb]
150 }
151 n, err = rd.r.Read(b)
152 rd.nb -= int64(n)
153 154 return
155 }
156