github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/cpio/archive.go (about)

     1  // Copyright 2013-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cpio
     6  
     7  import (
     8  	"io"
     9  	"strings"
    10  )
    11  
    12  // Archive is an in-memory list of files.
    13  //
    14  // Archive itself is a RecordWriter, and Archive.Reader() returns a new
    15  // RecordReader for the archive starting from the first file.
    16  type Archive struct {
    17  	// Files is a map of relative archive path -> record.
    18  	Files map[string]Record
    19  
    20  	// Order is a list of relative archive paths and represents the order
    21  	// in which Files were added.
    22  	Order []string
    23  }
    24  
    25  // InMemArchive returns an in-memory file archive.
    26  func InMemArchive() *Archive {
    27  	return &Archive{
    28  		Files: make(map[string]Record),
    29  	}
    30  }
    31  
    32  // ArchiveFromRecords creates a new Archive from the records.
    33  func ArchiveFromRecords(rs []Record) *Archive {
    34  	a := InMemArchive()
    35  	for _, r := range rs {
    36  		a.WriteRecord(r)
    37  	}
    38  	return a
    39  }
    40  
    41  // ArchiveFromReader reads records from r into a new Archive in memory.
    42  func ArchiveFromReader(r RecordReader) (*Archive, error) {
    43  	a := InMemArchive()
    44  	if err := Concat(a, r, nil); err != nil {
    45  		return nil, err
    46  	}
    47  	return a, nil
    48  }
    49  
    50  // WriteRecord implements RecordWriter and adds a record to the archive.
    51  //
    52  // WriteRecord uses Normalize to deduplicate paths.
    53  func (a *Archive) WriteRecord(r Record) error {
    54  	r.Name = Normalize(r.Name)
    55  	a.Files[r.Name] = r
    56  	a.Order = append(a.Order, r.Name)
    57  	return nil
    58  }
    59  
    60  // Empty returns whether the archive has any files in it.
    61  func (a *Archive) Empty() bool {
    62  	return len(a.Files) == 0
    63  }
    64  
    65  // Contains returns true if a record matching r is in the archive.
    66  func (a *Archive) Contains(r Record) bool {
    67  	r.Name = Normalize(r.Name)
    68  	if s, ok := a.Files[r.Name]; ok {
    69  		return Equal(r, s)
    70  	}
    71  	return false
    72  }
    73  
    74  // Get returns a record for the normalized path or false if there is none.
    75  //
    76  // The path is normalized using Normalize, so Get("/bin/bar") is the same as
    77  // Get("bin/bar") is the same as Get("bin//bar").
    78  func (a *Archive) Get(path string) (Record, bool) {
    79  	r, ok := a.Files[Normalize(path)]
    80  	return r, ok
    81  }
    82  
    83  // String implements fmt.Stringer.
    84  //
    85  // String lists files like ls would.
    86  func (a *Archive) String() string {
    87  	var b strings.Builder
    88  	r := a.Reader()
    89  	for {
    90  		record, err := r.ReadRecord()
    91  		if err != nil {
    92  			return b.String()
    93  		}
    94  		b.WriteString(record.String())
    95  		b.WriteString("\n")
    96  	}
    97  }
    98  
    99  type archiveReader struct {
   100  	a   *Archive
   101  	pos int
   102  }
   103  
   104  // Reader returns a RecordReader for the archive that starts at the first
   105  // record.
   106  func (a *Archive) Reader() RecordReader {
   107  	return &EOFReader{&archiveReader{a: a}}
   108  }
   109  
   110  // ReadRecord implements RecordReader.
   111  func (ar *archiveReader) ReadRecord() (Record, error) {
   112  	if ar.pos >= len(ar.a.Order) {
   113  		return Record{}, io.EOF
   114  	}
   115  
   116  	path := ar.a.Order[ar.pos]
   117  	ar.pos++
   118  	return ar.a.Files[path], nil
   119  }