internal/format: move fmtFlags to internal/format

And rename it to Parser.
Also export fields that need to be exported.

Change-Id: Iacc8b783a6756cd876caf6e3dd3d4c90b57209c6
Reviewed-on: https://go-review.googlesource.com/79236
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/internal/format/parser.go b/internal/format/parser.go
new file mode 100644
index 0000000..68a8e8e
--- /dev/null
+++ b/internal/format/parser.go
@@ -0,0 +1,352 @@
+// 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
+
+	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
+}
+
+// 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)
+	}
+
+	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
+}
diff --git a/internal/format/parser_test.go b/internal/format/parser_test.go
new file mode 100644
index 0000000..7229908
--- /dev/null
+++ b/internal/format/parser_test.go
@@ -0,0 +1,32 @@
+// 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 "testing"
+
+// TODO: most of Parser is tested in x/message. Move some tests here.
+
+func TestParsenum(t *testing.T) {
+	testCases := []struct {
+		s          string
+		start, end int
+		num        int
+		isnum      bool
+		newi       int
+	}{
+		{"a123", 0, 4, 0, false, 0},
+		{"1234", 1, 1, 0, false, 1},
+		{"123a", 0, 4, 123, true, 3},
+		{"12a3", 0, 4, 12, true, 2},
+		{"1234", 0, 4, 1234, true, 4},
+		{"1a234", 1, 3, 0, false, 1},
+	}
+	for _, tt := range testCases {
+		num, isnum, newi := parsenum(tt.s, tt.start, tt.end)
+		if num != tt.num || isnum != tt.isnum || newi != tt.newi {
+			t.Errorf("parsenum(%q, %d, %d) = %d, %v, %d, want %d, %v, %d", tt.s, tt.start, tt.end, num, isnum, newi, tt.num, tt.isnum, tt.newi)
+		}
+	}
+}
diff --git a/message/fmt_test.go b/message/fmt_test.go
index 45baebe..aea36d8 100755
--- a/message/fmt_test.go
+++ b/message/fmt_test.go
@@ -1869,26 +1869,3 @@
 		}
 	}
 }
-
-func TestParsenum(t *testing.T) {
-	testCases := []struct {
-		s          string
-		start, end int
-		num        int
-		isnum      bool
-		newi       int
-	}{
-		{"a123", 0, 4, 0, false, 0},
-		{"1234", 1, 1, 0, false, 1},
-		{"123a", 0, 4, 123, true, 3},
-		{"12a3", 0, 4, 12, true, 2},
-		{"1234", 0, 4, 1234, true, 4},
-		{"1a234", 1, 3, 0, false, 1},
-	}
-	for _, tt := range testCases {
-		num, isnum, newi := parsenum(tt.s, tt.start, tt.end)
-		if num != tt.num || isnum != tt.isnum || newi != tt.newi {
-			t.Errorf("parsenum(%q, %d, %d) = %d, %v, %d, want %d, %v, %d", tt.s, tt.start, tt.end, num, isnum, newi, tt.num, tt.isnum, tt.newi)
-		}
-	}
-}
diff --git a/message/format.go b/message/format.go
index d5534ae..9fb4c6b 100644
--- a/message/format.go
+++ b/message/format.go
@@ -8,6 +8,8 @@
 	"bytes"
 	"strconv"
 	"unicode/utf8"
+
+	"golang.org/x/text/internal/format"
 )
 
 const (
@@ -20,93 +22,20 @@
 	unsigned = false
 )
 
-// flags placed in a separate struct for easy clearing.
-type fmtFlags struct {
-	verb rune
-
-	widPresent  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
-
-	wid  int // width
-	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
-	state    state
-}
-
-func (p *fmtFlags) Text() string { return p.format[p.startPos:p.endPos] }
-
-func (p *fmtFlags) init(format string) {
-	p.format = format
-	p.startPos = 0
-	p.endPos = 0
-	p.argNum = 0
-}
-
-type state int
-
-const (
-	text state = iota
-	substitution
-	badWidth
-	badPrec
-	noVerb
-	badArgNum
-	missingArg
-)
-
 // A formatInfo is the raw formatter used by Printf etc.
 // It prints into a buffer that must be set up separately.
 type formatInfo struct {
 	buf *bytes.Buffer
 
-	fmtFlags
+	format.Parser
 
 	// intbuf is large enough to store %b of an int64 with a sign and
 	// avoids padding at the end of the struct on 32 bit architectures.
 	intbuf [68]byte
 }
 
-func (f *fmtFlags) clearflags() {
-	f.widPresent = false
-	f.precPresent = false
-	f.minus = false
-	f.plus = false
-	f.sharp = false
-	f.space = false
-	f.zero = false
-
-	f.plusV = false
-	f.sharpV = false
-}
-
 func (f *formatInfo) init(buf *bytes.Buffer) {
 	f.buf = buf
-	f.clearflags()
 }
 
 // writePadding generates n bytes of padding.
@@ -117,7 +46,7 @@
 	f.buf.Grow(n)
 	// Decide which byte the padding should be filled with.
 	padByte := byte(' ')
-	if f.zero {
+	if f.Zero {
 		padByte = byte('0')
 	}
 	// Fill padding with padByte.
@@ -128,12 +57,12 @@
 
 // pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
 func (f *formatInfo) pad(b []byte) {
-	if !f.widPresent || f.wid == 0 {
+	if !f.WidthPresent || f.Width == 0 {
 		f.buf.Write(b)
 		return
 	}
-	width := f.wid - utf8.RuneCount(b)
-	if !f.minus {
+	width := f.Width - utf8.RuneCount(b)
+	if !f.Minus {
 		// left padding
 		f.writePadding(width)
 		f.buf.Write(b)
@@ -146,12 +75,12 @@
 
 // padString appends s to f.buf, padded on left (!f.minus) or right (f.minus).
 func (f *formatInfo) padString(s string) {
-	if !f.widPresent || f.wid == 0 {
+	if !f.WidthPresent || f.Width == 0 {
 		f.buf.WriteString(s)
 		return
 	}
-	width := f.wid - utf8.RuneCountInString(s)
-	if !f.minus {
+	width := f.Width - utf8.RuneCountInString(s)
+	if !f.Minus {
 		// left padding
 		f.writePadding(width)
 		f.buf.WriteString(s)
@@ -179,8 +108,8 @@
 	// for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits
 	// into the already allocated intbuf with a capacity of 68 bytes.
 	prec := 4
-	if f.precPresent && f.prec > 4 {
-		prec = f.prec
+	if f.PrecPresent && f.Prec > 4 {
+		prec = f.Prec
 		// Compute space needed for "U+" , number, " '", character, "'".
 		width := 2 + prec + 2 + utf8.UTFMax + 1
 		if width > len(buf) {
@@ -192,7 +121,7 @@
 	i := len(buf)
 
 	// For %#U we want to add a space and a quoted character at the end of the buffer.
-	if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
+	if f.Sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
 		i--
 		buf[i] = '\''
 		i -= utf8.RuneLen(rune(u))
@@ -224,10 +153,10 @@
 	i--
 	buf[i] = 'U'
 
-	oldZero := f.zero
-	f.zero = false
+	oldZero := f.Zero
+	f.Zero = false
 	f.pad(buf[i:])
-	f.zero = oldZero
+	f.Zero = oldZero
 }
 
 // fmt_integer formats signed and unsigned integers.
@@ -240,9 +169,9 @@
 	buf := f.intbuf[0:]
 	// The already allocated f.intbuf with a capacity of 68 bytes
 	// is large enough for integer formatting when no precision or width is set.
-	if f.widPresent || f.precPresent {
+	if f.WidthPresent || f.PrecPresent {
 		// Account 3 extra bytes for possible addition of a sign and "0x".
-		width := 3 + f.wid + f.prec // wid and prec are always positive.
+		width := 3 + f.Width + f.Prec // wid and prec are always positive.
 		if width > len(buf) {
 			// We're going to need a bigger boat.
 			buf = make([]byte, width)
@@ -253,19 +182,19 @@
 	// If both are specified the f.zero flag is ignored and
 	// padding with spaces is used instead.
 	prec := 0
-	if f.precPresent {
-		prec = f.prec
+	if f.PrecPresent {
+		prec = f.Prec
 		// Precision of 0 and value of 0 means "print nothing" but padding.
 		if prec == 0 && u == 0 {
-			oldZero := f.zero
-			f.zero = false
-			f.writePadding(f.wid)
-			f.zero = oldZero
+			oldZero := f.Zero
+			f.Zero = false
+			f.writePadding(f.Width)
+			f.Zero = oldZero
 			return
 		}
-	} else if f.zero && f.widPresent {
-		prec = f.wid
-		if negative || f.plus || f.space {
+	} else if f.Zero && f.WidthPresent {
+		prec = f.Width
+		if negative || f.Plus || f.Space {
 			prec-- // leave room for sign
 		}
 	}
@@ -313,7 +242,7 @@
 	}
 
 	// Various prefixes: 0x, -, etc.
-	if f.sharp {
+	if f.Sharp {
 		switch base {
 		case 8:
 			if buf[i] != '0' {
@@ -332,26 +261,26 @@
 	if negative {
 		i--
 		buf[i] = '-'
-	} else if f.plus {
+	} else if f.Plus {
 		i--
 		buf[i] = '+'
-	} else if f.space {
+	} else if f.Space {
 		i--
 		buf[i] = ' '
 	}
 
 	// Left padding with zeros has already been handled like precision earlier
 	// or the f.zero flag is ignored due to an explicitly set precision.
-	oldZero := f.zero
-	f.zero = false
+	oldZero := f.Zero
+	f.Zero = false
 	f.pad(buf[i:])
-	f.zero = oldZero
+	f.Zero = oldZero
 }
 
 // truncate truncates the string to the specified precision, if present.
 func (f *formatInfo) truncate(s string) string {
-	if f.precPresent {
-		n := f.prec
+	if f.PrecPresent {
+		n := f.Prec
 		for i := range s {
 			n--
 			if n < 0 {
@@ -376,46 +305,46 @@
 		length = len(s)
 	}
 	// Set length to not process more bytes than the precision demands.
-	if f.precPresent && f.prec < length {
-		length = f.prec
+	if f.PrecPresent && f.Prec < length {
+		length = f.Prec
 	}
 	// Compute width of the encoding taking into account the f.sharp and f.space flag.
 	width := 2 * length
 	if width > 0 {
-		if f.space {
+		if f.Space {
 			// Each element encoded by two hexadecimals will get a leading 0x or 0X.
-			if f.sharp {
+			if f.Sharp {
 				width *= 2
 			}
 			// Elements will be separated by a space.
 			width += length - 1
-		} else if f.sharp {
+		} else if f.Sharp {
 			// Only a leading 0x or 0X will be added for the whole string.
 			width += 2
 		}
 	} else { // The byte slice or string that should be encoded is empty.
-		if f.widPresent {
-			f.writePadding(f.wid)
+		if f.WidthPresent {
+			f.writePadding(f.Width)
 		}
 		return
 	}
 	// Handle padding to the left.
-	if f.widPresent && f.wid > width && !f.minus {
-		f.writePadding(f.wid - width)
+	if f.WidthPresent && f.Width > width && !f.Minus {
+		f.writePadding(f.Width - width)
 	}
 	// Write the encoding directly into the output buffer.
 	buf := f.buf
-	if f.sharp {
+	if f.Sharp {
 		// Add leading 0x or 0X.
 		buf.WriteByte('0')
 		buf.WriteByte(digits[16])
 	}
 	var c byte
 	for i := 0; i < length; i++ {
-		if f.space && i > 0 {
+		if f.Space && i > 0 {
 			// Separate elements with a space.
 			buf.WriteByte(' ')
-			if f.sharp {
+			if f.Sharp {
 				// Add leading 0x or 0X for each element.
 				buf.WriteByte('0')
 				buf.WriteByte(digits[16])
@@ -431,8 +360,8 @@
 		buf.WriteByte(digits[c&0xF])
 	}
 	// Handle padding to the right.
-	if f.widPresent && f.wid > width && f.minus {
-		f.writePadding(f.wid - width)
+	if f.WidthPresent && f.Width > width && f.Minus {
+		f.writePadding(f.Width - width)
 	}
 }
 
@@ -451,12 +380,12 @@
 // if the string does not contain any control characters other than tab.
 func (f *formatInfo) fmt_q(s string) {
 	s = f.truncate(s)
-	if f.sharp && strconv.CanBackquote(s) {
+	if f.Sharp && strconv.CanBackquote(s) {
 		f.padString("`" + s + "`")
 		return
 	}
 	buf := f.intbuf[:0]
-	if f.plus {
+	if f.Plus {
 		f.pad(strconv.AppendQuoteToASCII(buf, s))
 	} else {
 		f.pad(strconv.AppendQuote(buf, s))
@@ -483,7 +412,7 @@
 		r = utf8.RuneError
 	}
 	buf := f.intbuf[:0]
-	if f.plus {
+	if f.Plus {
 		f.pad(strconv.AppendQuoteRuneToASCII(buf, r))
 	} else {
 		f.pad(strconv.AppendQuoteRune(buf, r))
@@ -494,8 +423,8 @@
 // for strconv.AppendFloat and therefore fits into a byte.
 func (f *formatInfo) fmt_float(v float64, size int, verb rune, prec int) {
 	// Explicit precision in format specifier overrules default precision.
-	if f.precPresent {
-		prec = f.prec
+	if f.PrecPresent {
+		prec = f.Prec
 	}
 	// Format number, reserving space for leading + sign if needed.
 	num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size)
@@ -506,25 +435,25 @@
 	}
 	// f.space means to add a leading space instead of a "+" sign unless
 	// the sign is explicitly asked for by f.plus.
-	if f.space && num[0] == '+' && !f.plus {
+	if f.Space && num[0] == '+' && !f.Plus {
 		num[0] = ' '
 	}
 	// Special handling for infinities and NaN,
 	// which don't look like a number so shouldn't be padded with zeros.
 	if num[1] == 'I' || num[1] == 'N' {
-		oldZero := f.zero
-		f.zero = false
+		oldZero := f.Zero
+		f.Zero = false
 		// Remove sign before NaN if not asked for.
-		if num[1] == 'N' && !f.space && !f.plus {
+		if num[1] == 'N' && !f.Space && !f.Plus {
 			num = num[1:]
 		}
 		f.pad(num)
-		f.zero = oldZero
+		f.Zero = oldZero
 		return
 	}
 	// The sharp flag forces printing a decimal point for non-binary formats
 	// and retains trailing zeros, which we may need to restore.
-	if f.sharp && verb != 'b' {
+	if f.Sharp && verb != 'b' {
 		digits := 0
 		switch verb {
 		case 'v', 'g', 'G':
@@ -563,12 +492,12 @@
 		num = append(num, tail...)
 	}
 	// We want a sign if asked for and if the sign is not positive.
-	if f.plus || num[0] != '+' {
+	if f.Plus || num[0] != '+' {
 		// If we're zero padding to the left we want the sign before the leading zeros.
 		// Achieve this by writing the sign out and then padding the unsigned number.
-		if f.zero && f.widPresent && f.wid > len(num) {
+		if f.Zero && f.WidthPresent && f.Width > len(num) {
 			f.buf.WriteByte(num[0])
-			f.writePadding(f.wid - len(num))
+			f.writePadding(f.Width - len(num))
 			f.buf.Write(num[1:])
 			return
 		}
diff --git a/message/message.go b/message/message.go
index db80e47..8a84d0d 100644
--- a/message/message.go
+++ b/message/message.go
@@ -120,7 +120,7 @@
 
 func lookupAndFormat(p *Printer, r Reference, a []interface{}) {
 	p.printer.reset()
-	p.printer.fmt.args = a
+	p.printer.fmt.Reset(a)
 	var id, msg string
 	switch v := r.(type) {
 	case string:
@@ -142,8 +142,8 @@
 // Arg implements catmsg.Renderer.
 func (p *printer) Arg(i int) interface{} { // TODO, also return "ok" bool
 	i--
-	if uint(i) < uint(len(p.fmt.args)) {
-		return p.fmt.args[i]
+	if uint(i) < uint(len(p.fmt.Args)) {
+		return p.fmt.Args[i]
 	}
 	return nil
 }
diff --git a/message/print.go b/message/print.go
index 4d80d51..5a55d41 100644
--- a/message/print.go
+++ b/message/print.go
@@ -67,9 +67,6 @@
 
 func (p *printer) reset() {
 	p.Buffer.Reset()
-	p.fmt.argNum = 0
-	p.fmt.startPos = 0
-	p.fmt.reordered = false
 	p.panicking = false
 	p.erroring = false
 	p.fmt.init(&p.Buffer)
@@ -78,22 +75,22 @@
 // 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.wid, p.fmt.widPresent }
+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) 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
+		return p.fmt.Minus
 	case '+':
-		return p.fmt.plus || p.fmt.plusV
+		return p.fmt.Plus || p.fmt.PlusV
 	case '#':
-		return p.fmt.sharp || p.fmt.sharpV
+		return p.fmt.Sharp || p.fmt.SharpV
 	case ' ':
-		return p.fmt.space
+		return p.fmt.Space
 	case '0':
-		return p.fmt.zero
+		return p.fmt.Zero
 	}
 	return false
 }
@@ -109,28 +106,6 @@
 	return val
 }
 
-// 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
-}
-
 func (p *printer) unknownType(v reflect.Value) {
 	if !v.IsValid() {
 		p.WriteString(nilAngleString)
@@ -174,23 +149,23 @@
 // 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
+	sharp := p.fmt.Sharp
+	p.fmt.Sharp = leading0x
 	p.fmt.fmt_integer(v, 16, unsigned, ldigits)
-	p.fmt.sharp = sharp
+	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 {
+		if p.fmt.SharpV && !isSigned {
 			p.fmt0x64(v, true)
 			return
 		}
 		fallthrough
 	case 'd':
-		if p.fmt.sharp || p.fmt.sharpV {
+		if p.fmt.Sharp || p.fmt.SharpV {
 			p.fmt.fmt_integer(v, 10, isSigned, ldigits)
 		} else {
 			p.fmtDecimalInt(v, isSigned)
@@ -228,19 +203,19 @@
 		verb = 'g'
 		fallthrough
 	case 'g', 'G':
-		if p.fmt.sharp || p.fmt.sharpV {
+		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 {
+		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 {
+		if p.fmt.Sharp || p.fmt.SharpV {
 			p.fmt.fmt_float(v, size, verb, 6)
 		} else {
 			p.fmtDecimalFloat(v, size, 6)
@@ -252,9 +227,9 @@
 
 func (p *printer) setFlags(f *number.Formatter) {
 	f.Flags &^= number.ElideSign
-	if p.fmt.plus || p.fmt.space {
+	if p.fmt.Plus || p.fmt.Space {
 		f.Flags |= number.AlwaysSign
-		if !p.fmt.plus {
+		if !p.fmt.Plus {
 			f.Flags |= number.ElideSign
 		}
 	} else {
@@ -264,13 +239,13 @@
 
 func (p *printer) updatePadding(f *number.Formatter) {
 	f.Flags &^= number.PadMask
-	if p.fmt.minus {
+	if p.fmt.Minus {
 		f.Flags |= number.PadAfterSuffix
 	} else {
 		f.Flags |= number.PadBeforePrefix
 	}
 	f.PadRune = ' '
-	f.FormatWidth = uint16(p.fmt.wid)
+	f.FormatWidth = uint16(p.fmt.Width)
 }
 
 func (p *printer) initDecimal(minFrac, maxFrac int) {
@@ -281,15 +256,15 @@
 	f.MaxFractionDigits = int16(maxFrac)
 	p.setFlags(f)
 	f.PadRune = 0
-	if p.fmt.widPresent {
-		if p.fmt.zero {
-			wid := p.fmt.wid
+	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 {
+			if p.fmt.Plus || p.fmt.Space {
 				wid--
 			}
 			if wid > 0 && wid > int(f.MinIntegerDigits) {
@@ -312,9 +287,9 @@
 	f.MinExponentDigits = 2
 	p.setFlags(f)
 	f.PadRune = 0
-	if p.fmt.widPresent {
+	if p.fmt.WidthPresent {
 		f.Flags &^= number.PadMask
-		if p.fmt.zero {
+		if p.fmt.Zero {
 			f.PadRune = f.Digit(0)
 			f.Flags |= number.PadAfterPrefix
 		} else {
@@ -329,13 +304,13 @@
 	var d number.Decimal
 
 	f := &p.toDecimal
-	if p.fmt.precPresent {
+	if p.fmt.PrecPresent {
 		p.setFlags(f)
-		f.MinIntegerDigits = uint8(p.fmt.prec)
+		f.MinIntegerDigits = uint8(p.fmt.Prec)
 		f.MaxIntegerDigits = 0
 		f.MinFractionDigits = 0
 		f.MaxFractionDigits = 0
-		if p.fmt.widPresent {
+		if p.fmt.WidthPresent {
 			p.updatePadding(f)
 		}
 	} else {
@@ -349,8 +324,8 @@
 
 func (p *printer) fmtDecimalFloat(v float64, size, prec int) {
 	var d number.Decimal
-	if p.fmt.precPresent {
-		prec = p.fmt.prec
+	if p.fmt.PrecPresent {
+		prec = p.fmt.Prec
 	}
 	p.initDecimal(prec, prec)
 	d.ConvertFloat(p.toDecimal.RoundingContext, v, size)
@@ -361,8 +336,8 @@
 
 func (p *printer) fmtVariableFloat(v float64, size int) {
 	prec := -1
-	if p.fmt.precPresent {
-		prec = p.fmt.prec
+	if p.fmt.PrecPresent {
+		prec = p.fmt.Prec
 	}
 	var d number.Decimal
 	p.initScientific(0, prec)
@@ -401,8 +376,8 @@
 
 func (p *printer) fmtScientific(v float64, size, prec int) {
 	var d number.Decimal
-	if p.fmt.precPresent {
-		prec = p.fmt.prec
+	if p.fmt.PrecPresent {
+		prec = p.fmt.Prec
 	}
 	p.initScientific(prec, prec)
 	rc := p.toScientific.RoundingContext
@@ -450,11 +425,11 @@
 			p.WriteString("i)")
 			return
 		}
-		oldPlus := p.fmt.plus
-		p.fmt.plus = true
+		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
+		p.fmt.Plus = oldPlus
 	default:
 		p.badVerb(verb)
 	}
@@ -463,7 +438,7 @@
 func (p *printer) fmtString(v string, verb rune) {
 	switch verb {
 	case 'v':
-		if p.fmt.sharpV {
+		if p.fmt.SharpV {
 			p.fmt.fmt_q(v)
 		} else {
 			p.fmt.fmt_s(v)
@@ -484,7 +459,7 @@
 func (p *printer) fmtBytes(v []byte, verb rune, typeString string) {
 	switch verb {
 	case 'v', 'd':
-		if p.fmt.sharpV {
+		if p.fmt.SharpV {
 			p.WriteString(typeString)
 			if v == nil {
 				p.WriteString(nilParenString)
@@ -533,7 +508,7 @@
 
 	switch verb {
 	case 'v':
-		if p.fmt.sharpV {
+		if p.fmt.SharpV {
 			p.WriteByte('(')
 			p.WriteString(value.Type().String())
 			p.WriteString(")(")
@@ -547,14 +522,14 @@
 			if u == 0 {
 				p.fmt.padString(nilAngleString)
 			} else {
-				p.fmt0x64(uint64(u), !p.fmt.sharp)
+				p.fmt0x64(uint64(u), !p.fmt.Sharp)
 			}
 		}
 	case 'p':
-		p.fmt0x64(uint64(u), !p.fmt.sharp)
+		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.fmt.Sharp = true // Print as standard go. TODO: does this make sense?
 		}
 		p.fmtInteger(uint64(u), unsigned, verb)
 	default:
@@ -578,9 +553,9 @@
 			panic(err)
 		}
 
-		oldFlags := p.fmt.fmtFlags
+		oldFlags := p.fmt.Parser
 		// For this output we want default behavior.
-		p.fmt.clearflags()
+		p.fmt.ClearFlags()
 
 		p.WriteString(percentBangString)
 		p.WriteRune(verb)
@@ -590,7 +565,7 @@
 		p.panicking = false
 		p.WriteByte(')')
 
-		p.fmt.fmtFlags = oldFlags
+		p.fmt.Parser = oldFlags
 	}
 }
 
@@ -613,7 +588,7 @@
 	}
 
 	// If we're doing Go syntax and the argument knows how to supply it, take care of it now.
-	if p.fmt.sharpV {
+	if p.fmt.SharpV {
 		if stringer, ok := p.arg.(fmt.GoStringer); ok {
 			handled = true
 			defer p.catchPanic(p.arg, verb)
@@ -774,7 +749,7 @@
 	case reflect.String:
 		p.fmtString(f.String(), verb)
 	case reflect.Map:
-		if p.fmt.sharpV {
+		if p.fmt.SharpV {
 			p.WriteString(f.Type().String())
 			if f.IsNil() {
 				p.WriteString(nilParenString)
@@ -787,7 +762,7 @@
 		keys := f.MapKeys()
 		for i, key := range keys {
 			if i > 0 {
-				if p.fmt.sharpV {
+				if p.fmt.SharpV {
 					p.WriteString(commaSpaceString)
 				} else {
 					p.WriteByte(' ')
@@ -797,25 +772,25 @@
 			p.WriteByte(':')
 			p.printValue(f.MapIndex(key), verb, depth+1)
 		}
-		if p.fmt.sharpV {
+		if p.fmt.SharpV {
 			p.WriteByte('}')
 		} else {
 			p.WriteByte(']')
 		}
 	case reflect.Struct:
-		if p.fmt.sharpV {
+		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 {
+				if p.fmt.SharpV {
 					p.WriteString(commaSpaceString)
 				} else {
 					p.WriteByte(' ')
 				}
 			}
-			if p.fmt.plusV || p.fmt.sharpV {
+			if p.fmt.PlusV || p.fmt.SharpV {
 				if name := f.Type().Field(i).Name; name != "" {
 					p.WriteString(name)
 					p.WriteByte(':')
@@ -827,7 +802,7 @@
 	case reflect.Interface:
 		value := f.Elem()
 		if !value.IsValid() {
-			if p.fmt.sharpV {
+			if p.fmt.SharpV {
 				p.WriteString(f.Type().String())
 				p.WriteString(nilParenString)
 			} else {
@@ -860,7 +835,7 @@
 				return
 			}
 		}
-		if p.fmt.sharpV {
+		if p.fmt.SharpV {
 			p.WriteString(f.Type().String())
 			if f.Kind() == reflect.Slice && f.IsNil() {
 				p.WriteString(nilParenString)
@@ -903,81 +878,6 @@
 	}
 }
 
-// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type.
-func (p *fmtFlags) 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 *fmtFlags) 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
-}
-
 func (p *printer) badArgNum(verb rune) {
 	p.WriteString(percentBangString)
 	p.WriteRune(verb)
@@ -990,182 +890,25 @@
 	p.WriteString(missingString)
 }
 
-func (p *fmtFlags) Scan() bool {
-	p.state = text
-	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.state = substitution
-
-	// 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.wid, p.widPresent = p.intFromArg()
-
-		if !p.widPresent {
-			p.state = badWidth
-		}
-
-		// We have a negative width, so take its value and ensure
-		// that the minus flag is set
-		if p.wid < 0 {
-			p.wid = -p.wid
-			p.minus = true
-			p.zero = false // Do not pad with zeros to the right.
-		}
-		afterIndex = false
-	} else {
-		p.wid, p.widPresent, i = parsenum(format, i, end)
-		if afterIndex && p.widPresent { // "%[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.state = badPrec
-			}
-			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)
-	}
-
-	if i >= end {
-		p.endPos = i
-		p.state = noVerb
-		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.state = text
-	case !p.goodArgNum:
-		p.state = badArgNum
-	case p.argNum >= len(p.args): // No argument left over to print for the current verb.
-		p.state = missingArg
-	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
-}
-
-func (p *printer) doPrintf(format string) {
-	p.fmt.fmtFlags.init(format)
-
-	for p.fmt.Scan() {
-		switch p.fmt.state {
-		case text:
+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 substitution:
-			p.printArg(p.Arg(p.fmt.argNum), p.fmt.verb)
-		case badWidth:
+		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 badPrec:
+			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 noVerb:
+			p.printArg(p.Arg(p.fmt.ArgNum), p.fmt.Verb)
+		case format.StatusNoVerb:
 			p.WriteString(noVerbString)
-		case badArgNum:
-			p.badArgNum(p.fmt.verb)
-		case missingArg:
-			p.missingArg(p.fmt.verb)
+		case format.StatusBadArgNum:
+			p.badArgNum(p.fmt.Verb)
+		case format.StatusMissingArg:
+			p.missingArg(p.fmt.Verb)
 		default:
 			panic("unreachable")
 		}
@@ -1175,10 +918,10 @@
 	// 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()
+	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:] {
+		for i, arg := range p.fmt.Args[p.fmt.ArgNum:] {
 			if i > 0 {
 				p.WriteString(commaSpaceString)
 			}