// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tar

import (
	
	
	
	
	
)

// hasNUL reports whether the NUL character exists within s.
func hasNUL( string) bool {
	return strings.Contains(, "\x00")
}

// isASCII reports whether the input is an ASCII C-style string.
func isASCII( string) bool {
	for ,  := range  {
		if  >= 0x80 ||  == 0x00 {
			return false
		}
	}
	return true
}

// toASCII converts the input to an ASCII C-style string.
// This is a best effort conversion, so invalid characters are dropped.
func toASCII( string) string {
	if isASCII() {
		return 
	}
	 := make([]byte, 0, len())
	for ,  := range  {
		if  < 0x80 &&  != 0x00 {
			 = append(, byte())
		}
	}
	return string()
}

type parser struct {
	err error // Last error seen
}

type formatter struct {
	err error // Last error seen
}

// parseString parses bytes as a NUL-terminated C-style string.
// If a NUL byte is not found then the whole slice is returned as a string.
func (*parser) ( []byte) string {
	if  := bytes.IndexByte(, 0);  >= 0 {
		return string([:])
	}
	return string()
}

// formatString copies s into b, NUL-terminating if possible.
func ( *formatter) ( []byte,  string) {
	if len() > len() {
		.err = ErrFieldTooLong
	}
	copy(, )
	if len() < len() {
		[len()] = 0
	}

	// Some buggy readers treat regular files with a trailing slash
	// in the V7 path field as a directory even though the full path
	// recorded elsewhere (e.g., via PAX record) contains no trailing slash.
	if len() > len() && [len()-1] == '/' {
		 := len(strings.TrimRight([:len()-1], "/"))
		[] = 0 // Replace trailing slash with NUL terminator
	}
}

// fitsInBase256 reports whether x can be encoded into n bytes using base-256
// encoding. Unlike octal encoding, base-256 encoding does not require that the
// string ends with a NUL character. Thus, all n bytes are available for output.
//
// If operating in binary mode, this assumes strict GNU binary mode; which means
// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
// equivalent to the sign bit in two's complement form.
func fitsInBase256( int,  int64) bool {
	 := uint(-1) * 8
	return  >= 9 || ( >= -1<< &&  < 1<<)
}

// parseNumeric parses the input as being encoded in either base-256 or octal.
// This function may return negative numbers.
// If parsing fails or an integer overflow occurs, err will be set.
func ( *parser) ( []byte) int64 {
	// Check for base-256 (binary) format first.
	// If the first bit is set, then all following bits constitute a two's
	// complement encoded number in big-endian byte order.
	if len() > 0 && [0]&0x80 != 0 {
		// Handling negative numbers relies on the following identity:
		//	-a-1 == ^a
		//
		// If the number is negative, we use an inversion mask to invert the
		// data bytes and treat the value as an unsigned number.
		var  byte // 0x00 if positive or zero, 0xff if negative
		if [0]&0x40 != 0 {
			 = 0xff
		}

		var  uint64
		for ,  := range  {
			 ^=  // Inverts c only if inv is 0xff, otherwise does nothing
			if  == 0 {
				 &= 0x7f // Ignore signal bit in first byte
			}
			if ( >> 56) > 0 {
				.err = ErrHeader // Integer overflow
				return 0
			}
			 = <<8 | uint64()
		}
		if ( >> 63) > 0 {
			.err = ErrHeader // Integer overflow
			return 0
		}
		if  == 0xff {
			return ^int64()
		}
		return int64()
	}

	// Normal case is base-8 (octal) format.
	return .parseOctal()
}

// formatNumeric encodes x into b using base-8 (octal) encoding if possible.
// Otherwise it will attempt to use base-256 (binary) encoding.
func ( *formatter) ( []byte,  int64) {
	if fitsInOctal(len(), ) {
		.formatOctal(, )
		return
	}

	if fitsInBase256(len(), ) {
		for  := len() - 1;  >= 0; -- {
			[] = byte()
			 >>= 8
		}
		[0] |= 0x80 // Highest bit indicates binary format
		return
	}

	.formatOctal(, 0) // Last resort, just write zero
	.err = ErrFieldTooLong
}

func ( *parser) ( []byte) int64 {
	// Because unused fields are filled with NULs, we need
	// to skip leading NULs. Fields may also be padded with
	// spaces or NULs.
	// So we remove leading and trailing NULs and spaces to
	// be sure.
	 = bytes.Trim(, " \x00")

	if len() == 0 {
		return 0
	}
	,  := strconv.ParseUint(.parseString(), 8, 64)
	if  != nil {
		.err = ErrHeader
	}
	return int64()
}

func ( *formatter) ( []byte,  int64) {
	if !fitsInOctal(len(), ) {
		 = 0 // Last resort, just write zero
		.err = ErrFieldTooLong
	}

	 := strconv.FormatInt(, 8)
	// Add leading zeros, but leave room for a NUL.
	if  := len() - len() - 1;  > 0 {
		 = strings.Repeat("0", ) + 
	}
	.formatString(, )
}

// fitsInOctal reports whether the integer x fits in a field n-bytes long
// using octal encoding with the appropriate NUL terminator.
func fitsInOctal( int,  int64) bool {
	 := uint(-1) * 3
	return  >= 0 && ( >= 22 ||  < 1<<)
}

// parsePAXTime takes a string of the form %d.%d as described in the PAX
// specification. Note that this implementation allows for negative timestamps,
// which is allowed for by the PAX specification, but not always portable.
func parsePAXTime( string) (time.Time, error) {
	const  = 9

	// Split string into seconds and sub-seconds parts.
	, ,  := strings.Cut(, ".")

	// Parse the seconds.
	,  := strconv.ParseInt(, 10, 64)
	if  != nil {
		return time.Time{}, ErrHeader
	}
	if len() == 0 {
		return time.Unix(, 0), nil // No sub-second values
	}

	// Parse the nanoseconds.
	if strings.Trim(, "0123456789") != "" {
		return time.Time{}, ErrHeader
	}
	if len() <  {
		 += strings.Repeat("0", -len()) // Right pad
	} else {
		 = [:] // Right truncate
	}
	,  := strconv.ParseInt(, 10, 64) // Must succeed
	if len() > 0 && [0] == '-' {
		return time.Unix(, -1*), nil // Negative correction
	}
	return time.Unix(, ), nil
}

// formatPAXTime converts ts into a time of the form %d.%d as described in the
// PAX specification. This function is capable of negative timestamps.
func formatPAXTime( time.Time) ( string) {
	,  := .Unix(), .Nanosecond()
	if  == 0 {
		return strconv.FormatInt(, 10)
	}

	// If seconds is negative, then perform correction.
	 := ""
	if  < 0 {
		 = "-"             // Remember sign
		 = -( + 1)     // Add a second to secs
		 = -( - 1e9) // Take that second away from nsecs
	}
	return strings.TrimRight(fmt.Sprintf("%s%d.%09d", , , ), "0")
}

// parsePAXRecord parses the input PAX record string into a key-value pair.
// If parsing is successful, it will slice off the currently read record and
// return the remainder as r.
func parsePAXRecord( string) (, ,  string,  error) {
	// The size field ends at the first space.
	, ,  := strings.Cut(, " ")
	if ! {
		return "", "", , ErrHeader
	}

	// Parse the first token as a decimal integer.
	,  := strconv.ParseInt(, 10, 0) // Intentionally parse as native int
	if  != nil ||  < 5 ||  > int64(len()) {
		return "", "", , ErrHeader
	}
	 -= int64(len() + 1) // convert from index in s to index in rest
	if  <= 0 {
		return "", "", , ErrHeader
	}

	// Extract everything between the space and the final newline.
	, ,  := [:-1], [-1:], [:]
	if  != "\n" {
		return "", "", , ErrHeader
	}

	// The first equals separates the key from the value.
	, ,  = strings.Cut(, "=")
	if ! {
		return "", "", , ErrHeader
	}

	if !validPAXRecord(, ) {
		return "", "", , ErrHeader
	}
	return , , , nil
}

// formatPAXRecord formats a single PAX record, prefixing it with the
// appropriate length.
func formatPAXRecord(,  string) (string, error) {
	if !validPAXRecord(, ) {
		return "", ErrHeader
	}

	const  = 3 // Extra padding for ' ', '=', and '\n'
	 := len() + len() + 
	 += len(strconv.Itoa())
	 := strconv.Itoa() + " " +  + "=" +  + "\n"

	// Final adjustment if adding size field increased the record size.
	if len() !=  {
		 = len()
		 = strconv.Itoa() + " " +  + "=" +  + "\n"
	}
	return , nil
}

// validPAXRecord reports whether the key-value pair is valid where each
// record is formatted as:
//
//	"%d %s=%s\n" % (size, key, value)
//
// Keys and values should be UTF-8, but the number of bad writers out there
// forces us to be a more liberal.
// Thus, we only reject all keys with NUL, and only reject NULs in values
// for the PAX version of the USTAR string fields.
// The key must not contain an '=' character.
func validPAXRecord(,  string) bool {
	if  == "" || strings.Contains(, "=") {
		return false
	}
	switch  {
	case paxPath, paxLinkpath, paxUname, paxGname:
		return !hasNUL()
	default:
		return !hasNUL()
	}
}