Add gddo-admin.
diff --git a/gddo-admin/block.go b/gddo-admin/block.go
new file mode 100644
index 0000000..73d9307
--- /dev/null
+++ b/gddo-admin/block.go
@@ -0,0 +1,33 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package main
+
+import (
+	"github.com/garyburd/gddo/database"
+	"log"
+	"os"
+)
+
+var blockCommand = &command{
+	name:  "block",
+	run:   block,
+	usage: "block path",
+}
+
+func block(c *command) {
+	if len(c.flag.Args()) != 1 {
+		c.printUsage()
+		os.Exit(1)
+	}
+	db, err := database.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := db.Block(c.flag.Args()[0]); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/gddo-admin/crawl.go b/gddo-admin/crawl.go
new file mode 100644
index 0000000..25543c4
--- /dev/null
+++ b/gddo-admin/crawl.go
@@ -0,0 +1,65 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"strings"
+
+	"github.com/garyburd/gddo/database"
+	"github.com/garyburd/redigo/redis"
+)
+
+var crawlCommand = &command{
+	name:  "crawl",
+	run:   crawl,
+	usage: "crawl [new]",
+}
+
+func crawl(c *command) {
+	if len(c.flag.Args()) > 1 {
+		c.printUsage()
+		os.Exit(1)
+	}
+	db, err := database.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if len(c.flag.Args()) == 1 {
+		p, err := ioutil.ReadFile(c.flag.Args()[0])
+		if err != nil {
+			log.Fatal(err)
+		}
+		for _, p := range strings.Fields(string(p)) {
+			db.AddNewCrawl(p)
+		}
+	}
+
+	conn := db.Pool.Get()
+	defer conn.Close()
+	paths, err := redis.Strings(conn.Do("SMEMBERS", "newCrawl"))
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("NEW")
+	for _, path := range paths {
+		fmt.Println(path)
+	}
+
+	paths, err = redis.Strings(conn.Do("SMEMBERS", "badCrawl"))
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("BAD")
+	for _, path := range paths {
+		fmt.Println(path)
+	}
+}
diff --git a/gddo-admin/dangle.go b/gddo-admin/dangle.go
new file mode 100644
index 0000000..c8011d7
--- /dev/null
+++ b/gddo-admin/dangle.go
@@ -0,0 +1,59 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/garyburd/gddo/database"
+	"github.com/garyburd/gosrc"
+)
+
+var dangleCommand = &command{
+	name:  "dangle",
+	run:   dangle,
+	usage: "dangle",
+}
+
+func dangle(c *command) {
+	if len(c.flag.Args()) != 0 {
+		c.printUsage()
+		os.Exit(1)
+	}
+	db, err := database.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	m := make(map[string]int)
+	err = db.Do(func(pi *database.PackageInfo) error {
+		m[pi.PDoc.ImportPath] |= 1
+		for _, p := range pi.PDoc.Imports {
+			if gosrc.IsValidPath(p) {
+				m[p] |= 2
+			}
+		}
+		for _, p := range pi.PDoc.TestImports {
+			if gosrc.IsValidPath(p) {
+				m[p] |= 2
+			}
+		}
+		for _, p := range pi.PDoc.XTestImports {
+			if gosrc.IsValidPath(p) {
+				m[p] |= 2
+			}
+		}
+		return nil
+	})
+
+	for p, v := range m {
+		if v == 2 {
+			fmt.Println(p)
+		}
+	}
+}
diff --git a/gddo-admin/delete.go b/gddo-admin/delete.go
new file mode 100644
index 0000000..93f9a08
--- /dev/null
+++ b/gddo-admin/delete.go
@@ -0,0 +1,34 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/garyburd/gddo/database"
+)
+
+var deleteCommand = &command{
+	name:  "delete",
+	run:   del,
+	usage: "delete path",
+}
+
+func del(c *command) {
+	if len(c.flag.Args()) != 1 {
+		c.printUsage()
+		os.Exit(1)
+	}
+	db, err := database.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := db.Delete(c.flag.Args()[0]); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/gddo-admin/main.go b/gddo-admin/main.go
new file mode 100644
index 0000000..223c94c
--- /dev/null
+++ b/gddo-admin/main.go
@@ -0,0 +1,70 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+// Command gddo-admin is the GoDoc.org command line administration tool.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"strings"
+)
+
+type command struct {
+	name  string
+	run   func(c *command)
+	flag  flag.FlagSet
+	usage string
+}
+
+func (c *command) printUsage() {
+	fmt.Fprintf(os.Stderr, "%s %s\n", os.Args[0], c.usage)
+	c.flag.PrintDefaults()
+}
+
+var commands = []*command{
+	blockCommand,
+	reindexCommand,
+	deleteCommand,
+	popularCommand,
+	dangleCommand,
+	crawlCommand,
+	statsCommand,
+}
+
+func printUsage() {
+	var n []string
+	for _, c := range commands {
+		n = append(n, c.name)
+	}
+	fmt.Fprintf(os.Stderr, "%s %s\n", os.Args[0], strings.Join(n, "|"))
+	flag.PrintDefaults()
+	for _, c := range commands {
+		c.printUsage()
+	}
+}
+
+func main() {
+	flag.Usage = printUsage
+	flag.Parse()
+	args := flag.Args()
+	if len(args) >= 1 {
+		for _, c := range commands {
+			if args[0] == c.name {
+				c.flag.Usage = func() {
+					c.printUsage()
+					os.Exit(2)
+				}
+				c.flag.Parse(args[1:])
+				c.run(c)
+				return
+			}
+		}
+	}
+	printUsage()
+	os.Exit(2)
+}
diff --git a/gddo-admin/popular.go b/gddo-admin/popular.go
new file mode 100644
index 0000000..5c82d36
--- /dev/null
+++ b/gddo-admin/popular.go
@@ -0,0 +1,44 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/garyburd/gddo/database"
+)
+
+var (
+	popularCommand = &command{
+		name:  "popular",
+		usage: "popular",
+	}
+)
+
+func init() {
+	popularCommand.run = popular
+}
+
+func popular(c *command) {
+	if len(c.flag.Args()) != 0 {
+		c.printUsage()
+		os.Exit(1)
+	}
+	db, err := database.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	pkgs, err := db.PopularWithScores()
+	if err != nil {
+		log.Fatal(err)
+	}
+	for _, pkg := range pkgs {
+		fmt.Println(pkg.Path, pkg.Synopsis)
+	}
+}
diff --git a/gddo-admin/reindex.go b/gddo-admin/reindex.go
new file mode 100644
index 0000000..8187a1a
--- /dev/null
+++ b/gddo-admin/reindex.go
@@ -0,0 +1,68 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package main
+
+import (
+	"log"
+	"os"
+	"time"
+
+	"github.com/garyburd/gddo/database"
+	"github.com/garyburd/gddo/doc"
+)
+
+var reindexCommand = &command{
+	name:  "reindex",
+	run:   reindex,
+	usage: "reindex",
+}
+
+func fix(pdoc *doc.Package) {
+	/*
+	   	for _, v := range pdoc.Consts {
+	   	}
+	   	for _, v := range pdoc.Vars {
+	   	}
+	   	for _, v := range pdoc.Funcs {
+	   	}
+	   	for _, t := range pdoc.Types {
+	   		for _, v := range t.Consts {
+	   		}
+	   		for _, v := range t.Vars {
+	   		}
+	   		for _, v := range t.Funcs {
+	   		}
+	   		for _, v := range t.Methods {
+	   		}
+	   	}
+	       for _, notes := range pdoc.Notes {
+	           for _, v := range notes {
+	           }
+	       }
+	*/
+}
+
+func reindex(c *command) {
+	if len(c.flag.Args()) != 0 {
+		c.printUsage()
+		os.Exit(1)
+	}
+	db, err := database.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	var n int
+	err = db.Do(func(pi *database.PackageInfo) error {
+		n += 1
+		fix(pi.PDoc)
+		return db.Put(pi.PDoc, time.Time{}, false)
+	})
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Updated %d documents", n)
+}
diff --git a/gddo-admin/stats.go b/gddo-admin/stats.go
new file mode 100644
index 0000000..b73d12a
--- /dev/null
+++ b/gddo-admin/stats.go
@@ -0,0 +1,67 @@
+// Copyright 2013 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"sort"
+
+	"github.com/garyburd/gddo/database"
+)
+
+var statsCommand = &command{
+	name:  "stats",
+	run:   stats,
+	usage: "stats",
+}
+
+type itemSize struct {
+	path string
+	size int
+}
+
+type bySizeDesc []itemSize
+
+func (p bySizeDesc) Len() int           { return len(p) }
+func (p bySizeDesc) Less(i, j int) bool { return p[i].size > p[j].size }
+func (p bySizeDesc) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func stats(c *command) {
+	if len(c.flag.Args()) != 0 {
+		c.printUsage()
+		os.Exit(1)
+	}
+	db, err := database.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	var packageSizes []itemSize
+	projectSizes := make(map[string]int)
+	err = db.Do(func(pi *database.PackageInfo) error {
+		packageSizes = append(packageSizes, itemSize{pi.PDoc.ImportPath, pi.Size})
+		projectSizes[pi.PDoc.ProjectRoot] += pi.Size
+		return nil
+	})
+
+	var sizes []itemSize
+	for path, size := range projectSizes {
+		sizes = append(sizes, itemSize{path, size})
+	}
+	sort.Sort(bySizeDesc(sizes))
+	for _, size := range sizes {
+		fmt.Printf("%6d %s\n", size.size, size.path)
+	}
+
+	sort.Sort(bySizeDesc(packageSizes))
+	for _, size := range packageSizes {
+		fmt.Printf("%6d %s\n", size.size, size.path)
+	}
+
+}