...
Run Format

Source file src/archive/tar/strconv.go

     1	// Copyright 2016 The Go 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 tar
     6	
     7	import (
     8		"bytes"
     9		"fmt"
    10		"strconv"
    11		"strings"
    12		"time"
    13	)
    14	
    15	func isASCII(s string) bool {
    16		for _, c := range s {
    17			if c >= 0x80 {
    18				return false
    19			}
    20		}
    21		return true
    22	}
    23	
    24	func toASCII(s string) string {
    25		if isASCII(s) {
    26			return s
    27		}
    28		var buf bytes.Buffer
    29		for _, c := range s {
    30			if c < 0x80 {
    31				buf.WriteByte(byte(c))
    32			}
    33		}
    34		return buf.String()
    35	}
    36	
    37	type parser struct {
    38		err error // Last error seen
    39	}
    40	
    41	type formatter struct {
    42		err error // Last error seen
    43	}
    44	
    45	// parseString parses bytes as a NUL-terminated C-style string.
    46	// If a NUL byte is not found then the whole slice is returned as a string.
    47	func (*parser) parseString(b []byte) string {
    48		n := 0
    49		for n < len(b) && b[n] != 0 {
    50			n++
    51		}
    52		return string(b[0:n])
    53	}
    54	
    55	// Write s into b, terminating it with a NUL if there is room.
    56	func (f *formatter) formatString(b []byte, s string) {
    57		if len(s) > len(b) {
    58			f.err = ErrFieldTooLong
    59			return
    60		}
    61		ascii := toASCII(s)
    62		copy(b, ascii)
    63		if len(ascii) < len(b) {
    64			b[len(ascii)] = 0
    65		}
    66	}
    67	
    68	// fitsInBase256 reports whether x can be encoded into n bytes using base-256
    69	// encoding. Unlike octal encoding, base-256 encoding does not require that the
    70	// string ends with a NUL character. Thus, all n bytes are available for output.
    71	//
    72	// If operating in binary mode, this assumes strict GNU binary mode; which means
    73	// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
    74	// equivalent to the sign bit in two's complement form.
    75	func fitsInBase256(n int, x int64) bool {
    76		var binBits = uint(n-1) * 8
    77		return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
    78	}
    79	
    80	// parseNumeric parses the input as being encoded in either base-256 or octal.
    81	// This function may return negative numbers.
    82	// If parsing fails or an integer overflow occurs, err will be set.
    83	func (p *parser) parseNumeric(b []byte) int64 {
    84		// Check for base-256 (binary) format first.
    85		// If the first bit is set, then all following bits constitute a two's
    86		// complement encoded number in big-endian byte order.
    87		if len(b) > 0 && b[0]&0x80 != 0 {
    88			// Handling negative numbers relies on the following identity:
    89			//	-a-1 == ^a
    90			//
    91			// If the number is negative, we use an inversion mask to invert the
    92			// data bytes and treat the value as an unsigned number.
    93			var inv byte // 0x00 if positive or zero, 0xff if negative
    94			if b[0]&0x40 != 0 {
    95				inv = 0xff
    96			}
    97	
    98			var x uint64
    99			for i, c := range b {
   100				c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing
   101				if i == 0 {
   102					c &= 0x7f // Ignore signal bit in first byte
   103				}
   104				if (x >> 56) > 0 {
   105					p.err = ErrHeader // Integer overflow
   106					return 0
   107				}
   108				x = x<<8 | uint64(c)
   109			}
   110			if (x >> 63) > 0 {
   111				p.err = ErrHeader // Integer overflow
   112				return 0
   113			}
   114			if inv == 0xff {
   115				return ^int64(x)
   116			}
   117			return int64(x)
   118		}
   119	
   120		// Normal case is base-8 (octal) format.
   121		return p.parseOctal(b)
   122	}
   123	
   124	// Write x into b, as binary (GNUtar/star extension).
   125	func (f *formatter) formatNumeric(b []byte, x int64) {
   126		if fitsInBase256(len(b), x) {
   127			for i := len(b) - 1; i >= 0; i-- {
   128				b[i] = byte(x)
   129				x >>= 8
   130			}
   131			b[0] |= 0x80 // Highest bit indicates binary format
   132			return
   133		}
   134	
   135		f.formatOctal(b, 0) // Last resort, just write zero
   136		f.err = ErrFieldTooLong
   137	}
   138	
   139	func (p *parser) parseOctal(b []byte) int64 {
   140		// Because unused fields are filled with NULs, we need
   141		// to skip leading NULs. Fields may also be padded with
   142		// spaces or NULs.
   143		// So we remove leading and trailing NULs and spaces to
   144		// be sure.
   145		b = bytes.Trim(b, " \x00")
   146	
   147		if len(b) == 0 {
   148			return 0
   149		}
   150		x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
   151		if perr != nil {
   152			p.err = ErrHeader
   153		}
   154		return int64(x)
   155	}
   156	
   157	func (f *formatter) formatOctal(b []byte, x int64) {
   158		s := strconv.FormatInt(x, 8)
   159		// Add leading zeros, but leave room for a NUL.
   160		if n := len(b) - len(s) - 1; n > 0 {
   161			s = strings.Repeat("0", n) + s
   162		}
   163		f.formatString(b, s)
   164	}
   165	
   166	// parsePAXTime takes a string of the form %d.%d as described in the PAX
   167	// specification. Note that this implementation allows for negative timestamps,
   168	// which is allowed for by the PAX specification, but not always portable.
   169	func parsePAXTime(s string) (time.Time, error) {
   170		const maxNanoSecondDigits = 9
   171	
   172		// Split string into seconds and sub-seconds parts.
   173		ss, sn := s, ""
   174		if pos := strings.IndexByte(s, '.'); pos >= 0 {
   175			ss, sn = s[:pos], s[pos+1:]
   176		}
   177	
   178		// Parse the seconds.
   179		secs, err := strconv.ParseInt(ss, 10, 64)
   180		if err != nil {
   181			return time.Time{}, ErrHeader
   182		}
   183		if len(sn) == 0 {
   184			return time.Unix(secs, 0), nil // No sub-second values
   185		}
   186	
   187		// Parse the nanoseconds.
   188		if strings.Trim(sn, "0123456789") != "" {
   189			return time.Time{}, ErrHeader
   190		}
   191		if len(sn) < maxNanoSecondDigits {
   192			sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
   193		} else {
   194			sn = sn[:maxNanoSecondDigits] // Right truncate
   195		}
   196		nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
   197		if len(ss) > 0 && ss[0] == '-' {
   198			return time.Unix(secs, -1*int64(nsecs)), nil // Negative correction
   199		}
   200		return time.Unix(secs, int64(nsecs)), nil
   201	}
   202	
   203	// TODO(dsnet): Implement formatPAXTime.
   204	
   205	// parsePAXRecord parses the input PAX record string into a key-value pair.
   206	// If parsing is successful, it will slice off the currently read record and
   207	// return the remainder as r.
   208	//
   209	// A PAX record is of the following form:
   210	//	"%d %s=%s\n" % (size, key, value)
   211	func parsePAXRecord(s string) (k, v, r string, err error) {
   212		// The size field ends at the first space.
   213		sp := strings.IndexByte(s, ' ')
   214		if sp == -1 {
   215			return "", "", s, ErrHeader
   216		}
   217	
   218		// Parse the first token as a decimal integer.
   219		n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
   220		if perr != nil || n < 5 || int64(len(s)) < n {
   221			return "", "", s, ErrHeader
   222		}
   223	
   224		// Extract everything between the space and the final newline.
   225		rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:]
   226		if nl != "\n" {
   227			return "", "", s, ErrHeader
   228		}
   229	
   230		// The first equals separates the key from the value.
   231		eq := strings.IndexByte(rec, '=')
   232		if eq == -1 {
   233			return "", "", s, ErrHeader
   234		}
   235		return rec[:eq], rec[eq+1:], rem, nil
   236	}
   237	
   238	// formatPAXRecord formats a single PAX record, prefixing it with the
   239	// appropriate length.
   240	func formatPAXRecord(k, v string) string {
   241		const padding = 3 // Extra padding for ' ', '=', and '\n'
   242		size := len(k) + len(v) + padding
   243		size += len(strconv.Itoa(size))
   244		record := fmt.Sprintf("%d %s=%s\n", size, k, v)
   245	
   246		// Final adjustment if adding size field increased the record size.
   247		if len(record) != size {
   248			size = len(record)
   249			record = fmt.Sprintf("%d %s=%s\n", size, k, v)
   250		}
   251		return record
   252	}
   253	

View as plain text