sophuwu.site > manhttpd
finished project?
sophuwu sophie@skisiel.com
Wed, 03 Apr 2024 10:22:56 +0200
commit

bc4756faf5d6e3403653b605b7acc7c99a5854ff

parent

a22bff606498d08942feb35c5d74b5dd1a3ef911

3 files changed, 86 insertions(+), 54 deletions(-)

jump to
M dark_theme.cssdark_theme.css

@@ -1,9 +1,9 @@

*{ - font-family: Ubuntu, Helvetica, sans-serif; + font-family: Ubuntu!important; color: #c7c7c7; } table.head, table.foot { - width: 100%; + width: inherit; } td.head-rtitle, td.foot-os { text-align: right;

@@ -28,7 +28,7 @@ font-weight: bold;

font-family: inherit; } code, pre { - font-family: Ubuntu Mono, Monospaced, serif!important; + font-family: Ubuntu!important; } i, em { color: #7f7f7f;

@@ -38,7 +38,11 @@ color: #f0f0f0;

} hr { color: #363636; - margin-top: 0; + margin: 5px 0; +} +form{ + padding:0; + margin:0; } .flexy { display: flex;

@@ -46,25 +50,32 @@ flex-direction: row;

justify-content: center; } body, html { + margin: 0; + padding: 0; + height: 100%; background-color: #1f1e23; color: #c7c7c7; } h1, h2, h3, h4, h5, h6 { color: #8d53a6; + padding: 0; + margin: 0; } -.section { - padding: 2px 10px; +section, .head, .foot { + margin: 10px 5px; + padding: 4px 6px; } -p, dt { +p, dt, table { margin: 2px 10px; + padding: 5px 0; } a { color: #ff7597; text-decoration: underline; } a:hover { + color: #db335d; text-decoration: underline; - text-weight: bold; } input, textarea { background-color: #343434;

@@ -83,4 +94,20 @@ input[type="submit"] {

background-color: #343434; font-weight: bold; cursor: pointer; +} +.flexy{ + position: fixed; + z-index: 5; + width: 100%; + padding: 3px 0; + margin: 0; + top: 0; + background-color: #1f1e23; + border-bottom: #7f7f7f 1px solid; +} +.content{ + width: calc(100% - 10px); + transform: translateY(40px); + margin: 0; + padding: 0; }
M index.htmlindex.html

@@ -3,24 +3,17 @@ <head>

<meta charset="utf-8"> <title>{{ title }}</title> <link rel="stylesheet" type="text/css" href="/style.css"> - <script> - - </script> </head> <body> <div class="flexy"> - <h3 style="margin: 2px;">Find Man Pages: </h3> + <h3 style="margin: 2px;">Man Pages:</h3> <FORM method="post" action="/"> <INPUT TYPE="text" name="q" autocomplete="off"> - <INPUT TYPE="submit" name="t" VALUE="Open"> - <INPUT TYPE="submit" name="t" VALUE="Search"> + <INPUT TYPE="submit" VALUE="Search"> </FORM> </div> - <HR> + <div class="content"> {{ content }} - <HR> - <p> - Host: <a href="{{ host }}">{{ hostname }}</a> - </p> + </div> </body> </html>
M main.gomain.go

@@ -31,7 +31,7 @@ CFG.Hostname, _ = os.Hostname()

index = strings.ReplaceAll(index, "{{ hostname }}", CFG.Hostname) b, e := exec.Command("which", "mandoc").Output() if e != nil || len(b) == 0 { - log.Fatal("Fatal: no mandoc") + log.Fatal("Fatal: no mandoc `apt-get install mandoc`") } CFG.Mandoc = strings.TrimSpace(string(b)) CFG.Addr = os.Getenv("ListenPort")

@@ -42,7 +42,6 @@ }

func main() { GetCFG() - http.HandleFunc("/style.css", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/css; charset=utf-8") w.Header().Set("Content-Length", fmt.Sprint(len(css)))

@@ -57,7 +56,6 @@ w.Write(favicon)

}) http.HandleFunc("/", indexHandler) http.ListenAndServe(":"+CFG.Addr, nil) - } func WriteHtml(w http.ResponseWriter, r *http.Request, title, html string) {

@@ -70,35 +68,44 @@ fmt.Fprint(w, out)

} var LinkRemover = regexp.MustCompile(`(<a [^>]*>)|(</a>)`).ReplaceAllString -var HTMLManName = regexp.MustCompile(`(<b>)?[a-zA-Z0-9_\-]+(</b>)?\([0-9a-z]+\)`) +var HTMLManName = regexp.MustCompile(`(?:<b>)?([a-zA-Z0-9_\-]+)(?:</b>)?\(([0-9][0-9a-z]*)\)`) type ManPage struct { Name string Section string Desc string + Path string } -func (m *ManPage) Path() string { - arg := "-w" +func (m *ManPage) Where() error { + var arg = []string{"-w", m.Name} if m.Section != "" { - arg += "s" + m.Section + arg = []string{"-w", "-s" + m.Section, m.Name} } - b, _ := exec.Command("man", arg, m.Name).Output() - return strings.TrimSpace(string(b)) + b, err := exec.Command("man", arg...).Output() + m.Path = strings.TrimSpace(string(b)) + return err } func (m *ManPage) Html() string { - b, err := exec.Command(CFG.Mandoc, "-c", "-K", "utf-8", "-T", "html", "-O", "fragment", m.Path()).Output() + if m.Where() != nil { + return fmt.Sprintf("<p>404: Unable to locate page %s</p>", m.Name) + } + b, err := exec.Command(CFG.Mandoc, "-Thtml", "-O", "fragment", m.Path).Output() if err != nil { - return fmt.Sprintf("<p>404: Page %s not found.</p>", m.Name) + return fmt.Sprintf("<p>500: server error loading %s</p>", m.Name) } html := LinkRemover(string(b), "") + html = HTMLManName.ReplaceAllStringFunc(html, func(s string) string { + m := HTMLManName.FindStringSubmatch(s) + return fmt.Sprintf(`<a href="/%s.%s">%s(%s)</a>`, m[1], m[2], m[1], m[2]) + }) return html } -var ManDotName = regexp.MustCompile(`^([a-zA-Z0-9_\-]+)(?:.([0-9a-z]+))?$`).FindStringSubmatch +var ManDotName = regexp.MustCompile(`^([a-zA-Z0-9_\-]+)(?:\.([0-9a-z]+))?$`) func NewManPage(s string) (m ManPage) { - name := ManDotName(s) + name := ManDotName.FindStringSubmatch(s) if len(name) >= 2 { m.Name = name[1] }

@@ -108,21 +115,35 @@ }

return } +var RxWords = regexp.MustCompile(`("[^"]+")|([^ ]+)`).FindAllString +var RxWhatIs = regexp.MustCompile(`([a-zA-Z0-9_\-]+) [(]([0-9a-z]+)[)][\- ]+(.*)`).FindAllStringSubmatch + func searchHandler(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - args := "-l\n-" + strings.Join(r.Form["arg"], "\n-") - search := strings.ReplaceAll(r.Form["search"][0], "\r", "") - args += "\n" + search - cmd := exec.Command("apropos", strings.Split(args, "\n")...) - cmd.Env = append(cmd.Env, os.Environ()...) + _ = r.ParseForm() + q := r.Form.Get("q") + if q == "" || ManDotName.MatchString(q) { + http.Redirect(w, r, "/"+q, http.StatusFound) + return + } + var args = RxWords("-l "+q, -1) + for i := range args { + args[i] = strings.TrimSpace(args[i]) + args[i] = strings.TrimPrefix(args[i], `"`) + args[i] = strings.TrimSuffix(args[i], `"`) + } + cmd := exec.Command("apropos", args...) b, e := cmd.Output() - if e != nil { - http.Error(w, "no results", http.StatusNotFound) + if len(b) < 1 || e != nil { + WriteHtml(w, r, "Search", fmt.Sprintf("<p>404: no resualts matching %s</p>", q)) return } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, strings.ReplaceAll(string(b), "\n", "<br>")) + var output string + for _, line := range RxWhatIs(string(b), -1) { // strings.Split(string(b), "\n") { + if len(line) == 4 { + output += fmt.Sprintf(`<p><a href="/%s.%s">%s (%s)</a> - %s</p>%c`, line[1], line[2], line[1], line[2], line[3], 10) + } + } + WriteHtml(w, r, "Search", output) } func indexHandler(w http.ResponseWriter, r *http.Request) {

@@ -132,20 +153,11 @@ man := NewManPage(r.URL.Path[1:])

WriteHtml(w, r, man.Name, man.Html()) return } - if r.Method == "POST" { searchHandler(w, r) return } - if !strings.HasPrefix(r.URL.RawQuery, "man=") && r.URL.RawQuery != "" { - r.URL.RawQuery = "man=" + r.URL.RawQuery - } - _ = r.ParseForm() - var man ManPage - man.Name = r.Form.Get("man") - if man.Name == "" { - WriteHtml(w, r, "Index", "") - return - } + WriteHtml(w, r, "Index", "") + return }