sophuwu.site > manhttpd
making server self contained?
sophuwu sophie@skisiel.com
Wed, 10 Jan 2024 20:45:26 +0100
commit

74f2dd399125052e1527740bc556ff04fba822b2

parent

23d8bbe7fc98a138de51f97bd70638801f2d6eba

8 files changed, 225 insertions(+), 61 deletions(-)

jump to
M .gitignore.gitignore

@@ -1,2 +1,5 @@

.idea build/ +man2htmlsrc/.idea +man2htmlsrc/m2h +man2htmlsrc/mh
M dark_theme.cssdark_theme.css

@@ -1,12 +1,53 @@

+* { + border-radius: 5px; + color: #c7c7c7; + margin: 5px 0; + padding: 0; +} + +hr { + color: #363636; + margin: 10px 0; +} + +.flexy { + display: flex; + flex-direction: row; + justify-content: flex-start; +} body, html { - background-color: #1e1e1e; + background-color: #1f1e23; color: #c7c7c7; + padding: 0 5px; } input, textarea { background-color: #343434; - color: #c7c7c7; + border: 1px solid #c7c7c7; + padding: 4px; +} + +input[type="text"], textarea { + height: inherit; + width: 300px; +} + +label { + margin: 0 10px; + padding: 0; +} + +input[type="checkbox"] { + width: fit-content; + height: fit-content; +} + +input[type="submit"] { + background-color: #343434; + font-weight: bold; + cursor: pointer; + margin: 0 10px; } h1, h2, h3, h4, h5, h6 {
M go.modgo.mod

@@ -1,3 +1,8 @@

module manpages go 1.21.5 + +require ( + github.com/mandelsoft/filepath v0.0.0-20200909114706-3df73d378d55 // indirect + github.com/mandelsoft/vfs v0.4.0 // indirect +)
A go.sum

@@ -0,0 +1,49 @@

+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/mandelsoft/filepath v0.0.0-20200909114706-3df73d378d55 h1:mFdiUG86O2iW+iDEpZKXf64efMWO4JvDT+zN3znUGIc= +github.com/mandelsoft/filepath v0.0.0-20200909114706-3df73d378d55/go.mod h1:n4xEiUD2HNHnn2w5ZKF0qgjDecHVCWAl5DxZ7+pcFU8= +github.com/mandelsoft/vfs v0.4.0 h1:BUx3XIQUhHYOVT/voRudLrYE1VLa+t35jBn6572d7fM= +github.com/mandelsoft/vfs v0.4.0/go.mod h1:k83vb5I4cqRGJh3TUUVbf2oTF8FrYhvixQ+FwIAgP1Y= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
A index.html

@@ -0,0 +1,34 @@

+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Man Pages Index</TITLE> +<link rel="stylesheet" href="/style.css"> +</HEAD> +<BODY> +<H1>Man Pages Index</H1> +<HR> +<H2>Open Page:</H2> +<p>Input name of page.</p> +<FORM method="get" action="/"> + <INPUT TYPE="text" NAME="man"> + <INPUT TYPE="submit" VALUE="Submit"> +</FORM> +<HR> +<H2>Search Pages:</H2> +<p>Interpret each line as a separate keyword.</p> +<FORM method="post" action="/"> + <div class="flexy"> + <textarea rows="5" name="search"></textarea><br> + <div> + <LABEL><INPUT TYPE="checkbox" NAME="arg" VALUE="r"> <B>Regex</B> match all keywords with regex.</LABEL><BR> + <LABEL><INPUT TYPE="checkbox" NAME="arg" VALUE="w"> <B>Wildcard</B> match all keywords by wildcard.</LABEL><BR> + <LABEL><INPUT TYPE="checkbox" NAME="arg" VALUE="e"> <B>Exact</B> match keywords exactly.</LABEL><BR> + <LABEL><INPUT TYPE="checkbox" NAME="arg" VALUE="a"> <B>And</B> all keywords must match.</LABEL><BR> + <INPUT TYPE="submit" VALUE="Submit"> + </div> + </div> +</FORM> +<HR> +<p>HOST: {{ hostname }} PORT: {{ port }}</p><br> +</BODY> +</HTML>
M main.gomain.go

@@ -1,12 +1,18 @@

package main import ( + "bytes" _ "embed" "fmt" + "log" "net/http" + "os" "os/exec" "strings" ) + +//go:embed index.html +var index []byte //go:embed font.css var font []byte

@@ -14,12 +20,35 @@

//go:embed dark_theme.css var css []byte +var CFG struct { + Hostname string + ListenAddr string + ListenPort string + MANPATH []string +} + func init() { + CFG.Hostname, _ = os.Hostname() + if CFG.Hostname == "" { + os.Getenv("HOSTNAME") + } + CFG.ListenAddr = os.Getenv("ListenAddr") + CFG.ListenPort = os.Getenv("ListenPort") + if CFG.ListenAddr == "" || CFG.ListenPort == "" || CFG.Hostname == "" { + log.Fatal("ListenAddr, ListenPort and Hostname must be set") + } + b, err := exec.Command("/usr/bin/manpath", "-g").Output() + if err != nil { + log.Fatal("Fatal: unable to get manpath") + } + CFG.MANPATH = strings.Split(string(b), ":") + css = append(css, font...) + index = bytes.ReplaceAll(index, []byte("{{ hostname }}"), []byte(CFG.Hostname)) + index = bytes.ReplaceAll(index, []byte("{{ port }}"), []byte(CFG.ListenPort)) } func main() { - http.HandleFunc("/cgi-bin/man/", handleWIS) 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)))

@@ -27,66 +56,31 @@ w.WriteHeader(http.StatusOK)

w.Write(css) }) http.Handle("/favicon.ico", http.NotFoundHandler()) - http.Handle("/", handleTMP) - http.ListenAndServe("0.0.0.0:3234", nil) -} - -func getARG(exe, pth string) string { - i := strings.Index(pth, exe) - if i == -1 || len(pth) <= i+len(exe) { - return "" - } - return pth[i+len(exe):] -} - -func getEXE(s string) string { - mandex := [4]string{"man2html", "manwhatis", "mansearch", "mansec"} - for k := range mandex { - if strings.Contains(s, mandex[k]) { - return mandex[k] - } - } - return "" + http.HandleFunc("/", handler) + http.ListenAndServe(CFG.ListenAddr+":"+CFG.ListenPort, nil) } -func handleWIS(w http.ResponseWriter, r *http.Request) { - - var opt []string +func handler(w http.ResponseWriter, r *http.Request) { - exe := getEXE(r.URL.Path) - if exe == "" { - http.RedirectHandler("/cgi-bin/man/man2html", 404) - } - if s := getARG(exe, r.URL.Path); s != "" { - opt = append(opt, s) + if r.URL.Path != "/" { + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + return } - q := "QUERY_STRING=" + r.URL.RawQuery + _ = r.ParseForm() - r.ParseForm() - rx := r.Form.Get("query") - if strings.ContainsAny(rx, `*&|`) { - // rx = fmt.Sprintf(`%s`, rx) - opt = append(opt, rx) - exe = "mansearch" + man := r.Form.Get("man") + if man == "" { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write(index) + return } - cmd := exec.Command("/usr/lib/cgi-bin/man/"+exe, opt...) - - cmd.Env = append(cmd.Env, q) - // cmd.Env = append(cmd.Env, "MANPATH=/usr/man:/usr/share/man:/usr/local/man:/usr/local/share/man:/usr/X11R6/man:/opt/man:/snap/man") + // cmd := exec.Command(man) - b, _ := cmd.CombinedOutput() + // b, _ := cmd.CombinedOutput() w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusOK) - page := string(b) - page = page[strings.Index(page, "<!"):] - i := strings.Index(page, "</HEAD>") - if i == -1 { - i = strings.Index(page, "</head>") - } - fmt.Fprintln(w, page[:i]) - fmt.Fprintln(w, `<link rel="stylesheet" href="/style.css">`) - fmt.Fprintln(w, page[i:]) }
M man2htmlsrc/man2html.cman2htmlsrc/man2html.c

@@ -31,7 +31,7 @@ #define BD_INDENT 2

#define SIZE(a) (sizeof(a)/sizeof(*a)) #define DOCTYPE "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" -#define CONTENTTYPE "Content-type: text/html; charset=UTF-8\n\n" +#define CONTENTTYPE "" static char NEWLINE[2]="\n"; static char idxlabel[6] = "ixAAA";

@@ -2044,7 +2044,7 @@ if (!t) t=fname;

fprintf(stderr, "ln -s %s.html %s.html\n", h, t); s=strrchr(t, '.');if (!s) s=t; printf(CONTENTTYPE DOCTYPE); - printf("<HTML><HEAD><TITLE> Man page of %s</TITLE>\n" + printf("<HTML><HEAD><link rel=\"stylesheet\" href=\"/style.css\"><TITLE> Man page of %s</TITLE>\n" "</HEAD><BODY>\n" "See the man page for <A HREF=\"%s.html\">%s</A>.\n" "</BODY></HTML>\n",

@@ -2318,7 +2318,7 @@ char *s, *q;

int skip=0; output_possible=1; printf(CONTENTTYPE DOCTYPE); - out_html("<HTML><HEAD><TITLE>Man page of "); + out_html("<HTML><HEAD><link rel=\"stylesheet\" href=\"/style.css\"><TITLE>Man page of "); scan_troff(wordlist[0], 0, &t); /* we need to remove all html tags */ for (s=q=t; *s; s++) {

@@ -3199,13 +3199,13 @@ va_list p;

switch(status) { case 403: - printf("Status: 403 Forbidden\n"); + //printf("Status: 403 Forbidden\n"); break; case 404: - printf("Status: 404 Not Found\n"); + //printf("Status: 404 Not Found\n"); break; case 500: - printf("Status: 500 Internal Server Error\n"); + //printf("Status: 500 Internal Server Error\n"); break; case 0: default:

@@ -3213,7 +3213,7 @@ break;

} printf(CONTENTTYPE DOCTYPE); - printf("<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n" + printf("<HTML><HEAD><link rel=\"stylesheet\" href=\"/style.css\"><TITLE>%s</TITLE></HEAD>\n" "<BODY>\n<H1>%s</H1>\n", s, s); va_start(p, t); vfprintf(stdout, t, p);

@@ -3416,4 +3416,4 @@ fclose(idxfile);

if (buf) free(buf); return 0; -} +}
A virfs.go

@@ -0,0 +1,38 @@

+package main + +import ( + "github.com/mandelsoft/vfs/pkg/memoryfs" + "github.com/mandelsoft/vfs/pkg/vfs" + "os/exec" + "path/filepath" + "strings" +) + +func Cmd(s string) []byte { + arg := strings.Split(s, " ") + cmd := exec.Command(arg[0], arg[1:]...) + out, _ := cmd.CombinedOutput() + return out +} + +func Err(err error) { + if err != nil { + panic(err) + } +} + +func main() { + fs := memoryfs.New() + var err error + var file string + + file = filepath.Join(fs.FSTempDir(), "test.txt") + + err = vfs.WriteFile(fs, file, Cmd("man --pager=cat man"), 0644) + Err(err) + + var b []byte + b, err = vfs.ReadFile(fs, file) + Err(err) + println(string(b)) +}