github.com/freddyisaac/sicortex-golang@v0.0.0-20231019035217-e03519e66f60/src/archive/tar/strconv.go (about) 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 }