forked from mirror/oddmu
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0d814b8f3 | ||
|
|
41f6d9e48e | ||
|
|
7beaf3e375 | ||
|
|
8f277714d7 | ||
|
|
11eace1b3c | ||
|
|
ad778a3068 | ||
|
|
6ec69d27eb | ||
|
|
0a093182e9 | ||
|
|
9952f2363b | ||
|
|
4d14517668 | ||
|
|
0051a5ca66 | ||
|
|
2436cc1114 | ||
|
|
96993b794a |
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
This software is Copyright (c) 2015–2024 by Alex Schroeder.
|
||||
This software is Copyright (c) 2015–2026 by Alex Schroeder.
|
||||
|
||||
This is free software, licensed under:
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -39,10 +39,13 @@ test:
|
||||
rm -rf testdata/*
|
||||
go test -shuffle on .
|
||||
|
||||
# go install golang.org/x/lint/golint@latest
|
||||
# go install github.com/go-critic/go-critic/cmd/go-critic@latest
|
||||
check:
|
||||
golint
|
||||
gocritic check
|
||||
go-critic check
|
||||
|
||||
# go install golang.org/x/tools/cmd/goimports@latest
|
||||
fix:
|
||||
goimports -w *.go
|
||||
|
||||
|
||||
12
README.md
12
README.md
@@ -65,6 +65,11 @@ changes.
|
||||
This man page documents the "version" subcommand which you can use to
|
||||
get the installed Oddmu version.
|
||||
|
||||
[oddmu-man(1)](https://alexschroeder.ch/view/oddmu/oddmu-man.1): Oddmu
|
||||
comes with a "man" subcommand to print the manual pages. This man page
|
||||
documents the subcommand, but I guess if you can read the man page,
|
||||
you don't need the "man" subcommand.
|
||||
|
||||
Working locally:
|
||||
|
||||
[oddmu-links(1)](https://alexschroeder.ch/view/oddmu/oddmu-links.1):
|
||||
@@ -220,6 +225,13 @@ into `$HOME/.local/share/man/`.
|
||||
make install
|
||||
```
|
||||
|
||||
This installs `oddmu` into `/usr/local/bin` and the manual pages into
|
||||
`/usr/local/share/man`:
|
||||
|
||||
```sh
|
||||
sudo make install PREFIX=/usr/local
|
||||
```
|
||||
|
||||
Here's an example using [GNU Stow](https://www.gnu.org/software/stow/)
|
||||
to install it into `/usr/local/stow` in a way that allows you to
|
||||
uninstall it later:
|
||||
|
||||
16
RELEASE
16
RELEASE
@@ -1,22 +1,22 @@
|
||||
When preparing a new release
|
||||
----------------------------
|
||||
|
||||
1. Run tests
|
||||
1. run tests
|
||||
|
||||
2. Update man/oddmu-releases.7.txt
|
||||
2. update man/oddmu-releases.7.txt
|
||||
- add missing items
|
||||
- change "(unreleased)"
|
||||
|
||||
3. make docs
|
||||
3. check copyright year in LICENSE
|
||||
|
||||
4. Make sure all files are checked in
|
||||
4. make docs
|
||||
|
||||
5. Tag the release and push the tag to all remotes
|
||||
5. make sure all files are checked in
|
||||
|
||||
6. cd man && make upload
|
||||
6. tag the release and push the tag to the remote
|
||||
|
||||
7. make dist
|
||||
|
||||
8. create a new release at https://github.com/kensanata/oddmu/releases
|
||||
8. make dist-upload
|
||||
|
||||
9. upload the four .tar.gz binaries to the GitHub release
|
||||
9. cd man && make upload
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<atom:link href="https://example.org/view/{{.Dir}}{{.PrevYear}}.rss?n={{.N}}" rel="previous" type="application/rss+xml"/>{{end}}{{if .Next}}
|
||||
<atom:link href="https://example.org/view/{{.Path}}.rss?from={{.Next}}&n={{.N}}" rel="next" type="application/rss+xml"/>{{end}}{{if .NextYear}}
|
||||
<atom:link href="https://example.org/view/{{.Dir}}{{.NextYear}}.rss?n={{.N}}" rel="next" type="application/rss+xml"/>{{end}}{{if .Complete}}
|
||||
<generator>Oddμ https://src.alexschroeder.ch/oddmu.git/</generator>
|
||||
<fh:complete/>{{end}}
|
||||
<description>This is the digital garden of Your Name.</description>
|
||||
<image>
|
||||
|
||||
@@ -78,7 +78,7 @@ func (p *Page) printFeed(w io.Writer, ti time.Time) subcommands.ExitStatus {
|
||||
fmt.Fprintf(os.Stderr, "Cannot write prefix: %s\n", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
loadTemplates()
|
||||
initTemplates()
|
||||
templates.RLock()
|
||||
defer templates.RUnlock()
|
||||
err = templates.template["feed.html"].Execute(w, f)
|
||||
|
||||
41
go.mod
41
go.mod
@@ -1,41 +1,40 @@
|
||||
module src.alexschroeder.ch/oddmu
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.3
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/edwvee/exiffix v0.0.0-20210922235313-0f6cbda5e58f
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.3
|
||||
github.com/gen2brain/heic v0.3.1
|
||||
github.com/gen2brain/webp v0.5.2
|
||||
github.com/gomarkdown/markdown v0.0.0-20250207164621-7a1f277a159e
|
||||
github.com/edwvee/exiffix v0.0.0-20240229113213-0dbb146775be
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.13
|
||||
github.com/gen2brain/heic v0.4.9
|
||||
github.com/gen2brain/webp v0.5.5
|
||||
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a
|
||||
github.com/google/subcommands v1.2.0
|
||||
github.com/hexops/gotextdiff v1.0.3
|
||||
github.com/microcosm-cc/bluemonday v1.0.26
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/muesli/reflow v0.3.0
|
||||
github.com/pemistahl/lingua-go v1.4.0
|
||||
github.com/sergi/go-diff v1.3.1
|
||||
github.com/sergi/go-diff v1.4.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/ebitengine/purego v0.8.1 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.6 // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/tetratelabs/wazero v1.8.1 // indirect
|
||||
golang.org/x/image v0.15.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.11.0 // indirect
|
||||
golang.org/x/image v0.35.0 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
78
go.sum
78
go.sum
@@ -1,26 +1,30 @@
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
|
||||
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
|
||||
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/edwvee/exiffix v0.0.0-20210922235313-0f6cbda5e58f h1:RMnUwTnNR070mFAEIoqMYjNirHj8i0h79VXTYyBCyVA=
|
||||
github.com/edwvee/exiffix v0.0.0-20210922235313-0f6cbda5e58f/go.mod h1:KoE3Ti1qbQXCb3s/XGj0yApHnbnNnn1bXTtB5Auq/Vc=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gen2brain/heic v0.3.1 h1:ClY5YTdXdIanw7pe9ZVUM9XcsqH6CCCa5CZBlm58qOs=
|
||||
github.com/gen2brain/heic v0.3.1/go.mod h1:m2sVIf02O7wfO8mJm+PvE91lnq4QYJy2hseUon7So10=
|
||||
github.com/gen2brain/webp v0.5.2 h1:aYdjbU/2L98m+bqUdkYMOIY93YC+EN3HuZLMaqgMD9U=
|
||||
github.com/gen2brain/webp v0.5.2/go.mod h1:Nb3xO5sy6MeUAHhru9H3GT7nlOQO5dKRNNlE92CZrJw=
|
||||
github.com/gomarkdown/markdown v0.0.0-20250207164621-7a1f277a159e h1:ESHlT0RVZphh4JGBz49I5R6nTdC8Qyc08vU25GQHzzQ=
|
||||
github.com/gomarkdown/markdown v0.0.0-20250207164621-7a1f277a159e/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/edwvee/exiffix v0.0.0-20240229113213-0dbb146775be h1:FNPYI8/ifKGW7kdBdlogyGGaPXZmOXBbV1uz4Amr3s0=
|
||||
github.com/edwvee/exiffix v0.0.0-20240229113213-0dbb146775be/go.mod h1:G3dK5MziX9e4jUa8PWjowCOPCcyQwxsZ5a0oYA73280=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gen2brain/heic v0.4.9 h1:sHM7kjMV2+AlrSfYNsJiD0NrqAJqIMWAOqMSZ0HXrU8=
|
||||
github.com/gen2brain/heic v0.4.9/go.mod h1:0/0SrVQnUhOA3ekFY5/lApZYniF/DsgS3g9COWe83dM=
|
||||
github.com/gen2brain/webp v0.5.5 h1:MvQR75yIPU/9nSqYT5h13k4URaJK3gf9tgz/ksRbyEg=
|
||||
github.com/gen2brain/webp v0.5.5/go.mod h1:xOSMzp4aROt2KFW++9qcK/RBTOVC2S9tJG66ip/9Oc0=
|
||||
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=
|
||||
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
@@ -33,10 +37,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/pemistahl/lingua-go v1.4.0 h1:ifYhthrlW7iO4icdubwlduYnmwU37V1sbNrwhKBR4rM=
|
||||
@@ -45,32 +49,30 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550=
|
||||
github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
|
||||
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
|
||||
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
39
man/oddmu-man.1
Normal file
39
man/oddmu-man.1
Normal file
@@ -0,0 +1,39 @@
|
||||
.\" Generated by scdoc 1.11.3
|
||||
.\" Complete documentation for this program is not available as a GNU info page
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "ODDMU-MAN" "1" "2026-02-11"
|
||||
.PP
|
||||
.SH NAME
|
||||
.PP
|
||||
oddmu-man - print the manual pages
|
||||
.PP
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBoddmu man\fR
|
||||
.PP
|
||||
\fBoddmu man\fR \fItopic\fR
|
||||
.PP
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The "man" subcommand lists the topics available or prints the manual page for
|
||||
the given topic.\&
|
||||
.PP
|
||||
Example:
|
||||
.PP
|
||||
.nf
|
||||
.RS 4
|
||||
oddmu man apache
|
||||
.fi
|
||||
.RE
|
||||
.PP
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fIoddmu\fR(1)
|
||||
.PP
|
||||
.SH AUTHORS
|
||||
.PP
|
||||
Maintained by Alex Schroeder <alex@gnu.\&org>.\&
|
||||
30
man/oddmu-man.1.txt
Normal file
30
man/oddmu-man.1.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
ODDMU-MAN(1)
|
||||
|
||||
# NAME
|
||||
|
||||
oddmu-man - print the manual pages
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*oddmu man*
|
||||
|
||||
*oddmu man* _topic_
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The "man" subcommand lists the topics available or prints the manual page for
|
||||
the given topic.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
oddmu man apache
|
||||
```
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
_oddmu_(1)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Maintained by Alex Schroeder <alex@gnu.org>.
|
||||
@@ -5,7 +5,7 @@
|
||||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "ODDMU-RELEASES" "7" "2026-02-06"
|
||||
.TH "ODDMU-RELEASES" "7" "2026-02-11"
|
||||
.PP
|
||||
.SH NAME
|
||||
.PP
|
||||
@@ -15,6 +15,14 @@ oddmu-releases - what'\&s new?\&
|
||||
.PP
|
||||
This page lists user-visible features and template changes to consider.\&
|
||||
.PP
|
||||
.SS 1.21 (unreleased)
|
||||
.PP
|
||||
Write any missing templates when Oddmu starts up.\&
|
||||
.PP
|
||||
Add man subcommand to print manual pages.\&
|
||||
.PP
|
||||
Both of these features make it possible to distribute just the binary.\&
|
||||
.PP
|
||||
.SS 1.20 (2026)
|
||||
.PP
|
||||
Add -shrink and -glob options to the \fIstatic\fR subcommand.\& See \fIoddmu-static\fR(1)
|
||||
|
||||
@@ -8,6 +8,14 @@ oddmu-releases - what's new?
|
||||
|
||||
This page lists user-visible features and template changes to consider.
|
||||
|
||||
## 1.21 (unreleased)
|
||||
|
||||
Write any missing templates when Oddmu starts up.
|
||||
|
||||
Add man subcommand to print manual pages.
|
||||
|
||||
Both of these features make it possible to distribute just the binary.
|
||||
|
||||
## 1.20 (2026)
|
||||
|
||||
Add -shrink and -glob options to the _static_ subcommand. See _oddmu-static_(1)
|
||||
|
||||
90
man/oddmu.1
90
man/oddmu.1
@@ -5,7 +5,7 @@
|
||||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "ODDMU" "1" "2026-01-03"
|
||||
.TH "ODDMU" "1" "2026-02-11"
|
||||
.PP
|
||||
.SH NAME
|
||||
.PP
|
||||
@@ -32,10 +32,11 @@ create, most likely.\&
|
||||
.PP
|
||||
See \fIoddmu\fR(5) for details about the page formatting.\&
|
||||
.PP
|
||||
If you request a page that doesn'\&t exist, Oddmu tries to find a matching
|
||||
Markdown file by appending the extension ".\&md" to the page name.\& In the example
|
||||
above, the page name requested is "index" and the file name Oddmu tries to read
|
||||
is "index.\&md".\& If no such file exists, Oddmu offers you to create the page.\&
|
||||
If you request a file that exists, like "index.\&md", Oddmu serves it as-is.\& If
|
||||
you request a file that doesn'\&t exist, like "index", Oddmu checks if a matching
|
||||
Markdown file exists by appending the extension ".\&md".\& If such a file is found,
|
||||
it is turned into HTML and shown.\& If no such file exists, Oddmu offers you to
|
||||
create the page.\&
|
||||
.PP
|
||||
If your files don'\&t provide their own title ("# title"), the file name (without
|
||||
".\&md") is used for the page title.\&
|
||||
@@ -98,9 +99,9 @@ curl --form body="Did you bring a towel?"
|
||||
When calling the \fIdrop\fR action, the query parameters used are \fIname\fR for the
|
||||
target filename and \fIfile\fR for the file to upload.\& If the query parameter
|
||||
\fImaxwidth\fR is set, an attempt is made to decode and resize the image.\& JPG, PNG,
|
||||
WEBP and HEIC files can be decoded.\& Only JPG and PNG files can be encoded,
|
||||
however.\& If the target name ends in \fI.\&jpg\fR, the \fIquality\fR query parameter is
|
||||
also taken into account.\& To upload some thumbnails:
|
||||
WEBP and HEIC files can be decoded.\& Only JPG, PNG and WEBP files can be encoded,
|
||||
however.\& If the target name ends in \fI.\&jpg\fR or \fI.\&png\fR, the \fIquality\fR query
|
||||
parameter is also taken into account.\& To upload some thumbnails:
|
||||
.PP
|
||||
.nf
|
||||
.RS 4
|
||||
@@ -121,12 +122,13 @@ curl \&'http://localhost:8080/search/?q=towel\&'
|
||||
.RE
|
||||
.PP
|
||||
The page name to act upon is optionally taken from the query parameter \fIid\fR.\& In
|
||||
this case, the directory must also be part of the query parameter and not of the
|
||||
URL path.\&
|
||||
this case, the directory must still be part of the path and may not be part of
|
||||
the \fIid\fR.\& This is enforced so that the path can be used by a webserver for
|
||||
access control.\&
|
||||
.PP
|
||||
.nf
|
||||
.RS 4
|
||||
curl \&'http://localhost:8080/view/?id=man/oddmu\&.1\&.txt\&'
|
||||
curl \&'http://localhost:8080/view/man/?id=oddmu\&.1\&.txt\&'
|
||||
.fi
|
||||
.RE
|
||||
.PP
|
||||
@@ -142,8 +144,9 @@ curl --remote-name \&'http://localhost:8080/archive/man/man\&.zip
|
||||
.PP
|
||||
.SH CONFIGURATION
|
||||
.PP
|
||||
The template files are the HTML files in the working directory.\& Please change
|
||||
these templates!\&
|
||||
The template files are the HTML files in the working directory.\& If they are
|
||||
missing, the default files are written to disk as soon as they are required.\&
|
||||
Please change these templates!\&
|
||||
.PP
|
||||
The first change you should make is to replace the name and email address in the
|
||||
footer of \fIview.\&html\fR.\& Look for "Your Name" and "example.\&org".\&
|
||||
@@ -302,55 +305,62 @@ current date of the machine Oddmu is running on is used.\& If a link already
|
||||
exists on the changes page, it is moved up to the current date.\& If that leaves
|
||||
an old date without any links, that date heading is removed.\&
|
||||
.PP
|
||||
If you want to link to the changes page, you need to do this yourself.\& Add a
|
||||
link from the index, for example.\& The "view.\&html" template currently doesn'\&t do
|
||||
it.\& See \fIoddmu-templates\fR(5) if you want to add the link to the template.\&
|
||||
.PP
|
||||
A page whose name starts with an ISO date (YYYY-MM-DD, e.\&g.\& "2023-10-28") is
|
||||
called a \fBblog\fR page.\& When creating or editing blog pages, links to it are added
|
||||
from other pages.\&
|
||||
from other pages as follows:
|
||||
.PP
|
||||
.PD 0
|
||||
.IP \(bu 4
|
||||
If the blog page name starts with the current year, a link is created from the
|
||||
index page back to the blog page being created or edited.\& Again, you can prevent
|
||||
this from happening by deselecting the checkbox "Add link to the list of
|
||||
changes.\&" The index page can be edited like every other page, so it'\&s easy to
|
||||
undo mistakes.\&
|
||||
index page back to the blog page being created or edited.\& Again, you can
|
||||
prevent this from happening by deselecting the checkbox "Add link to the list
|
||||
of changes.\&" The index page can be edited like every other page, so it'\&s easy
|
||||
to undo mistakes.\&
|
||||
.PD
|
||||
.PP
|
||||
.PD 0
|
||||
.IP \(bu 4
|
||||
For every \fBhashtag\fR used, another link might be created.\& If a page named like
|
||||
the hashtag exists, a backlink is added to it, linking to the new or edited blog
|
||||
page.\&
|
||||
the hashtag exists, a backlink is added to it, linking to the new or edited
|
||||
blog page.\&
|
||||
.PD
|
||||
.PP
|
||||
.PD 0
|
||||
.IP \(bu 4
|
||||
If a link to the new or edited blog page already exists but it'\&s title is no
|
||||
longer correct, it is updated.\&
|
||||
.PD
|
||||
.PP
|
||||
New links added for blog pages are added at the top of the first unnumbered list
|
||||
using the asterisk ('\&*'\&).\& If no such list exists, a new one is started at the
|
||||
bottom of the page.\& This allows you to have a different unnumbered list further
|
||||
up on the page, as long as it uses the minus for items ('\&-'\&).\&
|
||||
.PP
|
||||
Changes made locally do not create any links on the changes page, the index page
|
||||
or on any hashtag pages.\& See \fIoddmu-notify\fR(1) for a way to add the necessary
|
||||
links to the changes page and possibly to the index and hashtag pages.\&
|
||||
Changes made locally to the source files (using an editor) do not create any
|
||||
links on the changes page, the index page or on any hashtag pages.\& See
|
||||
\fIoddmu-notify\fR(1) for a way to add the necessary links to the changes page and
|
||||
possibly to the index and hashtag pages.\&
|
||||
.PP
|
||||
A hashtag consists of a number sign ('\&#'\&) followed by Unicode letters, numbers
|
||||
or the underscore ('\&_'\&).\& Thus, a hashtag ends with punctuation or whitespace.\&
|
||||
.PP
|
||||
The page names, titles and hashtags are loaded into memory when the server
|
||||
starts.\& If you have a lot of pages, this takes a lot of memory.\&
|
||||
starts.\& If you have a lot of pages, this takes a lot of memory.\& Oddmu watches
|
||||
the working directory and any subdirectories for changes made to page files and
|
||||
updates this cache when necessary.\&
|
||||
.PP
|
||||
Oddmu watches the working directory and any subdirectories for changes made
|
||||
directly.\& Thus, in theory, it'\&s not necessary to restart it after making such
|
||||
changes.\&
|
||||
.PP
|
||||
You cannot edit uploaded files.\& If you upload a file called "hello.\&txt" and
|
||||
attempt to edit it by using "/edit/hello.\&txt" you create a page with the name
|
||||
"hello.\&txt.\&md" instead.\&
|
||||
Uploaded files cannot be edited unless they end with ".\&md".\& If you upload a file
|
||||
called "hello.\&txt" and attempt to edit it by using "/edit/hello.\&txt" you create
|
||||
a page with the name "hello.\&txt.\&md" instead.\&
|
||||
.PP
|
||||
In order to delete uploaded files via the web, create an empty file and upload
|
||||
it.\& In order to delete a wiki page, save an empty page.\&
|
||||
.PP
|
||||
Note that some HTML file names are special: they act as templates.\& See
|
||||
\fIoddmu-templates\fR(5) for their names and their use.\&
|
||||
\fIoddmu-templates\fR(5) for their names and their use.\& Oddmu watches the working
|
||||
directory and any subdirectories for changes made to template files and reloads
|
||||
them.\& There is no need to restart the server after making changes to the
|
||||
templates.\&
|
||||
.PP
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
@@ -420,6 +430,14 @@ If you want to stop using Oddmu:
|
||||
\fIoddmu-export\fR(1), on how to export all the files as one big RSS file
|
||||
.PD
|
||||
.PP
|
||||
And finally, if you don'\&t have the man pages, you can still read the original
|
||||
documents:
|
||||
.PP
|
||||
.PD 0
|
||||
.IP \(bu 4
|
||||
\fIoddmu-man\fR(1), to get help even if you don'\&t have the man pages installed
|
||||
.PD
|
||||
.PP
|
||||
.SH AUTHORS
|
||||
.PP
|
||||
Maintained by Alex Schroeder <alex@gnu.\&org>.\&
|
||||
|
||||
@@ -25,10 +25,11 @@ create, most likely.
|
||||
|
||||
See _oddmu_(5) for details about the page formatting.
|
||||
|
||||
If you request a page that doesn't exist, Oddmu tries to find a matching
|
||||
Markdown file by appending the extension ".md" to the page name. In the example
|
||||
above, the page name requested is "index" and the file name Oddmu tries to read
|
||||
is "index.md". If no such file exists, Oddmu offers you to create the page.
|
||||
If you request a file that exists, like "index.md", Oddmu serves it as-is. If
|
||||
you request a file that doesn't exist, like "index", Oddmu checks if a matching
|
||||
Markdown file exists by appending the extension ".md". If such a file is found,
|
||||
it is turned into HTML and shown. If no such file exists, Oddmu offers you to
|
||||
create the page.
|
||||
|
||||
If your files don't provide their own title ("# title"), the file name (without
|
||||
".md") is used for the page title.
|
||||
@@ -71,9 +72,9 @@ curl --form body="Did you bring a towel?" \
|
||||
When calling the _drop_ action, the query parameters used are _name_ for the
|
||||
target filename and _file_ for the file to upload. If the query parameter
|
||||
_maxwidth_ is set, an attempt is made to decode and resize the image. JPG, PNG,
|
||||
WEBP and HEIC files can be decoded. Only JPG and PNG files can be encoded,
|
||||
however. If the target name ends in _.jpg_, the _quality_ query parameter is
|
||||
also taken into account. To upload some thumbnails:
|
||||
WEBP and HEIC files can be decoded. Only JPG, PNG and WEBP files can be encoded,
|
||||
however. If the target name ends in _.jpg_ or _.png_, the _quality_ query
|
||||
parameter is also taken into account. To upload some thumbnails:
|
||||
|
||||
```
|
||||
for f in *.jpg; do
|
||||
@@ -90,11 +91,12 @@ curl 'http://localhost:8080/search/?q=towel'
|
||||
```
|
||||
|
||||
The page name to act upon is optionally taken from the query parameter _id_. In
|
||||
this case, the directory must also be part of the query parameter and not of the
|
||||
URL path.
|
||||
this case, the directory must still be part of the path and may not be part of
|
||||
the _id_. This is enforced so that the path can be used by a webserver for
|
||||
access control.
|
||||
|
||||
```
|
||||
curl 'http://localhost:8080/view/?id=man/oddmu.1.txt'
|
||||
curl 'http://localhost:8080/view/man/?id=oddmu.1.txt'
|
||||
```
|
||||
|
||||
The base name for the _archive_ action is used by the browser to save the
|
||||
@@ -107,8 +109,9 @@ curl --remote-name 'http://localhost:8080/archive/man/man.zip
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
The template files are the HTML files in the working directory. Please change
|
||||
these templates!
|
||||
The template files are the HTML files in the working directory. If they are
|
||||
missing, the default files are written to disk as soon as they are required.
|
||||
Please change these templates!
|
||||
|
||||
The first change you should make is to replace the name and email address in the
|
||||
footer of _view.html_. Look for "Your Name" and "example.org".
|
||||
@@ -247,55 +250,53 @@ current date of the machine Oddmu is running on is used. If a link already
|
||||
exists on the changes page, it is moved up to the current date. If that leaves
|
||||
an old date without any links, that date heading is removed.
|
||||
|
||||
If you want to link to the changes page, you need to do this yourself. Add a
|
||||
link from the index, for example. The "view.html" template currently doesn't do
|
||||
it. See _oddmu-templates_(5) if you want to add the link to the template.
|
||||
|
||||
A page whose name starts with an ISO date (YYYY-MM-DD, e.g. "2023-10-28") is
|
||||
called a *blog* page. When creating or editing blog pages, links to it are added
|
||||
from other pages.
|
||||
from other pages as follows:
|
||||
|
||||
If the blog page name starts with the current year, a link is created from the
|
||||
index page back to the blog page being created or edited. Again, you can prevent
|
||||
this from happening by deselecting the checkbox "Add link to the list of
|
||||
changes." The index page can be edited like every other page, so it's easy to
|
||||
undo mistakes.
|
||||
- If the blog page name starts with the current year, a link is created from the
|
||||
index page back to the blog page being created or edited. Again, you can
|
||||
prevent this from happening by deselecting the checkbox "Add link to the list
|
||||
of changes." The index page can be edited like every other page, so it's easy
|
||||
to undo mistakes.
|
||||
|
||||
For every *hashtag* used, another link might be created. If a page named like
|
||||
the hashtag exists, a backlink is added to it, linking to the new or edited blog
|
||||
page.
|
||||
- For every *hashtag* used, another link might be created. If a page named like
|
||||
the hashtag exists, a backlink is added to it, linking to the new or edited
|
||||
blog page.
|
||||
|
||||
If a link to the new or edited blog page already exists but it's title is no
|
||||
longer correct, it is updated.
|
||||
- If a link to the new or edited blog page already exists but it's title is no
|
||||
longer correct, it is updated.
|
||||
|
||||
New links added for blog pages are added at the top of the first unnumbered list
|
||||
using the asterisk ('\*'). If no such list exists, a new one is started at the
|
||||
bottom of the page. This allows you to have a different unnumbered list further
|
||||
up on the page, as long as it uses the minus for items ('-').
|
||||
|
||||
Changes made locally do not create any links on the changes page, the index page
|
||||
or on any hashtag pages. See _oddmu-notify_(1) for a way to add the necessary
|
||||
links to the changes page and possibly to the index and hashtag pages.
|
||||
Changes made locally to the source files (using an editor) do not create any
|
||||
links on the changes page, the index page or on any hashtag pages. See
|
||||
_oddmu-notify_(1) for a way to add the necessary links to the changes page and
|
||||
possibly to the index and hashtag pages.
|
||||
|
||||
A hashtag consists of a number sign ('#') followed by Unicode letters, numbers
|
||||
or the underscore ('\_'). Thus, a hashtag ends with punctuation or whitespace.
|
||||
|
||||
The page names, titles and hashtags are loaded into memory when the server
|
||||
starts. If you have a lot of pages, this takes a lot of memory.
|
||||
starts. If you have a lot of pages, this takes a lot of memory. Oddmu watches
|
||||
the working directory and any subdirectories for changes made to page files and
|
||||
updates this cache when necessary.
|
||||
|
||||
Oddmu watches the working directory and any subdirectories for changes made
|
||||
directly. Thus, in theory, it's not necessary to restart it after making such
|
||||
changes.
|
||||
|
||||
You cannot edit uploaded files. If you upload a file called "hello.txt" and
|
||||
attempt to edit it by using "/edit/hello.txt" you create a page with the name
|
||||
"hello.txt.md" instead.
|
||||
Uploaded files cannot be edited unless they end with ".md". If you upload a file
|
||||
called "hello.txt" and attempt to edit it by using "/edit/hello.txt" you create
|
||||
a page with the name "hello.txt.md" instead.
|
||||
|
||||
In order to delete uploaded files via the web, create an empty file and upload
|
||||
it. In order to delete a wiki page, save an empty page.
|
||||
|
||||
Note that some HTML file names are special: they act as templates. See
|
||||
_oddmu-templates_(5) for their names and their use.
|
||||
_oddmu-templates_(5) for their names and their use. Oddmu watches the working
|
||||
directory and any subdirectories for changes made to template files and reloads
|
||||
them. There is no need to restart the server after making changes to the
|
||||
templates.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
@@ -334,6 +335,11 @@ If you want to stop using Oddmu:
|
||||
|
||||
- _oddmu-export_(1), on how to export all the files as one big RSS file
|
||||
|
||||
And finally, if you don't have the man pages, you can still read the original
|
||||
documents:
|
||||
|
||||
- _oddmu-man_(1), to get help even if you don't have the man pages installed
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Maintained by Alex Schroeder <alex@gnu.org>.
|
||||
|
||||
81
man_cmd.go
Normal file
81
man_cmd.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
type manCmd struct {
|
||||
}
|
||||
|
||||
func (cmd *manCmd) SetFlags(f *flag.FlagSet) {
|
||||
}
|
||||
|
||||
func (*manCmd) Name() string { return "man" }
|
||||
func (*manCmd) Synopsis() string { return "show a manual page" }
|
||||
func (*manCmd) Usage() string {
|
||||
return `man <topic>:
|
||||
Print a manual page on a topic. If no topic is given, the
|
||||
available topics are listed. Substrings are possible.
|
||||
`
|
||||
}
|
||||
|
||||
// A filesystem with a read-only copy of the man pages at build time.
|
||||
//
|
||||
//go:embed man/*.txt
|
||||
var manFiles embed.FS
|
||||
|
||||
func (cmd *manCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
topic := strings.Join(f.Args(), " ")
|
||||
return manCli(os.Stdout, topic)
|
||||
}
|
||||
|
||||
func manCli(w io.Writer, topic string) subcommands.ExitStatus {
|
||||
entries, err := manFiles.ReadDir("man")
|
||||
if err != nil {
|
||||
fmt.Println("An error in the build resulted in unreadable manual pages:", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if (topic == "") {
|
||||
fmt.Println("Topics:")
|
||||
names := []string{}
|
||||
for _, entry := range entries {
|
||||
names = append(names, entry.Name())
|
||||
}
|
||||
slices.Sort(names)
|
||||
for _, name := range names {
|
||||
fmt.Println(name)
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
var candidate string
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if strings.Contains(name, topic) {
|
||||
if candidate != "" {
|
||||
fmt.Printf("The topic '%s' matches both %s and %s, maybe more.\n", topic, candidate, name)
|
||||
fmt.Println("Please be more specific.")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
candidate = name
|
||||
}
|
||||
}
|
||||
if candidate == "" {
|
||||
fmt.Printf("No manual page matching topic '%s' found\n", topic)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
b, err := manFiles.ReadFile("man/" + candidate)
|
||||
if err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
os.Stdout.Write(b)
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
27
man_test.go
27
man_test.go
@@ -61,8 +61,8 @@ func TestManTemplates(t *testing.T) {
|
||||
assert.Greater(t, count, 0, "no templates were found")
|
||||
}
|
||||
|
||||
// Does oddmu-templates(5) mention all the templates?
|
||||
func TestManTemplateAttributess(t *testing.T) {
|
||||
// Does oddmu-templates(5) mention all the template attributes?
|
||||
func TestManTemplateAttributes(t *testing.T) {
|
||||
mfp := "man/oddmu-templates.5.txt"
|
||||
b, err := os.ReadFile(mfp)
|
||||
man := string(b)
|
||||
@@ -106,7 +106,7 @@ func TestManActions(t *testing.T) {
|
||||
wiki := string(b)
|
||||
count := 0
|
||||
// this doesn't match the root handler
|
||||
re := regexp.MustCompile(`\.HandleFunc\("(/[a-z]+/)", makeHandler\([a-z]+Handler, (true|false)(, http\.Method(Get|Post))+\)\)`)
|
||||
re := regexp.MustCompile(`mux\.HandleFunc\("(/[a-z]+/)", makeHandler\([a-z]+Handler, (true|false)(, http\.Method(Get|Post))+\)\)`)
|
||||
for _, match := range re.FindAllStringSubmatch(wiki, -1) {
|
||||
count++
|
||||
var path string
|
||||
@@ -122,6 +122,27 @@ func TestManActions(t *testing.T) {
|
||||
assert.Contains(t, main, "\n- _/_", "root")
|
||||
}
|
||||
|
||||
// Does oddmu(1) mention all the commands?
|
||||
func TestManCommands(t *testing.T) {
|
||||
b, err := os.ReadFile("man/oddmu.1.txt")
|
||||
assert.NoError(t, err)
|
||||
main := string(b)
|
||||
b, err = os.ReadFile("wiki.go")
|
||||
assert.NoError(t, err)
|
||||
wiki := string(b)
|
||||
count := 0
|
||||
re := regexp.MustCompile(`subcommands\.Register\(&([a-z]+)Cmd`)
|
||||
for _, match := range re.FindAllStringSubmatch(wiki, -1) {
|
||||
count++
|
||||
command := match[1]
|
||||
ref := "_oddmu-" + command + "_(1)"
|
||||
assert.Contains(t, main, ref, "link to the '%s' command", command)
|
||||
}
|
||||
assert.Greater(t, count, 0, "no commands were found")
|
||||
// root handler is manual
|
||||
assert.Contains(t, main, "\n- _/_", "root")
|
||||
}
|
||||
|
||||
// Does the README link to all the man pages and all the Go source files,
|
||||
// excluding the command and test files?
|
||||
func TestReadme(t *testing.T) {
|
||||
|
||||
@@ -77,7 +77,7 @@ func searchCli(w io.Writer, cmd *searchCmd, args []string) subcommands.ExitStatu
|
||||
case cmd.files:
|
||||
for _, p := range items {
|
||||
name := filepath.FromSlash(p.Name) + ".md\n"
|
||||
fmt.Fprintf(w, name)
|
||||
fmt.Fprint(w, name)
|
||||
}
|
||||
default:
|
||||
for _, p := range items {
|
||||
|
||||
@@ -8,10 +8,13 @@ import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// SitemapURL is a URL of the sitemap.
|
||||
type SitemapURL struct {
|
||||
// Loc is the actual location.
|
||||
Loc string
|
||||
}
|
||||
|
||||
// Sitemap is the sitemap itself, containing a list of URLs.
|
||||
type Sitemap struct {
|
||||
URL []*SitemapURL
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type sitemapCmd struct {
|
||||
base string
|
||||
base string
|
||||
filter string
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func (cmd *sitemapCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfac
|
||||
// is important so that test code can ensure no other test running in parallel can interfere with the list of known
|
||||
// pages (by adding or deleting pages).
|
||||
func sitemapCli(w io.Writer, idx *indexStore, base, filter string) subcommands.ExitStatus {
|
||||
loadTemplates()
|
||||
initTemplates()
|
||||
template := "sitemap.html"
|
||||
t := templates.template[template]
|
||||
if t == nil {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<meta name="generator" content="Oddμ <https://src.alexschroeder.ch/oddmu.git/>"/>
|
||||
<title>{{.Title}}</title>
|
||||
<style>
|
||||
html { max-width: 65ch; padding: 1ch; margin: auto; color: #111; background-color: #ffe }
|
||||
|
||||
@@ -72,7 +72,7 @@ func staticCli(source, target string, jobs int, glob string, shrink, verbose, qu
|
||||
index.RLock()
|
||||
defer index.RUnlock()
|
||||
loadLanguages()
|
||||
loadTemplates()
|
||||
initTemplates()
|
||||
tasks := make(chan args, 10000)
|
||||
results := make(chan error, jobs)
|
||||
done := make(chan bool, jobs)
|
||||
|
||||
61
templates.go
61
templates.go
@@ -1,44 +1,80 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A filesystem with a read-only copy of the templates at build time.
|
||||
//
|
||||
//go:embed *.html
|
||||
var templateDefaults embed.FS
|
||||
|
||||
// templateFiles are the various HTML template files used. These files must exist in the root directory for Oddmu
|
||||
// to be able to generate HTML output. This always requires a template.
|
||||
var templateFiles = []string{"edit.html", "add.html", "view.html", "preview.html",
|
||||
"diff.html", "search.html", "static.html", "upload.html", "feed.html",
|
||||
"sitemap.html"}
|
||||
var templateFiles = []string{}
|
||||
|
||||
// templateStore controls access to map of parsed HTML templates. Make sure to lock and unlock as appropriate. See
|
||||
// renderTemplate and loadTemplates.
|
||||
// templateStore controls access to the map of parsed HTML templates. Make sure to lock and unlock as appropriate.
|
||||
// See renderTemplate and loadTemplates.
|
||||
type templateStore struct {
|
||||
sync.RWMutex
|
||||
|
||||
// template is a map of parsed HTML templates. The key is their filepath name. By default, the map only contains
|
||||
// top-level templates like "view.html". Subdirectories may contain their own templates which override the
|
||||
// templates in the root directory. If so, they are filepaths like "dir/view.html".
|
||||
// template is a map of parsed HTML templates. The key is their filepath name. By default, the map only
|
||||
// contains top-level templates like "view.html". Subdirectories may contain their own templates which
|
||||
// override the templates in the root directory. If so, they are filepaths like "dir/view.html". This is a
|
||||
// map because we need to add and remove templates as time passes.
|
||||
template map[string]*template.Template
|
||||
}
|
||||
|
||||
var templates templateStore
|
||||
|
||||
// loadTemplates loads the templates. If templates have already been loaded, return immediately.
|
||||
func loadTemplates() {
|
||||
// initTemplates loads the templates and writes them to disk if they are missing. If templates have already been
|
||||
// loaded, return immediately.
|
||||
func initTemplates() {
|
||||
if templates.template != nil {
|
||||
return
|
||||
}
|
||||
templates.Lock()
|
||||
defer templates.Unlock()
|
||||
// walk the directory, load templates and add directories
|
||||
templates.template = make(map[string]*template.Template)
|
||||
// load the defaults and make a list of the default names
|
||||
entries, err := templateDefaults.ReadDir(".")
|
||||
if err != nil {
|
||||
log.Println("An error in the build resulted in unreadable default templates:", err)
|
||||
}
|
||||
// if loading or parsing fails, continue as perhaps the files exist in the file-system
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
templateFiles = append(templateFiles, name)
|
||||
b, err := fs.ReadFile(templateDefaults, name)
|
||||
if err != nil {
|
||||
log.Printf("Cannot read built-in default template %s: %s\n", name, err)
|
||||
continue
|
||||
}
|
||||
t := template.New(name)
|
||||
templates.template[name], err = t.Parse(string(b))
|
||||
if err != nil {
|
||||
log.Printf("Cannot parse built-in default template %s: %s\n", name, err)
|
||||
}
|
||||
_, err = os.Stat(name)
|
||||
if err != nil {
|
||||
err = os.WriteFile(name, b, 0644)
|
||||
if err == nil {
|
||||
log.Printf("Wrote built-in default template %s\n", name)
|
||||
} else {
|
||||
log.Printf("Cannot write built-in default template %s: %s\n", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// walk the directory, load templates and add directories
|
||||
filepath.Walk(".", loadTemplate)
|
||||
log.Println(len(templates.template), "templates loaded")
|
||||
}
|
||||
@@ -54,7 +90,6 @@ func loadTemplate(fp string, info fs.FileInfo, err error) error {
|
||||
t, err := template.ParseFiles(fp)
|
||||
if err != nil {
|
||||
log.Println("Cannot parse template:", fp, err)
|
||||
// ignore error
|
||||
} else {
|
||||
templates.template[fp] = t
|
||||
}
|
||||
@@ -92,7 +127,7 @@ func removeTemplate(fp string) {
|
||||
// renderTemplate is the helper that is used to render the templates with data.
|
||||
// A template in the same directory is preferred, if it exists.
|
||||
func renderTemplate(w http.ResponseWriter, dir, tmpl string, data any) {
|
||||
loadTemplates()
|
||||
initTemplates()
|
||||
base := tmpl + ".html"
|
||||
templates.RLock()
|
||||
defer templates.RUnlock()
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
<meta name="generator" content="Oddμ <https://src.alexschroeder.ch/oddmu.git/>"/>
|
||||
<title>{{.Title}}</title>
|
||||
<style>
|
||||
html { max-width: 70ch; padding: 1ch; margin: auto; color: #111; background-color: #ffe }
|
||||
|
||||
1
wiki.go
1
wiki.go
@@ -230,6 +230,7 @@ func commands() {
|
||||
subcommands.Register(&exportCmd{}, "")
|
||||
subcommands.Register(&hashtagsCmd{}, "")
|
||||
subcommands.Register(&feedCmd{}, "")
|
||||
subcommands.Register(&manCmd{}, "")
|
||||
subcommands.Register(&htmlCmd{}, "")
|
||||
subcommands.Register(&listCmd{}, "")
|
||||
subcommands.Register(&linksCmd{}, "")
|
||||
|
||||
Reference in New Issue
Block a user