From bda44e93541fa478abf3ce4b3461f026a90fa8cb Mon Sep 17 00:00:00 2001
From: Thomas Voss
Date: Mon, 11 Sep 2023 05:15:20 +0200
Subject: Move the site from HTML to GSP
---
Makefile | 22 +-
include/fmt-code | 2 +-
include/footer.gsp | 14 +
include/footer.html | 14 -
include/head.gsp | 5 +
include/head.html | 5 -
include/lib.m4 | 17 +-
include/nav.gsp | 4 +
include/nav.html | 4 -
src/ame/index.gsp | 92 +++
src/ame/index.html | 105 ----
src/index.gsp | 55 ++
src/index.html | 59 --
src/prj/index.gsp | 40 ++
src/prj/index.html | 45 --
src/prj/mkpass/basic-usage.sh.gsp | 6 +
src/prj/mkpass/basic-usage.sh.html | 6 -
src/prj/mkpass/index.gsp | 106 ++++
src/prj/mkpass/index.html | 102 ----
src/prj/mkpass/length-flag.sh.gsp | 4 +
src/prj/mkpass/length-flag.sh.html | 4 -
"src/prj/mmv/camel-to-snake-na\303\257ve.sh.gsp" | 2 +
"src/prj/mmv/camel-to-snake-na\303\257ve.sh.html" | 2 -
src/prj/mmv/camel-to-snake-smart.sh.gsp | 1 +
src/prj/mmv/camel-to-snake-smart.sh.html | 1 -
src/prj/mmv/examples/camel-to-snake.sh.gsp | 1 +
src/prj/mmv/examples/camel-to-snake.sh.html | 1 -
src/prj/mmv/examples/hyphens.sh.gsp | 1 +
src/prj/mmv/examples/hyphens.sh.html | 1 -
src/prj/mmv/examples/i-flag.sh.gsp | 1 +
src/prj/mmv/examples/i-flag.sh.html | 1 -
src/prj/mmv/examples/lowercase.sh.gsp | 1 +
src/prj/mmv/examples/lowercase.sh.html | 1 -
src/prj/mmv/examples/number.sh.gsp | 2 +
src/prj/mmv/examples/number.sh.html | 2 -
src/prj/mmv/examples/swap.sh.gsp | 1 +
src/prj/mmv/examples/swap.sh.html | 1 -
src/prj/mmv/examples/vipe.sh.gsp | 1 +
src/prj/mmv/examples/vipe.sh.html | 1 -
src/prj/mmv/index.gsp | 645 +++++++++++++++++++++
src/prj/mmv/index.html | 667 ----------------------
src/prj/mmv/ls-files.sh.gsp | 2 +
src/prj/mmv/ls-files.sh.html | 2 -
src/prj/mmv/manual-mv.sh.gsp | 5 +
src/prj/mmv/manual-mv.sh.html | 5 -
src/prj/mmv/mmv-rev-zero.sh.gsp | 2 +
src/prj/mmv/mmv-rev-zero.sh.html | 2 -
src/prj/mmv/mmv-rev.sh.gsp | 2 +
src/prj/mmv/mmv-rev.sh.html | 2 -
src/prj/mmv/mmv-tr.sh.gsp | 1 +
src/prj/mmv/mmv-tr.sh.html | 1 -
src/prj/mmv/mmv-verbose.sh.gsp | 8 +
src/prj/mmv/mmv-verbose.sh.html | 8 -
src/prj/mmv/reverse-embedded-newline.sh.gsp | 3 +
src/prj/mmv/reverse-embedded-newline.sh.html | 3 -
src/prj/mmv/sed-debugging-rev.sh.gsp | 3 +
src/prj/mmv/sed-debugging-rev.sh.html | 3 -
src/prj/mmv/sed-debugging.sh.gsp | 3 +
src/prj/mmv/sed-debugging.sh.html | 3 -
src/prj/mmv/sha1sum-long-example.sh.gsp | 11 +
src/prj/mmv/sha1sum-long-example.sh.html | 11 -
src/prj/mmv/sha1sum-short-example.sh.gsp | 6 +
src/prj/mmv/sha1sum-short-example.sh.html | 6 -
src/prj/mmv/tr.sh.gsp | 3 +
src/prj/mmv/tr.sh.html | 3 -
src/prj/mmv/vim.gsp | 2 +
src/prj/mmv/vim.html | 2 -
src/prj/mmv/vipe.sh.gsp | 1 +
src/prj/mmv/vipe.sh.html | 1 -
src/prj/totp/basic-usage.sh.gsp | 7 +
src/prj/totp/basic-usage.sh.html | 7 -
src/prj/totp/index.gsp | 134 +++++
src/prj/totp/index.html | 140 -----
src/prj/totp/zbarimg.sh.gsp | 4 +
src/prj/totp/zbarimg.sh.html | 4 -
src/srp/fw-ec/fn-lock-1.diff.gsp | 12 +
src/srp/fw-ec/fn-lock-1.diff.html | 12 -
src/srp/fw-ec/fn-lock-2.diff.gsp | 36 ++
src/srp/fw-ec/fn-lock-2.diff.html | 36 --
src/srp/fw-ec/hybrid.diff.gsp | 88 +++
src/srp/fw-ec/hybrid.diff.html | 88 ---
src/srp/fw-ec/index.gsp | 229 ++++++++
src/srp/fw-ec/index.html | 263 ---------
src/srp/fw-ec/kbd-sc-cb.c.gsp | 6 +
src/srp/fw-ec/kbd-sc-cb.c.html | 6 -
src/srp/fw-ec/led.diff.gsp | 32 ++
src/srp/fw-ec/led.diff.html | 32 --
src/srp/index.gsp | 40 ++
src/srp/index.html | 47 --
src/style.css | 2 +
src/www/index.gsp | 118 ++++
src/www/index.html | 123 ----
92 files changed, 1771 insertions(+), 1844 deletions(-)
create mode 100644 include/footer.gsp
delete mode 100644 include/footer.html
create mode 100644 include/head.gsp
delete mode 100644 include/head.html
create mode 100644 include/nav.gsp
delete mode 100644 include/nav.html
create mode 100644 src/ame/index.gsp
delete mode 100644 src/ame/index.html
create mode 100644 src/index.gsp
delete mode 100644 src/index.html
create mode 100644 src/prj/index.gsp
delete mode 100644 src/prj/index.html
create mode 100644 src/prj/mkpass/basic-usage.sh.gsp
delete mode 100644 src/prj/mkpass/basic-usage.sh.html
create mode 100644 src/prj/mkpass/index.gsp
delete mode 100644 src/prj/mkpass/index.html
create mode 100644 src/prj/mkpass/length-flag.sh.gsp
delete mode 100644 src/prj/mkpass/length-flag.sh.html
create mode 100644 "src/prj/mmv/camel-to-snake-na\303\257ve.sh.gsp"
delete mode 100644 "src/prj/mmv/camel-to-snake-na\303\257ve.sh.html"
create mode 100644 src/prj/mmv/camel-to-snake-smart.sh.gsp
delete mode 100644 src/prj/mmv/camel-to-snake-smart.sh.html
create mode 100644 src/prj/mmv/examples/camel-to-snake.sh.gsp
delete mode 100644 src/prj/mmv/examples/camel-to-snake.sh.html
create mode 100644 src/prj/mmv/examples/hyphens.sh.gsp
delete mode 100644 src/prj/mmv/examples/hyphens.sh.html
create mode 100644 src/prj/mmv/examples/i-flag.sh.gsp
delete mode 100644 src/prj/mmv/examples/i-flag.sh.html
create mode 100644 src/prj/mmv/examples/lowercase.sh.gsp
delete mode 100644 src/prj/mmv/examples/lowercase.sh.html
create mode 100644 src/prj/mmv/examples/number.sh.gsp
delete mode 100644 src/prj/mmv/examples/number.sh.html
create mode 100644 src/prj/mmv/examples/swap.sh.gsp
delete mode 100644 src/prj/mmv/examples/swap.sh.html
create mode 100644 src/prj/mmv/examples/vipe.sh.gsp
delete mode 100644 src/prj/mmv/examples/vipe.sh.html
create mode 100644 src/prj/mmv/index.gsp
delete mode 100644 src/prj/mmv/index.html
create mode 100644 src/prj/mmv/ls-files.sh.gsp
delete mode 100644 src/prj/mmv/ls-files.sh.html
create mode 100644 src/prj/mmv/manual-mv.sh.gsp
delete mode 100644 src/prj/mmv/manual-mv.sh.html
create mode 100644 src/prj/mmv/mmv-rev-zero.sh.gsp
delete mode 100644 src/prj/mmv/mmv-rev-zero.sh.html
create mode 100644 src/prj/mmv/mmv-rev.sh.gsp
delete mode 100644 src/prj/mmv/mmv-rev.sh.html
create mode 100644 src/prj/mmv/mmv-tr.sh.gsp
delete mode 100644 src/prj/mmv/mmv-tr.sh.html
create mode 100644 src/prj/mmv/mmv-verbose.sh.gsp
delete mode 100644 src/prj/mmv/mmv-verbose.sh.html
create mode 100644 src/prj/mmv/reverse-embedded-newline.sh.gsp
delete mode 100644 src/prj/mmv/reverse-embedded-newline.sh.html
create mode 100644 src/prj/mmv/sed-debugging-rev.sh.gsp
delete mode 100644 src/prj/mmv/sed-debugging-rev.sh.html
create mode 100644 src/prj/mmv/sed-debugging.sh.gsp
delete mode 100644 src/prj/mmv/sed-debugging.sh.html
create mode 100644 src/prj/mmv/sha1sum-long-example.sh.gsp
delete mode 100644 src/prj/mmv/sha1sum-long-example.sh.html
create mode 100644 src/prj/mmv/sha1sum-short-example.sh.gsp
delete mode 100644 src/prj/mmv/sha1sum-short-example.sh.html
create mode 100644 src/prj/mmv/tr.sh.gsp
delete mode 100644 src/prj/mmv/tr.sh.html
create mode 100644 src/prj/mmv/vim.gsp
delete mode 100644 src/prj/mmv/vim.html
create mode 100644 src/prj/mmv/vipe.sh.gsp
delete mode 100644 src/prj/mmv/vipe.sh.html
create mode 100644 src/prj/totp/basic-usage.sh.gsp
delete mode 100644 src/prj/totp/basic-usage.sh.html
create mode 100644 src/prj/totp/index.gsp
delete mode 100644 src/prj/totp/index.html
create mode 100644 src/prj/totp/zbarimg.sh.gsp
delete mode 100644 src/prj/totp/zbarimg.sh.html
create mode 100644 src/srp/fw-ec/fn-lock-1.diff.gsp
delete mode 100644 src/srp/fw-ec/fn-lock-1.diff.html
create mode 100644 src/srp/fw-ec/fn-lock-2.diff.gsp
delete mode 100644 src/srp/fw-ec/fn-lock-2.diff.html
create mode 100644 src/srp/fw-ec/hybrid.diff.gsp
delete mode 100644 src/srp/fw-ec/hybrid.diff.html
create mode 100644 src/srp/fw-ec/index.gsp
delete mode 100644 src/srp/fw-ec/index.html
create mode 100644 src/srp/fw-ec/kbd-sc-cb.c.gsp
delete mode 100644 src/srp/fw-ec/kbd-sc-cb.c.html
create mode 100644 src/srp/fw-ec/led.diff.gsp
delete mode 100644 src/srp/fw-ec/led.diff.html
create mode 100644 src/srp/index.gsp
delete mode 100644 src/srp/index.html
create mode 100644 src/www/index.gsp
delete mode 100644 src/www/index.html
diff --git a/Makefile b/Makefile
index e45abb9..3a461bb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,12 @@
.PHONY: check clean serve
-gendeps = ${filter-out %/index.html,${wildcard ${1}/*}}
+gendeps = ${filter-out %/index.gsp,${wildcard ${1}/*}}
includes := ${wildcard include/*}
srcdirs := ${shell find src -type d}
outdirs := ${srcdirs:src%=out%}
-sources := ${shell find src -type f -regextype egrep -regex '.*/(index\.html|.*\.(css|svg|png|dot))'}
+sources := ${shell find src -type f -regextype egrep -regex '.*/(index\.gsp|.*\.(css|svg|png|dot))'}
outputs := ${sources:src/%=out/%}
+outputs := ${outputs:%.gsp=%.html}
outputs := ${outputs:%.dot=%.svg}
all: ${outdirs} ${outputs} fonts
@@ -24,21 +25,22 @@ out/%.svg: src/%.dot
@dot -Tsvg $< >$@
@printf 'DOT\t%s\n' "$@"
-out/%.html: src/%.html ${includes} src/style.css
- @PATH="$$PATH:./include" m4 -P ${foreach dir,${^D},-I${dir}} include/lib.m4 $< >$@
- @printf 'M4\t%s\n' "$@"
+out/%.html: src/%.gsp ${includes} src/style.css
+ @PATH="$$PATH:./include" \
+ m4 -P ${foreach dir,${^D},-I${dir}} include/lib.m4 $< | gsp >$@
+ @printf 'GSP\t%s\n' "$@"
-src/srp/fw-ec/index.html: ${call gendeps,src/srp/fw-ec}
+src/srp/fw-ec/index.gsp: ${call gendeps,src/srp/fw-ec}
@touch $@
-src/prj/mmv/index.html: ${call gendeps,src/prj/mmv}
+src/prj/mmv/index.gsp: ${call gendeps,src/prj/mmv}
@touch $@
-src/prj/mkpass/index.html: ${call gendeps,src/prj/mkpass}
+src/prj/mkpass/index.gsp: ${call gendeps,src/prj/mkpass}
@touch $@
-src/prj/totp/index.html: ${call gendeps,src/prj/totp}
+src/prj/totp/index.gsp: ${call gendeps,src/prj/totp}
@touch $@
check:
- LANG=en_US.UTF-8 find src -name 'index.html' -exec \
+ LANG=en_US.UTF-8 find src -name 'index.gsp' -exec \
aspell --home-dir=./ --ignore-case check {} \;
clean:
diff --git a/include/fmt-code b/include/fmt-code
index 9da3828..c02a389 100755
--- a/include/fmt-code
+++ b/include/fmt-code
@@ -1,3 +1,3 @@
#!/bin/sh
-sed 's|.*|&|' "${2%/*}/$1"
+sed 's|.*|>code{-&}|; $s/>//' "${2%/*}/$1"
diff --git a/include/footer.gsp b/include/footer.gsp
new file mode 100644
index 0000000..f613fcf
--- /dev/null
+++ b/include/footer.gsp
@@ -0,0 +1,14 @@
+m4_define(m4___id__, m4_esyscmd(git rev-list -1 HEAD m4___caller__))
+small {-
+ Page last edited:
+ @a
+ href="https://git.thomasvoss.com/www.thomasvoss.com/commit?id=m4___id__"
+ target="_blank"
+ {-
+ m4_esyscmd(
+ git log -1 --pretty='format:%cI' m4___caller__ \
+ | ifne xargs date +'%A %d %B %Y — %T %Z' -d \
+ | ifne -n echo 'No commit yet…'
+ )
+ }
+}
diff --git a/include/footer.html b/include/footer.html
deleted file mode 100644
index 6b45a2c..0000000
--- a/include/footer.html
+++ /dev/null
@@ -1,14 +0,0 @@
-m4_define(m4___id__, m4_esyscmd(git rev-list -1 HEAD m4___caller__))
-
- Page last edited:
-
- m4_esyscmd(
- git log -1 --pretty='format:%cI' m4___caller__ \
- | ifne xargs date +'%A %d %B %Y — %T %Z' -d \
- | ifne -n echo 'No commit yet…'
- )
-
-
diff --git a/include/head.gsp b/include/head.gsp
new file mode 100644
index 0000000..407589a
--- /dev/null
+++ b/include/head.gsp
@@ -0,0 +1,5 @@
+meta charset="UTF-8" {}
+meta name="viewport" content="width=device-width, initial-scale=1.0" {}
+link href="/favicon.svg" rel="shortcut icon" type="image/svg" {}
+link href="/style.css" rel="stylesheet" {}
+title {-The Mango Tree}
diff --git a/include/head.html b/include/head.html
deleted file mode 100644
index 97aa283..0000000
--- a/include/head.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-The Mango Tree
diff --git a/include/lib.m4 b/include/lib.m4
index 34cf2c3..3d0e50f 100644
--- a/include/lib.m4
+++ b/include/lib.m4
@@ -2,12 +2,27 @@ m4_dnl To avoid annoying conflicts with quote characters, use
m4_dnl U+2068 FIRST STRONG ISOLATE and U+2069 POP DIRECTIONAL ISOLATE
m4_changequote(,)
+m4_dnl We also need to avoid the comment character ever doing absolutely
+m4_dnl anything. I don’t think I’ll ever use ASCII character 1 (SOH) lol.
+m4_changecom()
+
+m4_dnl Macro to create a page footer
m4_define(
m4_footer,
- m4_define(m4___caller__, m4___file__)m4_include(footer.html)
+ m4_define(m4___caller__, m4___file__)m4_include(footer.gsp)
)
+m4_dnl Macro to properly format code blocks
m4_define(
m4_fmt_code,
m4_esyscmd(fmt-code "$1" m4___file__)m4_dnl
)
+
+m4_dnl Macro to escape characters in gsp(5) text-nodes
+m4_define(
+ m4_gsp_quote,
+ m4_patsubst($1, [@}\\], \\\&)
+)
+
+m4_dnl Macro for generating abbrevations
+m4_define(m4_abbr, @abbr .m4_translit($1, A-Z2, a-zt) {-$1})
diff --git a/include/nav.gsp b/include/nav.gsp
new file mode 100644
index 0000000..93a7ae5
--- /dev/null
+++ b/include/nav.gsp
@@ -0,0 +1,4 @@
+menu {
+ li {a href="/" {-Home}}
+ li {a href=".." {-Back}}
+}
diff --git a/include/nav.html b/include/nav.html
deleted file mode 100644
index 9f8f2c2..0000000
--- a/include/nav.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/ame/index.gsp b/src/ame/index.gsp
new file mode 100644
index 0000000..e0ed55b
--- /dev/null
+++ b/src/ame/index.gsp
@@ -0,0 +1,92 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-About Me}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ One obvious advantage [of lisp syntax] is that there hardly *is* any
+ syntax. You can learn enough Lisp syntax to write useful programs
+ in about ten minutes.
+ }
+ }
+ figcaption {-Mark J. Dominus}
+ }
+ }
+
+ main {
+ p {-
+ If you are a prospective employer, you can view my m4_abbr(CV)
+ @a href="https://cv.thomasvoss.com" target="_blank" {-here}.
+ }
+
+ h2 {-Who Am I}
+
+ p {-
+ I’m Thomas. A self-taught software developer from Brazil and the
+ Netherlands who grew up in the Middle East. I started to code at the
+ age of 9, writing batch scripts @x-ref {-1} on my schools Windows 7
+ machines to toggle folder visibility with a password. That became
+ necessary because the genius m4_abbr(IT) guys at our school decided we
+ should all save our work onto a shared m4_abbr(NAS) where everyone has
+ read+write access to everyone elses work. @em {-How could that ever go
+ wrong.}
+ }
+
+ aside {
+ p data-ref="1" {-
+ Shoutouts to
+ @a
+ href="https://www.instructables.com/member/Prof.%20Pickle/"
+ target="_blank"
+ {-
+ Prof. Pickle on Instructables
+ }
+ by the way. Most of his stuff seems to have been deleted all these
+ years later, but he was the guy I learnt to code from.
+ }
+ }
+
+ p {-
+ These days my interests lie mostly in m4_abbr(CLI) development. I take
+ a great joy in writing simple yet highly effective tools to solve
+ problems not only in the easiest way, but also in the most extensible
+ way possible. I think my batch file-renaming utility @em {-mmv}
+ @a href="/prj/mmv" {-does a great job at that}.
+ }
+
+ p {-
+ That being said, I also have a great interest in operating systems,
+ shells, and really anything that could be considered remotely low-level.
+ I’m not totally afraid of front-end development, although I @em {-did}
+ write this site in plain m4_abbr(HTML) and m4_abbr(CSS) because modern
+ m4_abbr(HTML) frameworks are the worst excuses for software I have ever
+ seen.
+ }
+
+ p {-
+ As for my religious- and political beliefs, let’s make those
+ clear:
+ @ul {
+ li {-Spaces should never be used for indentation}
+ li {-Tabs should never be used for alignment}
+ li {-Emacs is better than Vim}
+ li {-The dominance of VSCode is an embarrassment to our industry}
+ li {-
+ Object-oriented programming, Java, JavaScript, and m4_abbr(XML) were
+ all massive mistakes
+ }
+ li {-C is far superior to C++}
+ li {-Rust is not gods chosen language (even if it’s pretty cool)}
+ }
+ }
+ }
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/ame/index.html b/src/ame/index.html
deleted file mode 100644
index 20ebacd..0000000
--- a/src/ame/index.html
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
About Me
- m4_include(nav.html)
-
-
-
-
-
-
-
- If you are a prospective employer, you can view
- my CV
- here.
-
-
-
Who Am I
-
-
- I’m Thomas. A self-taught software developer from Brazil and
- the Netherlands who grew up in the Middle East. I started to
- code at the age of 9, writing batch scripts 1 on
- my schools Windows 7 machines to toggle folder visibility with a
- password. That became necessary because the
- genius IT guys at our school decided we
- should all save our work onto a
- shared NAS where everyone has
- read+write access to everyone elses work. How could that
- ever go wrong.
-
-
-
-
-
- These days my interests lie mostly
- in CLI development. I take a great joy
- in writing simple yet highly effective tools to solve problems
- not only in the easiest way, but also in the most extensible way
- possible. I think my batch file-renaming
- utility mmvdoes a great job at
- that.
-
-
-
- That being said, I also have a great interest in operating
- systems, shells, and really anything that could be considered
- remotely low-level. I’m not totally afraid of front-end
- development, although I did write this site in plain
- HTML and CSS
- because modern HTML frameworks are the
- worst excuses for software I have ever seen.
-
-
-
- As for my religious- and political beliefs, let’s make those
- clear:
-
-
Spaces should never be used for indentation
-
Tabs should never be used for alignment
-
Emacs is better than Vim
-
- The dominance of VSCode is an embarrassment to our
- industry
-
-
- Object-oriented programming, Java, JavaScript,
- and XML were all massive
- mistakes
-
-
C is far superior to C++
-
Rust is not gods chosen language (even if it’s pretty cool)
-
-
-
-
-
-
-
-
-
diff --git a/src/index.gsp b/src/index.gsp
new file mode 100644
index 0000000..9f2808c
--- /dev/null
+++ b/src/index.gsp
@@ -0,0 +1,55 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ h1 {-Root Page}
+
+ figure .quote {
+ blockquote {
+ p {=
+ If Java had true garbage collection, most programs would delete
+ themselves upon execution.
+ }
+ }
+ figcaption {-Robert Sewell}
+ }
+ }
+
+ main {
+ p {-
+ Welcome to my plot of the internet. This site is mostly oriented
+ towards topics related to software development, but you might eventually
+ find some things that are completely unrelated as well. The “source
+ code” for the site (if you can call m4_abbr(HTML) @x-ref{-1} and a basic
+ build system source code)
+ @a
+ href="https://git.thomasvoss.com/www.thomasvoss.com"
+ target="_blank"
+ {-are available through git}.
+ }
+
+ aside {
+ p data-ref="1" {-
+ The site is actually written in my own m4_abbr(GSP) language, but it
+ transpiles into m4_abbr(HTML). Also, this site doesn’t even have any
+ JavaScript. There truly is no code.
+ }
+ }
+
+ p {-
+ Now go branch off to a subsection of the site:
+ }
+
+ ul {
+ li {a href="prj" {-My Projects}}
+ li {a href="srp" {-Software-Related Posts}}
+ li {a href="www" {-Other Websites}}
+ li {a href="ame" {-About Me}}
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+ }
+}
diff --git a/src/index.html b/src/index.html
deleted file mode 100644
index 19747dc..0000000
--- a/src/index.html
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
Root Page
-
-
-
-
-
-
- Welcome to my plot of the internet. This site is mostly oriented
- towards topics related to software development, but you might
- eventually find some things that are completely unrelated as
- well. The “source code” for the site (if you can call
- HTML 1 and a basic build
- system source code)
-
- are available through git.
-
-
-
-
-
- Now go branch off to a subsection of the site:
-
-
-
-
-
-
-
-
diff --git a/src/prj/index.gsp b/src/prj/index.gsp
new file mode 100644
index 0000000..a5da5ca
--- /dev/null
+++ b/src/prj/index.gsp
@@ -0,0 +1,40 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-My Projects}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ Computers make it easier to do a lot of things, but most of the
+ things they make it easier to do don’t need to be done.
+ }
+ }
+ figcaption {-Andy Rooney}
+ }
+ }
+
+ main {
+ p {-
+ In this section of the website I cover some of my projects. Some are
+ completed, some are ongoing; really I’ll just add a post here if I ever
+ have something I feel is interesting enough to share.
+ }
+
+ p {-Posts:}
+ ul {
+ li {a href="totp" {-totp — generate TOTP codes}}
+ li {a href="mkpass" {-mkpass — make a password}}
+ li {a href="mmv" {-mmv, mpc — mapped file moves and -copies}}
+ }
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/prj/index.html b/src/prj/index.html
deleted file mode 100644
index b8c8bc9..0000000
--- a/src/prj/index.html
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
My Projects
- m4_include(nav.html)
-
-
-
-
-
-
-
- In this section of the website I cover some of my projects. Some are
- completed, some are ongoing; really I’ll just add a post here if I ever
- have something I feel is interesting enough to share.
-
-
-
-
-
-
-
-
diff --git a/src/prj/mkpass/basic-usage.sh.gsp b/src/prj/mkpass/basic-usage.sh.gsp
new file mode 100644
index 0000000..bd8ddf7
--- /dev/null
+++ b/src/prj/mkpass/basic-usage.sh.gsp
@@ -0,0 +1,6 @@
+$ @span .sh-fn {-mkpass}
+m4_gsp_quote(;%_)1S%wYO-unC6%D2pz9'bx^YFI>"VX;T[jzOUsiUr}r/R#T0Qs*XMT*fUef|2L)m4_dnl
+$ @span .sh-fn {-mkpass} a-zA-Z0-9
+qTneHVHfwH3b1nCanKKW24lIcsRO2TUAgp7AGbZInfsV8ZjdsR35ZikHIzyUu06x
+$ @span .sh-fn {-mkpass} [:alpha:][:digit:]
+JlGe9kLOT1ik3CRvOb8VxHqHzluG6oLJ9VI8BVGFTn2lODu3pvTv5ZqeXy3XfT1R
diff --git a/src/prj/mkpass/basic-usage.sh.html b/src/prj/mkpass/basic-usage.sh.html
deleted file mode 100644
index ab1aca8..0000000
--- a/src/prj/mkpass/basic-usage.sh.html
+++ /dev/null
@@ -1,6 +0,0 @@
-$ mkpass
-;%_)1S%wYO-unC6%D2pz9'bx^YFI>"VX;T[jzOUsiUr}r/R#T0Qs*XMT*fUef|2L
-$ mkpass a-zA-Z0-9
-qTneHVHfwH3b1nCanKKW24lIcsRO2TUAgp7AGbZInfsV8ZjdsR35ZikHIzyUu06x
-$ mkpass [:alpha:][:digit:]
-JlGe9kLOT1ik3CRvOb8VxHqHzluG6oLJ9VI8BVGFTn2lODu3pvTv5ZqeXy3XfT1R
diff --git a/src/prj/mkpass/index.gsp b/src/prj/mkpass/index.gsp
new file mode 100644
index 0000000..a7ff910
--- /dev/null
+++ b/src/prj/mkpass/index.gsp
@@ -0,0 +1,106 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-Easy Password Generation}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ Why does JavaScript suck? The answer is simple: history.
+ JavaScript was created by a really smart guy called Brendan Eich
+ over a period of a week or so. He did it because Microsoft was
+ largely perceived as evil, and his aim was to create a programming
+ platform in the Netscape navigator. That way, the browser would run
+ everywhere, and the OS wouldn't matter as much. HAHA! Down with
+ Microsoft. You can see how well that worked out.
+ }
+ }
+ figcaption {-Sahil Malik}
+ }
+ }
+
+ main {
+ p {
+ em {-
+ You can find the @code{-mkpass} git repository over at
+ @a
+ href="https://git.sr.ht/~mango/mkpass"
+ target="_blank"
+ {-sourcehut}
+ or
+ @a
+ href="https://github.com/Mango0x45/mkpass"
+ target="_blank"
+ {-GitHub}.
+ }
+ }
+
+ h2 {-Table of Contents}
+ ul {
+ li {a href="#prologue" {-Prologue}}
+ li {a href="#usage" {-Basic Usage}}
+ }
+
+ h2 #prologue {-Prologue}
+ p {-
+ Password generators are incredibly useful for those who don’t use the
+ same password everywhere. The issue is that everyone uses the same
+ password everywhere. Hopefully when people see how easy password
+ generation can be, that’ll change; there really is no excuse to not use
+ different passwords these days. @code{-mkpass} aims to be the absolute
+ simplest password generator possible while still providing the
+ functionality you need.ls
+ }
+
+ p {-
+ Two factor authentication is also something you should be using — and
+ something that is very easy to manage — and something that I will be
+ posting about shortly™.
+ }
+
+ h2 #usage {-Basic Usage}
+ p {-
+ The most basic usage of @code{-mkpass} is to just call @code{-mkpass}.
+ By default this will generate a 64-character password made up of random
+ printable characters. If you need to use specific characters (for
+ example, maybe you can only use alphanumeric characters) then you can
+ simply specify a
+ @code {
+ a
+ href="https://www.man7.org/linux/man-pages/man1/tr.1.html"
+ target="_blank"
+ {-tr(1)}
+ }
+ style range:
+ }
+
+ figure {
+ pre { m4_fmt_code(basic-usage.sh.gsp) }
+ }
+
+ p {-
+ You can also specify the length of the password using
+ the @code{--l} flag:
+ }
+
+ figure {
+ pre { m4_fmt_code(length-flag.sh.gsp) }
+ }
+
+ p {-
+ And that ladies and gentlemen, is the entire program. A nice minimal
+ tool that does one thing and one thing only, while integrating nicely
+ with the UNIX environment. This is (in my opinion), an example of
+ well-designed software.
+ }
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/prj/mkpass/index.html b/src/prj/mkpass/index.html
deleted file mode 100644
index 8180358..0000000
--- a/src/prj/mkpass/index.html
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
Easy Password Generation
- m4_include(nav.html)
-
-
-
-
-
-
-
-
- You can find the mkpass git repository over at
- sourcehut
- or GitHub.
-
-
- Password generators are incredibly useful for those who don’t use the
- same password everywhere. The issue is that everyone uses the same
- password everywhere. Hopefully when people see how easy password
- generation can be, that’ll change; there really is no excuse to not use
- different passwords these days. mkpass aims to be the
- absolute simplest password generator possible while still providing the
- functionality you need.ls
-
-
-
- Two factor authentication is also something you should be using — and
- something that is very easy to manage — and something that I will be
- posting about shortly™.
-
-
-
Basic Usage
-
- The most basic usage of mkpass is to just
- call mkpass. By default this will generate a 64-character
- password made up of random printable characters. If you need to use
- specific characters (for example, maybe you can only use alphanumeric
- characters) then you can simply specify a
- tr(1) style range:
-
-
-
-
-
- You can also specify the length of the password using
- the -l flag:
-
-
-
-
-
- And that ladies and gentlemen, is the entire program. A nice minimal
- tool that does one thing and one thing only, while integrating nicely
- with the UNIX environment. This is (in my opinion), an example of
- well-designed software.
-
-
-
-
-
-
-
-
diff --git a/src/prj/mkpass/length-flag.sh.gsp b/src/prj/mkpass/length-flag.sh.gsp
new file mode 100644
index 0000000..24ed26e
--- /dev/null
+++ b/src/prj/mkpass/length-flag.sh.gsp
@@ -0,0 +1,4 @@
+$ @span .sh-fn {-mkpass} a-z
+hxjgusvfxzfasluhlkxvsdszxbzoffkyruauiggigjmhptivctnudnkiararlwcn
+$ @span .sh-fn {-mkpass} -l 12 a-z
+wymyggnmwkwz
diff --git a/src/prj/mkpass/length-flag.sh.html b/src/prj/mkpass/length-flag.sh.html
deleted file mode 100644
index f9405a1..0000000
--- a/src/prj/mkpass/length-flag.sh.html
+++ /dev/null
@@ -1,4 +0,0 @@
-$ mkpass a-z
-hxjgusvfxzfasluhlkxvsdszxbzoffkyruauiggigjmhptivctnudnkiararlwcn
-$ mkpass -l 12 a-z
-wymyggnmwkwz
diff --git "a/src/prj/mmv/camel-to-snake-na\303\257ve.sh.gsp" "b/src/prj/mmv/camel-to-snake-na\303\257ve.sh.gsp"
new file mode 100644
index 0000000..cd0d156
--- /dev/null
+++ "b/src/prj/mmv/camel-to-snake-na\303\257ve.sh.gsp"
@@ -0,0 +1,2 @@
+@span .sh-cmt {-# If you aren’t a shell-guru, take a moment to figure out how this works!}
+$ @span .sh-fn {-ls} *.[ch] | @span .sh-fn {-sed} @span .sh-str {-'p; s/[A-Z]/\\L_&/g'} | @span .sh-fn {-xargs} -L2 mv
diff --git "a/src/prj/mmv/camel-to-snake-na\303\257ve.sh.html" "b/src/prj/mmv/camel-to-snake-na\303\257ve.sh.html"
deleted file mode 100644
index 573da41..0000000
--- "a/src/prj/mmv/camel-to-snake-na\303\257ve.sh.html"
+++ /dev/null
@@ -1,2 +0,0 @@
-# If you aren’t a shell-guru, take a moment to figure out how this works!
-$ ls *.[ch] | sed'p; s/[A-Z]/\L_&/g' | xargs -L2 mv
diff --git a/src/prj/mmv/camel-to-snake-smart.sh.gsp b/src/prj/mmv/camel-to-snake-smart.sh.gsp
new file mode 100644
index 0000000..9ce6123
--- /dev/null
+++ b/src/prj/mmv/camel-to-snake-smart.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-ls} *.[ch] | @span .sh-fn {-mmv} sed @span .sh-str {-'s/[A-Z]/\\L_&/g'}
diff --git a/src/prj/mmv/camel-to-snake-smart.sh.html b/src/prj/mmv/camel-to-snake-smart.sh.html
deleted file mode 100644
index 191e87f..0000000
--- a/src/prj/mmv/camel-to-snake-smart.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ ls *.[ch] | mmv sed 's/[A-Z]/\L_&/g'
diff --git a/src/prj/mmv/examples/camel-to-snake.sh.gsp b/src/prj/mmv/examples/camel-to-snake.sh.gsp
new file mode 100644
index 0000000..7948861
--- /dev/null
+++ b/src/prj/mmv/examples/camel-to-snake.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-git} ls-files @span .sh-str {-'*.[ch]'} | @span .sh-fn {-mmv} sed @span .sh-str {-'s/[A-Z]/\\L_&/g'}
diff --git a/src/prj/mmv/examples/camel-to-snake.sh.html b/src/prj/mmv/examples/camel-to-snake.sh.html
deleted file mode 100644
index bd67492..0000000
--- a/src/prj/mmv/examples/camel-to-snake.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ git ls-files '*.[ch]' | mmv sed 's/[A-Z]/\L_&/g'
diff --git a/src/prj/mmv/examples/hyphens.sh.gsp b/src/prj/mmv/examples/hyphens.sh.gsp
new file mode 100644
index 0000000..8f9cc76
--- /dev/null
+++ b/src/prj/mmv/examples/hyphens.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-ls} | @span .sh-fn {-mmv} tr ' ' '-'
diff --git a/src/prj/mmv/examples/hyphens.sh.html b/src/prj/mmv/examples/hyphens.sh.html
deleted file mode 100644
index ca49946..0000000
--- a/src/prj/mmv/examples/hyphens.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ ls | mmv tr ' ' '-'
diff --git a/src/prj/mmv/examples/i-flag.sh.gsp b/src/prj/mmv/examples/i-flag.sh.gsp
new file mode 100644
index 0000000..6977635
--- /dev/null
+++ b/src/prj/mmv/examples/i-flag.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-ls} --zero | @span .sh-fn {-mmv} -0i cmd
diff --git a/src/prj/mmv/examples/i-flag.sh.html b/src/prj/mmv/examples/i-flag.sh.html
deleted file mode 100644
index c22c7c9..0000000
--- a/src/prj/mmv/examples/i-flag.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ ls --zero | mmv -0i cmd
diff --git a/src/prj/mmv/examples/lowercase.sh.gsp b/src/prj/mmv/examples/lowercase.sh.gsp
new file mode 100644
index 0000000..72a8e4c
--- /dev/null
+++ b/src/prj/mmv/examples/lowercase.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-find} . -print0 | @span .sh-fn {-mmv} -0 tr A-Z a-z
diff --git a/src/prj/mmv/examples/lowercase.sh.html b/src/prj/mmv/examples/lowercase.sh.html
deleted file mode 100644
index 84abb92..0000000
--- a/src/prj/mmv/examples/lowercase.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ find . -print0 | mmv -0 tr A-Z a-z
diff --git a/src/prj/mmv/examples/number.sh.gsp b/src/prj/mmv/examples/number.sh.gsp
new file mode 100644
index 0000000..ce82f24
--- /dev/null
+++ b/src/prj/mmv/examples/number.sh.gsp
@@ -0,0 +1,2 @@
+$ @span .sh-fn {-ls} @span .sh-str {-'The Fellowship of the Ring.mp4'} … @span .sh-str {-'The Two Towers.mp4'} | \\
+ @span .sh-fn {-mmv} awk @span .sh-str {-'{ gsub(" ", "-"); printf "%02d-%s", NR, tolower($0) \}'}
diff --git a/src/prj/mmv/examples/number.sh.html b/src/prj/mmv/examples/number.sh.html
deleted file mode 100644
index 5e8e74a..0000000
--- a/src/prj/mmv/examples/number.sh.html
+++ /dev/null
@@ -1,2 +0,0 @@
-$ ls'The Fellowship of the Ring.mp4' … 'The Two Towers.mp4' | \
- mmv awk '{ gsub(" ", "-"); printf "%02d-%s", NR, tolower($0) }'
diff --git a/src/prj/mmv/examples/swap.sh.gsp b/src/prj/mmv/examples/swap.sh.gsp
new file mode 100644
index 0000000..0249751
--- /dev/null
+++ b/src/prj/mmv/examples/swap.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-ls} foo bar | @span .sh-fn {-mmv} tac
diff --git a/src/prj/mmv/examples/swap.sh.html b/src/prj/mmv/examples/swap.sh.html
deleted file mode 100644
index 02c9c28..0000000
--- a/src/prj/mmv/examples/swap.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ ls foo bar | mmv tac
diff --git a/src/prj/mmv/examples/vipe.sh.gsp b/src/prj/mmv/examples/vipe.sh.gsp
new file mode 100644
index 0000000..b738ae1
--- /dev/null
+++ b/src/prj/mmv/examples/vipe.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-ls} | @span .sh-fn {-mmv} -0e vipe
diff --git a/src/prj/mmv/examples/vipe.sh.html b/src/prj/mmv/examples/vipe.sh.html
deleted file mode 100644
index 933039a..0000000
--- a/src/prj/mmv/examples/vipe.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ ls | mmv -0e vipe
diff --git a/src/prj/mmv/index.gsp b/src/prj/mmv/index.gsp
new file mode 100644
index 0000000..3c2fd97
--- /dev/null
+++ b/src/prj/mmv/index.gsp
@@ -0,0 +1,645 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-Moving Files the Right Way}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ I think the OpenBSD crowd is a bunch of masturbating monkeys, in
+ that they make such a big deal about concentrating on security to
+ the point where they pretty much admit that nothing else matters to
+ them.
+ }
+ }
+ figcaption {-Linus Torvalds}
+ }
+ }
+
+ main {
+ p {
+ em {-
+ You can find the @code{-mmv} git repository over at
+ @a
+ href="https://git.sr.ht/~mango/mmv"
+ target="_blank"
+ {-sourcehut}
+ or
+ @a
+ href="https://github.com/Mango0x45/mmv"
+ target="_blank"
+ {-GitHub}.
+ }
+ }
+
+ p {-
+ NOTE: As of the
+ @a href="https://git.sr.ht/~mango/mmv/refs/v1.2.0" {-v1.2.0}
+ release there is now also the @code{-mcp} utility. It behaves the same
+ as the @code{-mmv} utility but it copies files instead of moving them.
+ It also doesn’t support the ‘@code{--n}’ flag as it doesn’t need to deal
+ with backups.
+ }
+
+ h2 {-Table of Contents}
+
+ ul {
+ li {a href="#prologue" {-Prologue}}
+ li {a href="#moving" {-Advanced Moving and Pitfalls}}
+ li {a href="#mapping" {-Name Mapping with @code{-mmv}}}
+ li {a href="#newlines" {-Filenames with Embedded Newlines}}
+ ul {
+ li {a href="0-flag" {-The Simple Case}}
+ li {a href="#e-flag" {-Encoding Newlines}}
+ }
+ li {a href="#i-flag" {-Individual Execution}}
+ li {a href="#safety" {-Safety}}
+ li {a href="#examples" {-Examples}}
+ }
+
+ h2 #prologue {-Prologue}
+ p {-
+ File moving and renaming is one of the most common tasks we undertake on
+ the command-line. We basically always do this with the @code{-mv}
+ utility, and it gets the job done most of the time. Want to rename one
+ file? Use @code{-mv}! Want to move a bunch of files into a directory?
+ Use @code{-mv}! How could mv ever go wrong? Well I’m glad you asked!
+ }
+
+ h2 #moving {-Advanced Moving and Pitfalls}
+ p {-
+ Let’s start off nice and simple. You just inherited a C project that
+ uses the sacrilegious
+ @a
+ href="https://en.wikipedia.org/wiki/Camel_case"
+ target="_blank"
+ {-camelCase}
+ naming convention for its files:
+ }
+
+ figure {
+ pre { m4_fmt_code(ls-files.sh.gsp) }
+ }
+
+ p {-
+ This deeply upsets you, as it upsets me. So you decide you want to
+ switch all these files to use
+ @a
+ href="https://en.wikipedia.org/wiki/Snake_case"
+ target="_blank"
+ {-snake_case},
+ like a normal person. Well how would you do this? You use @code{-mv}!
+ This is what you might end up doing:
+ }
+
+ figure {
+ pre { m4_fmt_code(manual-mv.sh.gsp) }
+ }
+
+ p {-
+ Well… it works I guess, but it’s a pretty shitty way of renaming these
+ files. Luckily we only had 5, but what if this was a much larger
+ project with many more files to rename? Things would get tedious. So
+ instead we can use a pipeline for this:
+ }
+
+ figure {
+ pre { m4_fmt_code(camel-to-snake-naïve.sh.gsp) }
+ }
+
+ aside {
+ p {-
+ The given example assumes your @code{-sed} implementation supports
+ ‘@code{-\\L}’ which is a non-standard m4_abbr(GNU) extension.
+ }
+ }
+
+ p {-
+ That works and it gets the job done, but it’s not really ideal is
+ it? There are a couple of issues with this.
+ }
+
+ ol {
+ li {
+ p {-
+ You’re writing more complicated code. This has the obvious drawback
+ of potentially being more error-prone, but also risks taking more
+ time to write than you’d like as you might have forgotten if
+ @code{-xargs} actually has an ‘@code{--L}’ option or not (which
+ would require reading the
+ @a
+ href="https://www.man7.org/linux/man-pages/man1/xargs.1.html"
+ target="_blank"
+ {
+ code {-xargs(1)}
+ }
+ manual).
+ }
+ }
+ li {
+ p {-
+ If you try to rename the file @em{-foo} to @em{-bar} but @em{-bar}
+ already exists, you end up deleting a file you may not have wanted
+ to.
+ }
+ }
+ li {
+ p {-
+ In a similar vein to the previous point, you need to be very careful
+ about schemes like renaming the file @em{-a} to @em{-b} and @em{-b}
+ to @em{-c}. You run the risk of turning @em{-a} into @em{-c} and
+ losing the file @em{-b} entirely.
+ }
+ }
+ li {
+ p {-
+ Moving symbolic links is its own whole can of worms. If a symlink
+ points to a relative location then you need to make sure you keep
+ pointing to the right place. If the symlink is absolute however
+ then you can leave it untouched. But what if the symlink points to
+ a file that you’re moving as part of your batch move operation? Now
+ you need to handle that too.
+ }
+ }
+ }
+
+ h2 #mapping {-Name Mapping with @code{-mmv}}
+
+ p {-
+ What is @code{-mmv}? It’s the solution to all your problems, that’s
+ what it is! @code{-mmv} takes as its argument(s) a utility and that
+ utilities arguments and uses that to create a mapping between old and
+ new filenames — similar to the @code{-map()} function found in many
+ programming languages. I think to best convey how the tool functions, I
+ should provide an example. Let’s try to do the same thing we did
+ previously where we tried to turn camelCase files to snake_case, but
+ using @code{-mmv}:
+ }
+
+ figure {
+ pre { m4_fmt_code(camel-to-snake-smart.sh.gsp) }
+ }
+
+ p {-Let me break down how this works.}
+
+ p {-
+ @code{-mmv} starts by reading a series of filenames separated by
+ newlines from the standard input. Yes, sometimes filenames have
+ newlines in them and yes there is a way to handle them but I shall get
+ to that later. The filenames that @code{-mmv} reads from the standard
+ input will be referred to as the @em{-input files}. Once all the input
+ files have been read, the utility specified by the arguments is spawned;
+ in this case that would be @code{-sed} with the argument
+ @code{-'s/[A-Z]/\\L_&/g'}. The input files are then piped into
+ @code{-sed} the exact same way that they would have been if we ran the
+ above commands without @code{-mmv}, and the output of @code{-sed} then
+ forms what will be referred to as the @em{-output files}. Once a
+ complete list of output files is accumulated, each input file gets
+ renamed to its corresponding output file.
+ }
+
+ p {-
+ Let’s look at a simpler example. Say we want to rename 2 files in the
+ current directory to use lowercase letters, we could use the following
+ command:
+ }
+
+ figure {
+ pre { m4_fmt_code(mmv-tr.sh.gsp) }
+ }
+
+ p {-
+ In the above example @code{-mmv} reads 2 lines from standard input,
+ those being @em{-LICENSE} and @em{-README}. Those are our 2 input files
+ now. The @code{-tr} utility is then spawned and the input files are
+ piped into it. We can simulate this in the shell:
+ }
+
+ figure {
+ pre { m4_fmt_code(tr.sh.gsp) }
+ }
+
+ p {-
+ As you can see above, @code{-tr} has produced 2 lines of output; these
+ are our 2 output files. Since we now have our 2 input files and 2
+ output files, @code{-mmv} can go ahead and rename the files. In this
+ case it will rename @em{-LICENSE} to @em{-license} and @em{-README} to
+ @em{-readme}. For some examples, check the @a href="#examples"
+ {-examples} section of this page down below.
+ }
+
+ h2 #newlines {-Filenames with Embedded Newlines}
+
+ p {-
+ People are retarded, and as a result we have filenames with newlines in
+ them. All it would have taken to solve this issue for everyone was for
+ literally @strong{-anybody} during the early UNIX days to go “@em{-hey,
+ this is a bad idea!}”, but alas, we must deal with this. Newlines are
+ of course not the only special characters filenames can contain, but
+ they are the single most infuriating to deal with; the UNIX utilities
+ all being line-oriented really doesn’t work well with these files.
+ }
+
+ p {-
+ So how does @code{-mmv} deal with special characters, and newlines in
+ particular? Well it does so by providing the user with the @code{--0}
+ and @code{--e} flags:
+ }
+
+ dl {
+ dt { code{--0} }
+ dd {
+ p {-
+ Tell @code{-mmv} to expect its input to not be separated by newlines
+ (‘@code{-\\n}’), but by NUL bytes (‘@code{-\\0}’). NUL bytes are
+ the only characters not allowed in filenames besides forward
+ slashes, so they are an obvious choice for an alternative separator.
+ }
+ }
+ dt { code{--e} }
+ dd {
+ p {-
+ Encode newlines in filenames before passing them to the provided
+ utility. Newline characters are replaced by the literal string
+ ‘@code{-\\n}’ and backslashes by the literal string ‘@code{-\\\\}’.
+ After processing, the resulting output is decoded again.
+ }
+ p {-
+ If combined with the @code{--0} flag, then while input will be read
+ assuming a NUL-byte input-seperator, the encoded input files will be
+ written to the spawned process newline-seperated.
+ }
+ }
+ }
+
+ h3 id="0-flag" {-The Simple Case}
+
+ p {-
+ In order to better understand these flags and how they work let’s go
+ though another example. We have 2 files — one with and one without an
+ embedded newline — and our goal is to simply reverse these filenames.
+ In this example I am going to be displaying newlines in filenames with
+ the “@code{-$'\\n'}” syntax as this is how my shell displays embedded
+ newlines.
+ }
+
+ p {-
+ We can start by just trying to naïvely pass these 2 files to @code{-mmv}
+ and use @code{-rev} to reverse the names, but this doesn’t work:
+ }
+
+ figure {
+ pre { m4_fmt_code(mmv-rev.sh.gsp) }
+ }
+
+ p {-
+ The reason this doesn’t work is because due to the line-oriented nature
+ of @code{-ls} and @code{-rev}, we are actually trying to rename the
+ files @em{-foo}, @em{-bar}, and @em{-baz} to the new filenames
+ @em{-zab}, @em{-rab}, and @em{-oof}. As can be seen in the following
+ diagram, the embedded newline is causing our input to be ambiguous and
+ @code{-mmv} can’t reliably proceed anymore @x-ref{-1}:
+ }
+
+ figure {
+ object data="conflict.svg" type="image/svg+xml" {-}
+ }
+
+ aside {
+ p data-ref="1" {-
+ The reason you get a cryptic “file not found” error message is because
+ @code{-mmv} tries to assert that all the input files actually exist
+ before doing anything. Since “foo” isn’t a real file, we error out.
+ }
+ }
+
+ p {-
+ The first thing we need to do in order to proceed is to pass the
+ @code{--0} flag to @code{-mmv}. This will tell @code{-mmv} that we want
+ to use the NUL-byte as our input separator and not the newline. We also
+ need @code{-ls} to actually provide us with the filenames delimited by
+ NUL-bytes. Luckily m4_abbr(GNU) @code{-ls} gives us the @code{---zero}
+ flag to do just that:
+ }
+
+ figure {
+ pre { m4_fmt_code(mmv-rev-zero.sh.gsp) }
+ }
+
+ p {-
+ So we’re getting places, but we aren’t quite there yet. The issue we’re
+ getting now is that @code{-mmv} recieved 2 input files from the standard
+ input, but @code{-rev} produced 3 output files. Why is that? Well
+ let’s try our hand at a little bit of command-line debugging with
+ @code{-sed}:
+ }
+
+ figure {
+ pre { m4_fmt_code(sed-debugging.sh.gsp) }
+ }
+
+ p {-
+ If you aren’t quite sure what the above is doing, here’s a quick
+ summary:
+ }
+
+ ul {
+ li {-
+ The @code{--U} flag given to @code{-ls} tells it not to sort our
+ output. This is purely just to keep this example clear to the reader.
+ }
+ li {-
+ The @code{--n} flag given to @code{-sed} tells it not to print the
+ input line automatically at the end of the provided script.
+ }
+ li {-
+ The @code{-l} command in @code{-sed} prints the current input in a
+ “visually unambiguous form”.
+ }
+ }
+
+ p {-
+ In the @code{-sed} output, we can see that @samp{-$} represents the end
+ of a line, and @samp{-\\000} represents the NUL-byte. All looks good
+ here, we have two inputs seperated by NUL-bytes. Now let’s try to throw
+ in @code{-rev}:
+ }
+
+ figure {
+ pre { m4_fmt_code(sed-debugging-rev.sh.gsp) }
+ }
+
+ p {-
+ Well wouldn’t you know it? Since @code{-rev} @em{-also} works with
+ newline-seperated input, it reversed out NUL-byte seperators and now
+ gives us 3 outputs. Luckily the folks over at @em{-util-linux} provided
+ us with the @code{--0} flag here too, so that we can properly handle
+ NUL-delimited input. Combining all of this together we get a final
+ working product:
+ }
+
+ figure {
+ pre { m4_fmt_code(reverse-embedded-newline.sh.gsp) }
+ }
+
+ h3 #e-flag {-Encoding Newlines}
+
+ p {-
+ Sometimes we want to rename a bunch of files, but the command we want to
+ use doesn’t support NUL-bytes as nicely as we would like. In these
+ cases, you may want to consider encoding your newline characters into
+ the literal string ‘@code{-\\n}’ and then passing your input
+ newline-seperated to your given command with the @code{--e} flag.
+ }
+
+ p {-
+ For a real-world example, perhaps you want to edit some filenames in
+ vim, or whatever other editor you use. Well we can do this incredibly
+ easily with the @code{-vipe} utility from the
+ @a href="https://joeyh.name/code/moreutils/" {-moreutils}
+ collection. The @code{-vipe} command simply reads input from the
+ standard input, opens it up in your editor, and then prints the
+ resulting output to the standard output; perfect for @code{-mmv}! We do
+ not really want to deal with NUL-bytes in our text-editor though, so
+ let’s just encode our newlines:
+ }
+
+ figure {
+ pre { m4_fmt_code(vipe.sh.gsp) }
+ }
+
+ aside {
+ p {-
+ Notice how you still need to pass the @code{--0} flag to @code{-mmv}
+ know that our inputfiles may have embedded newlines.
+ }
+ }
+
+ p {-
+ When running the above code example, you will see the following in your
+ editor:
+ }
+
+ figure {
+ pre { m4_fmt_code(vim.gsp) }
+ }
+
+ p {-
+ After you exit your editor, @code{-mmv} will decode all occurances of
+ ‘@code{-\\n}’ back into a newline, and all occurances of ‘@code{-\\\\}’
+ back into a backslash:
+ }
+
+ figure {
+ object data="e-flag.svg" type="image/svg+xml" {-}
+ }
+
+ h2 #i-flag {-Individual Execution}
+ p {-
+ The previous examples are great and all, but what do you do if your
+ mapping command doesn’t have the concept of an input seperator at all?
+ This is where the @code{--i} flag comes into play. With the @code{--i}
+ flag we can get @code{-mmv} to execute our mapping command for every
+ input filename. This means that as long as we can work with a complete
+ buffer, we don’t need to worry about seperators.
+ }
+
+ p {-
+ To be honest, I cannot really think of any situation where you might
+ actually need to do this. If you can think of one, please @a
+ href="mailto:mail@thomasvoss.com" {-email me} and I’ll update the
+ example on this page. Regardless, let’s imagine that we wanted to
+ rename some files so that their filenames are replaced with their
+ filename
+ @a
+ href="https://en.wikipedia.org/wiki/SHA-1"
+ target="_blank"
+ {-m4_abbr(SHA)-1 hash}.
+ On Linux we have the @code{-sha1sum} program which reads input from the
+ standard input and outputs the m4_abbr(SHA)-1 hash. This is how we
+ would use it with @code{-mmv}:
+ }
+
+ figure {
+ pre { m4_fmt_code(sha1sum-long-example.sh.gsp) }
+ }
+
+ p {-
+ Another approach is to invoke @code{-mmv} twice:
+ }
+
+ figure {
+ pre { m4_fmt_code(sha1sum-short-example.sh.gsp) }
+ }
+
+ p {-
+ If you are confused about why we need to make a call to @code{-awk},
+ it’s because the @code{-sha1sum} program outputs 2 columns of data. The
+ first column is our hash and the second column is the filename where the
+ to-be-hashed data was read from. We don’t want the second column.
+ }
+
+ p {-
+ Unlike in previous examples where one process was spawned to map all our
+ filenames, with the @code{--i} flag we are spawning a new instance for
+ each filename. If you struggle to visualize this, perhaps the following
+ diagrams help:
+ }
+
+ figure {
+ figcaption {-Invoking @code{-mmv} without @code{--i}}
+ object data="without-i-flag.svg" type="image/svg+xml" {-}
+ }
+
+ figure {
+ figcaption {-Invoking @code{-mmv} with @code{--i}}
+ object data="with-i-flag.svg" type="image/svg+xml" {-}
+ }
+
+ h2 #safety {-Safety}
+ p {-
+ When compared to the standard @code{-for f in *; do mv $f …; done} or
+ @code{-ls | … | xargs -L2 mv} constructs, @code{-mmv} is significantly
+ more safe to use. These are some of the safety features that are built
+ into the tool:
+ }
+
+ ol {
+ li {-
+ If the number of input- and output files differs, execution is aborted
+ before making any changes.
+ }
+ li {-
+ If an input file is renamed to the name of another input file, the
+ second input file is not lost (i.e. you can rename @em{-a} to @em{-b}
+ and @em{-b} to @em{-a} with no problem).
+ }
+ li {-
+ All input files must be unique and all output files must be unique.
+ Otherwise execution is aborted before making any changes.
+ }
+ li {-
+ In the case that something goes wrong during execution (perhaps you
+ tried to move a file to a non-existant directory, or a syscall
+ failed), a backup of your input files is saved automatically by
+ @code{-mmv} for recovery.
+ }
+ }
+
+ p {-
+ Due to the way @code{-mmv} handles #2, when things do go wrong you may
+ find that all of your input files have disappeared. Don’t worry though,
+ @code{-mmv} takes a backup of your code before doing anything. If you
+ run @code{-mmv} with the @code{--v} option for verbose output, you’ll
+ notice it backing up your stuff in the @code{-$XDG_CACHE_DIR} directory:
+ }
+
+ figure {
+ pre { m4_fmt_code(mmv-verbose.sh.gsp) }
+ }
+
+ p {-
+ Upon successful execution the @code{-$XDG_CACHE_DIR/mmv/TIMESTAMP}
+ directory will be automatically removed, but it remains when things go
+ wrong so that you can recover any missing data. The names of the
+ backup-subdirectories in the @code{-$XDG_CACHE_DIR/mmv} directory are
+ timestamps of when the directories were created. This should make it
+ easier for you to figure out which directory you need to recover if you
+ happen to have multiple of these.
+ }
+
+ h2 #examples {-Examples}
+
+ aside {
+ p {-
+ All of these examples are ripped straight from the @code{-mmv(1)}
+ manual page. If you installed @code{-mmv} through a package manager or
+ via @code{-make install} then you should have the manual installed on
+ your system.
+ }
+ }
+
+ p {-Swap the files @em{-foo} and @em{-bar}:}
+ figure {
+ pre { m4_fmt_code(examples/swap.sh.gsp) }
+ }
+
+ p {-
+ Rename all files in the current directory to use hyphens (‘-’) instead
+ of spaces:
+ }
+ figure {
+ pre { m4_fmt_code(examples/hyphens.sh.gsp) }
+ }
+
+ p {-
+ Rename a given list of movies to use lowercase letters and hyphens
+ instead of uppercase letters and spaces, and number them so that they’re
+ properly ordered in globs (e.g. rename @em{-The Return of the King.mp4}
+ to @em{-02-the-return-of-the-king.mp4}):
+ }
+ figure {
+ pre { m4_fmt_code(examples/number.sh.gsp) }
+ }
+
+ p {-
+ Rename files interactively in your editor while encoding newline into
+ the literal string ‘@code{-\\n}’, making use of
+ @code {
+ a
+ href="https://linux.die.net/man/1/vipe"
+ target="_blank"
+ {-vipe(1)}
+ }
+ from @em{-moreutils}:
+ }
+ figure {
+ pre { m4_fmt_code(examples/vipe.sh.gsp) }
+ }
+
+ p {-
+ Rename all C source code- and header files in a git repository
+ to use snake_case instead of camelCase using
+ the m4_abbr(GNU)
+ @code {
+ a
+ href="https://www.man7.org/linux/man-pages/man1/sed.1.html"
+ target="_blank"
+ {-sed(1)}
+ }
+ ‘@code{-\\n}’ extension:
+ }
+ figure {
+ pre { m4_fmt_code(examples/camel-to-snake.sh.gsp) }
+ }
+
+ p {-
+ Lowercase all filenames within a directory hierarchy which may contain
+ newline characters:
+ }
+ figure {
+ pre { m4_fmt_code(examples/lowercase.sh.gsp) }
+ }
+
+ p {-
+ Map filenames which may contain newlines in the current directory with
+ the command ‘@code{-cmd}’, which itself does not support nul-byte
+ separated entries. This only works assuming your mapping doesn’t
+ require any context outside of the given input filename (for example,
+ you would not be able to number your files as this requires knowledge of
+ the input files position in the input list):
+ }
+ figure {
+ pre { m4_fmt_code(examples/i-flag.sh.gsp) }
+ }
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/prj/mmv/index.html b/src/prj/mmv/index.html
deleted file mode 100644
index 09aadb1..0000000
--- a/src/prj/mmv/index.html
+++ /dev/null
@@ -1,667 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
Moving Files the Right Way
- m4_include(nav.html)
-
-
-
-
-
-
-
-
- You can find the mmv git repository over at
- sourcehut
- or GitHub.
-
-
-
-
- NOTE: As of the
- v1.2.0 release
- there is now also the mcp utility. It behaves the same as
- the mmv utility but it copies files instead of moving them.
- It also doesn’t support the ‘-n’ flag as it doesn’t need to
- deal with backups.
-
- File moving and renaming is one of the most common tasks we
- undertake on the command-line. We basically always do this with
- the mv utility, and it gets the job done most of the
- time. Want to rename one file? Use mv! Want to
- move a bunch of files into a directory? Use mv!
- How could mv ever go wrong? Well I’m glad you asked!
-
-
-
Advanced Moving and Pitfalls
-
- Let’s start off nice and simple. You just inherited a C project
- that uses the sacrilegious
- camelCase
- naming convention for its files:
-
-
-
-
-
- This deeply upsets you, as it upsets me. So you decide you want
- to switch all these files to use
- snake_case,
- like a normal person. Well how would you do this? You use
- mv! This is what you might end up doing:
-
-
-
-
-
- Well… it works I guess, but it’s a pretty shitty way of renaming
- these files. Luckily we only had 5, but what if this was a much
- larger project with many more files to rename? Things would get
- tedious. So instead we can use a pipeline for
- this:
-
-
-
-
-
-
-
- That works and it gets the job done, but it’s not really ideal is
- it? There are a couple of issues with this.
-
-
-
-
-
- You’re writing more complicated code. This has the
- obvious drawback of potentially being more error-prone,
- but also risks taking more time to write than you’d like
- as you might have forgotten if xargs
- actually has an ‘-L’ option or not (which
- would require reading the
- xargs(1) manual).
-
-
-
-
- If you try to rename the file foo
- to bar but bar already exists, you end
- up deleting a file you may not have wanted to.
-
-
-
-
- In a similar vein to the previous point, you need to be
- very careful about schemes like renaming the
- file a to b and b
- to c. You run the risk of turning a
- into c and losing the file b entirely.
-
-
-
-
- Moving symbolic links is its own whole can of worms. If
- a symlink points to a relative location then you need to
- make sure you keep pointing to the right place. If the
- symlink is absolute however then you can leave it
- untouched. But what if the symlink points to a file
- that you’re moving as part of your batch move operation?
- Now you need to handle that too.
-
-
-
-
-
Name Mapping with mmv
-
-
- What is mmv? It’s the solution to all your
- problems, that’s what it is! mmv takes as its
- argument(s) a utility and that utilities arguments and uses that
- to create a mapping between old and new filenames — similar to
- the map() function found in many programming
- languages. I think to best convey how the tool functions, I
- should provide an example. Let’s try to do the same thing we did
- previously where we tried to turn camelCase files to snake_case,
- but using mmv:
-
-
-
-
-
Let me break down how this works.
-
-
- mmv starts by reading a series of filenames
- separated by newlines from the standard input. Yes, sometimes
- filenames have newlines in them and yes there is a way to handle
- them but I shall get to that later. The filenames that
- mmv reads from the standard input will be referred
- to as the input files. Once all the input files have
- been read, the utility specified by the arguments is spawned; in
- this case that would be sed with the argument
- 's/[A-Z]/\L_&/g'. The input files are then piped
- into sed the exact same way that they would have
- been if we ran the above commands without mmv, and
- the output of sed then forms what will be referred
- to as the output files. Once a complete list of output
- files is accumulated, each input file gets renamed to its
- corresponding output file.
-
-
-
- Let’s look at a simpler example. Say we want to rename 2 files
- in the current directory to use lowercase letters, we could use
- the following command:
-
-
-
-
-
- In the above example mmv reads 2 lines from
- standard input, those being LICENSE
- and README. Those are our 2 input files now.
- The tr utility is then spawned and the input files
- are piped into it. We can simulate this in the shell:
-
-
-
-
-
- As you can see above, tr has produced 2 lines of
- output; these are our 2 output files. Since we now have our 2
- input files and 2 output files, mmv can go ahead
- and rename the files. In this case it will rename
- LICENSE to license and
- README to readme. For some examples, check
- the examples section of this page down
- below.
-
-
-
Filenames with Embedded Newlines
-
-
- People are retarded, and as a result we have filenames with
- newlines in them. All it would have taken to solve this issue
- for everyone was for literally anybody during
- the early UNIX days to go “hey, this is a bad idea!”,
- but alas, we must deal with this. Newlines are of course not
- the only special characters filenames can contain, but they are
- the single most infuriating to deal with; the UNIX utilities all
- being line-oriented really doesn’t work well with these files.
-
-
-
- So how does mmv deal with special characters, and
- newlines in particular? Well it does so by providing the user
- with the -0 and -e flags:
-
-
-
-
-0
-
-
- Tell mmv to expect its input to not be
- separated by newlines (‘\n’), but by NUL
- bytes (‘\0’). NUL bytes are the only
- characters not allowed in filenames besides forward
- slashes, so they are an obvious choice for an
- alternative separator.
-
-
-
-e
-
-
- Encode newlines in filenames before passing them to the
- provided utility. Newline characters are replaced by the
- literal string ‘\n’ and backslashes by the
- literal string ‘\\’. After processing, the
- resulting output is decoded again.
-
-
- If combined with the -0 flag, then while
- input will be read assuming a NUL-byte input-seperator,
- the encoded input files will be written to the spawned
- process newline-seperated.
-
-
-
-
-
The Simple Case
-
-
- In order to better understand these flags and how they work
- let’s go though another example. We have 2 files — one with and
- one without an embedded newline — and our goal is to simply
- reverse these filenames. In this example I am going to be
- displaying newlines in filenames with the “$'\n'”
- syntax as this is how my shell displays embedded newlines.
-
-
-
- We can start by just trying to naïvely pass these 2 files
- to mmv and use rev to reverse the
- names, but this doesn’t work:
-
-
-
-
-
- The reason this doesn’t work is because due to the line-oriented
- nature of ls and rev, we are actually
- trying to rename the files foo, bar, and
- baz to the new filenames zab,
- rab, and oof. As can be seen in the following
- diagram, the embedded newline is causing our input to be ambiguous
- and mmv can’t reliably proceed
- anymore 1:
-
-
-
-
-
-
-
- The first thing we need to do in order to proceed is to pass
- the -0 flag to mmv. This will
- tell mmv that we want to use the NUL-byte as our
- input separator and not the newline. We also need ls
- to actually provide us with the filenames delimited by NUL-bytes.
- Luckily GNU ls gives us the
- --zero flag to do just that:
-
-
-
-
-
- So we’re getting places, but we aren’t quite there yet. The
- issue we’re getting now is that mmv recieved 2
- input files from the standard input, but rev
- produced 3 output files. Why is that? Well let’s try our hand
- at a little bit of command-line debugging with sed:
-
-
-
-
-
- If you aren’t quite sure what the above is doing, here’s a quick
- summary:
-
-
-
-
- The -U flag given to ls tells it
- not to sort our output. This is purely just to keep this
- example clear to the reader.
-
-
- The -n flag given to sed tells it
- not to print the input line automatically at the end of the
- provided script.
-
-
- The l command in sed prints the
- current input in a “visually unambiguous form”.
-
-
-
-
- In the sed output, we can see that $
- represents the end of a line, and \000 represents
- the NUL-byte. All looks good here, we have two inputs seperated
- by NUL-bytes. Now let’s try to throw in rev:
-
-
-
-
-
- Well wouldn’t you know it? Since revalso
- works with newline-seperated input, it reversed out NUL-byte
- seperators and now gives us 3 outputs. Luckily the folks over
- at util-linux provided us with the -0 flag
- here too, so that we can properly handle NUL-delimited input.
- Combining all of this together we get a final working product:
-
-
-
-
-
Encoding Newlines
-
-
- Sometimes we want to rename a bunch of files, but the command we
- want to use doesn’t support NUL-bytes as nicely as we would
- like. In these cases, you may want to consider encoding your
- newline characters into the literal string ‘\n’ and
- then passing your input newline-seperated to your given command
- with the -e flag.
-
-
-
- For a real-world example, perhaps you want to edit some
- filenames in vim, or whatever other editor you use. Well we can
- do this incredibly easily with the vipe utility
- from
- the moreutils
- collection. The vipe command simply reads input
- from the standard input, opens it up in your editor, and then
- prints the resulting output to the standard output; perfect
- for mmv! We do not really want to deal with
- NUL-bytes in our text-editor though, so let’s just encode our
- newlines:
-
-
-
-
-
-
-
- When running the above code example, you will see the following
- in your editor:
-
-
-
-
-
- After you exit your editor, mmv will decode all
- occurances of ‘\n’ back into a newline, and all
- occurances of ‘\\’ back into a backslash:
-
-
-
-
-
Individual Execution
-
- The previous examples are great and all, but what do you do if
- your mapping command doesn’t have the concept of an input
- seperator at all? This is where the -i flag comes
- into play. With the -i flag we can
- get mmv to execute our mapping command for every
- input filename. This means that as long as we can work with a
- complete buffer, we don’t need to worry about seperators.
-
-
-
- To be honest, I cannot really think of any situation where you
- might actually need to do this. If you can think of one,
- please email me and
- I’ll update the example on this page. Regardless, let’s imagine
- that we wanted to rename some files so that their filenames are
- replaced with their filename
-
- SHA-1 hash.
- On Linux we have the sha1sum program which reads
- input from the standard input and outputs the SHA-1 hash. This
- is how we would use it with mmv:
-
-
-
-
-
- Another approach is to invoke mmv twice:
-
-
-
-
-
- If you are confused about why we need to make a call
- to awk, it’s because the sha1sum
- program outputs 2 columns of data. The first column is our hash
- and the second column is the filename where the to-be-hashed
- data was read from. We don’t want the second column.
-
-
-
- Unlike in previous examples where one process was spawned to map
- all our filenames, with the -i flag we are spawning
- a new instance for each filename. If you struggle to visualize
- this, perhaps the following diagrams help:
-
-
-
-
-
-
-
Safety
-
- When compared to the standard for f in *; do mv $f …;
- done or ls | … | xargs -L2 mv
- constructs, mmv is significantly more safe to use.
- These are some of the safety features that are built into the
- tool:
-
-
-
-
- If the number of input- and output files differs, execution
- is aborted before making any changes.
-
-
- If an input file is renamed to the name of another input
- file, the second input file is not lost (i.e. you can rename
- a to b and b to a with
- no problem).
-
-
- All input files must be unique and all output files must be
- unique. Otherwise execution is aborted before making any
- changes.
-
-
- In the case that something goes wrong during execution
- (perhaps you tried to move a file to a non-existant
- directory, or a syscall failed), a backup of your input
- files is saved automatically by mmv for
- recovery.
-
-
-
-
- Due to the way mmv handles #2, when things do go
- wrong you may find that all of your input files have
- disappeared. Don’t worry though, mmv takes a
- backup of your code before doing anything. If you
- run mmv with the -v option for verbose
- output, you’ll notice it backing up your stuff in
- the $XDG_CACHE_DIR directory:
-
-
-
-
-
- Upon successful execution
- the $XDG_CACHE_DIR/mmv/TIMESTAMP directory will be
- automatically removed, but it remains when things go wrong so
- that you can recover any missing data. The names of the
- backup-subdirectories in the $XDG_CACHE_DIR/mmv
- directory are timestamps of when the directories were created.
- This should make it easier for you to figure out which directory
- you need to recover if you happen to have multiple of these.
-
-
-
Examples
-
-
-
-
Swap the files foo and bar:
-
-
-
- Rename all files in the current directory to use hyphens (‘-’)
- instead of spaces:
-
-
-
-
- Rename a given list of movies to use lowercase letters and
- hyphens instead of uppercase letters and spaces, and number them
- so that they’re properly ordered in globs (e.g. rename The
- Return of the King.mp4 to
- 02-the-return-of-the-king.mp4):
-
-
-
-
- Rename files interactively in your editor while encoding newline
- into the literal string ‘\n’, making use
- of vipe(1) from moreutils:
-
-
-
-
- Rename all C source code- and header files in a git repository
- to use snake_case instead of camelCase using
- the GNU
- sed(1) ‘\n’ extension:
-
-
-
-
- Lowercase all filenames within a directory hierarchy which may
- contain newline characters:
-
-
-
-
- Map filenames which may contain newlines in the current
- directory with the command ‘cmd’, which itself does
- not support nul-byte separated entries. This only works
- assuming your mapping doesn’t require any context outside of the
- given input filename (for example, you would not be able to
- number your files as this requires knowledge of the input files
- position in the input list):
-
-
-
-
-
-
-
-
-
diff --git a/src/prj/mmv/ls-files.sh.gsp b/src/prj/mmv/ls-files.sh.gsp
new file mode 100644
index 0000000..95e6af3
--- /dev/null
+++ b/src/prj/mmv/ls-files.sh.gsp
@@ -0,0 +1,2 @@
+$ @span .sh-fn {-ls}
+bytecodeVm.c fastLexer.c fastLexer.h slowParser.c slowParser.h
diff --git a/src/prj/mmv/ls-files.sh.html b/src/prj/mmv/ls-files.sh.html
deleted file mode 100644
index d24b5af..0000000
--- a/src/prj/mmv/ls-files.sh.html
+++ /dev/null
@@ -1,2 +0,0 @@
-$ ls
-bytecodeVm.c fastLexer.c fastLexer.h slowParser.c slowParser.h
diff --git a/src/prj/mmv/manual-mv.sh.gsp b/src/prj/mmv/manual-mv.sh.gsp
new file mode 100644
index 0000000..0dbc3e5
--- /dev/null
+++ b/src/prj/mmv/manual-mv.sh.gsp
@@ -0,0 +1,5 @@
+$ @span .sh-fn {-mv} bytecodeVm.c bytecode_vm.c
+$ @span .sh-fn {-mv} fastLexer.c fast_lexer.c
+$ @span .sh-fn {-mv} fastLexer.h fast_lexer.h
+$ @span .sh-fn {-mv} slowParser.c slow_parser.c
+$ @span .sh-fn {-mv} slowParser.h slow_parser.h
diff --git a/src/prj/mmv/manual-mv.sh.html b/src/prj/mmv/manual-mv.sh.html
deleted file mode 100644
index 2484d9f..0000000
--- a/src/prj/mmv/manual-mv.sh.html
+++ /dev/null
@@ -1,5 +0,0 @@
-$ mv bytecodeVm.c bytecode_vm.c
-$ mv fastLexer.c fast_lexer.c
-$ mv fastLexer.h fast_lexer.h
-$ mv slowParser.c slow_parser.c
-$ mv slowParser.h slow_parser.h
diff --git a/src/prj/mmv/mmv-rev-zero.sh.gsp b/src/prj/mmv/mmv-rev-zero.sh.gsp
new file mode 100644
index 0000000..ef8d158
--- /dev/null
+++ b/src/prj/mmv/mmv-rev-zero.sh.gsp
@@ -0,0 +1,2 @@
+$ @span .sh-fn {-ls} --zero foo$'\\n'bar baz | @span .sh-fn {-mmv} -0 rev
+mmv: Files have been added or removed during editing
diff --git a/src/prj/mmv/mmv-rev-zero.sh.html b/src/prj/mmv/mmv-rev-zero.sh.html
deleted file mode 100644
index 4be992e..0000000
--- a/src/prj/mmv/mmv-rev-zero.sh.html
+++ /dev/null
@@ -1,2 +0,0 @@
-$ ls --zero foo$'\n'bar baz | mmv -0 rev
-mmv: Files have been added or removed during editing
diff --git a/src/prj/mmv/mmv-rev.sh.gsp b/src/prj/mmv/mmv-rev.sh.gsp
new file mode 100644
index 0000000..e2e6a1c
--- /dev/null
+++ b/src/prj/mmv/mmv-rev.sh.gsp
@@ -0,0 +1,2 @@
+$ @span .sh-fn {-ls} foo$'\\n'bar baz | @span .sh-fn {-mmv} rev
+mmv: No such file or directory (os error 2)
diff --git a/src/prj/mmv/mmv-rev.sh.html b/src/prj/mmv/mmv-rev.sh.html
deleted file mode 100644
index 3d2e683..0000000
--- a/src/prj/mmv/mmv-rev.sh.html
+++ /dev/null
@@ -1,2 +0,0 @@
-$ ls foo$'\n'bar baz | mmv rev
-mmv: No such file or directory (os error 2)
diff --git a/src/prj/mmv/mmv-tr.sh.gsp b/src/prj/mmv/mmv-tr.sh.gsp
new file mode 100644
index 0000000..83a728b
--- /dev/null
+++ b/src/prj/mmv/mmv-tr.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-ls} LICENSE README | @span .sh-fn {-mmv} tr A-Z a-z
diff --git a/src/prj/mmv/mmv-tr.sh.html b/src/prj/mmv/mmv-tr.sh.html
deleted file mode 100644
index 4d8773a..0000000
--- a/src/prj/mmv/mmv-tr.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ ls LICENSE README | mmv tr A-Z a-z
diff --git a/src/prj/mmv/mmv-verbose.sh.gsp b/src/prj/mmv/mmv-verbose.sh.gsp
new file mode 100644
index 0000000..dc4dfa0
--- /dev/null
+++ b/src/prj/mmv/mmv-verbose.sh.gsp
@@ -0,0 +1,8 @@
+$ @span .sh-fn {-ls} foo bar | @span .sh-fn {-mmv} -v awk @span .sh-str {-'{ printf "%d-%s\\n", NR, $0 \}'}
+…
+created directory ‘/home/thomas/.cache/mmv/1692102229’
+created directory ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv’
+copied ‘/home/thomas/code/repo/Mango0x45/mmv/bar’ -> ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv/bar’
+created directory ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv’
+copied ‘/home/thomas/code/repo/Mango0x45/mmv/foo’ -> ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv/foo’
+…
diff --git a/src/prj/mmv/mmv-verbose.sh.html b/src/prj/mmv/mmv-verbose.sh.html
deleted file mode 100644
index 3767416..0000000
--- a/src/prj/mmv/mmv-verbose.sh.html
+++ /dev/null
@@ -1,8 +0,0 @@
-$ ls foo bar | mmv -v awk '{ printf "%d-%s\n", NR, $0 }'
-…
-created directory ‘/home/thomas/.cache/mmv/1692102229’
-created directory ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv’
-copied ‘/home/thomas/code/repo/Mango0x45/mmv/bar’ -> ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv/bar’
-created directory ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv’
-copied ‘/home/thomas/code/repo/Mango0x45/mmv/foo’ -> ‘/home/thomas/.cache/mmv/1692102229/home/thomas/code/repo/Mango0x45/mmv/foo’
-…
diff --git a/src/prj/mmv/reverse-embedded-newline.sh.gsp b/src/prj/mmv/reverse-embedded-newline.sh.gsp
new file mode 100644
index 0000000..3a91d08
--- /dev/null
+++ b/src/prj/mmv/reverse-embedded-newline.sh.gsp
@@ -0,0 +1,3 @@
+$ @span .sh-fn {-ls} --zero foo$'\\n'bar baz | @span .sh-fn {-mmv} -0 rev -0
+$ @span .sh-fn {-ls}
+'rab'$'\\n''oof' zab
diff --git a/src/prj/mmv/reverse-embedded-newline.sh.html b/src/prj/mmv/reverse-embedded-newline.sh.html
deleted file mode 100644
index ff84d5c..0000000
--- a/src/prj/mmv/reverse-embedded-newline.sh.html
+++ /dev/null
@@ -1,3 +0,0 @@
-$ ls --zero foo$'\n'bar baz | mmv -0 rev -0
-$ ls
-'rab'$'\n''oof' zab
diff --git a/src/prj/mmv/sed-debugging-rev.sh.gsp b/src/prj/mmv/sed-debugging-rev.sh.gsp
new file mode 100644
index 0000000..39b7c5a
--- /dev/null
+++ b/src/prj/mmv/sed-debugging-rev.sh.gsp
@@ -0,0 +1,3 @@
+$ @span .sh-fn {-ls} -U --zero foo$'\\n'bar baz | @span .sh-fn {-rev} | @span .sh-fn {-sed} -n l
+oof$
+\\000zab\\000rab$
diff --git a/src/prj/mmv/sed-debugging-rev.sh.html b/src/prj/mmv/sed-debugging-rev.sh.html
deleted file mode 100644
index f1fddb1..0000000
--- a/src/prj/mmv/sed-debugging-rev.sh.html
+++ /dev/null
@@ -1,3 +0,0 @@
-$ ls -U --zero foo$'\n'bar baz | rev | sed -n l
-oof$
-\000zab\000rab$
diff --git a/src/prj/mmv/sed-debugging.sh.gsp b/src/prj/mmv/sed-debugging.sh.gsp
new file mode 100644
index 0000000..3db277f
--- /dev/null
+++ b/src/prj/mmv/sed-debugging.sh.gsp
@@ -0,0 +1,3 @@
+$ @span .sh-fn {-ls} -U --zero foo$'\\n'bar baz | @span .sh-fn {-sed} -n l
+foo$
+bar\\000baz\\000$
diff --git a/src/prj/mmv/sed-debugging.sh.html b/src/prj/mmv/sed-debugging.sh.html
deleted file mode 100644
index e61cde4..0000000
--- a/src/prj/mmv/sed-debugging.sh.html
+++ /dev/null
@@ -1,3 +0,0 @@
-$ ls -U --zero foo$'\n'bar baz | sed -n l
-foo$
-bar\000baz\000$
diff --git a/src/prj/mmv/sha1sum-long-example.sh.gsp b/src/prj/mmv/sha1sum-long-example.sh.gsp
new file mode 100644
index 0000000..f997509
--- /dev/null
+++ b/src/prj/mmv/sha1sum-long-example.sh.gsp
@@ -0,0 +1,11 @@
+$ @span .sh-fn {-touch} foo bar
+$ @span .sh-fn {-cat} <<@span .sh-hd {-EOF} >hash-filename
+@span .sh-hd {-#!/bin/sh}
+
+@span .sh-hd {-sha1sum | awk '{ print \\$1 \}'}
+@span .sh-hd {-EOF}
+$ @span .sh-fn {-chmod} +x hash-filename
+$ @span .sh-fn {-ls} foo bar | @span .sh-fn {-mmv} -i ./hash-filename
+$ @span .sh-fn {-ls}
+e242ed3bffccdf271b7fbaf34ed72d089537b42f hash-filename
+f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
diff --git a/src/prj/mmv/sha1sum-long-example.sh.html b/src/prj/mmv/sha1sum-long-example.sh.html
deleted file mode 100644
index ddbda86..0000000
--- a/src/prj/mmv/sha1sum-long-example.sh.html
+++ /dev/null
@@ -1,11 +0,0 @@
-$ touch foo bar
-$ cat <<EOF >hash-filename
-#!/bin/sh
-
-sha1sum | awk '{ print \$1 }'
-EOF
-$ chmod +x hash-filename
-$ ls foo bar | mmv -i ./hash-filename
-$ ls
-e242ed3bffccdf271b7fbaf34ed72d089537b42f hash-filename
-f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
diff --git a/src/prj/mmv/sha1sum-short-example.sh.gsp b/src/prj/mmv/sha1sum-short-example.sh.gsp
new file mode 100644
index 0000000..0811dde
--- /dev/null
+++ b/src/prj/mmv/sha1sum-short-example.sh.gsp
@@ -0,0 +1,6 @@
+$ @span .sh-fn {-touch} foo bar
+$ @span .sh-fn {-ls} | @span .sh-fn {-mmv} -i sha1sum
+$ @span .sh-fn {-ls} | @span .sh-fn {-mmv} awk @span .sh-str {-'{ print $1 \}'}
+$ @span .sh-fn {-ls}
+e242ed3bffccdf271b7fbaf34ed72d089537b42f
+f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
diff --git a/src/prj/mmv/sha1sum-short-example.sh.html b/src/prj/mmv/sha1sum-short-example.sh.html
deleted file mode 100644
index 99e781c..0000000
--- a/src/prj/mmv/sha1sum-short-example.sh.html
+++ /dev/null
@@ -1,6 +0,0 @@
-$ touch foo bar
-$ ls | mmv -i sha1sum
-$ ls | mmv awk '{ print $1 }'
-$ ls
-e242ed3bffccdf271b7fbaf34ed72d089537b42f
-f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
diff --git a/src/prj/mmv/tr.sh.gsp b/src/prj/mmv/tr.sh.gsp
new file mode 100644
index 0000000..e357de8
--- /dev/null
+++ b/src/prj/mmv/tr.sh.gsp
@@ -0,0 +1,3 @@
+$ @span .sh-fn {-ls} LICENSE README | @span .sh-fn {-tr} A-Z a-z
+license
+readme
diff --git a/src/prj/mmv/tr.sh.html b/src/prj/mmv/tr.sh.html
deleted file mode 100644
index db08c38..0000000
--- a/src/prj/mmv/tr.sh.html
+++ /dev/null
@@ -1,3 +0,0 @@
-$ ls LICENSE README | tr A-Z a-z
-license
-readme
diff --git a/src/prj/mmv/vim.gsp b/src/prj/mmv/vim.gsp
new file mode 100644
index 0000000..1c20dca
--- /dev/null
+++ b/src/prj/mmv/vim.gsp
@@ -0,0 +1,2 @@
+foo\\nbar
+baz
diff --git a/src/prj/mmv/vim.html b/src/prj/mmv/vim.html
deleted file mode 100644
index cd89cd8..0000000
--- a/src/prj/mmv/vim.html
+++ /dev/null
@@ -1,2 +0,0 @@
-foo\nbar
-baz
diff --git a/src/prj/mmv/vipe.sh.gsp b/src/prj/mmv/vipe.sh.gsp
new file mode 100644
index 0000000..690db73
--- /dev/null
+++ b/src/prj/mmv/vipe.sh.gsp
@@ -0,0 +1 @@
+$ @span .sh-fn {-ls} --zero foo$'\\n'bar baz | @span .sh-fn {-mmv} -0e vipe
diff --git a/src/prj/mmv/vipe.sh.html b/src/prj/mmv/vipe.sh.html
deleted file mode 100644
index c56ff08..0000000
--- a/src/prj/mmv/vipe.sh.html
+++ /dev/null
@@ -1 +0,0 @@
-$ ls --zero foo$'\n'bar baz | mmv -0e vipe
diff --git a/src/prj/totp/basic-usage.sh.gsp b/src/prj/totp/basic-usage.sh.gsp
new file mode 100644
index 0000000..f1afa3f
--- /dev/null
+++ b/src/prj/totp/basic-usage.sh.gsp
@@ -0,0 +1,7 @@
+$ @span .sh-var {-code}=@span .sh-ex {-`mkpass A-Z0-7`}
+$ @span .sh-fn {-totp} $@span .sh-var {-code}
+475867
+$ @span .sh-fn {-echo} $@span .sh-var {-code} | @span .sh-fn {-totp}
+475867
+$ @span .sh-fn {-totp} -d 10 $@span .sh-var {-code}
+0718732338
diff --git a/src/prj/totp/basic-usage.sh.html b/src/prj/totp/basic-usage.sh.html
deleted file mode 100644
index 9023218..0000000
--- a/src/prj/totp/basic-usage.sh.html
+++ /dev/null
@@ -1,7 +0,0 @@
-$ code=`mkpass A-Z0-7`
-$ totp $code
-475867
-$ echo $code | totp
-475867
-$ totp -d 10 $code
-0718732338
diff --git a/src/prj/totp/index.gsp b/src/prj/totp/index.gsp
new file mode 100644
index 0000000..ea5637a
--- /dev/null
+++ b/src/prj/totp/index.gsp
@@ -0,0 +1,134 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-Easy Password Generation}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ The C preprocessor is worse than m4, and I would kill myself before I
+ had to use m4.
+ }
+ }
+ figcaption {-Arav K.}
+ }
+ }
+
+ main {
+ p {
+ em {-
+ You can find the @code{-totp} git repository over at
+ @a href="https://git.sr.ht/~mango/totp" target="_blank" {-sourcehut}
+ or
+ @a href="https://github.com/Mango0x45/totp" target="_blank" {-GitHub}.
+ }
+ }
+
+ h2 {-Table of Contents}
+ ul {
+ li {a href="#prologue" {-Prologue}}
+ li {a href="#terms" {-Terminology}}
+ li {a href="#usage" {-Basic Usage}}
+ li {a href="#qr" {-Working with QR Codes}}
+ }
+
+ h2 #prologue {-Prologue}
+ p {-
+ m4_abbr(TOTP) codes are pretty cool, and really easay to do. They’re
+ also the backbone of modern two-factor authentication. With
+ @code{-totp} I hope to handling m4_abbr(TOTP) codes as easy and
+ extensible as possible
+ }
+
+ h2 #terms {-Terminology}
+ p {-
+ There are a few terms that I will be using throughout this post, so it’s
+ good to make sure that we’re all on the same page about what I’m
+ referring to.
+ }
+
+ dl {
+ dt {-Secret}
+ dd {
+ p {-
+ Your @em{-secret} is a
+ @a
+ href="https://en.wikipedia.org/wiki/Base32"
+ target="_blank"
+ {-base32}
+ encoded secret key that you should under no circumstances share with
+ anyone else. It is from this secret key that we can generate valid
+ m4_abbr(TOTP) codes.
+ }
+ }
+
+ dt {-Digits}
+ dd {
+ p {-
+ Your @em {-digits} is the length of the generated m4_abbr(TOTP) in
+ digits. If @em{-digits} is 8, then your generated key could be
+ ‘01234567’. When dealing with m4_abbr(2FA) this is typically 6.
+ }
+ }
+
+ dt {-Period}
+ dd {
+ p {-
+ Your @em{-period} it the duration for which the generated key is
+ valid in seconds. When working with m4_abbr(2FA) this is typically
+ 30.
+ }
+ }
+ }
+
+ h2 #usage {-Basic Usage}
+
+ p {-
+ @code{-totp} takes secret keys as command-line arguments, but also reads
+ them from the standard input if none are provided. It assumes that
+ @em{-digits} is 6 and @em{-period} is 30. These defaults can be changed
+ with the @code{--d} and @code{--p} flags.
+ }
+
+ figure {
+ pre { m4_fmt_code(basic-usage.sh.gsp) }
+ }
+
+ aside {
+ p {-
+ I’m using @code{-mkpass} to generate a random secret. You can
+ see my post about @code{-mkpass} @a href="/prj/mkpass" {-here}.
+ }
+ }
+
+ h2 #qr {-Working with m4_abbr(QR) Codes}
+ p {-
+ Often times when enabling m4_abbr(2FA) on your account on some website
+ or platform, you will be shown a m4_abbr(QR) code you can scan with your
+ m4_abbr(2FA) mobile application. These m4_abbr(QR) codes contain
+ @em{-otpauth} m4_abbr(URI)s. We can extract these from downloaded
+ images using utilities such as @code{-zbarimg} and use them in
+ @code{-totp} using the @code{--u} flag to enable ‘m4_abbr(URI) mode’
+ }
+
+ figure {
+ pre { m4_fmt_code(zbarimg.sh.gsp) }
+ }
+
+ p {-
+ …and that’s all! There’s nothing else you need. You can use secret
+ keys and otpauth m4_abbr(URI)s, and you can configure the @em{-digits}
+ and @em{-period} of the generated codes. You can generate multiple keys
+ at once, and all outputs are printed to the standard output.
+ }
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/prj/totp/index.html b/src/prj/totp/index.html
deleted file mode 100644
index aa040d4..0000000
--- a/src/prj/totp/index.html
+++ /dev/null
@@ -1,140 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
Easy Password Generation
- m4_include(nav.html)
-
-
-
-
-
-
-
-
- You can find the totp git repository over at
- sourcehut
- or GitHub.
-
-
- TOTP codes are pretty cool, and really easy to
- do. They’re also the backbone of modern two-factor authentication.
- With totp I hope to make
- handling TOTP codes as easy and extensible as
- possible.
-
-
-
Terminology
-
- There are a few terms that I will be using throughout this post, so it’s
- good to make sure that we’re all on the same page about what I’m
- referring to.
-
-
-
-
Secret
-
-
- Your secret is
- a base32 encoded secret key that you should under
- no circumstances share with anyone else. It is from this secret key
- that we can generate valid TOTP codes.
-
-
-
-
Digits
-
-
- Your digits is the length of the generated
- TOTP in digits. If digits is 8,
- then your generated key could be ‘01234567’. When dealing
- with 2FA this is typically 6.
-
-
-
-
Period
-
-
- Your period it the duration for which the generated key is
- valid in seconds. When working with 2FA
- this is typically 30.
-
-
-
-
-
Basic Usage
-
- totp takes secret keys as command-line arguments, but also
- reads them from the standard input if none are provided. It assumes
- that digits is 6 and period is 30. These defaults can
- be changed with the -d and -p flags.
-
-
-
-
-
-
-
Working with QR Codes
-
- Often times when enabling 2FA on your account
- on some website or platform, you will be shown
- a QR code you can scan with
- your 2FA mobile application.
- These QR codes
- contain otpauth URIs. We can extract
- these from downloaded images using utilities such
- as zbarimg and use them in totp using
- the -u flag to enable ‘URI mode’
-
-
-
-
- …and that’s all! There’s nothing else you need. You can use secret keys
- and otpauth URIs, and you can configure
- the digits and period of the generated codes. You can
- generate multiple keys at once, and all outputs are printed to the
- standard output.
-
-
-
-
-
-
-
diff --git a/src/prj/totp/zbarimg.sh.gsp b/src/prj/totp/zbarimg.sh.gsp
new file mode 100644
index 0000000..cd0669b
--- /dev/null
+++ b/src/prj/totp/zbarimg.sh.gsp
@@ -0,0 +1,4 @@
+$ @span .sh-fn {-zbarimg} -q my-qr-code.svg @span .sh-cmt {-# Also works with jpg, png, etc.}
+QR-Code:otpauth://totp/GitHub:Mango0x45?secret=O1AIWMONKWVRJY4H&issuer=GitHub
+$ @span .sh-fn {-zbarimg} -q my-qr-code.svg | @span .sh-fn {-sed} s/QR-Code:// | @span .sh-fn {-totp} -u
+554210
diff --git a/src/prj/totp/zbarimg.sh.html b/src/prj/totp/zbarimg.sh.html
deleted file mode 100644
index 862eb78..0000000
--- a/src/prj/totp/zbarimg.sh.html
+++ /dev/null
@@ -1,4 +0,0 @@
-$ zbarimg -q my-qr-code.svg # Also works with jpg, png, etc.
-QR-Code:otpauth://totp/GitHub:Mango0x45?secret=O1AIWMONKWVRJY4H&issuer=GitHub
-$ zbarimg -q my-qr-code.svg | sed s/QR-Code:// | totp -u
-554210
diff --git a/src/srp/fw-ec/fn-lock-1.diff.gsp b/src/srp/fw-ec/fn-lock-1.diff.gsp
new file mode 100644
index 0000000..f847e41
--- /dev/null
+++ b/src/srp/fw-ec/fn-lock-1.diff.gsp
@@ -0,0 +1,12 @@
+@span .diff-meta {-diff --git a/board/hx20/board.h b/board/hx20/board.h}
+@span .diff-meta {-index 7b4ea288a..cfc6a61a2 100644}
+@span .diff-meta {---- a/board/hx20/board.h}
+@span .diff-meta {-+++ b/board/hx20/board.h}
+@span .diff-loc {-\@\@ -218,7 +218,6 \@\@}
+ #define CONFIG_CMD_LEDTEST
+ #define CONFIG_LED_PWM_COUNT 3
+ #define CONFIG_LED_PWM_TASK_DISABLED
+@span .diff-del {--#define CONFIG_CAPSLED_SUPPORT}
+
+ #ifdef CONFIG_ACCEL_KX022
+ #define CONFIG_LID_ANGLE
diff --git a/src/srp/fw-ec/fn-lock-1.diff.html b/src/srp/fw-ec/fn-lock-1.diff.html
deleted file mode 100644
index 9222d4b..0000000
--- a/src/srp/fw-ec/fn-lock-1.diff.html
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/board/hx20/board.h b/board/hx20/board.h
-index 7b4ea288a..cfc6a61a2 100644
---- a/board/hx20/board.h
-+++ b/board/hx20/board.h
-@@ -218,7 +218,6 @@
- #define CONFIG_CMD_LEDTEST
- #define CONFIG_LED_PWM_COUNT 3
- #define CONFIG_LED_PWM_TASK_DISABLED
--#define CONFIG_CAPSLED_SUPPORT
-
- #ifdef CONFIG_ACCEL_KX022
- #define CONFIG_LID_ANGLE
diff --git a/src/srp/fw-ec/fn-lock-2.diff.gsp b/src/srp/fw-ec/fn-lock-2.diff.gsp
new file mode 100644
index 0000000..7f9e702
--- /dev/null
+++ b/src/srp/fw-ec/fn-lock-2.diff.gsp
@@ -0,0 +1,36 @@
+@span .diff-meta {-diff --git a/board/hx20/keyboard_customization.c b/board/hx20/keyboard_customization.c}
+@span .diff-meta {-index 2b91f2e0c..9a5050a0f 100644}
+@span .diff-meta {---- a/board/hx20/keyboard_customization.c}
+@span .diff-meta {-+++ b/board/hx20/keyboard_customization.c}
+@span .diff-loc {-\@\@ -249,6 +249,23 \@\@ int fn_table_set(int8_t pressed, uint32_t fn_bit)}
+ return false;
+ \}
+
+@span .diff-ins {-+static void hx20_update_fnkey_led(void) {}
+@span .diff-ins {-+ /* Turn the capslock light into a fn-lock light */}
+@span .diff-ins {-+ gpio_set_level(GPIO_CAP_LED_L, (Fn_key & FN_LOCKED) ? 1 : 0);}
+@span .diff-ins {-+\}}
+@span .diff-ins {-+}
+@span .diff-ins {-+/* Set the fn-lock light to the correct setting when the system resumes */}
+@span .diff-ins {-+void hx20_fnkey_resume(void) {}
+@span .diff-ins {-+ hx20_update_fnkey_led();}
+@span .diff-ins {-+\}}
+@span .diff-ins {-+DECLARE_HOOK(HOOK_CHIPSET_RESUME, hx20_fnkey_resume, HOOK_PRIO_DEFAULT);}
+@span .diff-ins {-+}
+@span .diff-ins {-+/* Disable the fn-lock light on suspend */}
+@span .diff-ins {-+void hx20_fnkey_suspend(void) {}
+@span .diff-ins {-+ gpio_set_level(GPIO_CAP_LED_L, 0);}
+@span .diff-ins {-+\}}
+@span .diff-ins {-+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, hx20_fnkey_suspend, HOOK_PRIO_DEFAULT);}
+@span .diff-ins {-+}
+ void fnkey_shutdown(void) {
+ uint8_t current_kb = 0;
+
+@span .diff-loc {-\@\@ -420,6 +437,7 \@\@ int functional_hotkey(uint16_t *key_code, int8_t pressed)}
+ Fn_key &= ~FN_LOCKED;
+ else
+ Fn_key |= FN_LOCKED;
+@span .diff-ins {-+ hx20_update_fnkey_led();}
+ \}
+ return EC_ERROR_UNIMPLEMENTED;
+ \}
diff --git a/src/srp/fw-ec/fn-lock-2.diff.html b/src/srp/fw-ec/fn-lock-2.diff.html
deleted file mode 100644
index f1a7a39..0000000
--- a/src/srp/fw-ec/fn-lock-2.diff.html
+++ /dev/null
@@ -1,36 +0,0 @@
-diff --git a/board/hx20/keyboard_customization.c b/board/hx20/keyboard_customization.c
-index 2b91f2e0c..9a5050a0f 100644
---- a/board/hx20/keyboard_customization.c
-+++ b/board/hx20/keyboard_customization.c
-@@ -249,6 +249,23 @@ int fn_table_set(int8_t pressed, uint32_t fn_bit)
- return false;
- }
-
-+static void hx20_update_fnkey_led(void) {
-+ /* Turn the capslock light into a fn-lock light */
-+ gpio_set_level(GPIO_CAP_LED_L, (Fn_key & FN_LOCKED) ? 1 : 0);
-+}
-+
-+/* Set the fn-lock light to the correct setting when the system resumes */
-+void hx20_fnkey_resume(void) {
-+ hx20_update_fnkey_led();
-+}
-+DECLARE_HOOK(HOOK_CHIPSET_RESUME, hx20_fnkey_resume, HOOK_PRIO_DEFAULT);
-+
-+/* Disable the fn-lock light on suspend */
-+void hx20_fnkey_suspend(void) {
-+ gpio_set_level(GPIO_CAP_LED_L, 0);
-+}
-+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, hx20_fnkey_suspend, HOOK_PRIO_DEFAULT);
-+
- void fnkey_shutdown(void) {
- uint8_t current_kb = 0;
-
-@@ -420,6 +437,7 @@ int functional_hotkey(uint16_t *key_code, int8_t pressed)
- Fn_key &= ~FN_LOCKED;
- else
- Fn_key |= FN_LOCKED;
-+ hx20_update_fnkey_led();
- }
- return EC_ERROR_UNIMPLEMENTED;
- }
diff --git a/src/srp/fw-ec/hybrid.diff.gsp b/src/srp/fw-ec/hybrid.diff.gsp
new file mode 100644
index 0000000..dfd4518
--- /dev/null
+++ b/src/srp/fw-ec/hybrid.diff.gsp
@@ -0,0 +1,88 @@
+@span .diff-meta {-diff --git a/board/hx20/keyboard_customization.c b/board/hx20/keyboard_customization.c}
+@span .diff-meta {-index 9a5050a0f..2756f17ce 100644}
+@span .diff-meta {---- a/board/hx20/keyboard_customization.c}
+@span .diff-meta {-+++ b/board/hx20/keyboard_customization.c}
+@span .diff-loc {-\@\@ -22,12 +22,15 \@\@}
+ #define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args)
+ #define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args)
+
+@span .diff-ins {-+/* The scancode for the caps-lock key, which is now a hybrid key */}
+@span .diff-ins {-+#define SCANCODE_CTRL_ESC 0x0101}
+@span .diff-ins {-+}
+ uint16_t scancode_set2[KEYBOARD_COLS_MAX][KEYBOARD_ROWS] = {
+ {0x0021, 0x007B, 0x0079, 0x0072, 0x007A, 0x0071, 0x0069, 0xe04A\},
+ {0xe071, 0xe070, 0x007D, 0xe01f, 0x006c, 0xe06c, 0xe07d, 0x0077\},
+ {0x0015, 0x0070, 0x00ff, 0x000D, 0x000E, 0x0016, 0x0067, 0x001c\},
+ {0xe011, 0x0011, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000\},
+@span .diff-del {-- {0xe05a, 0x0029, 0x0024, 0x000c, 0x0058, 0x0026, 0x0004, 0xe07a\},}
+@span .diff-ins {-+ {0xe05a, 0x0029, 0x0024, 0x000c, 0x0101, 0x0026, 0x0004, 0xe07a\},}
+ {0x0022, 0x001a, 0x0006, 0x0005, 0x001b, 0x001e, 0x001d, 0x0076\},
+ {0x002A, 0x0032, 0x0034, 0x002c, 0x002e, 0x0025, 0x002d, 0x002b\},
+ {0x003a, 0x0031, 0x0033, 0x0035, 0x0036, 0x003d, 0x003c, 0x003b\},
+@span .diff-loc {-\@\@ -497,6 +500,55 \@\@ int functional_hotkey(uint16_t *key_code, int8_t pressed)}
+ return EC_SUCCESS;
+ \}
+
+@span .diff-ins {-+int try_ctrl_esc(uint16_t *key_code, int8_t pressed) {}
+@span .diff-ins {-+ static enum {}
+@span .diff-ins {-+ NONE,}
+@span .diff-ins {-+ HELD,}
+@span .diff-ins {-+ CTRL}
+@span .diff-ins {-+ \} ctrl_esc_state;}
+@span .diff-ins {-+}
+@span .diff-ins {-+ if (*key_code == SCANCODE_CTRL_ESC) {}
+@span .diff-ins {-+ /* If we pressed the caps key, enter the HELD state. Otherwise,}
+@span .diff-ins {-+ * we are either releasing from the HELD state or the CTRL}
+@span .diff-ins {-+ * state. In both cases we should reset the state to NONE, but}
+@span .diff-ins {-+ * when releasing from the HELD state we want to send an ESC and}
+@span .diff-ins {-+ * when releasing from the CTRL state we want to end the CTRL.}
+@span .diff-ins {-+ *}
+@span .diff-ins {-+ * Also important to note is that even before we know if we’re}
+@span .diff-ins {-+ * going to be acting as ESC or CTRL, we need to send a press-}
+@span .diff-ins {-+ * event of the CTRL key because you can chord CTRL with mouse-}
+@span .diff-ins {-+ * clicks too, not just other keys.}
+@span .diff-ins {-+ */}
+@span .diff-ins {-+ if (pressed) {}
+@span .diff-ins {-+ ctrl_esc_state = HELD;}
+@span .diff-ins {-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 1);}
+@span .diff-ins {-+ \} else if (ctrl_esc_state == HELD) {}
+@span .diff-ins {-+ ctrl_esc_state = NONE;}
+@span .diff-ins {-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 0);}
+@span .diff-ins {-+ simulate_keyboard(SCANCODE_ESC, 1);}
+@span .diff-ins {-+ simulate_keyboard(SCANCODE_ESC, 0);}
+@span .diff-ins {-+ \} else if (ctrl_esc_state == CTRL) {}
+@span .diff-ins {-+ ctrl_esc_state = NONE;}
+@span .diff-ins {-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 0);}
+@span .diff-ins {-+ \}}
+@span .diff-ins {-+}
+@span .diff-ins {-+ return EC_ERROR_UNIMPLEMENTED;}
+@span .diff-ins {-+ \}}
+@span .diff-ins {-+}
+@span .diff-ins {-+ /* If we get here then we are dealing with a key that isn’t the caps}
+@span .diff-ins {-+ * key. In that case we need to handle all 3 states. If the state is}
+@span .diff-ins {-+ * NONE then we can just exit from this function. If it’s HELD and we}
+@span .diff-ins {-+ * are pressing a key, then that’s a key-chord and we need to start a}
+@span .diff-ins {-+ * CTRL. Finally, if we are in the CTRL state, there is nothing to do.}
+@span .diff-ins {-+ */}
+@span .diff-ins {-+ if (ctrl_esc_state == HELD && pressed) {}
+@span .diff-ins {-+ ctrl_esc_state = CTRL;}
+@span .diff-ins {-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 1);}
+@span .diff-ins {-+ \}}
+@span .diff-ins {-+}
+@span .diff-ins {-+ return EC_SUCCESS;}
+@span .diff-ins {-+\}}
+@span .diff-ins {-+}
+ enum ec_error_list keyboard_scancode_callback(uint16_t *make_code,
+ int8_t pressed)
+ {
+@span .diff-loc {-\@\@ -521,6 +573,10 \@\@ enum ec_error_list keyboard_scancode_callback(uint16_t *make_code,}
+ if (!pos_get_state())
+ return EC_SUCCESS;
+
+@span .diff-ins {-+ r = try_ctrl_esc(make_code, pressed);}
+@span .diff-ins {-+ if (r != EC_SUCCESS)}
+@span .diff-ins {-+ return r;}
+@span .diff-ins {-+}
+ r = hotkey_F1_F12(make_code, Fn_key, pressed);
+ if (r != EC_SUCCESS)
+ return r;
diff --git a/src/srp/fw-ec/hybrid.diff.html b/src/srp/fw-ec/hybrid.diff.html
deleted file mode 100644
index 0ad9717..0000000
--- a/src/srp/fw-ec/hybrid.diff.html
+++ /dev/null
@@ -1,88 +0,0 @@
-diff --git a/board/hx20/keyboard_customization.c b/board/hx20/keyboard_customization.c
-index 9a5050a0f..2756f17ce 100644
---- a/board/hx20/keyboard_customization.c
-+++ b/board/hx20/keyboard_customization.c
-@@ -22,12 +22,15 @@
- #define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args)
- #define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args)
-
-+/* The scancode for the caps-lock key, which is now a hybrid key */
-+#define SCANCODE_CTRL_ESC 0x0101
-+
- uint16_t scancode_set2[KEYBOARD_COLS_MAX][KEYBOARD_ROWS] = {
- {0x0021, 0x007B, 0x0079, 0x0072, 0x007A, 0x0071, 0x0069, 0xe04A},
- {0xe071, 0xe070, 0x007D, 0xe01f, 0x006c, 0xe06c, 0xe07d, 0x0077},
- {0x0015, 0x0070, 0x00ff, 0x000D, 0x000E, 0x0016, 0x0067, 0x001c},
- {0xe011, 0x0011, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
-- {0xe05a, 0x0029, 0x0024, 0x000c, 0x0058, 0x0026, 0x0004, 0xe07a},
-+ {0xe05a, 0x0029, 0x0024, 0x000c, 0x0101, 0x0026, 0x0004, 0xe07a},
- {0x0022, 0x001a, 0x0006, 0x0005, 0x001b, 0x001e, 0x001d, 0x0076},
- {0x002A, 0x0032, 0x0034, 0x002c, 0x002e, 0x0025, 0x002d, 0x002b},
- {0x003a, 0x0031, 0x0033, 0x0035, 0x0036, 0x003d, 0x003c, 0x003b},
-@@ -497,6 +500,55 @@ int functional_hotkey(uint16_t *key_code, int8_t pressed)
- return EC_SUCCESS;
- }
-
-+int try_ctrl_esc(uint16_t *key_code, int8_t pressed) {
-+ static enum {
-+ NONE,
-+ HELD,
-+ CTRL
-+ } ctrl_esc_state;
-+
-+ if (*key_code == SCANCODE_CTRL_ESC) {
-+ /* If we pressed the caps key, enter the HELD state. Otherwise,
-+ * we are either releasing from the HELD state or the CTRL
-+ * state. In both cases we should reset the state to NONE, but
-+ * when releasing from the HELD state we want to send an ESC and
-+ * when releasing from the CTRL state we want to end the CTRL.
-+ *
-+ * Also important to note is that even before we know if we’re
-+ * going to be acting as ESC or CTRL, we need to send a press-
-+ * event of the CTRL key because you can chord CTRL with mouse-
-+ * clicks too, not just other keys.
-+ */
-+ if (pressed) {
-+ ctrl_esc_state = HELD;
-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 1);
-+ } else if (ctrl_esc_state == HELD) {
-+ ctrl_esc_state = NONE;
-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 0);
-+ simulate_keyboard(SCANCODE_ESC, 1);
-+ simulate_keyboard(SCANCODE_ESC, 0);
-+ } else if (ctrl_esc_state == CTRL) {
-+ ctrl_esc_state = NONE;
-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 0);
-+ }
-+
-+ return EC_ERROR_UNIMPLEMENTED;
-+ }
-+
-+ /* If we get here then we are dealing with a key that isn’t the caps
-+ * key. In that case we need to handle all 3 states. If the state is
-+ * NONE then we can just exit from this function. If it’s HELD and we
-+ * are pressing a key, then that’s a key-chord and we need to start a
-+ * CTRL. Finally, if we are in the CTRL state, there is nothing to do.
-+ */
-+ if (ctrl_esc_state == HELD && pressed) {
-+ ctrl_esc_state = CTRL;
-+ simulate_keyboard(SCANCODE_LEFT_CTRL, 1);
-+ }
-+
-+ return EC_SUCCESS;
-+}
-+
- enum ec_error_list keyboard_scancode_callback(uint16_t *make_code,
- int8_t pressed)
- {
-@@ -521,6 +573,10 @@ enum ec_error_list keyboard_scancode_callback(uint16_t *make_code,
- if (!pos_get_state())
- return EC_SUCCESS;
-
-+ r = try_ctrl_esc(make_code, pressed);
-+ if (r != EC_SUCCESS)
-+ return r;
-+
- r = hotkey_F1_F12(make_code, Fn_key, pressed);
- if (r != EC_SUCCESS)
- return r;
diff --git a/src/srp/fw-ec/index.gsp b/src/srp/fw-ec/index.gsp
new file mode 100644
index 0000000..254becc
--- /dev/null
+++ b/src/srp/fw-ec/index.gsp
@@ -0,0 +1,229 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-Framework is Awesome}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ UNIX was not designed to stop its users from doing stupid things, as
+ that would also stop them from doing clever things.
+ }
+ }
+ figcaption {-Doug Gywn}
+ }
+ }
+
+ main {
+ h2 {-Table of Contents}
+
+ ul {
+ li {a href="#framework" {-Framework and the EC}}
+ li {a href="#led" {-LED Fun!}}
+ li {a href="#more" {-There’s More Than One LED‽}}
+ li {a href="#hybrid" {-The Hybrid Key}}
+ li {a href="#next" {-What’s Next?}}
+ }
+
+ h2 #framework {-Framework and the m4_abbr(EC)}
+ p {-
+ @a href="https://frame.work" target="_blank" {-Framework}
+ — for those unaware — is the coolest laptop manufacturer ever. Their
+ whole @em{-shtick} is producing laptops that give the user the ability
+ to easily and effortlessly disassemble, repair, and modify their
+ hardware. I highly suggest checking them out if you’re interested in
+ computer hardware at all. The laptops even have hotswappable I/O!
+ }
+
+ p {-
+ Anyways getting back on topic, Framework has also been giving power to
+ the user on the software-side of things too! A good while ago they
+ open-sourced the
+ @a
+ href="https://github.com/FrameworkComputer/EmbeddedController"
+ target="_blank"
+ {-code for the embedded controller}
+ of their laptops, which offers all sorts of possibilities for
+ customization of the keyboard, m4_abbr(LED) lights, and more.
+ }
+
+ h2 #led {-m4_abbr(LED) Fun!}
+ p {-
+ This is an area of the m4_abbr(EC) which I have not really looked at or
+ touched much. I do want to play around with this a lot more in the
+ coming future though! So far just for shits-and-giggles, I’ve patched
+ the m4_abbr(EC) to make the power-button m4_abbr(LED) green instead of
+ the normal boring white:
+ }
+
+ aside {
+ p {-
+ Just a tip: if you want to try any of these patches out,
+ simply copy the diffs and paste them
+ into @code {-git apply}.
+ }
+ }
+
+ figure {
+ figcaption {
+ code {-~/board/hx20/led.c}
+ }
+ pre { m4_fmt_code(led.diff.gsp) }
+ }
+
+ p {-
+ As you can see, it’s all fairly simple. I just had to change our
+ @code{-EC_LED_COLOR_WHITE} for @code{-EC_LED_COLOR_GREEN}. The codebase
+ defines a few colors, but they’re defined as m4_abbr(RGB) tuples which
+ is awesome, because it opens the door to custom m4_abbr(RGB) effects in
+ the future!
+ }
+
+ h2 #more {-There’s More Than One m4_abbr(LED)‽}
+ p {-
+ That’s right! The Framework laptop I own (13″; the 16″ releases soon
+ though!) has 3 more m4_abbr(LED) lights. One on the left of the
+ chassis, one on right of the chassis, and one on the capslock key. The
+ capslock m4_abbr(LED) acts as an indicator of whether or not you’ve got
+ capslock enabled. This is useless to me though, because my custom
+ keyboard layout doesn’t even support capslock (see the next section) —
+ so I patched it to be a function-lock indicator instead!
+ }
+
+ p {-
+ Here’s the diff — but do take care if you want to apply similar patches
+ to your laptop! The files I’m editing are under @code{-board/hx20}
+ since I’m on an 11th Gen Intel m4_abbr(CPU). If you have a different
+ m4_abbr(CPU), you will probably need to fuck with different code:
+ }
+
+ figure {
+ figcaption {
+ code {-~/board/hx20/board.h}
+ }
+ pre { m4_fmt_code(fn-lock-1.diff.gsp) }
+ }
+
+ figure {
+ figcaption {
+ code {-~/board/hx20/keyboard-customization.c}
+ }
+ pre { m4_fmt_code(fn-lock-2.diff.gsp) }
+ }
+
+ p {-
+ As you can see, toggling the capslock m4_abbr(LED) is as simple as
+ invoking @code{-gpio_set_level()}. Not only that, but disabling its
+ functionality with the capslock key is as easy as undefining the
+ @code{-CONFIG_CAPSLOCK_SUPPORT} macro. Figuring out if the function key
+ is locked is also really easy. The @code{-Fn_key} global variable is a
+ bit-field containing information pertaining to the function key, and we
+ also conveniently already have the @code{-FN_LOCKED} constant defined
+ that we can bitwise-AND with @code{-Fn_key} to check the locked state!
+ }
+
+ p {-
+ We also setup some hooks with the @code{-DECLARE_HOOK()} macro. These
+ just ensure that we are behaving properly on system resume and -suspend.
+ }
+
+ h2 #hybrid {-The Hybrid Key}
+ p {-
+ Wouldn’t it be cool if a physical key could represent two keys at the
+ same time? I thought so too. Like all Emacs users, I suffer from a
+ distinct lack of easily-accessible modifier keys. I need escape because
+ I use Vim bindings; I need left-control because all the Emacs bindings
+ use it; I need super for my window-managers’ bindings; I need left-alt
+ so I can type characters that don’t come on a standard American keyboard
+ (such as @em{-ß}, @em{-€}, and @em{-é}), and now I have a problem. All
+ my modifiers are taken, but Emacs still needs a meta key to work!
+ }
+
+ figure {
+ figcaption {-
+ @cite{-Workflow} by Randall Munroe
+ }
+ img alt="XKCD Comic 1172" src="1172.png" {}
+ }
+
+ p {-
+ What will I ever do‽ Well thanks to Framework making the m4_abbr(EC)
+ open-source, and conveniently giving me a file called
+ @code{-keyboard_customization.c}, I’m going to take two keys and stick
+ them in one! The basic premise is this: the capslock key is arguably
+ the easiest modifier key to hit, and it’s currently bound to the escape
+ key on my system. This is inefficient though, because nobody makes
+ key-bindings that chord the escape-key with another key;
+ chords @x-ref{-1} are always done with a modifier like control, and
+ Emacs is no different. So my plan was to make it so that the capslock
+ key when used on its own mimics an escape-key, while instead mimicking
+ the left-control-key when used in a chord with another key.
+ }
+
+ p {-
+ It took me a little longer this time to figure out how to implement what
+ I wanted since the code isn’t as clear, but it was still a surprisingly
+ easy feature to patch into the m4_abbr(EC)! I basically just updated
+ the scancode table, swapping out the capslock scancode for my own random
+ one that I called @code{-SCANCODE_CTRL_ESC}. I then created a new
+ function called @code{-try_ctrl_esc()} which is called in the on-keyup
+ and -down callback function. The @code{-try_ctrl_esc()} function
+ handles all of the logic as you can see in the following diff; it’s
+ basically just a state machine:
+ }
+
+ aside {
+ p data-ref="1" {-
+ If you’re confused by what I mean by a “key-chord”, I am simply
+ referring to pressing multiple keys in conjunction, such as when you
+ press “@kbd{-Ctrl + C}” to copy text.
+ }
+ }
+
+ figure {
+ figcaption {
+ code {-~/board/hx20/keyboard_customization.c}
+ }
+ pre { m4_fmt_code(hybrid.diff.gsp) }
+ }
+
+ p {-
+ One thing that’s good to take note of is what I return from
+ @code{-try_ctrl_esc()}. The general pattern for handling a keyup or
+ -down event is to stick the following code into
+ @code{-keyboard_scancode_callback()}:
+ }
+
+ figure {
+ figcaption {-
+ @code{-keyboard_scancode_callback()} in
+ @code{-~/board/hx20/keyboard_customization.c}
+ }
+ pre { m4_fmt_code(kbd-sc-cb.c.gsp) }
+ }
+
+ p {-
+ In @code{-my_handler_function()} (or whatever you decide to name it),
+ you attempt to handle the event. If you don’t want to handle a
+ particular event and instead want to pass it on to the next handler, you
+ need to return @code{-EC_SUCCESS}. If you managed to successfully
+ handle the event though, then you need to return an error such as
+ @code{-EC_ERROR_UNIMPLEMENTED}. It’s pretty stupid and makes very
+ little sense from a naming perspective, but oh well…
+ }
+
+ h2 #next {-What’s Next?}
+ p {-
+ m4_abbr(RGB) m4_abbr(LED)s maybe.
+ }
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/srp/fw-ec/index.html b/src/srp/fw-ec/index.html
deleted file mode 100644
index 1a2d2ec..0000000
--- a/src/srp/fw-ec/index.html
+++ /dev/null
@@ -1,263 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
- Framework
-
- — for those unaware — is the coolest laptop manufacturer ever.
- Their whole shtick is producing laptops that give the
- user the ability to easily and effortlessly disassemble, repair,
- and modify their hardware. I highly suggest checking them out if
- you’re interested in computer hardware at all. The laptops even
- have hotswappable I/O!
-
-
-
- Anyways getting back on topic, Framework has also been giving
- power to the user on the software-side of things too! A good
- while ago they open-sourced the
-
- code for the embedded controller
-
- of their laptops, which offers all sorts of possibilities for
- customization of the keyboard,
- LED
- lights, and more.
-
-
-
LED Fun!
-
-
- This is an area of the
- EC
- which I have not really looked at or touched much. I do want to
- play around with this a lot more in the coming future though! So
- far just for shits-and-giggles, I’ve patched the
- EC
- to make the power-button
- LED
- green instead of the normal boring white:
-
-
-
-
-
-
-
- As you can see, it’s all fairly simple. I just had to change our
- EC_LED_COLOR_WHITE for
- EC_LED_COLOR_GREEN. The codebase defines a few
- colors, but they’re defined as RGB
- tuples which is awesome, because it opens the door to
- custom RGB effects in the future!
-
-
-
There’s More Than One LED‽
-
-
- That’s right! The Framework laptop I own (13″; the 16″ releases
- soon though!) has 3 more LED lights.
- One on the left of the chassis, one on right of the chassis, and
- one on the capslock key. The capslock
- LED acts as an indicator of whether or
- not you’ve got capslock enabled. This is useless to me though,
- because my custom keyboard layout doesn’t even support capslock
- (see the next section) — so I patched it to be a function-lock
- indicator instead!
-
-
-
- Here’s the diff — but do take care if you want to apply similar
- patches to your laptop! The files I’m editing are under
- board/hx20 since I’m on an 11th Gen Intel
- CPU. If you have a different
- CPU, you will probably need to fuck with
- different code:
-
-
-
-
-
-
-
- As you can see, toggling the capslock
- LED is as simple as
- invoking gpio_set_level(). Not only that, but
- disabling its functionality with the capslock key is as easy as
- undefining the CONFIG_CAPSLOCK_SUPPORT macro.
- Figuring out if the function key is locked is also really easy.
- The Fn_key global variable is a bit-field containing
- information pertaining to the function key, and we also
- conveniently already have the FN_LOCKED constant
- defined that we can bitwise-AND with Fn_key to check
- the locked state!
-
-
-
- We also setup some hooks with the DECLARE_HOOK()
- macro. These just ensure that we are behaving properly on system
- resume and -suspend.
-
-
-
The Hybrid Key
-
-
- Wouldn’t it be cool if a physical key could represent two keys at
- the same time? I thought so too. Like all Emacs users, I suffer
- from a distinct lack of easily-accessible modifier keys. I need
- escape because I use Vim bindings; I need left-control because
- all the Emacs bindings use it; I need super for my
- window-managers’ bindings; I need left-alt so I can type
- characters that don’t come on a standard American keyboard (such
- as ß, €, and é), and now I have a
- problem. All my modifiers are taken, but Emacs still needs a
- meta key to work!
-
-
-
-
- What will I ever do‽ Well thanks to Framework making
- the EC open-source, and conveniently
- giving me a file called keyboard_customization.c,
- I’m going to take two keys and stick them in one! The basic
- premise is this: the capslock key is arguably the easiest
- modifier key to hit, and it’s currently bound to the escape key
- on my system. This is inefficient though, because nobody makes
- key-bindings that chord the escape-key with another key;
- chords 1 are always done with a modifier like
- control, and Emacs is no different. So my plan was to make it
- so that the capslock key when used on its own mimics an
- escape-key, while instead mimicking the left-control-key when
- used in a chord with another key.
-
-
-
- It took me a little longer this time to figure out how to
- implement what I wanted since the code isn’t as clear, but it was
- still a surprisingly easy feature to patch into the
- EC! I basically just updated the
- scancode table, swapping out the capslock scancode for my own
- random one that I called SCANCODE_CTRL_ESC. I then
- created a new function called try_ctrl_esc() which
- is called in the on-keyup and -down callback function. The
- try_ctrl_esc() function handles all of the logic as
- you can see in the following diff; it’s basically just a state
- machine:
-
-
-
-
-
-
-
- One thing that’s good to take note of is what I return from
- try_ctrl_esc(). The general pattern for handling a
- keyup or -down event is to stick the following code into
- keyboard_scancode_callback():
-
-
-
-
-
- In my_handler_function() (or whatever you decide to
- name it), you attempt to handle the event. If you don’t want to
- handle a particular event and instead want to pass it on to the
- next handler, you need to return EC_SUCCESS. If you
- managed to successfully handle the event though, then you need to
- return an error such as EC_ERROR_UNIMPLEMENTED.
- It’s pretty stupid and makes very little sense from a naming
- perspective, but oh well…
-
-
-
What’s Next?
-
-
- RGB
- LEDs
- maybe.
-
-
-
-
-
-
-
-
diff --git a/src/srp/fw-ec/kbd-sc-cb.c.gsp b/src/srp/fw-ec/kbd-sc-cb.c.gsp
new file mode 100644
index 0000000..fa03466
--- /dev/null
+++ b/src/srp/fw-ec/kbd-sc-cb.c.gsp
@@ -0,0 +1,6 @@
+@span .c-cmt {-/* “make_code” is the scancode. “pressed” is a boolean that is true if this is a}
+ @span .c-cmt {-keydown event, and false if it’s a keyup. */}
+
+r = @span .c-fn {-my_handler_function}(make_code, pressed);
+@span .c-kw {-if} (r != @span .c-pp {-EC_SUCCESS})
+ @span .c-kw {-return} r;
diff --git a/src/srp/fw-ec/kbd-sc-cb.c.html b/src/srp/fw-ec/kbd-sc-cb.c.html
deleted file mode 100644
index 34b7f8d..0000000
--- a/src/srp/fw-ec/kbd-sc-cb.c.html
+++ /dev/null
@@ -1,6 +0,0 @@
-/* “make_code” is the scancode. “pressed” is a boolean that is true if this is a
- keydown event, and false if it’s a keyup. */
-
-r = my_handler_function(make_code, pressed);
-if (r != EC_SUCCESS)
- return r;
diff --git a/src/srp/fw-ec/led.diff.gsp b/src/srp/fw-ec/led.diff.gsp
new file mode 100644
index 0000000..36e2ab3
--- /dev/null
+++ b/src/srp/fw-ec/led.diff.gsp
@@ -0,0 +1,32 @@
+@span .diff-meta {-diff --git a/board/hx20/led.c b/board/hx20/led.c}
+@span .diff-meta {-index a4dc4564e..dacf73fda 100644}
+@span .diff-meta {---- a/board/hx20/led.c}
+@span .diff-meta {-+++ b/board/hx20/led.c}
+@span .diff-loc {-\@\@ -283,22 +283,22 \@\@ static void led_set_power(void)}
+ /* don't light up when at lid close */
+ if (!lid_is_open()) {
+ set_pwr_led_color(PWM_LED2, -1);
+@span .diff-del {-- enable_pwr_breath(PWM_LED2, EC_LED_COLOR_WHITE, breath_led_length, 0);}
+@span .diff-ins {-+ enable_pwr_breath(PWM_LED2, EC_LED_COLOR_GREEN, breath_led_length, 0);}
+ return;
+ \}
+
+ if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND))
+@span .diff-del {-- enable_pwr_breath(PWM_LED2, EC_LED_COLOR_WHITE, breath_led_length, 1);}
+@span .diff-ins {-+ enable_pwr_breath(PWM_LED2, EC_LED_COLOR_GREEN, breath_led_length, 1);}
+ else
+@span .diff-del {-- enable_pwr_breath(PWM_LED2, EC_LED_COLOR_WHITE, breath_led_length, 0);}
+@span .diff-ins {-+ enable_pwr_breath(PWM_LED2, EC_LED_COLOR_GREEN, breath_led_length, 0);}
+
+ if (chipset_in_state(CHIPSET_STATE_ON) | power_button_enable) {
+ if (charge_prevent_power_on(0))
+ set_pwr_led_color(PWM_LED2, (power_tick %
+ LED_TICKS_PER_CYCLE < LED_ON_TICKS) ?
+@span .diff-del {-- EC_LED_COLOR_WHITE : -1);}
+@span .diff-ins {-+ EC_LED_COLOR_GREEN : -1);}
+ else
+@span .diff-del {-- set_pwr_led_color(PWM_LED2, EC_LED_COLOR_WHITE);}
+@span .diff-ins {-+ set_pwr_led_color(PWM_LED2, EC_LED_COLOR_GREEN);}
+ \} else
+ set_pwr_led_color(PWM_LED2, -1);
+ \}
diff --git a/src/srp/fw-ec/led.diff.html b/src/srp/fw-ec/led.diff.html
deleted file mode 100644
index 9959b0d..0000000
--- a/src/srp/fw-ec/led.diff.html
+++ /dev/null
@@ -1,32 +0,0 @@
-diff --git a/board/hx20/led.c b/board/hx20/led.c
-index a4dc4564e..dacf73fda 100644
---- a/board/hx20/led.c
-+++ b/board/hx20/led.c
-@@ -283,22 +283,22 @@ static void led_set_power(void)
- /* don't light up when at lid close */
- if (!lid_is_open()) {
- set_pwr_led_color(PWM_LED2, -1);
-- enable_pwr_breath(PWM_LED2, EC_LED_COLOR_WHITE, breath_led_length, 0);
-+ enable_pwr_breath(PWM_LED2, EC_LED_COLOR_GREEN, breath_led_length, 0);
- return;
- }
-
- if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND))
-- enable_pwr_breath(PWM_LED2, EC_LED_COLOR_WHITE, breath_led_length, 1);
-+ enable_pwr_breath(PWM_LED2, EC_LED_COLOR_GREEN, breath_led_length, 1);
- else
-- enable_pwr_breath(PWM_LED2, EC_LED_COLOR_WHITE, breath_led_length, 0);
-+ enable_pwr_breath(PWM_LED2, EC_LED_COLOR_GREEN, breath_led_length, 0);
-
- if (chipset_in_state(CHIPSET_STATE_ON) | power_button_enable) {
- if (charge_prevent_power_on(0))
- set_pwr_led_color(PWM_LED2, (power_tick %
- LED_TICKS_PER_CYCLE < LED_ON_TICKS) ?
-- EC_LED_COLOR_WHITE : -1);
-+ EC_LED_COLOR_GREEN : -1);
- else
-- set_pwr_led_color(PWM_LED2, EC_LED_COLOR_WHITE);
-+ set_pwr_led_color(PWM_LED2, EC_LED_COLOR_GREEN);
- } else
- set_pwr_led_color(PWM_LED2, -1);
- }
diff --git a/src/srp/index.gsp b/src/srp/index.gsp
new file mode 100644
index 0000000..8b2ddad
--- /dev/null
+++ b/src/srp/index.gsp
@@ -0,0 +1,40 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-Sortware-Related Posts}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ Object-oriented programming is an exceptionally bad idea which could
+ only have originated in California.
+ }
+ }
+ figcaption {-Edsgar W. Dijkstra}
+ }
+ }
+
+ main {
+ p {-
+ In this section of the website I cover random software-related stuff
+ that isn’t related to actual projects of mine. Posts here could be
+ about anything from a cool program I found, to a patch I wrote, to me
+ complaining about bad software design.
+ }
+
+ p {-Posts:}
+
+ ul {
+ li {a href="fw-ec" {-Patching My Laptop’s Embedded Controller}}
+ }
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/srp/index.html b/src/srp/index.html
deleted file mode 100644
index f66d4ae..0000000
--- a/src/srp/index.html
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
Software-Related Posts
- m4_include(nav.html)
-
-
-
-
-
-
-
- In this section of the website I cover random software-related
- stuff that isn’t related to actual projects of mine. Posts here
- could be about anything from a cool program I found, to a patch I
- wrote, to me complaining about bad software design.
-
-
-
-
-
-
-
-
diff --git a/src/style.css b/src/style.css
index 78e8ef2..713e4bb 100644
--- a/src/style.css
+++ b/src/style.css
@@ -228,12 +228,14 @@ dl {
abbr.cv::before { content: 'Curriculum Vitæ'; }
abbr.ec::before { content: 'Embedded Controller'; }
abbr.gnu::before { content: 'GNU’s Not UNIX'; }
+ abbr.gsp::before { content: 'German Shorthaired Pointer'; }
abbr.html::before { content: 'Hypertext Markup Language'; }
abbr.it::before { content: 'Information Technology'; }
abbr.led::before { content: 'Light-Emitting Diode'; }
abbr.nas::before { content: 'Network Attached Storage'; }
abbr.qr::before { content: 'Quick Response'; }
abbr.rgb::before { content: 'Red Green Blue'; }
+ abbr.sha::before { content: 'Secure Hash Algorithm'; }
abbr.tfa::before { content: 'Two-Factor Authentication'; }
abbr.totp::before { content: 'Time-Based One-Time Password'; }
abbr.uri::before { content: 'Uniform Resource Identifier'; }
diff --git a/src/www/index.gsp b/src/www/index.gsp
new file mode 100644
index 0000000..c46c5bc
--- /dev/null
+++ b/src/www/index.gsp
@@ -0,0 +1,118 @@
+html lang="en" {
+ head { m4_include(head.gsp) }
+ body {
+ header {
+ div {
+ h1 {-Other Websites}
+ m4_include(nav.gsp)
+ }
+
+ figure .quote {
+ blockquote {
+ p {=
+ The essence of m4_abbr(XML) is this: the problem it solves it not
+ hard, and it does not solve the problem well.
+ }
+ }
+ figcaption {-Phil Wadler}
+ }
+ }
+
+ main {
+ p {-
+ On this section of the website you can find links to all sorts of other
+ websites I think are somewhat interesting. Some of these are my own
+ sites, and some of them aren’t. Feel free to explore.
+ }
+
+ h3 {-My Websites}
+ ul {
+ li {
+ a target="_blank" href="https://archive.thomasvoss.com" {-
+ @cite{-archive} — Mario Kart Wii time trials archive
+ }
+ }
+ li {
+ a target="_blank" href="https://cv.thomasvoss.com" {-
+ @cite{-cv} — my résumé/curriculum vitæ
+ }
+ }
+ li {
+ a target="_blank" href="https://euro.thomasvoss.com" {-
+ @cite{-euro} — my euro coin and -banknote collection
+ }
+ }
+ li {
+ a target="_blank" href="https://git.thomasvoss.com" {-
+ @cite{-git} — my git server
+ }
+ }
+ li {
+ a target="_blank" href="https://paste.thomasvoss.com" {-
+ @cite{-paste} — my paste server
+ }
+ }
+ li {
+ a target="_blank" href="https://retime.mcbe.wtf" {-
+ @cite{-retime} — a webtool for retiming speedruns
+ }
+ }
+ }
+
+ h3 {-Not My Websites}
+ ul {
+ li {
+ a target="_blank" href="https://classicshorts.com" {-
+ @cite{-classicshorts} — a collection of short stories
+ }
+ }
+ li {
+ a target="_blank" href="https://iannis.io" {-
+ @cite{-iannis.io} — blog posts on programming topics
+ }
+ }
+ li {
+ a target="_blank" href="https://redblobgames.com" {-
+ @cite{-redblobgames} — algorithms in the context of video games
+ }
+ }
+ li {
+ a target="_blank" href="https://tdmm.eu" {-
+ @cite{-tdmm} — blog posts on low-level development
+ }
+ }
+ li {
+ a target="_blank" href="https://blog.bal-e.org/" {-
+ @cite{-bal-e} — it’s like tdmm.eu I guess
+ }
+ }
+ li {
+ a target="_blank" href="http://textfiles.com" {-
+ @cite{-textfiles} — a collection of thousands of plain-text files
+ }
+ }
+ li {
+ a target="_blank" href="https://cat-v.org" {-
+ @cite{-cat-v} — @q{-The Internet is not for sissies.}
+ }
+ }
+ }
+
+ h3 {-Assorted Links}
+ ul {
+ li {
+ a
+ target="_blank"
+ href="https://web.archive.org/web/20230619115633/http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html"
+ {
+ cite {-Why Lisp Macros are Cool}
+ }
+ }
+ }
+ }
+
+ hr{}
+
+ footer { m4_footer }
+ }
+}
diff --git a/src/www/index.html b/src/www/index.html
deleted file mode 100644
index 82ac06c..0000000
--- a/src/www/index.html
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
- m4_include(head.html)
-
-
-
-
-
Other Websites
- m4_include(nav.html)
-
-
-
-
-
-
-
- On this section of the website you can find links to all sorts of
- other websites I think are somewhat interesting. Some of these
- are my own sites, and some of them aren’t. Feel free to explore.
-