add a %v format to print an arbitrary value in its "println" form.
also add code to print (pointers to) arrays, through %v.

R=rsc
DELTA=108  (70 added, 33 deleted, 5 changed)
OCL=19184
CL=19192
diff --git a/src/lib/fmt/print.go b/src/lib/fmt/print.go
index 361dfc5..4bc3790 100644
--- a/src/lib/fmt/print.go
+++ b/src/lib/fmt/print.go
@@ -255,6 +255,16 @@
 	return 0, false;
 }
 
+func getArrayPtr(v reflect.Value) (val reflect.ArrayValue, ok bool) {
+	if v.Kind() == reflect.PtrKind {
+		v = v.(reflect.PtrValue).Sub();
+		if v.Kind() == reflect.ArrayKind {
+			return v.(reflect.ArrayValue), true;
+		}
+	}
+	return nil, false;
+}
+
 // Convert ASCII to integer.  n is 0 (and got is false) if no number present.
 
 func parsenum(s string, start, end int) (n int, got bool, newi int) {
@@ -277,6 +287,56 @@
 	return num, isnum, start;
 }
 
+func (p *P) printField(field reflect.Value) (was_string bool) {
+	if stringer, ok := field.Interface().(String); ok {
+		p.addstr(stringer.String());
+		return false;	// this value is not a string
+	}
+	s := "";
+	switch field.Kind() {
+	case reflect.BoolKind:
+		s = p.fmt.boolean(field.(reflect.BoolValue).Get()).str();
+	case reflect.IntKind, reflect.Int8Kind, reflect.Int16Kind, reflect.Int32Kind, reflect.Int64Kind:
+		v, signed, ok := getInt(field);
+		s = p.fmt.d64(v).str();
+	case reflect.UintKind, reflect.Uint8Kind, reflect.Uint16Kind, reflect.Uint32Kind, reflect.Uint64Kind:
+		v, signed, ok := getInt(field);
+		s = p.fmt.ud64(uint64(v)).str();
+	case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind, reflect.Float80Kind:
+		v, ok := getFloat(field);
+		s = p.fmt.g64(v).str();
+	case reflect.StringKind:
+		v, ok := getString(field);
+		s = p.fmt.s(v).str();
+		was_string = true;
+	case reflect.PtrKind:
+		// pointer to array?
+		if v, ok := getArrayPtr(field); ok {
+			p.addstr("&[");
+			for i := 0; i < v.Len(); i++ {
+				if i > 0 {
+					p.addstr(" ");
+				}
+				p.printField(v.Elem(i));
+			}
+			p.addstr("]");
+			break;
+		}
+		v, ok := getPtr(field);
+		p.add('0');
+		p.add('x');
+		s = p.fmt.uX64(v).str();
+	case reflect.StructKind:
+		p.add('{');
+		p.doprint(field, true, false);
+		p.add('}');
+	default:
+		s = "?" + field.Type().String() + "?";
+	}
+	p.addstr(s);
+	return was_string;
+}
+
 func (p *P) doprintf(format string, v reflect.StructValue) {
 	p.ensure(len(format));	// a good starting size
 	end := len(format) - 1;
@@ -310,9 +370,11 @@
 		}
 		field := v.Field(fieldnum);
 		fieldnum++;
-		if formatter, ok := field.Interface().(Format); ok {
-			formatter.Format(p, c);
-			continue;
+		if c != 'T' {	// don't want thing to describe itself if we're asking for its type
+			if formatter, ok := field.Interface().(Format); ok {
+				formatter.Format(p, c);
+				continue;
+			}
 		}
 		s := "";
 		if p.wid_ok {
@@ -414,6 +476,14 @@
 					goto badtype
 				}
 
+			// arbitrary value; do your best
+			case 'v':
+				p.printField(field);
+
+			// the value's type
+			case 'T':
+				s = field.Type().String();
+
 			default:
 			badtype:
 				s = "%" + string(c) + "(" + field.Type().String() + ")%";
@@ -437,7 +507,6 @@
 	for fieldnum := 0; fieldnum < v.Len();  fieldnum++ {
 		// always add spaces if we're doing println
 		field := v.Field(fieldnum);
-		s := "";
 		if fieldnum > 0 {
 			if addspace {
 				p.add(' ')
@@ -446,40 +515,8 @@
 				p.add(' ')
 			}
 		}
-		if stringer, ok := field.Interface().(String); ok {
-			p.addstr(stringer.String());
-			prev_string = false;	// this value is not a string
-			continue;
-		}
-		switch field.Kind() {
-		case reflect.BoolKind:
-			s = p.fmt.boolean(field.(reflect.BoolValue).Get()).str();
-		case reflect.IntKind, reflect.Int8Kind, reflect.Int16Kind, reflect.Int32Kind, reflect.Int64Kind:
-			v, signed, ok := getInt(field);
-			s = p.fmt.d64(v).str();
-		case reflect.UintKind, reflect.Uint8Kind, reflect.Uint16Kind, reflect.Uint32Kind, reflect.Uint64Kind:
-			v, signed, ok := getInt(field);
-			s = p.fmt.ud64(uint64(v)).str();
-		case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind, reflect.Float80Kind:
-			v, ok := getFloat(field);
-			s = p.fmt.g64(v).str();
-		case reflect.StringKind:
-			v, ok := getString(field);
-			s = p.fmt.s(v).str();
-		case reflect.PtrKind:
-			v, ok := getPtr(field);
-			p.add('0');
-			p.add('x');
-			s = p.fmt.uX64(v).str();
-		case reflect.StructKind:
-			p.add('{');
-			p.doprint(field, true, false);
-			p.add('}');
-		default:
-			s = "?" + field.Type().String() + "?";
-		}
-		p.addstr(s);
-		prev_string = field.Kind() == reflect.StringKind;
+		was_string := p.printField(field);
+		prev_string = was_string;
 	}
 	if addnewline {
 		p.add('\n')