reader.go raw

   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