Niels de Vos bec6090996 build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These
packages are less important to update, as the they can not influence the
final executable that is part of the Ceph-CSI container-image.

By moving these dependencies out of the main Ceph-CSI go.mod, it is
easier to identify if a reported CVE affects Ceph-CSI, or only the
testing (like most of the Kubernetes CVEs).

Signed-off-by: Niels de Vos <ndevos@ibm.com>
2025-03-07 16:05:04 +00:00

985 lines
23 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 message
import (
"bytes"
"fmt" // TODO: consider copying interfaces from package fmt to avoid dependency.
"math"
"reflect"
"sync"
"unicode/utf8"
"golang.org/x/text/internal/format"
"golang.org/x/text/internal/number"
"golang.org/x/text/language"
"golang.org/x/text/message/catalog"
)
// Strings for use with buffer.WriteString.
// This is less overhead than using buffer.Write with byte arrays.
const (
commaSpaceString = ", "
nilAngleString = "<nil>"
nilParenString = "(nil)"
nilString = "nil"
mapString = "map["
percentBangString = "%!"
missingString = "(MISSING)"
badIndexString = "(BADINDEX)"
panicString = "(PANIC="
extraString = "%!(EXTRA "
badWidthString = "%!(BADWIDTH)"
badPrecString = "%!(BADPREC)"
noVerbString = "%!(NOVERB)"
invReflectString = "<invalid reflect.Value>"
)
var printerPool = sync.Pool{
New: func() interface{} { return new(printer) },
}
// newPrinter allocates a new printer struct or grabs a cached one.
func newPrinter(pp *Printer) *printer {
p := printerPool.Get().(*printer)
p.Printer = *pp
// TODO: cache most of the following call.
p.catContext = pp.cat.Context(pp.tag, p)
p.panicking = false
p.erroring = false
p.fmt.init(&p.Buffer)
return p
}
// free saves used printer structs in printerFree; avoids an allocation per invocation.
func (p *printer) free() {
p.Buffer.Reset()
p.arg = nil
p.value = reflect.Value{}
printerPool.Put(p)
}
// printer is used to store a printer's state.
// It implements "golang.org/x/text/internal/format".State.
type printer struct {
Printer
// the context for looking up message translations
catContext *catalog.Context
// buffer for accumulating output.
bytes.Buffer
// arg holds the current item, as an interface{}.
arg interface{}
// value is used instead of arg for reflect values.
value reflect.Value
// fmt is used to format basic items such as integers or strings.
fmt formatInfo
// panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion.
panicking bool
// erroring is set when printing an error string to guard against calling handleMethods.
erroring bool
}
// Language implements "golang.org/x/text/internal/format".State.
func (p *printer) Language() language.Tag { return p.tag }
func (p *printer) Width() (wid int, ok bool) { return p.fmt.Width, p.fmt.WidthPresent }
func (p *printer) Precision() (prec int, ok bool) { return p.fmt.Prec, p.fmt.PrecPresent }
func (p *printer) Flag(b int) bool {
switch b {
case '-':
return p.fmt.Minus
case '+':
return p.fmt.Plus || p.fmt.PlusV
case '#':
return p.fmt.Sharp || p.fmt.SharpV
case ' ':
return p.fmt.Space
case '0':
return p.fmt.Zero
}
return false
}
// getField gets the i'th field of the struct value.
// If the field is itself is an interface, return a value for
// the thing inside the interface, not the interface itself.
func getField(v reflect.Value, i int) reflect.Value {
val := v.Field(i)
if val.Kind() == reflect.Interface && !val.IsNil() {
val = val.Elem()
}
return val
}
func (p *printer) unknownType(v reflect.Value) {
if !v.IsValid() {
p.WriteString(nilAngleString)
return
}
p.WriteByte('?')
p.WriteString(v.Type().String())
p.WriteByte('?')
}
func (p *printer) badVerb(verb rune) {
p.erroring = true
p.WriteString(percentBangString)
p.WriteRune(verb)
p.WriteByte('(')
switch {
case p.arg != nil:
p.WriteString(reflect.TypeOf(p.arg).String())
p.WriteByte('=')
p.printArg(p.arg, 'v')
case p.value.IsValid():
p.WriteString(p.value.Type().String())
p.WriteByte('=')
p.printValue(p.value, 'v', 0)
default:
p.WriteString(nilAngleString)
}
p.WriteByte(')')
p.erroring = false
}
func (p *printer) fmtBool(v bool, verb rune) {
switch verb {
case 't', 'v':
p.fmt.fmt_boolean(v)
default:
p.badVerb(verb)
}
}
// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or
// not, as requested, by temporarily setting the sharp flag.
func (p *printer) fmt0x64(v uint64, leading0x bool) {
sharp := p.fmt.Sharp
p.fmt.Sharp = leading0x
p.fmt.fmt_integer(v, 16, unsigned, ldigits)
p.fmt.Sharp = sharp
}
// fmtInteger formats a signed or unsigned integer.
func (p *printer) fmtInteger(v uint64, isSigned bool, verb rune) {
switch verb {
case 'v':
if p.fmt.SharpV && !isSigned {
p.fmt0x64(v, true)
return
}
fallthrough
case 'd':
if p.fmt.Sharp || p.fmt.SharpV {
p.fmt.fmt_integer(v, 10, isSigned, ldigits)
} else {
p.fmtDecimalInt(v, isSigned)
}
case 'b':
p.fmt.fmt_integer(v, 2, isSigned, ldigits)
case 'o':
p.fmt.fmt_integer(v, 8, isSigned, ldigits)
case 'x':
p.fmt.fmt_integer(v, 16, isSigned, ldigits)
case 'X':
p.fmt.fmt_integer(v, 16, isSigned, udigits)
case 'c':
p.fmt.fmt_c(v)
case 'q':
if v <= utf8.MaxRune {
p.fmt.fmt_qc(v)
} else {
p.badVerb(verb)
}
case 'U':
p.fmt.fmt_unicode(v)
default:
p.badVerb(verb)
}
}
// fmtFloat formats a float. The default precision for each verb
// is specified as last argument in the call to fmt_float.
func (p *printer) fmtFloat(v float64, size int, verb rune) {
switch verb {
case 'b':
p.fmt.fmt_float(v, size, verb, -1)
case 'v':
verb = 'g'
fallthrough
case 'g', 'G':
if p.fmt.Sharp || p.fmt.SharpV {
p.fmt.fmt_float(v, size, verb, -1)
} else {
p.fmtVariableFloat(v, size)
}
case 'e', 'E':
if p.fmt.Sharp || p.fmt.SharpV {
p.fmt.fmt_float(v, size, verb, 6)
} else {
p.fmtScientific(v, size, 6)
}
case 'f', 'F':
if p.fmt.Sharp || p.fmt.SharpV {
p.fmt.fmt_float(v, size, verb, 6)
} else {
p.fmtDecimalFloat(v, size, 6)
}
default:
p.badVerb(verb)
}
}
func (p *printer) setFlags(f *number.Formatter) {
f.Flags &^= number.ElideSign
if p.fmt.Plus || p.fmt.Space {
f.Flags |= number.AlwaysSign
if !p.fmt.Plus {
f.Flags |= number.ElideSign
}
} else {
f.Flags &^= number.AlwaysSign
}
}
func (p *printer) updatePadding(f *number.Formatter) {
f.Flags &^= number.PadMask
if p.fmt.Minus {
f.Flags |= number.PadAfterSuffix
} else {
f.Flags |= number.PadBeforePrefix
}
f.PadRune = ' '
f.FormatWidth = uint16(p.fmt.Width)
}
func (p *printer) initDecimal(minFrac, maxFrac int) {
f := &p.toDecimal
f.MinIntegerDigits = 1
f.MaxIntegerDigits = 0
f.MinFractionDigits = uint8(minFrac)
f.MaxFractionDigits = int16(maxFrac)
p.setFlags(f)
f.PadRune = 0
if p.fmt.WidthPresent {
if p.fmt.Zero {
wid := p.fmt.Width
// Use significant integers for this.
// TODO: this is not the same as width, but so be it.
if f.MinFractionDigits > 0 {
wid -= 1 + int(f.MinFractionDigits)
}
if p.fmt.Plus || p.fmt.Space {
wid--
}
if wid > 0 && wid > int(f.MinIntegerDigits) {
f.MinIntegerDigits = uint8(wid)
}
}
p.updatePadding(f)
}
}
func (p *printer) initScientific(minFrac, maxFrac int) {
f := &p.toScientific
if maxFrac < 0 {
f.SetPrecision(maxFrac)
} else {
f.SetPrecision(maxFrac + 1)
f.MinFractionDigits = uint8(minFrac)
f.MaxFractionDigits = int16(maxFrac)
}
f.MinExponentDigits = 2
p.setFlags(f)
f.PadRune = 0
if p.fmt.WidthPresent {
f.Flags &^= number.PadMask
if p.fmt.Zero {
f.PadRune = f.Digit(0)
f.Flags |= number.PadAfterPrefix
} else {
f.PadRune = ' '
f.Flags |= number.PadBeforePrefix
}
p.updatePadding(f)
}
}
func (p *printer) fmtDecimalInt(v uint64, isSigned bool) {
var d number.Decimal
f := &p.toDecimal
if p.fmt.PrecPresent {
p.setFlags(f)
f.MinIntegerDigits = uint8(p.fmt.Prec)
f.MaxIntegerDigits = 0
f.MinFractionDigits = 0
f.MaxFractionDigits = 0
if p.fmt.WidthPresent {
p.updatePadding(f)
}
} else {
p.initDecimal(0, 0)
}
d.ConvertInt(p.toDecimal.RoundingContext, isSigned, v)
out := p.toDecimal.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
func (p *printer) fmtDecimalFloat(v float64, size, prec int) {
var d number.Decimal
if p.fmt.PrecPresent {
prec = p.fmt.Prec
}
p.initDecimal(prec, prec)
d.ConvertFloat(p.toDecimal.RoundingContext, v, size)
out := p.toDecimal.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
func (p *printer) fmtVariableFloat(v float64, size int) {
prec := -1
if p.fmt.PrecPresent {
prec = p.fmt.Prec
}
var d number.Decimal
p.initScientific(0, prec)
d.ConvertFloat(p.toScientific.RoundingContext, v, size)
// Copy logic of 'g' formatting from strconv. It is simplified a bit as
// we don't have to mind having prec > len(d.Digits).
shortest := prec < 0
ePrec := prec
if shortest {
prec = len(d.Digits)
ePrec = 6
} else if prec == 0 {
prec = 1
ePrec = 1
}
exp := int(d.Exp) - 1
if exp < -4 || exp >= ePrec {
p.initScientific(0, prec)
out := p.toScientific.Format([]byte(nil), &d)
p.Buffer.Write(out)
} else {
if prec > int(d.Exp) {
prec = len(d.Digits)
}
if prec -= int(d.Exp); prec < 0 {
prec = 0
}
p.initDecimal(0, prec)
out := p.toDecimal.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
}
func (p *printer) fmtScientific(v float64, size, prec int) {
var d number.Decimal
if p.fmt.PrecPresent {
prec = p.fmt.Prec
}
p.initScientific(prec, prec)
rc := p.toScientific.RoundingContext
d.ConvertFloat(rc, v, size)
out := p.toScientific.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
// fmtComplex formats a complex number v with
// r = real(v) and j = imag(v) as (r+ji) using
// fmtFloat for r and j formatting.
func (p *printer) fmtComplex(v complex128, size int, verb rune) {
// Make sure any unsupported verbs are found before the
// calls to fmtFloat to not generate an incorrect error string.
switch verb {
case 'v', 'b', 'g', 'G', 'f', 'F', 'e', 'E':
p.WriteByte('(')
p.fmtFloat(real(v), size/2, verb)
// Imaginary part always has a sign.
if math.IsNaN(imag(v)) {
// By CLDR's rules, NaNs do not use patterns or signs. As this code
// relies on AlwaysSign working for imaginary parts, we need to
// manually handle NaNs.
f := &p.toScientific
p.setFlags(f)
p.updatePadding(f)
p.setFlags(f)
nan := f.Symbol(number.SymNan)
extra := 0
if w, ok := p.Width(); ok {
extra = w - utf8.RuneCountInString(nan) - 1
}
if f.Flags&number.PadAfterNumber == 0 {
for ; extra > 0; extra-- {
p.WriteRune(f.PadRune)
}
}
p.WriteString(f.Symbol(number.SymPlusSign))
p.WriteString(nan)
for ; extra > 0; extra-- {
p.WriteRune(f.PadRune)
}
p.WriteString("i)")
return
}
oldPlus := p.fmt.Plus
p.fmt.Plus = true
p.fmtFloat(imag(v), size/2, verb)
p.WriteString("i)") // TODO: use symbol?
p.fmt.Plus = oldPlus
default:
p.badVerb(verb)
}
}
func (p *printer) fmtString(v string, verb rune) {
switch verb {
case 'v':
if p.fmt.SharpV {
p.fmt.fmt_q(v)
} else {
p.fmt.fmt_s(v)
}
case 's':
p.fmt.fmt_s(v)
case 'x':
p.fmt.fmt_sx(v, ldigits)
case 'X':
p.fmt.fmt_sx(v, udigits)
case 'q':
p.fmt.fmt_q(v)
case 'm':
ctx := p.cat.Context(p.tag, rawPrinter{p})
if ctx.Execute(v) == catalog.ErrNotFound {
p.WriteString(v)
}
default:
p.badVerb(verb)
}
}
func (p *printer) fmtBytes(v []byte, verb rune, typeString string) {
switch verb {
case 'v', 'd':
if p.fmt.SharpV {
p.WriteString(typeString)
if v == nil {
p.WriteString(nilParenString)
return
}
p.WriteByte('{')
for i, c := range v {
if i > 0 {
p.WriteString(commaSpaceString)
}
p.fmt0x64(uint64(c), true)
}
p.WriteByte('}')
} else {
p.WriteByte('[')
for i, c := range v {
if i > 0 {
p.WriteByte(' ')
}
p.fmt.fmt_integer(uint64(c), 10, unsigned, ldigits)
}
p.WriteByte(']')
}
case 's':
p.fmt.fmt_s(string(v))
case 'x':
p.fmt.fmt_bx(v, ldigits)
case 'X':
p.fmt.fmt_bx(v, udigits)
case 'q':
p.fmt.fmt_q(string(v))
default:
p.printValue(reflect.ValueOf(v), verb, 0)
}
}
func (p *printer) fmtPointer(value reflect.Value, verb rune) {
var u uintptr
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
u = value.Pointer()
default:
p.badVerb(verb)
return
}
switch verb {
case 'v':
if p.fmt.SharpV {
p.WriteByte('(')
p.WriteString(value.Type().String())
p.WriteString(")(")
if u == 0 {
p.WriteString(nilString)
} else {
p.fmt0x64(uint64(u), true)
}
p.WriteByte(')')
} else {
if u == 0 {
p.fmt.padString(nilAngleString)
} else {
p.fmt0x64(uint64(u), !p.fmt.Sharp)
}
}
case 'p':
p.fmt0x64(uint64(u), !p.fmt.Sharp)
case 'b', 'o', 'd', 'x', 'X':
if verb == 'd' {
p.fmt.Sharp = true // Print as standard go. TODO: does this make sense?
}
p.fmtInteger(uint64(u), unsigned, verb)
default:
p.badVerb(verb)
}
}
func (p *printer) catchPanic(arg interface{}, verb rune) {
if err := recover(); err != nil {
// If it's a nil pointer, just say "<nil>". The likeliest causes are a
// Stringer that fails to guard against nil or a nil pointer for a
// value receiver, and in either case, "<nil>" is a nice result.
if v := reflect.ValueOf(arg); v.Kind() == reflect.Ptr && v.IsNil() {
p.WriteString(nilAngleString)
return
}
// Otherwise print a concise panic message. Most of the time the panic
// value will print itself nicely.
if p.panicking {
// Nested panics; the recursion in printArg cannot succeed.
panic(err)
}
oldFlags := p.fmt.Parser
// For this output we want default behavior.
p.fmt.ClearFlags()
p.WriteString(percentBangString)
p.WriteRune(verb)
p.WriteString(panicString)
p.panicking = true
p.printArg(err, 'v')
p.panicking = false
p.WriteByte(')')
p.fmt.Parser = oldFlags
}
}
func (p *printer) handleMethods(verb rune) (handled bool) {
if p.erroring {
return
}
// Is it a Formatter?
if formatter, ok := p.arg.(format.Formatter); ok {
handled = true
defer p.catchPanic(p.arg, verb)
formatter.Format(p, verb)
return
}
if formatter, ok := p.arg.(fmt.Formatter); ok {
handled = true
defer p.catchPanic(p.arg, verb)
formatter.Format(p, verb)
return
}
// If we're doing Go syntax and the argument knows how to supply it, take care of it now.
if p.fmt.SharpV {
if stringer, ok := p.arg.(fmt.GoStringer); ok {
handled = true
defer p.catchPanic(p.arg, verb)
// Print the result of GoString unadorned.
p.fmt.fmt_s(stringer.GoString())
return
}
} else {
// If a string is acceptable according to the format, see if
// the value satisfies one of the string-valued interfaces.
// Println etc. set verb to %v, which is "stringable".
switch verb {
case 'v', 's', 'x', 'X', 'q':
// Is it an error or Stringer?
// The duplication in the bodies is necessary:
// setting handled and deferring catchPanic
// must happen before calling the method.
switch v := p.arg.(type) {
case error:
handled = true
defer p.catchPanic(p.arg, verb)
p.fmtString(v.Error(), verb)
return
case fmt.Stringer:
handled = true
defer p.catchPanic(p.arg, verb)
p.fmtString(v.String(), verb)
return
}
}
}
return false
}
func (p *printer) printArg(arg interface{}, verb rune) {
p.arg = arg
p.value = reflect.Value{}
if arg == nil {
switch verb {
case 'T', 'v':
p.fmt.padString(nilAngleString)
default:
p.badVerb(verb)
}
return
}
// Special processing considerations.
// %T (the value's type) and %p (its address) are special; we always do them first.
switch verb {
case 'T':
p.fmt.fmt_s(reflect.TypeOf(arg).String())
return
case 'p':
p.fmtPointer(reflect.ValueOf(arg), 'p')
return
}
// Some types can be done without reflection.
switch f := arg.(type) {
case bool:
p.fmtBool(f, verb)
case float32:
p.fmtFloat(float64(f), 32, verb)
case float64:
p.fmtFloat(f, 64, verb)
case complex64:
p.fmtComplex(complex128(f), 64, verb)
case complex128:
p.fmtComplex(f, 128, verb)
case int:
p.fmtInteger(uint64(f), signed, verb)
case int8:
p.fmtInteger(uint64(f), signed, verb)
case int16:
p.fmtInteger(uint64(f), signed, verb)
case int32:
p.fmtInteger(uint64(f), signed, verb)
case int64:
p.fmtInteger(uint64(f), signed, verb)
case uint:
p.fmtInteger(uint64(f), unsigned, verb)
case uint8:
p.fmtInteger(uint64(f), unsigned, verb)
case uint16:
p.fmtInteger(uint64(f), unsigned, verb)
case uint32:
p.fmtInteger(uint64(f), unsigned, verb)
case uint64:
p.fmtInteger(f, unsigned, verb)
case uintptr:
p.fmtInteger(uint64(f), unsigned, verb)
case string:
p.fmtString(f, verb)
case []byte:
p.fmtBytes(f, verb, "[]byte")
case reflect.Value:
// Handle extractable values with special methods
// since printValue does not handle them at depth 0.
if f.IsValid() && f.CanInterface() {
p.arg = f.Interface()
if p.handleMethods(verb) {
return
}
}
p.printValue(f, verb, 0)
default:
// If the type is not simple, it might have methods.
if !p.handleMethods(verb) {
// Need to use reflection, since the type had no
// interface methods that could be used for formatting.
p.printValue(reflect.ValueOf(f), verb, 0)
}
}
}
// printValue is similar to printArg but starts with a reflect value, not an interface{} value.
// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg.
func (p *printer) printValue(value reflect.Value, verb rune, depth int) {
// Handle values with special methods if not already handled by printArg (depth == 0).
if depth > 0 && value.IsValid() && value.CanInterface() {
p.arg = value.Interface()
if p.handleMethods(verb) {
return
}
}
p.arg = nil
p.value = value
switch f := value; value.Kind() {
case reflect.Invalid:
if depth == 0 {
p.WriteString(invReflectString)
} else {
switch verb {
case 'v':
p.WriteString(nilAngleString)
default:
p.badVerb(verb)
}
}
case reflect.Bool:
p.fmtBool(f.Bool(), verb)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p.fmtInteger(uint64(f.Int()), signed, verb)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p.fmtInteger(f.Uint(), unsigned, verb)
case reflect.Float32:
p.fmtFloat(f.Float(), 32, verb)
case reflect.Float64:
p.fmtFloat(f.Float(), 64, verb)
case reflect.Complex64:
p.fmtComplex(f.Complex(), 64, verb)
case reflect.Complex128:
p.fmtComplex(f.Complex(), 128, verb)
case reflect.String:
p.fmtString(f.String(), verb)
case reflect.Map:
if p.fmt.SharpV {
p.WriteString(f.Type().String())
if f.IsNil() {
p.WriteString(nilParenString)
return
}
p.WriteByte('{')
} else {
p.WriteString(mapString)
}
keys := f.MapKeys()
for i, key := range keys {
if i > 0 {
if p.fmt.SharpV {
p.WriteString(commaSpaceString)
} else {
p.WriteByte(' ')
}
}
p.printValue(key, verb, depth+1)
p.WriteByte(':')
p.printValue(f.MapIndex(key), verb, depth+1)
}
if p.fmt.SharpV {
p.WriteByte('}')
} else {
p.WriteByte(']')
}
case reflect.Struct:
if p.fmt.SharpV {
p.WriteString(f.Type().String())
}
p.WriteByte('{')
for i := 0; i < f.NumField(); i++ {
if i > 0 {
if p.fmt.SharpV {
p.WriteString(commaSpaceString)
} else {
p.WriteByte(' ')
}
}
if p.fmt.PlusV || p.fmt.SharpV {
if name := f.Type().Field(i).Name; name != "" {
p.WriteString(name)
p.WriteByte(':')
}
}
p.printValue(getField(f, i), verb, depth+1)
}
p.WriteByte('}')
case reflect.Interface:
value := f.Elem()
if !value.IsValid() {
if p.fmt.SharpV {
p.WriteString(f.Type().String())
p.WriteString(nilParenString)
} else {
p.WriteString(nilAngleString)
}
} else {
p.printValue(value, verb, depth+1)
}
case reflect.Array, reflect.Slice:
switch verb {
case 's', 'q', 'x', 'X':
// Handle byte and uint8 slices and arrays special for the above verbs.
t := f.Type()
if t.Elem().Kind() == reflect.Uint8 {
var bytes []byte
if f.Kind() == reflect.Slice {
bytes = f.Bytes()
} else if f.CanAddr() {
bytes = f.Slice(0, f.Len()).Bytes()
} else {
// We have an array, but we cannot Slice() a non-addressable array,
// so we build a slice by hand. This is a rare case but it would be nice
// if reflection could help a little more.
bytes = make([]byte, f.Len())
for i := range bytes {
bytes[i] = byte(f.Index(i).Uint())
}
}
p.fmtBytes(bytes, verb, t.String())
return
}
}
if p.fmt.SharpV {
p.WriteString(f.Type().String())
if f.Kind() == reflect.Slice && f.IsNil() {
p.WriteString(nilParenString)
return
}
p.WriteByte('{')
for i := 0; i < f.Len(); i++ {
if i > 0 {
p.WriteString(commaSpaceString)
}
p.printValue(f.Index(i), verb, depth+1)
}
p.WriteByte('}')
} else {
p.WriteByte('[')
for i := 0; i < f.Len(); i++ {
if i > 0 {
p.WriteByte(' ')
}
p.printValue(f.Index(i), verb, depth+1)
}
p.WriteByte(']')
}
case reflect.Ptr:
// pointer to array or slice or struct? ok at top level
// but not embedded (avoid loops)
if depth == 0 && f.Pointer() != 0 {
switch a := f.Elem(); a.Kind() {
case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
p.WriteByte('&')
p.printValue(a, verb, depth+1)
return
}
}
fallthrough
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
p.fmtPointer(f, verb)
default:
p.unknownType(f)
}
}
func (p *printer) badArgNum(verb rune) {
p.WriteString(percentBangString)
p.WriteRune(verb)
p.WriteString(badIndexString)
}
func (p *printer) missingArg(verb rune) {
p.WriteString(percentBangString)
p.WriteRune(verb)
p.WriteString(missingString)
}
func (p *printer) doPrintf(fmt string) {
for p.fmt.Parser.SetFormat(fmt); p.fmt.Scan(); {
switch p.fmt.Status {
case format.StatusText:
p.WriteString(p.fmt.Text())
case format.StatusSubstitution:
p.printArg(p.Arg(p.fmt.ArgNum), p.fmt.Verb)
case format.StatusBadWidthSubstitution:
p.WriteString(badWidthString)
p.printArg(p.Arg(p.fmt.ArgNum), p.fmt.Verb)
case format.StatusBadPrecSubstitution:
p.WriteString(badPrecString)
p.printArg(p.Arg(p.fmt.ArgNum), p.fmt.Verb)
case format.StatusNoVerb:
p.WriteString(noVerbString)
case format.StatusBadArgNum:
p.badArgNum(p.fmt.Verb)
case format.StatusMissingArg:
p.missingArg(p.fmt.Verb)
default:
panic("unreachable")
}
}
// Check for extra arguments, but only if there was at least one ordered
// argument. Note that this behavior is necessarily different from fmt:
// different variants of messages may opt to drop some or all of the
// arguments.
if !p.fmt.Reordered && p.fmt.ArgNum < len(p.fmt.Args) && p.fmt.ArgNum != 0 {
p.fmt.ClearFlags()
p.WriteString(extraString)
for i, arg := range p.fmt.Args[p.fmt.ArgNum:] {
if i > 0 {
p.WriteString(commaSpaceString)
}
if arg == nil {
p.WriteString(nilAngleString)
} else {
p.WriteString(reflect.TypeOf(arg).String())
p.WriteString("=")
p.printArg(arg, 'v')
}
}
p.WriteByte(')')
}
}
func (p *printer) doPrint(a []interface{}) {
prevString := false
for argNum, arg := range a {
isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
// Add a space between two non-string arguments.
if argNum > 0 && !isString && !prevString {
p.WriteByte(' ')
}
p.printArg(arg, 'v')
prevString = isString
}
}
// doPrintln is like doPrint but always adds a space between arguments
// and a newline after the last argument.
func (p *printer) doPrintln(a []interface{}) {
for argNum, arg := range a {
if argNum > 0 {
p.WriteByte(' ')
}
p.printArg(arg, 'v')
}
p.WriteByte('\n')
}