github.com/goplus/llgo@v0.8.3/xtool/ar/writer.go (about)

     1  /*
     2   * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package ar
    18  
    19  import (
    20  	"io"
    21  	"strconv"
    22  )
    23  
    24  // Writer provides sequential writing of an ar archive.
    25  // An ar archive is sequence of header file pairs
    26  // Call WriteHeader to begin writing a new file, then call Write to supply the file's data
    27  //
    28  // Example:
    29  // archive := ar.NewWriter(writer)
    30  // archive.WriteGlobalHeader()
    31  // header := new(ar.Header)
    32  // header.Size = 15 // bytes
    33  //
    34  //	if err := archive.WriteHeader(header); err != nil {
    35  //		return err
    36  //	}
    37  //
    38  // io.Copy(archive, data)
    39  type Writer struct {
    40  	w  io.Writer
    41  	nb int64 // number of unwritten bytes for the current file entry
    42  }
    43  
    44  // Create a new ar writer that writes to w
    45  func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
    46  
    47  func (aw *Writer) numeric(b []byte, x int64) {
    48  	s := strconv.FormatInt(x, 10)
    49  	for len(s) < len(b) {
    50  		s = s + " "
    51  	}
    52  	copy(b, []byte(s))
    53  }
    54  
    55  func (aw *Writer) octal(b []byte, x int64) {
    56  	s := "100" + strconv.FormatInt(x, 8)
    57  	for len(s) < len(b) {
    58  		s = s + " "
    59  	}
    60  	copy(b, []byte(s))
    61  }
    62  
    63  func (aw *Writer) string(b []byte, str string) {
    64  	s := str
    65  	for len(s) < len(b) {
    66  		s = s + " "
    67  	}
    68  	copy(b, []byte(s))
    69  }
    70  
    71  // Writes to the current entry in the ar archive
    72  // Returns ErrWriteTooLong if more than header.Size
    73  // bytes are written after a call to WriteHeader
    74  func (aw *Writer) Write(b []byte) (n int, err error) {
    75  	if int64(len(b)) > aw.nb {
    76  		b = b[0:aw.nb]
    77  		err = errWriteTooLong
    78  	}
    79  	n, werr := aw.w.Write(b)
    80  	aw.nb -= int64(n)
    81  	if werr != nil {
    82  		return n, werr
    83  	}
    84  
    85  	if len(b)%2 == 1 { // data size must be aligned to an even byte
    86  		n2, _ := aw.w.Write([]byte{'\n'})
    87  		return n + n2, err
    88  	}
    89  
    90  	return
    91  }
    92  
    93  func (aw *Writer) WriteGlobalHeader() error {
    94  	_, err := aw.w.Write([]byte(globalHeader))
    95  	return err
    96  }
    97  
    98  // Writes the header to the underlying writer and prepares
    99  // to receive the file payload
   100  func (aw *Writer) WriteHeader(hdr *Header) error {
   101  	aw.nb = int64(hdr.Size)
   102  	header := make([]byte, headerByteSize)
   103  	s := slicer(header)
   104  
   105  	aw.string(s.next(16), hdr.Name)
   106  	aw.numeric(s.next(12), hdr.ModTime.Unix())
   107  	aw.numeric(s.next(6), int64(hdr.Uid))
   108  	aw.numeric(s.next(6), int64(hdr.Gid))
   109  	aw.octal(s.next(8), hdr.Mode)
   110  	aw.numeric(s.next(10), hdr.Size)
   111  	aw.string(s.next(2), "`\n")
   112  
   113  	_, err := aw.w.Write(header)
   114  
   115  	return err
   116  }