summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2023-11-06 23:53:00 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2023-11-06 23:53:00 +0100
commitf5bd5efea9340c39c673d999d419a97bb6a20990 (patch)
tree5d725bbee6f3f554e14995f098c95467c872ee35
parentInitial commit. (diff)
downloadman-http-f5bd5efea9340c39c673d999d419a97bb6a20990.tar.gz
man-http-f5bd5efea9340c39c673d999d419a97bb6a20990.tar.xz
Add working example.HEADmaster
-rw-r--r--.gitignore3
-rw-r--r--Makefile29
-rwxr-xr-xhtml.py30
-rwxr-xr-xhttp.py35
-rw-r--r--roff.py57
5 files changed, 154 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..639a26d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.csv
+*.json
+*.7
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..515b9cd
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+.PHONY: all clean check
+
+# Can be replaced with 'wget'
+HTTPGET = curl -O
+
+all: http.7 html.7
+
+http-status-codes-1.csv:
+ $(HTTPGET) https://www.iana.org/assignments/http-status-codes/$@
+
+field-names.csv:
+ $(HTTPGET) https://www.iana.org/assignments/http-fields/$@
+
+entities.json:
+ $(HTTPGET) https://html.spec.whatwg.org/$@
+
+http.7: http.py http-status-codes-1.csv field-names.csv
+ ./$< > $@
+
+html.7: html.py entities.json
+ ./$< > $@
+
+check:
+ -flake8 *.py
+ -mypy *.py
+
+# Downloaded files aren't removed to save on bandwidth
+clean:
+ -rm *.7
diff --git a/html.py b/html.py
new file mode 100755
index 0000000..f5240d2
--- /dev/null
+++ b/html.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+import json
+import unicodedata
+from roff import (
+ now,
+ roff_table,
+ section_heading,
+ title_heading,
+)
+
+title_heading("HTML", 7,
+ footer_middle=f"{now():%Y-%m-%d}")
+
+with open('entities.json') as f:
+ data = json.load(f)
+
+
+section_heading("entities")
+
+
+roff_table([("Seq", "R"),
+ ("Char", "L"),
+ ("Formal Name", "L")],
+ ["code", "characters", "formal"],
+ [{"code": code,
+ "characters": data["characters"],
+ "formal": ", ".join(unicodedata.name(c, "[NONAME]")
+ for c in data["characters"])}
+ for (code, data) in data.items()])
diff --git a/http.py b/http.py
new file mode 100755
index 0000000..946bd5f
--- /dev/null
+++ b/http.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+
+import csv
+from roff import (
+ now,
+ roff_table,
+ section_heading,
+ subsection_heading,
+ title_heading,
+)
+title_heading('HTTP', 7,
+ footer_middle=f"{now():%Y-%m-%d}")
+
+section_heading("STATUS CODES")
+
+with open('http-status-codes-1.csv') as f:
+ entries = list(csv.DictReader(f))
+
+roff_table([("Code", 'R'),
+ ("Description", "L"),
+ ("Reference", 'L')],
+ ["Value", "Description", "Reference"],
+ entries)
+
+section_heading('HEADERS')
+
+with open("field-names.csv") as f:
+ entries = list(csv.DictReader(f))
+
+for entry in entries:
+ subsection_heading(entry["Field Name"])
+ print(entry["Status"] + ", ")
+ print(entry["Reference"])
+ print()
+ print(entry["Comments"])
diff --git a/roff.py b/roff.py
new file mode 100644
index 0000000..2690e2f
--- /dev/null
+++ b/roff.py
@@ -0,0 +1,57 @@
+"""
+Library for generating roff markup.
+
+This library is FAR from complete, and supports exactly what these
+scripts require.
+
+For information about roff commands meaning, see groff_man(7).
+"""
+
+import os
+from datetime import datetime
+from typing import TypeAlias, Literal
+
+
+Align: TypeAlias = Literal['R', 'L']
+
+
+def title_heading(topic: str,
+ section: int,
+ *,
+ footer_middle: str | None = None,
+ footer_inside: str | None = None,
+ header_middle: str | None = None):
+ print(f'.TH "{topic.upper()}" {section} "{footer_middle}"')
+
+
+def section_heading(s: str):
+ print(f'.SH "{s.upper()}"')
+
+
+def subsection_heading(s: str):
+ print(f'.SS "{s}"')
+
+
+def roff_table(headers: list[tuple[str, Align]],
+ keys: list[str],
+ entries: list[dict[str, str]]):
+ print(".TS")
+ print("tab(@);")
+ print(' '.join([a for (_, a) in headers] + ['.']))
+
+ print("@".join(h for (h, _) in headers))
+
+ for entry in entries:
+ print("@".join(entry[key] for key in keys))
+
+ print(".TE")
+
+
+def now():
+ if ct := os.getenv("CURRENT_TIME"):
+ if ct.isnumeric():
+ return datetime.fromtimestamp(int(ct))
+ else:
+ return datetime.fromisoformat(ct)
+ else:
+ return datetime.now()