mirror of
https://github.com/restic/restic.git
synced 2024-08-23 04:40:46 +02:00
Among others, this updates minio-go, so that the new "eu-west-3" zone for AWS is supported.
358 lines
8.5 KiB
Go
358 lines
8.5 KiB
Go
// Copyright 2017 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 format
|
|
|
|
import (
|
|
"reflect"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// A Parser parses a format string. The result from the parse are set in the
|
|
// struct fields.
|
|
type Parser struct {
|
|
Verb rune
|
|
|
|
WidthPresent bool
|
|
PrecPresent bool
|
|
Minus bool
|
|
Plus bool
|
|
Sharp bool
|
|
Space bool
|
|
Zero bool
|
|
|
|
// For the formats %+v %#v, we set the plusV/sharpV flags
|
|
// and clear the plus/sharp flags since %+v and %#v are in effect
|
|
// different, flagless formats set at the top level.
|
|
PlusV bool
|
|
SharpV bool
|
|
|
|
HasIndex bool
|
|
|
|
Width int
|
|
Prec int // precision
|
|
|
|
// retain arguments across calls.
|
|
Args []interface{}
|
|
// retain current argument number across calls
|
|
ArgNum int
|
|
|
|
// reordered records whether the format string used argument reordering.
|
|
Reordered bool
|
|
// goodArgNum records whether the most recent reordering directive was valid.
|
|
goodArgNum bool
|
|
|
|
// position info
|
|
format string
|
|
startPos int
|
|
endPos int
|
|
Status Status
|
|
}
|
|
|
|
// Reset initializes a parser to scan format strings for the given args.
|
|
func (p *Parser) Reset(args []interface{}) {
|
|
p.Args = args
|
|
p.ArgNum = 0
|
|
p.startPos = 0
|
|
p.Reordered = false
|
|
}
|
|
|
|
// Text returns the part of the format string that was parsed by the last call
|
|
// to Scan. It returns the original substitution clause if the current scan
|
|
// parsed a substitution.
|
|
func (p *Parser) Text() string { return p.format[p.startPos:p.endPos] }
|
|
|
|
// SetFormat sets a new format string to parse. It does not reset the argument
|
|
// count.
|
|
func (p *Parser) SetFormat(format string) {
|
|
p.format = format
|
|
p.startPos = 0
|
|
p.endPos = 0
|
|
}
|
|
|
|
// Status indicates the result type of a call to Scan.
|
|
type Status int
|
|
|
|
const (
|
|
StatusText Status = iota
|
|
StatusSubstitution
|
|
StatusBadWidthSubstitution
|
|
StatusBadPrecSubstitution
|
|
StatusNoVerb
|
|
StatusBadArgNum
|
|
StatusMissingArg
|
|
)
|
|
|
|
// ClearFlags reset the parser to default behavior.
|
|
func (p *Parser) ClearFlags() {
|
|
p.WidthPresent = false
|
|
p.PrecPresent = false
|
|
p.Minus = false
|
|
p.Plus = false
|
|
p.Sharp = false
|
|
p.Space = false
|
|
p.Zero = false
|
|
|
|
p.PlusV = false
|
|
p.SharpV = false
|
|
|
|
p.HasIndex = false
|
|
}
|
|
|
|
// Scan scans the next part of the format string and sets the status to
|
|
// indicate whether it scanned a string literal, substitution or error.
|
|
func (p *Parser) Scan() bool {
|
|
p.Status = StatusText
|
|
format := p.format
|
|
end := len(format)
|
|
if p.endPos >= end {
|
|
return false
|
|
}
|
|
afterIndex := false // previous item in format was an index like [3].
|
|
|
|
p.startPos = p.endPos
|
|
p.goodArgNum = true
|
|
i := p.startPos
|
|
for i < end && format[i] != '%' {
|
|
i++
|
|
}
|
|
if i > p.startPos {
|
|
p.endPos = i
|
|
return true
|
|
}
|
|
// Process one verb
|
|
i++
|
|
|
|
p.Status = StatusSubstitution
|
|
|
|
// Do we have flags?
|
|
p.ClearFlags()
|
|
|
|
simpleFormat:
|
|
for ; i < end; i++ {
|
|
c := p.format[i]
|
|
switch c {
|
|
case '#':
|
|
p.Sharp = true
|
|
case '0':
|
|
p.Zero = !p.Minus // Only allow zero padding to the left.
|
|
case '+':
|
|
p.Plus = true
|
|
case '-':
|
|
p.Minus = true
|
|
p.Zero = false // Do not pad with zeros to the right.
|
|
case ' ':
|
|
p.Space = true
|
|
default:
|
|
// Fast path for common case of ascii lower case simple verbs
|
|
// without precision or width or argument indices.
|
|
if 'a' <= c && c <= 'z' && p.ArgNum < len(p.Args) {
|
|
if c == 'v' {
|
|
// Go syntax
|
|
p.SharpV = p.Sharp
|
|
p.Sharp = false
|
|
// Struct-field syntax
|
|
p.PlusV = p.Plus
|
|
p.Plus = false
|
|
}
|
|
p.Verb = rune(c)
|
|
p.ArgNum++
|
|
p.endPos = i + 1
|
|
return true
|
|
}
|
|
// Format is more complex than simple flags and a verb or is malformed.
|
|
break simpleFormat
|
|
}
|
|
}
|
|
|
|
// Do we have an explicit argument index?
|
|
i, afterIndex = p.updateArgNumber(format, i)
|
|
|
|
// Do we have width?
|
|
if i < end && format[i] == '*' {
|
|
i++
|
|
p.Width, p.WidthPresent = p.intFromArg()
|
|
|
|
if !p.WidthPresent {
|
|
p.Status = StatusBadWidthSubstitution
|
|
}
|
|
|
|
// We have a negative width, so take its value and ensure
|
|
// that the minus flag is set
|
|
if p.Width < 0 {
|
|
p.Width = -p.Width
|
|
p.Minus = true
|
|
p.Zero = false // Do not pad with zeros to the right.
|
|
}
|
|
afterIndex = false
|
|
} else {
|
|
p.Width, p.WidthPresent, i = parsenum(format, i, end)
|
|
if afterIndex && p.WidthPresent { // "%[3]2d"
|
|
p.goodArgNum = false
|
|
}
|
|
}
|
|
|
|
// Do we have precision?
|
|
if i+1 < end && format[i] == '.' {
|
|
i++
|
|
if afterIndex { // "%[3].2d"
|
|
p.goodArgNum = false
|
|
}
|
|
i, afterIndex = p.updateArgNumber(format, i)
|
|
if i < end && format[i] == '*' {
|
|
i++
|
|
p.Prec, p.PrecPresent = p.intFromArg()
|
|
// Negative precision arguments don't make sense
|
|
if p.Prec < 0 {
|
|
p.Prec = 0
|
|
p.PrecPresent = false
|
|
}
|
|
if !p.PrecPresent {
|
|
p.Status = StatusBadPrecSubstitution
|
|
}
|
|
afterIndex = false
|
|
} else {
|
|
p.Prec, p.PrecPresent, i = parsenum(format, i, end)
|
|
if !p.PrecPresent {
|
|
p.Prec = 0
|
|
p.PrecPresent = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if !afterIndex {
|
|
i, afterIndex = p.updateArgNumber(format, i)
|
|
}
|
|
p.HasIndex = afterIndex
|
|
|
|
if i >= end {
|
|
p.endPos = i
|
|
p.Status = StatusNoVerb
|
|
return true
|
|
}
|
|
|
|
verb, w := utf8.DecodeRuneInString(format[i:])
|
|
p.endPos = i + w
|
|
p.Verb = verb
|
|
|
|
switch {
|
|
case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec.
|
|
p.startPos = p.endPos - 1
|
|
p.Status = StatusText
|
|
case !p.goodArgNum:
|
|
p.Status = StatusBadArgNum
|
|
case p.ArgNum >= len(p.Args): // No argument left over to print for the current verb.
|
|
p.Status = StatusMissingArg
|
|
case verb == 'v':
|
|
// Go syntax
|
|
p.SharpV = p.Sharp
|
|
p.Sharp = false
|
|
// Struct-field syntax
|
|
p.PlusV = p.Plus
|
|
p.Plus = false
|
|
fallthrough
|
|
default:
|
|
p.ArgNum++
|
|
}
|
|
return true
|
|
}
|
|
|
|
// intFromArg gets the ArgNumth element of Args. On return, isInt reports
|
|
// whether the argument has integer type.
|
|
func (p *Parser) intFromArg() (num int, isInt bool) {
|
|
if p.ArgNum < len(p.Args) {
|
|
arg := p.Args[p.ArgNum]
|
|
num, isInt = arg.(int) // Almost always OK.
|
|
if !isInt {
|
|
// Work harder.
|
|
switch v := reflect.ValueOf(arg); v.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
n := v.Int()
|
|
if int64(int(n)) == n {
|
|
num = int(n)
|
|
isInt = true
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
n := v.Uint()
|
|
if int64(n) >= 0 && uint64(int(n)) == n {
|
|
num = int(n)
|
|
isInt = true
|
|
}
|
|
default:
|
|
// Already 0, false.
|
|
}
|
|
}
|
|
p.ArgNum++
|
|
if tooLarge(num) {
|
|
num = 0
|
|
isInt = false
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// parseArgNumber returns the value of the bracketed number, minus 1
|
|
// (explicit argument numbers are one-indexed but we want zero-indexed).
|
|
// The opening bracket is known to be present at format[0].
|
|
// The returned values are the index, the number of bytes to consume
|
|
// up to the closing paren, if present, and whether the number parsed
|
|
// ok. The bytes to consume will be 1 if no closing paren is present.
|
|
func parseArgNumber(format string) (index int, wid int, ok bool) {
|
|
// There must be at least 3 bytes: [n].
|
|
if len(format) < 3 {
|
|
return 0, 1, false
|
|
}
|
|
|
|
// Find closing bracket.
|
|
for i := 1; i < len(format); i++ {
|
|
if format[i] == ']' {
|
|
width, ok, newi := parsenum(format, 1, i)
|
|
if !ok || newi != i {
|
|
return 0, i + 1, false
|
|
}
|
|
return width - 1, i + 1, true // arg numbers are one-indexed and skip paren.
|
|
}
|
|
}
|
|
return 0, 1, false
|
|
}
|
|
|
|
// updateArgNumber returns the next argument to evaluate, which is either the value of the passed-in
|
|
// argNum or the value of the bracketed integer that begins format[i:]. It also returns
|
|
// the new value of i, that is, the index of the next byte of the format to process.
|
|
func (p *Parser) updateArgNumber(format string, i int) (newi int, found bool) {
|
|
if len(format) <= i || format[i] != '[' {
|
|
return i, false
|
|
}
|
|
p.Reordered = true
|
|
index, wid, ok := parseArgNumber(format[i:])
|
|
if ok && 0 <= index && index < len(p.Args) {
|
|
p.ArgNum = index
|
|
return i + wid, true
|
|
}
|
|
p.goodArgNum = false
|
|
return i + wid, ok
|
|
}
|
|
|
|
// tooLarge reports whether the magnitude of the integer is
|
|
// too large to be used as a formatting width or precision.
|
|
func tooLarge(x int) bool {
|
|
const max int = 1e6
|
|
return x > max || x < -max
|
|
}
|
|
|
|
// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present.
|
|
func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
|
|
if start >= end {
|
|
return 0, false, end
|
|
}
|
|
for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
|
|
if tooLarge(num) {
|
|
return 0, false, end // Overflow; crazy long number most likely.
|
|
}
|
|
num = num*10 + int(s[newi]-'0')
|
|
isnum = true
|
|
}
|
|
return
|
|
}
|