blog · git · desktop · images · contact
2011-10-06
Auf so ziemlich jedem GNU/Linux-System ist heute GNU troff installiert, ein „einfaches“ – oder besser: altes – Textsatzsystem. Bei der Leistung heutiger Rechner ist es so leichtgewichtig geworden, dass man seine Existenz kaum mehr bemerkt. Trotzdem wird es bei jedem Aufruf einer Manpage verwendet, denn diese werden allesamt mit troff gesetzt (weswegen es auch ein Leichtes ist, eine Manpage als HTML oder PDF zu rendern). Früher wurden damit auch ganze Büche gesetzt. Das ein oder andere Buch dieser Liste ist ganz sicher bekannt.
Die Ergebnisse von groff finde ich durchaus ansprechend, insbesondere die Plaintext-Ausgabe. Das richtige Makro-Paket vorausgesetzt ist es auch nicht allzu schwer zu benutzen. Und es ist eben eine sehr schnelle und schlanke Angelegenheit – im Gegensatz zu zum Beispiel LaTeX. Installiert ist es ohnehin überall. Ich habe daher mal das Experiment gewagt, groff derart in Vim einzubinden, dass ich damit komfortabel E-Mails schreiben kann.
„plain groff“ ist nicht sonderlich komfortabel, vermutlich ähnlich wie
„plain TeX“ (damit habe ich leider noch keine Erfahrung). Man kommt also
nicht um ein Makro-Paket herum. Das mm-Paket (siehe groff_mm(7)
)
erschien mir auf den ersten Blick ganz sinnvoll, insbesondere weil man
damit sehr leicht sowohl „bulleted lists“ als auch automatisch
nummerierte Listen oder Fußnoten setzen kann.
Das erste Problem stellt sich dadurch, dass ich in Mails natürlich keine Seitenaufteilung will, sondern fortlaufenden Text wie in Manpages. Beim mm-Paket ist das aber der Fall, weil es eben für „richtige“ Dokumente gedacht ist. Sprich, die Ausgabe erfolgt zwar als Plaintext, aber unterbrochen durch einige Leerzeilen und Seitennummern. Den unmittelbaren Gedanken, doch einfach das mandoc-Paket zu verwenden, habe ich dann verworfen, weil damit keine Fußnoten unterstützt werden – und zum Beispiel URLs würde ich schon gerne per Fußnote in der Mail erwähnen. Also bleibt’s vorerst beim mm-Paket.
Eine eher wackelige Lösung ist, die Größe der Seite auf „sehr groß“ zu setzen. Das hat natürlich den Seiteneffekt, dass zwischen Text und Fußnoten furchtbar viel Platz ist, den man hinterher wieder entfernen muss. Trotzdem werde ich erstmal diesen Workaround verwenden.
Eine ganz einfache Mail könnte also so aussehen:
.PGNH
.PGFORM 72 10000 0
Hallo,
.P
das hier ist der Text. Das .P eben hat einen neuen Absatz eingeleitet.
Und so geht das dann weiter. Eine ganze Weile lang. Ein bisschen Text
brauche ich jetzt noch, damit man von der Formatierung auch etwas
erkennen kann. So, das sollte genügen. Kommen wir zu einer Liste:
.AL
.LI
Hallo, Welt.
.LI
Auch hier wird alles schön sauber eingerückt, was man an diesem langen
Element erkennen kann. Zumindest, wenn ich noch ein bisschen Blindtext
dazuschreibe.
.LE
.P
Das war's. Tschüss!
Und übersetzen kann man es so:
groff -m mm -Kutf8 -Tutf8 foo | cat -s
„.PGNH
“ gibt an, dass auf der ersten Seite kein Seitenkopf erscheinen
soll, und „.PGFORM
“ bestimmt dann die Größe der Seite. Die anderen
Makros kann man, wie bereits angedeutet, der Manpage „man groff_mm
“
entnehmen. Das Ergebnis:
Hallo,
das hier ist der Text. Das .P eben hat einen neuen Absatz eingeleitet.
Und so geht das dann weiter. Eine ganze Weile lang. Ein bisschen Text
brauche ich jetzt noch, damit man von der Formatierung auch etwas
erkennen kann. So, das sollte genügen. Kommen wir zu einer Liste:
1. Hallo, Welt.
2. Auch hier wird alles schön sauber eingerückt, was man an diesem
langen Element erkennen kann. Zumindest, wenn ich noch ein
bisschen Blindtext dazuschreibe.
Das war’s. Tschüss!
Damit habe ich jetzt gegen zwei „Regeln“ verstoßen:
cat -s
“ widerspricht ähnlich wie „cat -n
“ der Unix-Philosophie.
Es ist aber die kürzeste Möglichkeit, um aufeinanderfolgende
Leerzeilen (und davon habe ich durch den Seitengrößen-Workaround
viele) zu entfernen.So weit, so gut. Ich will natürlich weder die zwei Header-Zeilen immer manuell dort reinschreiben, noch kann ich die gesamte Mail mit groff setzen, denn es gibt ja auch Zitate und Mail-Header. Jetzt kommt also Vim ins Spiel. Los geht’s mit folgendem Mapping:
nmap <Leader>gh :silent a<CR>
\ROFFSTART<CR>
\.mso vain-plain.tmac<CR>
\ROFFEND<CR>
\.<CR>
\:silent normal k<CR>
Mein Leader ist das Komma, also kann ich über „,gh
“ zwei Marker
einfügen und einen Verweis auf ein eigenes Makro-Paket. In diesem kann
ich neben dem notwendigen Header von oben auch noch andere hilfreiche
Dinge verstauen. Der Pfad, in dem diese Datei gesucht wird, kann
übrigens über die Umgebungsvariable „$GROFF_TMAC_PATH
“ beeinflusst
werden. Der Cursor wird dann auf der Zeile mit „.mso ...
“ platziert,
sodass ich „o
“ drücken kann, um mit dem Schreiben zu beginnen. Durch
die Marker kann es mehrere getrennte Abschnitte geben, die später
einzeln durch groff geschickt werden. Und das passiert mit folgender
Funktion:
fun! DoGroff(outenc, ...) range
" mm als Default-Paket.
let l:package = a:0 >= 1 ? a:1 : "mm"
" Nimmt an, dass ROFFSTART und ROFFEND schön ordentlich in Paaren
" existieren -- sofern sie überhaupt an Start und Ende des Bereichs
" auftauchen. Dann werden sie auch gelöscht. Bereich für groff über
" l:rto entsprechend verringern.
let l:rto = a:lastline
if getline(a:firstline) == "ROFFSTART"
exe ":" . a:lastline . "d"
exe ":" . a:firstline . "d"
let @/ = ""
let l:rto = l:rto - 2
endif
" Ab nach groff. Input-Encoding aus Vim-Einstellung ablesen,
" Output-Encoding gemäß Parameter. Grotty-Farben deaktivieren.
" Leerzeilen am Ende (entstehen durch die lange Seite) löschen.
exe ":" . a:firstline . "," . l:rto
\ . "!groff -pet -m " . l:package
\ . " -K" . &fenc . " -T" . a:outenc
\ . " -P-c -P-u -P-o -P-b | cat -s"
endfun
Diese Funktion verwendet also als Eingabe-Encoding immer das aktuelle
„fenc
“ von Vim, das Ausgabe-Encoding wird als Parameter angegeben.
Außerdem akzeptiert die Funktion eine Bereichsangabe, was gleich noch
eine Rolle spielen wird. Die Marker entfernt sie automatisch, sofern es
sie überhaupt findet. Zuguterletzt wird der Text im angegebenen Bereich
durch groff als Filter geschickt. Die vielen Parameter „-P
“
deaktivieren Formatierungen wie Farben oder Fettdruck und sind
eigentlich nicht wirklich notwendig.
Folgende weitere Mappings:
nmap <Leader>gu :%call DoGroff("utf8")<CR>
nmap <Leader>ga :%call DoGroff("ascii")<CR>
nmap <Leader>gl :%call DoGroff("latin1")<CR>
vmap <Leader>gu :call DoGroff("utf8")<CR>
vmap <Leader>ga :call DoGroff("ascii")<CR>
vmap <Leader>gl :call DoGroff("latin1")<CR>
nmap <Leader>gm :sil %g/^ROFFSTART$/,/^ROFFEND$/ call DoGroff("utf8")<CR>
Ich kann also sehr leicht entweder die gesamte Datei, eine visuelle Markierung oder alle Bereiche zwischen den Markern auf die Funktion werfen und dabei das Ausgabe-Encoding angeben.
Das heißt, ich muss beim Schreiben einer Mail nur zwei Dinge tun:
,gh
“.,gm
“.Das war’s.
Mal sehen, wie dieses Experiment weitergeht. Sollte das hier jemand lesen, der sich besser mit groff auskennt und mir sagen kann, wie/ob ich die Seitenaufteilung auch ohne eine übernatürlich lange Seite loswerden kann, wäre ich natürlich dankbar. Falls ich es noch selbst rausfinde, trage ich’s nach.
PS.: Auf den Screenshots ist „smart quoting“ zu sehen. Das heißt, mein
"Hurz"
wird automatisch durch „Hurz“
ersetzt. Die Ausgabe muss dann
aber UTF-8 sein. Das geht so:
.\" Smart Quoting. Ersetzt "Foo" durch \(BqFoo\(lq.
.\" http://lists.gnu.org/archive/html/groff/2007-08/msg00091.html
.de smartq
.ds dblq0 \(Bq
.ds dblq1 \(lq
.nr dblqn 0
.char " \\\\*[dblq\\\\n[dblqn]]\\R'dblqn (1 - \\\\n[dblqn])'
..
.\" Smart Quoting wieder abschalten.
.de /smartq
.rchar "
..
Mit einem „.smartq
“ wird dieses Verhalten also aktiviert und mit
„./smartq
“ wieder deaktiviert.