cmd/namespace: Make the glob output simple by default

Change namespace glob to show a sorted list of matching result names by
default, and add an option (-l) to show everything.

BUG:https://github.com/veyron/release-issues/issues/1804
Change-Id: Ia979000b4c6b35ec892ecb9037afca7d0b263f00
diff --git a/cmd/namespace/doc.go b/cmd/namespace/doc.go
index c7a3aa8..8c8570e 100644
--- a/cmd/namespace/doc.go
+++ b/cmd/namespace/doc.go
@@ -70,11 +70,15 @@
 Returns all matching entries from the namespace.
 
 Usage:
-   namespace glob <pattern>
+   namespace glob [flags] <pattern>
 
 <pattern> is a glob pattern that is matched against all the names below the
 specified mount name.
 
+The namespace glob flags are:
+ -l=false
+   Long listing format.
+
 Namespace Mount
 
 Adds server <server> to the namespace with name <name>.
diff --git a/cmd/namespace/impl.go b/cmd/namespace/impl.go
index 5380f01..f7ff5f1 100644
--- a/cmd/namespace/impl.go
+++ b/cmd/namespace/impl.go
@@ -6,6 +6,7 @@
 
 import (
 	"fmt"
+	"sort"
 	"time"
 
 	"v.io/v23"
@@ -17,6 +18,7 @@
 )
 
 var (
+	flagLongGlob            bool
 	flagInsecureResolve     bool
 	flagInsecureResolveToMT bool
 )
@@ -49,18 +51,47 @@
 		vlog.Infof("ns.Glob(%q) failed: %v", pattern, err)
 		return err
 	}
+	if flagLongGlob {
+		// Show all the information we received.
+		for res := range c {
+			switch v := res.(type) {
+			case *naming.MountEntry:
+				fmt.Fprint(cmd.Stdout(), v.Name)
+				for _, s := range v.Servers {
+					delta := s.Deadline.Time.Sub(time.Now())
+					fmt.Fprintf(cmd.Stdout(), " %s (Expires in %d sec)", s.Server, int(delta.Seconds()))
+				}
+				fmt.Fprintln(cmd.Stdout())
+			case *naming.GlobError:
+				fmt.Fprintf(cmd.Stderr(), "Error: %s: %v\n", v.Name, v.Error)
+			}
+		}
+		return nil
+	}
+	// Show a sorted list of unique names, and any errors.
+	resultSet := make(map[string]struct{})
+	errors := []*naming.GlobError{}
 	for res := range c {
 		switch v := res.(type) {
 		case *naming.MountEntry:
-			fmt.Fprint(cmd.Stdout(), v.Name)
-			for _, s := range v.Servers {
-				fmt.Fprintf(cmd.Stdout(), " %s (Deadline %s)", s.Server, s.Deadline.Time)
+			if v.Name != "" {
+				resultSet[v.Name] = struct{}{}
 			}
-			fmt.Fprintln(cmd.Stdout())
 		case *naming.GlobError:
-			fmt.Fprintf(cmd.Stderr(), "Error: %s: %v\n", v.Name, v.Error)
+			errors = append(errors, v)
 		}
 	}
+	results := []string{}
+	for r := range resultSet {
+		results = append(results, r)
+	}
+	sort.Strings(results)
+	for _, result := range results {
+		fmt.Fprintln(cmd.Stdout(), result)
+	}
+	for _, err := range errors {
+		fmt.Fprintf(cmd.Stderr(), "Error: %s: %v\n", err.Name, err.Error)
+	}
 	return nil
 }
 
@@ -205,6 +236,7 @@
 }
 
 func root() *cmdline.Command {
+	cmdGlob.Flags.BoolVar(&flagLongGlob, "l", false, "Long listing format.")
 	cmdResolve.Flags.BoolVar(&flagInsecureResolve, "insecure", false, "Insecure mode: May return results from untrusted servers and invoke Resolve on untrusted mounttables")
 	cmdResolveToMT.Flags.BoolVar(&flagInsecureResolveToMT, "insecure", false, "Insecure mode: May return results from untrusted servers and invoke Resolve on untrusted mounttables")
 	return &cmdline.Command{