aboutsummaryrefslogtreecommitdiffgithub
path: root/tools
diff options
context:
space:
mode:
authorAustin Adams <git@austinjadams.com>2015-12-05 11:22:24 -0500
committerAustin Adams <git@austinjadams.com>2015-12-15 23:03:14 -0500
commitb67d4c9140ab15d069e2147acc448bee2de743f3 (patch)
treeb4f34f61539fabe8acf4b964c9bad936a0806117 /tools
downloadtoolbag-b67d4c9140ab15d069e2147acc448bee2de743f3.tar.gz
toolbag-b67d4c9140ab15d069e2147acc448bee2de743f3.tar.xz
initial commit
Diffstat (limited to 'tools')
-rw-r--r--tools/all.go61
-rw-r--r--tools/figlet.go166
-rw-r--r--tools/notfound.go112
-rw-r--r--tools/share/Makefile16
-rw-r--r--tools/share/notfound/Makefile27
-rw-r--r--tools/share/notfound/integral1.tex12
-rw-r--r--tools/share/notfound/matrix1.tex20
-rw-r--r--tools/share/notfound/series1.tex12
-rw-r--r--tools/share/templates/figlet.html57
-rw-r--r--tools/share/templates/notfound.html26
10 files changed, 509 insertions, 0 deletions
diff --git a/tools/all.go b/tools/all.go
new file mode 100644
index 0000000..00989ee
--- /dev/null
+++ b/tools/all.go
@@ -0,0 +1,61 @@
+// toolbag - dynamic tools for my website
+// Copyright (C) 2015 Austin Adams
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package tools
+
+import (
+ "net/http"
+ "strings"
+
+ tb "code.austinjadams.com/toolbag"
+)
+
+var (
+ IP = tb.NewToolFunc("ip", "show ip address", func(w http.ResponseWriter, r *http.Request) {
+ plainText(w)
+ w.Write([]byte(tidyAddress(r.RemoteAddr) + "\n"))
+ })
+
+ Headers = tb.NewToolFunc("headers", "write request headers", func(w http.ResponseWriter, r *http.Request) {
+ plainText(w)
+ r.Header.Write(w)
+ })
+)
+
+func All() *tb.ToolBag {
+ return tb.NewToolBag(IP, Headers, NewFiglet(), NewNotFound())
+}
+
+// nasty hack to remove port numbers and the brackets from ipv6 addresses
+// it's "nasty" because the go docs (unfortunately) make no guarantee
+// about the format of http.Request.RemoteAddr
+func tidyAddress(addr string) (result string) {
+ result = addr
+ if i := strings.LastIndex(result, ":"); i != -1 {
+ result = result[:i]
+ }
+ result = strings.Trim(result, "[]")
+ return
+}
+
+func splitLines(blob string) []string {
+ return strings.Split(strings.TrimSpace(blob), "\n")
+}
+
+func plainText(w http.ResponseWriter) {
+ // stop go from trying to guess the content-type of a response
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+}
diff --git a/tools/figlet.go b/tools/figlet.go
new file mode 100644
index 0000000..26e3318
--- /dev/null
+++ b/tools/figlet.go
@@ -0,0 +1,166 @@
+// toolbag - dynamic tools for my website
+// Copyright (C) 2015 Austin Adams
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package tools
+
+import (
+ "errors"
+ "flag"
+ "html/template"
+ "net/http"
+ "strings"
+
+ "code.austinjadams.com/execd"
+ tb "code.austinjadams.com/toolbag"
+)
+
+type Figlet struct {
+ defaultFont string
+ fonts map[string][]string
+ templ *template.Template
+ net, addr string
+
+ args struct {
+ template, unix, tcp string
+ }
+}
+
+func NewFiglet() *Figlet {
+ return &Figlet{}
+}
+
+func (f *Figlet) Name() string { return "figlet" }
+func (f *Figlet) Desc() string { return "a web frontend to figlet" }
+func (f *Figlet) Path() string { return "/" + f.Name() }
+func (f *Figlet) AddArgs() {
+ flag.StringVar(&f.args.template, tb.Arg(f, "template"), "", "path to template")
+ flag.StringVar(&f.args.unix, tb.Arg(f, "unix"), "", "path to unix socket to execd")
+ flag.StringVar(&f.args.tcp, tb.Arg(f, "tcp"), "", "tcp address to execd")
+}
+
+func (f *Figlet) makeClient() (*execd.Client, error) {
+ return execd.DialClient(f.net, f.addr)
+}
+
+func (f *Figlet) fontCategory(needle string) string {
+ for category, fonts := range f.fonts {
+ for _, font := range fonts {
+ if needle == font {
+ return category
+ }
+ }
+ }
+ // no match
+ return ""
+}
+
+func (f *Figlet) Init() error {
+ if f.args.template == "" {
+ return errors.New("missing template arg")
+ }
+ if (f.args.unix == "") == (f.args.tcp == "") {
+ return errors.New("specify either unix or tcp, but not both")
+ }
+
+ templ, err := template.ParseFiles(f.args.template)
+ if err != nil {
+ return err
+ }
+ f.templ = templ
+
+ if f.args.unix != "" {
+ f.net = "unix"
+ f.addr = f.args.unix
+ } else {
+ f.net = "tcp"
+ f.addr = f.args.tcp
+ }
+
+ client, err := f.makeClient()
+ if err != nil {
+ return err
+ }
+ // find default font
+ defaultFont, err := client.ExecString("", "fig", "default")
+ if err != nil {
+ return err
+ }
+ f.defaultFont = strings.TrimSpace(defaultFont)
+
+ // find categories of fonts
+ output, err := client.ExecString("", "fig", "ls")
+ if err != nil {
+ return err
+ }
+ f.fonts = make(map[string][]string)
+ for _, v := range splitLines(output) {
+ f.fonts[v] = nil
+ }
+
+ // find the fonts in each category
+ for category, _ := range f.fonts {
+ output, err = client.ExecString("", "fig", "ls", category)
+ if err != nil {
+ return err
+ }
+ f.fonts[category] = splitLines(output)
+ }
+
+ return nil
+}
+
+// serve
+func (f *Figlet) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ font := r.PostFormValue("font")
+ text := r.PostFormValue("text")
+ result := ""
+
+ if font == "" {
+ font = f.defaultFont
+ }
+
+ if text != "" {
+ category := f.fontCategory(font)
+ if category == "" {
+ tb.WhineString(f, w, "nice try, lad")
+ return
+ }
+
+ client, err := f.makeClient()
+ if err != nil {
+ tb.Whine(f, w, err)
+ return
+ }
+ // otherwise
+ defer client.Close()
+
+ result, err = client.ExecString(text, "fig", category, font)
+ if err != nil {
+ tb.Whine(f, w, err)
+ return
+ }
+ }
+
+ err := f.templ.Execute(w, &struct {
+ Font string
+ Fonts map[string][]string
+ Text, Result string
+ }{font, f.fonts, text, result})
+ if err != nil {
+ tb.LogWhine(f, err)
+ return
+ }
+}
diff --git a/tools/notfound.go b/tools/notfound.go
new file mode 100644
index 0000000..9e70cbb
--- /dev/null
+++ b/tools/notfound.go
@@ -0,0 +1,112 @@
+// toolbag - dynamic tools for my website
+// Copyright (C) 2015 Austin Adams
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package tools
+
+import (
+ "errors"
+ "flag"
+ "html/template"
+ "io/ioutil"
+ "math/rand"
+ "net/http"
+ "os"
+ "path"
+ "strconv"
+
+ tb "code.austinjadams.com/toolbag"
+)
+
+type notFoundImg struct {
+ Alt, Base64 string
+}
+
+type NotFound struct {
+ images []*notFoundImg
+ templ *template.Template
+
+ args struct {
+ dir, template string
+ }
+}
+
+func NewNotFound() *NotFound {
+ return &NotFound{}
+}
+
+func (nf *NotFound) Name() string { return "notfound" }
+func (nf *NotFound) Desc() string { return "not found page showing a random math problem" }
+
+// be a catch-all
+func (nf *NotFound) Path() string { return "/" }
+
+func (nf *NotFound) AddArgs() {
+ flag.StringVar(&nf.args.dir, tb.Arg(nf, "dir"), "", "directory with the math problems")
+ flag.StringVar(&nf.args.template, tb.Arg(nf, "template"), "", "path to template")
+}
+
+func (nf *NotFound) Init() error {
+ if nf.args.dir == "" {
+ return errors.New("missing dir arg")
+ }
+ if nf.args.template == "" {
+ return errors.New("missing template arg")
+ }
+
+ templ, err := template.ParseFiles(nf.args.template)
+ if err != nil {
+ return err
+ }
+ nf.templ = templ
+
+ f, err := os.Open(nf.args.dir)
+ if err != nil {
+ return err
+ }
+ names, err := f.Readdirnames(0)
+ if err != nil {
+ return err
+ }
+
+ for _, name := range names {
+ imgpath := path.Join(nf.args.dir, name)
+ contents, err := ioutil.ReadFile(imgpath)
+ if err != nil {
+ return err
+ }
+ pieces := splitLines(string(contents))
+
+ if len(pieces) != 2 {
+ return errors.New("malformed file " + imgpath + ". got " + strconv.Itoa(len(pieces)) +
+ " lines, expected 2 (first with alttext, second with base64)")
+ }
+
+ nf.images = append(nf.images, &notFoundImg{pieces[0], pieces[1]})
+ }
+ return nil
+}
+
+func (nf *NotFound) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusNotFound)
+ // deterministic, but who cares?
+ i := rand.Intn(len(nf.images))
+
+ err := nf.templ.Execute(w, nf.images[i])
+ // template probably already started writing the request, so just log the error
+ if err != nil {
+ tb.LogWhine(nf, err)
+ }
+}
diff --git a/tools/share/Makefile b/tools/share/Makefile
new file mode 100644
index 0000000..70673f8
--- /dev/null
+++ b/tools/share/Makefile
@@ -0,0 +1,16 @@
+NOTFOUND = notfound
+IMAGES = $(NOTFOUND)-img
+TARBALL = share.tar.xz
+DIRS = $(IMAGES) templates
+
+.PHONY: clean
+
+$(TARBALL): $(DIRS)
+ tar cvJf $@ $^
+
+$(IMAGES): $(NOTFOUND)
+ $(MAKE) -C $<
+
+clean:
+ rm -vf $(TARBALL)
+ $(MAKE) -C $(NOTFOUND) $@
diff --git a/tools/share/notfound/Makefile b/tools/share/notfound/Makefile
new file mode 100644
index 0000000..791ad90
--- /dev/null
+++ b/tools/share/notfound/Makefile
@@ -0,0 +1,27 @@
+# honestly, i made this number up by trial-and-error. it seems to work,
+# and i'm too lazy to do the math because i don't want some exact dpi
+DPI = 256
+OUT = ../notfound-img
+PROBLEMS = $(patsubst %.tex,$(OUT)/%,$(wildcard *.tex))
+
+.PHONY: all clean
+
+all: $(PROBLEMS)
+
+$(OUT):
+ mkdir $@
+
+$(OUT)/%: %.base64 $(OUT)
+ { tail -1 $(notdir $@).tex | cut -b 3-; cat $<; } >$@
+
+%.base64: %.png
+ base64 -w 0 <$< >$@
+
+%.png: %.dvi
+ dvipng -T tight -D $(DPI) -o $@ $<
+
+%.dvi: %.tex
+ latex $<
+
+clean:
+ rm -rvf $(OUT) *.base64 *.png *.dvi *.log *.aux
diff --git a/tools/share/notfound/integral1.tex b/tools/share/notfound/integral1.tex
new file mode 100644
index 0000000..364c77a
--- /dev/null
+++ b/tools/share/notfound/integral1.tex
@@ -0,0 +1,12 @@
+\nofiles
+\documentclass[letterpaper,12pt]{article}
+\usepackage{amsmath,amssymb,amsthm,tikz}
+\pagenumbering{gobble}
+\begin{document}
+
+\[
+\int_{2\sqrt{23}}^{30} \frac{e^{2\ln x}}{x}dx
+\]
+
+\end{document}
+% the definite integral from two times the square root of twenty-three to thirty of one over x times e to the power of the product of two and the natural log of x
diff --git a/tools/share/notfound/matrix1.tex b/tools/share/notfound/matrix1.tex
new file mode 100644
index 0000000..e5e2da3
--- /dev/null
+++ b/tools/share/notfound/matrix1.tex
@@ -0,0 +1,20 @@
+\nofiles
+\documentclass[letterpaper,12pt]{article}
+\usepackage{amsmath,amssymb,amsthm,tikz}
+\pagenumbering{gobble}
+\begin{document}
+
+\[
+\begin{bmatrix}
+3 & 2 & 1 & 5
+\end{bmatrix}
+\begin{bmatrix}
+1 & 1 & -3 \\
+-2 & 3 & 3 \\
+-5 & -9 & 2 \\
+2 & 0 & 1
+\end{bmatrix}
+\]
+
+\end{document}
+% the product of two matrices, the first containing three, two, one, and five; and the second containing one, one, and negative three on its first row; negative two, three, and three on its second row; negative five, negative nine, and two on its third row; and two, zero, and one on its fourth row
diff --git a/tools/share/notfound/series1.tex b/tools/share/notfound/series1.tex
new file mode 100644
index 0000000..8c53965
--- /dev/null
+++ b/tools/share/notfound/series1.tex
@@ -0,0 +1,12 @@
+\nofiles
+\documentclass[letterpaper,12pt]{article}
+\usepackage{amsmath,amssymb,amsthm,tikz}
+\pagenumbering{gobble}
+\begin{document}
+
+\[
+2 \sum_{k=0}^{\infty} (1.5\frac{67}{101})^k
+\]
+
+\end{document}
+% two times the sum of one point five times sixty-seven over one hundred one raised to the power of k, where k begins at zero and ends at positive infinity
diff --git a/tools/share/templates/figlet.html b/tools/share/templates/figlet.html
new file mode 100644
index 0000000..48a735d
--- /dev/null
+++ b/tools/share/templates/figlet.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>figlet frontend</title>
+ <style type="text/css">
+ form {
+ display: block;
+ width: 256px;
+ margin: 0 auto;
+ }
+ form pre {
+ text-align: center;
+ }
+ body > pre {
+ display: block;
+ width: 75%;
+ text-align: center;
+ padding: 8px 0;
+ margin: 16px auto 0 auto;
+
+ border: 1px solid #c9c9c9;
+ background-color: #f0f0f0;
+ border-radius: 2px;
+ }
+
+ /* idiotic defaults */
+ form input {
+ padding: 0;
+ margin: 0;
+ }
+
+ form input[type="text"] {
+ width: 210px;
+ margin-right: 5px;
+ }
+
+ form input[type="submit"] {
+ width: 35px;
+ }
+ </style>
+ </head>
+ <body>
+ <form method="post" action="">
+ <pre>
+ __ _ _ _
+ / _(_) __ _| | ___| |_
+| |_| |/ _` | |/ _ \ __|
+| _| | (_| | | __/ |_
+|_| |_|\__, |_|\___|\__|
+ |___/ <a href="http://www.figlet.org/">?</a></pre>
+ <input placeholder="some text" name="text" type="text" value="{{ .Text }}"><input type="submit" value="go">
+ <select name="font">{{ range $category, $fonts := .Fonts }}<optgroup label="{{ $category }}">{{ range $fonts }}<option{{ if eq $.Font . }} selected{{ end }}>{{ . }}</option>{{ end }}</optgroup>{{ end }}</select>
+ </form>
+ {{ if .Text }}<pre>{{ .Result }}</pre>{{ end }}
+ </body>
+</html>
+<!-- vim: set ts=2 sw=2 et : -->
diff --git a/tools/share/templates/notfound.html b/tools/share/templates/notfound.html
new file mode 100644
index 0000000..8150d54
--- /dev/null
+++ b/tools/share/templates/notfound.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>oops « austin adams</title>
+ <style type="text/css">
+ html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ background-color: white;
+ }
+
+ img {
+ display: block;
+ margin: auto;
+ position: relative;
+ top: 128px;
+ }
+ </style>
+</head>
+<body>
+ <img alt="{{ .Alt }}" src="data:image/png;base64,{{ .Base64 }}" />
+</body>
+</html>