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)
+ }
+
+}