1 // vim:ts=4:sw=4:noexpandtab
8 // This is a forked version of codesearch/regexp which returns the results
9 // in a structure instead of printing to stdout/stderr directly.
10 "github.com/Debian/dcs/cmd/dcs-web/varz"
11 "github.com/Debian/dcs/ranking"
12 "github.com/Debian/dcs/regexp"
23 listenAddress
= flag
.String("listen_address", ":28082", "listen address ([host]:port)")
24 unpackedPath
= flag
.String("unpacked_path",
26 "Path to the unpacked sources")
29 type SourceReply
struct {
30 // The number of the last used filename, needed for pagination
33 AllMatches
[]regexp
.Match
36 // Serves a single file for displaying it in /show
37 func File(w http
.ResponseWriter
, r
*http
.Request
) {
39 filename
:= r
.Form
.Get("file")
41 log
.Printf("requested filename *%s*\n", filename
)
42 // path.Join calls path.Clean so we get the shortest path without any "..".
43 absPath
:= path
.Join(*unpackedPath
, filename
)
44 log
.Printf("clean, absolute path is *%s*\n", absPath
)
45 if !strings
.HasPrefix(absPath
, *unpackedPath
) {
46 http
.Error(w
, "Path traversal is bad, mhkay?", http
.StatusForbidden
)
50 file
, err
:= os
.Open(absPath
)
52 http
.Error(w
, fmt
.Sprintf(`Could not open file "%s"`, absPath
), http
.StatusNotFound
)
60 func Source(w http
.ResponseWriter
, r
*http
.Request
) {
62 textQuery
:= r
.Form
.Get("q")
63 limit
, err
:= strconv
.ParseInt(r
.Form
.Get("limit"), 10, 0)
65 log
.Printf("%s\n", err
)
68 filenames
:= r
.Form
["filename"]
69 re
, err
:= regexp
.Compile(textQuery
)
71 log
.Printf("%s\n", err
)
75 log
.Printf("query: text = %s, regexp = %s\n", textQuery
, re
)
77 rankingopts
:= ranking
.RankingOptsFromQuery(r
.URL
.Query())
79 querystr
:= ranking
.NewQueryStr(textQuery
)
81 // Create one Goroutine per filename, which means all IO will be done in
83 output
:= make(chan []regexp
.Match
)
84 for _
, filename
:= range filenames
{
85 go func(filename
string) {
86 // TODO: figure out how to safely clone a dcs/regexp
87 re
, err
:= regexp
.Compile(textQuery
)
89 log
.Printf("%s\n", err
)
99 output
<- grep
.File(path
.Join(*unpackedPath
, filename
))
103 fmt
.Printf("done, now getting the results\n")
105 // TODO: also limit the number of matches per source-package, not only per file
106 var reply SourceReply
107 for idx
, filename
:= range filenames
{
108 fmt
.Printf("…in %s\n", filename
)
110 for idx
, match
:= range matches
{
111 if limit
> 0 && idx
== 5 {
112 // TODO: we somehow need to signal that there are more results
113 // (if there are more), so that the user can expand this.
116 fmt
.Printf("match: %s\n", match
)
117 match
.Ranking
= ranking
.PostRank(rankingopts
, &match
, &querystr
)
118 match
.Path
= match
.Path
[len(*unpackedPath
):]
119 reply
.AllMatches
= append(reply
.AllMatches
, match
)
121 if limit
> 0 && int64(len(reply
.AllMatches
)) >= limit
{
122 reply
.LastUsedFilename
= idx
126 jsonFiles
, err
:= json
.Marshal(&reply
)
128 log
.Printf("%s\n", err
)
131 _
, err
= w
.Write(jsonFiles
)
133 log
.Printf("%s\n", err
)
141 fmt
.Println("Debian Code Search source-backend")
143 http
.HandleFunc("/source", Source
)
144 http
.HandleFunc("/file", File
)
145 http
.HandleFunc("/varz", varz
.Varz
)
146 log
.Fatal(http
.ListenAndServe(*listenAddress
, nil))