summaryrefslogtreecommitdiff
path: root/src/pkg/fmt
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/fmt')
-rw-r--r--src/pkg/fmt/doc.go11
-rw-r--r--src/pkg/fmt/fmt_test.go21
-rw-r--r--src/pkg/fmt/format.go7
-rw-r--r--src/pkg/fmt/print.go15
-rw-r--r--src/pkg/fmt/scan.go41
-rw-r--r--src/pkg/fmt/scan_test.go62
6 files changed, 135 insertions, 22 deletions
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index e4d4f1844..79fe5758c 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -25,9 +25,10 @@
%c the character represented by the corresponding Unicode code point
%d base 10
%o base 8
+ %q a single-quoted character literal safely escaped with Go syntax.
%x base 16, with lower-case letters for a-f
%X base 16, with upper-case letters for A-F
- %U Unicode format: U+1234; same as "U+%0.4X"
+ %U Unicode format: U+1234; same as "U+%04X"
Floating-point and complex constituents:
%b decimalless scientific notation with exponent a power
of two, in the manner of strconv.Ftoa32, e.g. -123456p-78
@@ -134,10 +135,10 @@
The formats behave analogously to those of Printf with the
following exceptions:
- %p is not implemented
- %T is not implemented
- %e %E %f %F %g %g are all equivalent and scan any floating point or complex value
- %s and %v on strings scan a space-delimited token
+ %p is not implemented
+ %T is not implemented
+ %e %E %f %F %g %G are all equivalent and scan any floating point or complex value
+ %s and %v on strings scan a space-delimited token
The familiar base-setting prefixes 0 (octal) and 0x
(hexadecimal) are accepted when scanning integers without a
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index b3c0c5abe..122b9516b 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -132,12 +132,23 @@ var fmttests = []struct {
{"%q", `"`, `"\""`},
{"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`},
{"%q", "abc\xffdef", `"abc\xffdef"`},
- {"%q", "\u263a", `"\u263a"`},
+ {"%q", "\u263a", `"☺"`},
{"%q", "\U0010ffff", `"\U0010ffff"`},
+ // escaped characters
+ {"%q", 'x', `'x'`},
+ {"%q", 0, `'\x00'`},
+ {"%q", '\n', `'\n'`},
+ {"%q", '\u0e00', `'\u0e00'`}, // not a printable rune.
+ {"%q", '\U000c2345', `'\U000c2345'`}, // not a printable rune.
+ {"%q", int64(0x7FFFFFFF), `%!q(int64=2147483647)`},
+ {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`},
+ {"%q", '"', `'"'`},
+ {"%q", '\'', `'\''`},
+
// width
{"%5s", "abc", " abc"},
- {"%2s", "\u263a", " \u263a"},
+ {"%2s", "\u263a", " ☺"},
{"%-5s", "abc", "abc "},
{"%-8q", "abc", `"abc" `},
{"%05s", "abc", "00abc"},
@@ -147,9 +158,9 @@ var fmttests = []struct {
{"%.5s", "日本語日本語", "日本語日本"},
{"%.5s", []byte("日本語日本語"), "日本語日本"},
{"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`},
- {"%.3q", "日本語日本語", `"\u65e5\u672c\u8a9e"`},
- {"%.3q", []byte("日本語日本語"), `"\u65e5\u672c\u8a9e"`},
- {"%10.1q", "日本語日本語", ` "\u65e5"`},
+ {"%.3q", "日本語日本語", `"日本語"`},
+ {"%.3q", []byte("日本語日本語"), `"日本語"`},
+ {"%10.1q", "日本語日本語", ` "日"`},
// integers
{"%d", 12345, "12345"},
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index f9d2b4fca..5dcfb9677 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -296,6 +296,13 @@ func (f *fmt) fmt_q(s string) {
f.padString(quoted)
}
+// fmt_qc formats the integer as a single-quoted, escaped Go character constant.
+// If the character is not valid Unicode, it will print '\ufffd'.
+func (f *fmt) fmt_qc(c int64) {
+ quoted := strconv.QuoteRune(int(c))
+ f.padString(quoted)
+}
+
// floating-point
func doPrec(f *fmt, def int) int {
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 10e0fe7c8..c18a8ea38 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -9,6 +9,7 @@ import (
"io"
"os"
"reflect"
+ "unicode"
"utf8"
)
@@ -41,7 +42,7 @@ type State interface {
Precision() (prec int, ok bool)
// Flag returns whether the flag c, a character, has been set.
- Flag(int) bool
+ Flag(c int) bool
}
// Formatter is the interface implemented by values with a custom formatter.
@@ -332,6 +333,12 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) {
p.fmt.integer(v, 10, signed, ldigits)
case 'o':
p.fmt.integer(v, 8, signed, ldigits)
+ case 'q':
+ if 0 <= v && v <= unicode.MaxRune {
+ p.fmt.fmt_qc(v)
+ } else {
+ p.badVerb(verb, value)
+ }
case 'x':
p.fmt.integer(v, 16, signed, ldigits)
case 'U':
@@ -385,6 +392,12 @@ func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {
}
case 'o':
p.fmt.integer(int64(v), 8, unsigned, ldigits)
+ case 'q':
+ if 0 <= v && v <= unicode.MaxRune {
+ p.fmt.fmt_qc(int64(v))
+ } else {
+ p.badVerb(verb, value)
+ }
case 'x':
p.fmt.integer(int64(v), 16, unsigned, ldigits)
case 'X':
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index 42bc52c92..dd8548ceb 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -231,6 +231,7 @@ func (s *ss) UnreadRune() os.Error {
} else {
s.peekRune = s.prevRune
}
+ s.prevRune = -1
s.count--
return nil
}
@@ -457,6 +458,14 @@ func (s *ss) peek(ok string) bool {
return strings.IndexRune(ok, rune) >= 0
}
+func (s *ss) notEOF() {
+ // Guarantee there is data to be read.
+ if rune := s.getRune(); rune == eof {
+ panic(os.EOF)
+ }
+ s.UnreadRune()
+}
+
// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the
// buffer and returns true. Otherwise it return false.
func (s *ss) accept(ok string) bool {
@@ -476,11 +485,13 @@ func (s *ss) okVerb(verb int, okVerbs, typ string) bool {
// scanBool returns the value of the boolean represented by the next token.
func (s *ss) scanBool(verb int) bool {
+ s.skipSpace(false)
+ s.notEOF()
if !s.okVerb(verb, "tv", "boolean") {
return false
}
// Syntax-checking a boolean is annoying. We're not fastidious about case.
- switch s.mustReadRune() {
+ switch s.getRune() {
case '0':
return false
case '1':
@@ -531,6 +542,7 @@ func (s *ss) getBase(verb int) (base int, digits string) {
// scanNumber returns the numerical string with specified digits starting here.
func (s *ss) scanNumber(digits string, haveDigits bool) string {
+ s.notEOF()
if !haveDigits && !s.accept(digits) {
s.errorString("expected integer")
}
@@ -541,7 +553,8 @@ func (s *ss) scanNumber(digits string, haveDigits bool) string {
// scanRune returns the next rune value in the input.
func (s *ss) scanRune(bitSize int) int64 {
- rune := int64(s.mustReadRune())
+ s.notEOF()
+ rune := int64(s.getRune())
n := uint(bitSize)
x := (rune << (64 - n)) >> (64 - n)
if x != rune {
@@ -575,6 +588,7 @@ func (s *ss) scanInt(verb int, bitSize int) int64 {
return s.scanRune(bitSize)
}
s.skipSpace(false)
+ s.notEOF()
base, digits := s.getBase(verb)
haveDigits := false
if verb == 'U' {
@@ -607,6 +621,7 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 {
return uint64(s.scanRune(bitSize))
}
s.skipSpace(false)
+ s.notEOF()
base, digits := s.getBase(verb)
haveDigits := false
if verb == 'U' {
@@ -727,6 +742,7 @@ func (s *ss) scanComplex(verb int, n int) complex128 {
return 0
}
s.skipSpace(false)
+ s.notEOF()
sreal, simag := s.complexTokens()
real := s.convertFloat(sreal, n/2)
imag := s.convertFloat(simag, n/2)
@@ -740,6 +756,7 @@ func (s *ss) convertString(verb int) (str string) {
return ""
}
s.skipSpace(false)
+ s.notEOF()
switch verb {
case 'q':
str = s.quotedString()
@@ -748,16 +765,13 @@ func (s *ss) convertString(verb int) (str string) {
default:
str = string(s.token(true, notSpace)) // %s and %v just return the next word
}
- // Empty strings other than with %q are not OK.
- if len(str) == 0 && verb != 'q' && s.maxWid > 0 {
- s.errorString("Scan: no data for string")
- }
return
}
// quotedString returns the double- or back-quoted string represented by the next input characters.
func (s *ss) quotedString() string {
- quote := s.mustReadRune()
+ s.notEOF()
+ quote := s.getRune()
switch quote {
case '`':
// Back-quoted: Anything goes until EOF or back quote.
@@ -827,6 +841,7 @@ func (s *ss) hexByte() (b byte, ok bool) {
// hexString returns the space-delimited hexpair-encoded string.
func (s *ss) hexString() string {
+ s.notEOF()
for {
b, ok := s.hexByte()
if !ok {
@@ -860,6 +875,7 @@ func (s *ss) scanOne(verb int, field interface{}) {
}
return
}
+
switch v := field.(type) {
case *bool:
*v = s.scanBool(verb)
@@ -894,11 +910,13 @@ func (s *ss) scanOne(verb int, field interface{}) {
case *float32:
if s.okVerb(verb, floatVerbs, "float32") {
s.skipSpace(false)
+ s.notEOF()
*v = float32(s.convertFloat(s.floatToken(), 32))
}
case *float64:
if s.okVerb(verb, floatVerbs, "float64") {
s.skipSpace(false)
+ s.notEOF()
*v = s.convertFloat(s.floatToken(), 64)
}
case *string:
@@ -936,6 +954,7 @@ func (s *ss) scanOne(verb int, field interface{}) {
}
case reflect.Float32, reflect.Float64:
s.skipSpace(false)
+ s.notEOF()
v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits()))
case reflect.Complex64, reflect.Complex128:
v.SetComplex(s.scanComplex(verb, v.Type().Bits()))
@@ -946,13 +965,13 @@ func (s *ss) scanOne(verb int, field interface{}) {
}
}
-// errorHandler turns local panics into error returns. EOFs are benign.
+// errorHandler turns local panics into error returns.
func errorHandler(errp *os.Error) {
if e := recover(); e != nil {
if se, ok := e.(scanError); ok { // catch local error
- if se.err != os.EOF {
- *errp = se.err
- }
+ *errp = se.err
+ } else if eof, ok := e.(os.Error); ok && eof == os.EOF { // out of input
+ *errp = eof
} else {
panic(e)
}
diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go
index da13eb2d1..a4de8adb1 100644
--- a/src/pkg/fmt/scan_test.go
+++ b/src/pkg/fmt/scan_test.go
@@ -660,6 +660,68 @@ func TestEOF(t *testing.T) {
}
}
+// Verify that we see an EOF error if we run out of input.
+// This was a buglet: we used to get "expected integer".
+func TestEOFAtEndOfInput(t *testing.T) {
+ var i, j int
+ n, err := Sscanf("23", "%d %d", &i, &j)
+ if n != 1 || i != 23 {
+ t.Errorf("Sscanf expected one value of 23; got %d %d", n, i)
+ }
+ if err != os.EOF {
+ t.Errorf("Sscanf expected EOF; got %q", err)
+ }
+ n, err = Sscan("234", &i, &j)
+ if n != 1 || i != 234 {
+ t.Errorf("Sscan expected one value of 234; got %d %d", n, i)
+ }
+ if err != os.EOF {
+ t.Errorf("Sscan expected EOF; got %q", err)
+ }
+ // Trailing space is tougher.
+ n, err = Sscan("234 ", &i, &j)
+ if n != 1 || i != 234 {
+ t.Errorf("Sscan expected one value of 234; got %d %d", n, i)
+ }
+ if err != os.EOF {
+ t.Errorf("Sscan expected EOF; got %q", err)
+ }
+}
+
+var eofTests = []struct {
+ format string
+ v interface{}
+}{
+ {"%s", &stringVal},
+ {"%q", &stringVal},
+ {"%x", &stringVal},
+ {"%v", &stringVal},
+ {"%v", &bytesVal},
+ {"%v", &intVal},
+ {"%v", &uintVal},
+ {"%v", &boolVal},
+ {"%v", &float32Val},
+ {"%v", &complex64Val},
+ {"%v", &renamedStringVal},
+ {"%v", &renamedBytesVal},
+ {"%v", &renamedIntVal},
+ {"%v", &renamedUintVal},
+ {"%v", &renamedBoolVal},
+ {"%v", &renamedFloat32Val},
+ {"%v", &renamedComplex64Val},
+}
+
+func TestEOFAllTypes(t *testing.T) {
+ for i, test := range eofTests {
+ if _, err := Sscanf("", test.format, test.v); err != os.EOF {
+ t.Errorf("#%d: %s %T not eof on empty string: %s", i, test.format, test.v, err)
+ }
+ if _, err := Sscanf(" ", test.format, test.v); err != os.EOF {
+ t.Errorf("#%d: %s %T not eof on trailing blanks: %s", i, test.format, test.v, err)
+ }
+ }
+}
+
// Verify that, at least when using bufio, successive calls to Fscan do not lose runes.
func TestUnreadRuneWithBufio(t *testing.T) {
r := bufio.NewReader(strings.NewReader("123αb"))