refactor scan() and export as ScanEnum()
diff --git a/gcfg.go b/gcfg.go
index 31fee1b..f86b512 100644
--- a/gcfg.go
+++ b/gcfg.go
@@ -75,33 +75,67 @@
 	"true": true, "yes": true, "on": true, "1": true,
 	"false": false, "no": false, "off": false, "0": false}
 
-func scan(state fmt.ScanState, values map[string]interface{}) (
+const (
+	// initial size of buffer for ScanEnum
+	scanEnumBufferHint = 16
+)
+
+func strEq(s1, s2 string, fold bool) bool {
+	return fold && strings.EqualFold(s1, s2) || !fold && s1 == s2
+}
+
+// ScanEnum is a helper function to simplify the implementation of fmt.Scanner
+// methods for "enum-like" types, that is, user-defined types where the set of
+// values and string representations is fixed.
+//
+// State is the state passed to the implementation of the fmt.Scanner method.
+// Values holds as map values the values of the type, with their string
+// representations as keys.
+// If fold is true, comparison of the string representation uses
+// strings.EqualFold, otherwise the equal operator for strings.
+//
+// On a match, ScanEnum stops after reading the last rune of the matched string,
+// and returns the corresponding value together with a nil error.
+// On no match, ScanEnum attempts to unread the last rune (the first rune that
+// could not potentially match any of the values), and returns a non-nil error,
+// togetherwith a nil value for interface{}.
+// On I/O error, ScanEnum returns the I/O error, together with a nil value for
+// interface{}.
+//
+func ScanEnum(state fmt.ScanState, values map[string]interface{}, fold bool) (
 	interface{}, error) {
 	//
-	var rd []rune
-	var r rune
-	var err error
-	for r, _, err = state.ReadRune(); err == nil; r, _, err = state.ReadRune() {
+	rd := make([]rune, 0, scanEnumBufferHint)
+	keys := make(map[string]struct{}, len(values)) // potential keys
+	for s, _ := range values {
+		keys[s] = struct{}{}
+	}
+	for {
+		r, _, err := state.ReadRune()
+		if err != nil {
+			return nil, err
+		}
 		rd = append(rd, r)
-		part := false
-		for s, v := range values {
-			if strings.EqualFold(string(rd), s) {
-				return v, err
+		srd := string(rd)
+		lrd := len(srd)
+		for s, _ := range keys {
+			if strEq(srd, s, fold) {
+				return values[s], nil
 			}
-			if len(rd) < len(s) && strings.EqualFold(string(rd), s[:len(rd)]) {
-				part = true
+			if len(rd) < len(s) && !strEq(srd, s[:lrd], fold) {
+				delete(keys, s)
 			}
 		}
-		if part == false {
+		if len(keys) == 0 {
 			state.UnreadRune()
-			return nil, errors.New("unsupported value " + string(rd))
+			return nil, fmt.Errorf("unsupported value %q", srd)
 		}
 	}
-	return nil, err
+	panic("never reached")
 }
 
 func (b *gbool) Scan(state fmt.ScanState, verb rune) error {
-	v, err := scan(state, boolValues)
+	v, err := ScanEnum(state, boolValues, true)
 	switch bb := v.(type) {
 	case bool:
 		*b = gbool(bb)