summaryrefslogtreecommitdiff
path: root/tex/context
diff options
context:
space:
mode:
authorPhilipp Gesang <phg@phi-gamma.net>2013-04-08 15:21:45 +0200
committerPhilipp Gesang <phg@phi-gamma.net>2013-04-08 15:21:45 +0200
commit551b3c0d76045e7c28b0d484f383ef2ae1b8b09a (patch)
tree173aea68b69a53af9adf745d6b13a92790af461b /tex/context
parent1bbc9b6572e696b356809060f7a03894731940eb (diff)
downloadenigma-551b3c0d76045e7c28b0d484f383ef2ae1b8b09a.tar.gz
move enigma.lua -> tex/generic/enigma/
Diffstat (limited to 'tex/context')
-rw-r--r--tex/context/third/enigma/enigma.lua1662
1 files changed, 0 insertions, 1662 deletions
diff --git a/tex/context/third/enigma/enigma.lua b/tex/context/third/enigma/enigma.lua
deleted file mode 100644
index 9da4288..0000000
--- a/tex/context/third/enigma/enigma.lua
+++ /dev/null
@@ -1,1662 +0,0 @@
-#!/usr/bin/env texlua
------------------------------------------------------------------------
--- FILE: enigma.lua
--- USAGE: Call via interface from within a TeX session.
--- DESCRIPTION: Enigma logic.
--- REQUIREMENTS: LuaTeX capable format (Luaplain, ConTeXt).
--- AUTHOR: Philipp Gesang (Phg), <phg42 dot 2a at gmail dot com>
--- VERSION: release
--- CREATED: 2013-03-28 02:12:03+0100
------------------------------------------------------------------------
---
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Format Dependent Code]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\startparagraph
-Exported functionality will be collected in the table
-\identifier{enigma}.
-\stopparagraph
---ichd]]--
-
-local enigma = { machines = { }, callbacks = { } }
-local format_is_context = false
-
---[[ichd--
-\startparagraph
-Afaict, \LATEX\ for \LUATEX\ still lacks a globally accepted
-namespacing convention. This is more than bad, but we’ll have to cope
-with that. For this reason we brazenly introduce
-\identifier{packagedata} (in analogy to \CONTEXT’s
-\identifier{thirddata}) table as a package namespace proposal. If this
-module is called from a \LATEX\ or plain session, the table
-\identifier{packagedata} will already have been created so we will
-identify the format according to its presence or absence, respectively.
-\stopparagraph
---ichd]]--
-
-if packagedata then -- latex or plain
- packagedata.enigma = enigma
-elseif thirddata then -- context
- format_is_context = true
- thirddata.enigma = enigma
-else -- external call, mtx-script or whatever
- _ENV.enigma = enigma
-end
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Prerequisites]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startparagraph
-First of all, we generate local copies of all those library functions
-that are expected to be referenced frequently.
-The format-independent stuff comes first; it consists of functions from
-the
-\identifier{io},
-\identifier{lpeg},
-\identifier{math},
-\identifier{string},
-\identifier{table}, and
-\identifier{unicode}
-libraries.
-\stopparagraph
---ichd]]--
-
-local get_debug_info = debug.getinfo
-local ioread = io.read
-local iowrite = io.write
-local mathfloor = math.floor
-local mathrandom = math.random
-local next = next
-local nodecopy = node and node.copy
-local nodeid = node and node.id
-local nodeinsert_before = node and node.insert_before
-local nodeinsert_after = node and node.insert_after
-local nodelength = node and node.length
-local nodenew = node and node.new
-local noderemove = node and node.remove
-local nodeslide = node and node.slide
-local nodetraverse = node and node.traverse
-local nodetraverse_id = node and node.traverse_id
-local nodesinstallattributehandler
-local nodestasksappendaction
-local nodestasksdisableaction
-if format_is_context then
- nodesinstallattributehandler = nodes.installattributehandler
- nodestasksappendaction = nodes.tasks.appendaction
- nodestasksdisableaction = nodes.tasks.disableaction
-end
-local stringfind = string.find
-local stringformat = string.format
-local stringlower = string.lower
-local stringsub = string.sub
-local stringupper = string.upper
-local tableconcat = table.concat
-local tonumber = tonumber
-local type = type
-local utf8byte = unicode.utf8.byte
-local utf8char = unicode.utf8.char
-local utf8len = unicode.utf8.len
-local utf8lower = unicode.utf8.lower
-local utf8sub = unicode.utf8.sub
-local utfcharacters = string.utfcharacters
-
---- debugging tool (careful, this *will* break context!)
---dofile(kpse.find_file("lualibs-table.lua")) -- archaic version :(
---table.print = function (...) print(table.serialize(...)) end
-
-local tablecopy
-if format_is_context then
- tablecopy = table.copy
-else -- could use lualibs instead but not worth the overhead
- tablecopy = function (t) -- ignores tables as keys
- local result = { }
- for k, v in next, t do
- if type(v) == table then
- result[k] = tablecopy(v)
- else
- result[k] = v
- end
- end
- return result
- end
-end
-
-local GLYPH_NODE = node and nodeid"glyph"
-local GLUE_NODE = node and nodeid"glue"
-local GLUE_SPEC_NODE = node and nodeid"glue_spec"
-local KERN_NODE = node and nodeid"kern"
-local DISC_NODE = node and nodeid"disc"
-local HLIST_NODE = node and nodeid"hlist"
-local VLIST_NODE = node and nodeid"vlist"
-
-local IGNORE_NODES = node and {
---[GLUE_NODE] = true,
- [KERN_NODE] = true,
---[DISC_NODE] = true,
-} or { }
-
---[[ichd--
-\startparagraph
-The initialization of the module relies heavily on parsers generated by
-\type{LPEG}.
-\stopparagraph
---ichd]]--
-
-local lpeg = require "lpeg"
-
-local C, Cb, Cc, Cf, Cg,
- Cmt, Cp, Cs, Ct
- = lpeg.C, lpeg.Cb, lpeg.Cc, lpeg.Cf, lpeg.Cg,
- lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
-
-local P, R, S, V, lpegmatch
- = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
-
---local B = lpeg.version() == "0.10" and lpeg.B or nil
-
---[[ichd--
-\startparagraph
-By default the output to \type{stdout} will be zero. The verbosity
-level can be adjusted in order to alleviate debugging.
-\stopparagraph
---ichd]]--
---local verbose_level = 42
-local verbose_level = 0
-
---[[ichd--
-\startparagraph
-Historically, Enigma-encoded messages were restricted to a size of 250
-characters. With sufficient verbosity we will indicate whether this
-limit has been exceeded during the \TEX\ run.
-\stopparagraph
---ichd]]--
-local max_msg_length = 250
---[[ichd--
-\stopdocsection
---ichd]]--
-
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Globals]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startparagraph
-The following mappings are used all over the place as we convert back
-and forth between the characters (unicode) and their numerical
-representation.
-\stopparagraph
---ichd]]--
-
-local value_to_letter -- { [int] -> chr }
-local letter_to_value -- { [chr] -> int }
-local alpha_sorted -- string, length 26
-local raw_rotor_wiring -- { string0, .. string5, }
-local notches -- { [int] -> int } // rotor num -> notch pos
-local reflector_wiring -- { { [int] -> int }, ... } // symmetrical
-do
- value_to_letter = {
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
- "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
- }
-
- letter_to_value = {
- a = 01, b = 02, c = 03, d = 04, e = 05, f = 06, g = 07, h = 08,
- i = 09, j = 10, k = 11, l = 12, m = 13, n = 14, o = 15, p = 16,
- q = 17, r = 18, s = 19, t = 20, u = 21, v = 22, w = 23, x = 24,
- y = 25, z = 26,
- }
---[[ichd--
-\startparagraph
-The five rotors to simulate.\reference[listing:rotor_wiring]{}
-Their wirings are created from strings at runtime, see below the
-function \luafunction{get_rotors}.
-\stopparagraph
---ichd]]--
-
- --[[
- Nice: http://www.ellsbury.com/ultraenigmawirings.htm
- ]]--
- alpha_sorted = "abcdefghijklmnopqrstuvwxyz"
- raw_rotor_wiring = {
- [0] = alpha_sorted,
- "ekmflgdqvzntowyhxuspaibrcj",
- "ajdksiruxblhwtmcqgznpyfvoe",
- "bdfhjlcprtxvznyeiwgakmusqo",
- "esovpzjayquirhxlnftgkdcmwb",
- "vzbrgityupsdnhlxawmjqofeck",
- }
-
---[[ichd--
-\startparagraph
-Notches are assigned to rotors according to the Royal Army
-mnemonic.
-\stopparagraph
---ichd]]--
- notches = { }
- do
- local raw_notches = "rfwkannnn"
- --local raw_notches = "qevjz"
- local n = 1
- for chr in utfcharacters(raw_notches) do
- local pos = stringfind(alpha_sorted, chr)
- notches[n] = pos - 1
- n = n + 1
- end
- end
-
---[[ichd--
-\placetable[here][listing:reflector]%
- {The three reflectors and their substitution rules.}{%
- \starttabulate[|r|l|]
- \NC UKW a \NC AE BJ CM DZ FL GY HX IV KW NR OQ PU ST \NC \NR
- \NC UKW b \NC AY BR CU DH EQ FS GL IP JX KN MO TZ VW \NC \NR
- \NC UKW c \NC AF BV CP DJ EI GO HY KR LZ MX NW QT SU \NC \NR
- \stoptabulate
-}
---ichd]]--
-
- reflector_wiring = { }
- local raw_ukw = {
- { a = "e", b = "j", c = "m", d = "z", f = "l", g = "y", h = "x",
- i = "v", k = "w", n = "r", o = "q", p = "u", s = "t", },
- { a = "y", b = "r", c = "u", d = "h", e = "q", f = "s", g = "l",
- i = "p", j = "x", k = "n", m = "o", t = "z", v = "w", },
- { a = "f", b = "v", c = "p", d = "j", e = "i", g = "o", h = "y",
- k = "r", l = "z", m = "x", n = "w", q = "t", s = "u", },
- }
- for i=1, #raw_ukw do
- local new_wiring = { }
- local current_ukw = raw_ukw[i]
- for from, to in next, current_ukw do
- from = letter_to_value[from]
- to = letter_to_value[to]
- new_wiring[from] = to
- new_wiring[to] = from
- end
- reflector_wiring[i] = new_wiring
- end
-end
-
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Pretty printing for debug purposes]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startparagraph
-The functions below allow for formatting of the terminal output; they
-have no effect on the workings of the enigma simulator.
-\stopparagraph
---ichd]]--
-
-local emit
-local pprint_ciphertext
-local pprint_encoding
-local pprint_encoding_scheme
-local pprint_init
-local pprint_machine_step
-local pprint_new_machine
-local pprint_rotor
-local pprint_rotor_scheme
-local pprint_step
-local polite_key_request
-local key_invalid
-do
- local eol = "\n"
-
- local colorstring_template = "\027[%d;1m%s\027[0m"
- local colorize = function (s, color)
- color = color and color < 38 and color > 29 and color or 31
- return stringformat(colorstring_template,
- color,
- s)
- end
-
- local underline = function (s)
- return stringformat("\027[4;37m%s\027[0m", s)
- end
-
- local s_steps = [[Total characters encoded with machine “]]
- local f_warnsteps = [[ (%d over permitted maximum)]]
- pprint_machine_step = function (n, name)
- local sn
- name = colorize(name, 36)
- if n > max_msg_length then
- sn = colorize(n, 31) .. stringformat(f_warnsteps,
- n - max_msg_length)
- else
- sn = colorize(n, 37)
- end
- return s_steps .. name .. "”: " .. sn .. "."
- end
- local rotorstate = "[s \027[1;37m%s\027[0m n\027[1;37m%2d\027[0m]> "
- pprint_rotor = function (rotor)
- local visible = rotor.state % 26 + 1
- local w, n = rotor.wiring, (rotor.notch - visible) % 26 + 1
- local tmp = { }
- for i=1, 26 do
- local which = (i + rotor.state - 1) % 26 + 1
- local chr = value_to_letter[rotor.wiring.from[which]]
- if i == n then -- highlight positions of notches
- tmp[i] = colorize(stringupper(chr), 32)
- --elseif chr == value_to_letter[visible] then
- ---- highlight the character in window
- -- tmp[i] = colorize(chr, 33)
- else
- tmp[i] = chr
- end
- end
- return stringformat(rotorstate,
- stringupper(value_to_letter[visible]),
- n)
- .. tableconcat(tmp)
- end
-
- local rotor_scheme = underline"[rot not]"
- .. " "
- .. underline(alpha_sorted)
- pprint_rotor_scheme = function ()
- return rotor_scheme
- end
-
- local s_encoding_scheme = eol
- .. [[in > 1 => 2 => 3 > UKW > 3 => 2 => 1]]
- pprint_encoding_scheme = function ()
- return underline(s_encoding_scheme)
- end
- local s_step = " => "
- local stepcolor = 36
- local finalcolor = 32
- pprint_encoding = function (steps)
- local nsteps, result = #steps, { }
- for i=0, nsteps-1 do
- result[i+1] = colorize(value_to_letter[steps[i]], stepcolor)
- .. s_step
- end
- result[nsteps+1] = colorize(value_to_letter[steps[nsteps]],
- finalcolor)
- return tableconcat(result)
- end
-
- local init_announcement
- = colorize("\n" .. [[Initial position of rotors: ]],
- 37)
- pprint_init = function (init)
- local result = ""
- result = value_to_letter[init[1]] .. " "
- .. value_to_letter[init[2]] .. " "
- .. value_to_letter[init[3]]
- return init_announcement .. colorize(stringupper(result), 34)
- end
-
- local machine_announcement =
- [[Enigma machine initialized with the following settings.]] .. eol
- local s_ukw = colorize(" Reflector:", 37)
- local s_pb = colorize("Plugboard setting:", 37)
- local s_ring = colorize(" Ring positions:", 37)
- local empty_plugboard = colorize(" ——", 34)
- pprint_new_machine = function (m)
- local result = { "" }
- result[#result+1] = underline(machine_announcement)
- result[#result+1] = s_ukw
- .. " "
- .. colorize(
- stringupper(value_to_letter[m.reflector]),
- 34
- )
- local rings = ""
- for i=1, 3 do
- local this = m.ring[i]
- rings = rings
- .. " "
- .. colorize(stringupper(value_to_letter[this + 1]), 34)
- end
- result[#result+1] = s_ring .. rings
- if m.__raw.plugboard then
- local tpb, pb = m.__raw.plugboard, ""
- for i=1, #tpb do
- pb = pb .. " " .. colorize(tpb[i], 34)
- end
- result[#result+1] = s_pb .. pb
- else
- result[#result+1] = s_pb .. empty_plugboard
- end
- result[#result+1] = ""
- result[#result+1] = pprint_rotor_scheme()
- for i=1, 3 do
- result[#result+1] = pprint_rotor(m.rotors[i])
- end
- return tableconcat(result, eol) .. eol
- end
-
- local step_template = colorize([[Step № ]], 37)
- local chr_template = colorize([[ —— Input ]], 37)
- local pbchr_template = colorize([[ → ]], 37)
- pprint_step = function (n, chr, pb_chr)
- return eol
- .. step_template
- .. colorize(n, 34)
- .. chr_template
- .. colorize(stringupper(value_to_letter[chr]), 34)
- .. pbchr_template
- .. colorize(stringupper(value_to_letter[pb_chr]), 34)
- .. eol
- end
-
- -- Split the strings into lines, group them in bunches of five etc.
- local tw = 30
- local pprint_textblock = function (s)
- local len = utf8len(s)
- local position = 1 -- position in string
- local nline = 5 -- width of current line
- local out = utf8sub(s, position, position+4)
- repeat
- position = position + 5
- nline = nline + 6
- if nline > tw then
- out = out .. eol .. utf8sub(s, position, position+4)
- nline = 1
- else
- out = out .. " " .. utf8sub(s, position, position+4)
- end
- until position > len
- return out
- end
-
- local intext = colorize([[Input text:]], 37)
- local outtext = colorize([[Output text:]], 37)
- pprint_ciphertext = function (input, output, upper_p)
- if upper_p then
- input = stringupper(input)
- output = stringupper(output)
- end
- return eol
- .. intext
- .. eol
- .. pprint_textblock(input)
- .. eol .. eol
- .. outtext
- .. eol
- .. pprint_textblock(output)
- end
-
---[[ichd--
-\startparagraph
-\luafunction{emit} is the main wrapper function for
-\identifier{stdout}. Checks if the global verbosity setting exceeds
-the specified threshold, and only then pushes the output.
-\stopparagraph
---ichd]]--
- emit = function (v, f, ...)
- if f and v and verbose_level >= v then
- iowrite(f(...) .. eol)
- end
- return 0
- end
---[[ichd--
-\startparagraph
-The \luafunction{polite_key_request} will be called in case the
-\identifier{day_key} field of the machine setup is empty at the time of
-initialization.
-\stopparagraph
---ichd]]--
- local s_request = "\n\n "
- .. underline"This is an encrypted document." .. [[
-
-
- Please enter the document key for enigma machine
- “%s”.
-
- Key Format:
-
-Ref R1 R2 R3 I1 I2 I3 [P1 ..] Ref: reflector A/B/C
- Rn: rotor, I through V
- In: ring position, 01 through 26
- Pn: optional plugboard wiring, upto 32
-
->]]
- polite_key_request = function (name)
- return stringformat(s_request, colorize(name, 33))
- end
-
- local s_invalid_key = colorize"Warning!"
- .. " The specified key is invalid."
- key_invalid = function ()
- return s_invalid_key
- end
-end
-
---[[ichd--
-\startparagraph
-The functions \luafunction{new} and \luafunction{ask_for_day_key} are
-used outside their scope, so we declare them beforehand.
-\stopparagraph
---ichd]]--
-local new
-local ask_for_day_key
-do
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Rotation]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startparagraph
-The following function \luafunction{do_rotate} increments the
-rotational state of a single rotor. There are two tests for notches:
-\startitemize[n]
- \item whether it’s at the current character, and
- \item whether it’s at the next character.
-\stopitemize
-The latter is an essential prerequisite for double-stepping.
-\stopparagraph
---ichd]]--
- local do_rotate = function (rotor)
- rotor.state = rotor.state % 26 + 1
- return rotor,
- rotor.state == rotor.notch,
- rotor.state + 1 == rotor.notch
- end
-
---[[ichd--
-\startparagraph
-The \luafunction{rotate} function takes care of rotor (\emph{Walze})
-movement. This entails incrementing the next rotor whenever the notch
-has been reached and covers the corner case \emph{double stepping}.
-\stopparagraph
---ichd]]--
- local rotate = function (machine)
- local rotors = machine.rotors
- local rc, rb, ra = rotors[1], rotors[2], rotors[3]
-
- ra, nxt = do_rotate(ra)
- if nxt or machine.double_step then
- rb, nxt, nxxt = do_rotate(rb)
- if nxt then
- rc = do_rotate(rc)
- end
- if nxxt then
- --- weird: home.comcast.net/~dhhamer/downloads/rotors1.pdf
- machine.double_step = true
- else
- machine.double_step = false
- end
- end
- machine.rotors = { rc, rb, ra }
- end
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Input Preprocessing]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startparagraph
-Internally, we will use lowercase strings as they are a lot more
-readable than uppercase. Lowercasing happens prior to any further
-dealings with input. After the encoding or decoding has been
-accomplished, there will be an optional (re-)uppercasing.
-\stopparagraph
-
-\startparagraph
-Substitutions \reference[listing:preproc]{}are applied onto the
-lowercased input. You might want to avoid some of these, above all the
-rules for numbers, because they translate single digits only. The
-solution is to write out numbers above ten.
-\stopparagraph
---ichd]]--
-
- local pp_substitutions = {
- -- Umlauts are resolved.
- ["ö"] = "oe",
- ["ä"] = "ae",
- ["ü"] = "ue",
- ["ß"] = "ss",
- -- WTF?
- ["ch"] = "q",
- ["ck"] = "q",
- -- Punctuation -> “x”
- [","] = "x",
- ["."] = "x",
- [";"] = "x",
- [":"] = "x",
- ["/"] = "x",
- ["’"] = "x",
- ["‘"] = "x",
- ["„"] = "x",
- ["“"] = "x",
- ["“"] = "x",
- ["-"] = "x",
- ["–"] = "x",
- ["—"] = "x",
- ["!"] = "x",
- ["?"] = "x",
- ["‽"] = "x",
- ["("] = "x",
- [")"] = "x",
- ["["] = "x",
- ["]"] = "x",
- ["<"] = "x",
- [">"] = "x",
- -- Spaces are omitted.
- [" "] = "",
- ["\n"] = "",
- ["\t"] = "",
- ["\v"] = "",
- ["\\"] = "",
- -- Numbers are resolved.
- ["0"] = "null",
- ["1"] = "eins",
- ["2"] = "zwei",
- ["3"] = "drei",
- ["4"] = "vier",
- ["5"] = "fuenf",
- ["6"] = "sechs",
- ["7"] = "sieben",
- ["8"] = "acht",
- ["9"] = "neun",
- }
-
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[
- title={Main function chain to be applied to single characters},
-]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\startparagraph
-As far as the Enigma is concerned, there is no difference between
-encoding and decoding. Thus, we need only one function
-(\luafunction{encode_char}) to achieve the complete functionality.
-However, within every encoding step, characters will be wired
-differently in at least one of the rotors according to its rotational
-state. Rotation is simulated by adding the \identifier{state} field of
-each rotor to the letter value (its position on the ingoing end).
-\stopparagraph
-\placetable[here][table:dirs]{Directional terminology}{%
- \starttabulate[|l|r|l|]
- \NC boolean \NC direction \NC meaning \NC \AR
- \NC true \NC “from” \NC right to left \NC \AR
- \NC false \NC “to” \NC left to right \NC \AR
- \stoptabulate%
-}
-\startparagraph
-The function \luafunction{do_do_encode_char} returns the character
-substitution for one rotor. As a letter passes through each rotor
-twice, the argument \identifier{direction} determines which way the
-substitution is applied.
-\stopparagraph
---ichd]]--
- local do_do_encode_char = function (char, rotor, direction)
- local rw = rotor.wiring
- local rs = rotor.state
- local result = char
- if direction then -- from
- result = (result + rs - 1) % 26 + 1
- result = rw.from[result]
- result = (result - rs - 1) % 26 + 1
- else -- to
- result = (result + rs - 1) % 26 + 1
- result = rw.to[result]
- result = (result - rs - 1) % 26 + 1
- end
- return result
- end
-
---[[ichd--
-\startparagraph
-Behind the plugboard, every character undergoes seven substitutions:
-two for each rotor plus the central one through the reflector. The
-function \luafunction{do_encode_char}, although it returns the final
-result only, keeps every intermediary step inside a table for debugging
-purposes. This may look inefficient but is actually a great advantage
-whenever something goes wrong.
-\stopparagraph
---ichd]]--
- --- ra -> rb -> rc -> ukw -> rc -> rb -> ra
- local do_encode_char = function (rotors, reflector, char)
- local rc, rb, ra = rotors[1], rotors[2], rotors[3]
- local steps = { [0] = char }
- --
- steps[1] = do_do_encode_char(steps[0], ra, true)
- steps[2] = do_do_encode_char(steps[1], rb, true)
- steps[3] = do_do_encode_char(steps[2], rc, true)
- steps[4] = reflector_wiring[reflector][steps[3]]
- steps[5] = do_do_encode_char(steps[4], rc, false)
- steps[6] = do_do_encode_char(steps[5], rb, false)
- steps[7] = do_do_encode_char(steps[6], ra, false)
- emit(2, pprint_encoding_scheme)
- emit(2, pprint_encoding, steps)
- return steps[7]
- end
-
---[[ichd--
-\startparagraph
-Before an input character is passed on to the actual encoding routing,
-the function \luafunction{encode_char} matches it agains the latin
-alphabet.
-Characters failing this test are either passed through or ignored,
-depending on the machine option \identifier{other_chars}.
-Also, the counter of encoded characters is incremented at this stage
-and some pretty printer hooks reside here.
-\stopparagraph
-
-\startparagraph
-\luafunction{encode_char} contributes only one element of the encoding
-procedure: the plugboard (\emph{Steckerbrett}).
-Like the rotors described above, a character passed through this
-device twice; the plugboard marks the beginning and end of every step.
-For debugging purposes, the first substitution is stored in a separate
-local variable, \identifier{pb_char}.
-\stopparagraph
---ichd]]--
-
- local encode_char = function (machine, char)
- machine.step = machine.step + 1
- machine:rotate()
- local pb = machine.plugboard
- char = letter_to_value[char]
- local pb_char = pb[char] -- first plugboard substitution
- emit(2, pprint_step, machine.step, char, pb_char)
- emit(3, pprint_rotor_scheme)
- emit(3, pprint_rotor, machine.rotors[1])
- emit(3, pprint_rotor, machine.rotors[2])
- emit(3, pprint_rotor, machine.rotors[3])
- char = do_encode_char(machine.rotors,
- machine.reflector,
- pb_char)
- return value_to_letter[pb[char]] -- second plugboard substitution
- end
-
- local get_random_pattern = function ()
- local a, b, c
- = mathrandom(1,26), mathrandom(1,26), mathrandom(1,26)
- return value_to_letter[a]
- .. value_to_letter[b]
- .. value_to_letter[c]
- end
-
- local pattern_to_state = function (pat)
- return {
- letter_to_value[stringsub(pat, 1, 1)],
- letter_to_value[stringsub(pat, 2, 2)],
- letter_to_value[stringsub(pat, 3, 3)],
- }
- end
-
- local set_state = function (machine, state)
- local rotors = machine.rotors
- for i=1, 3 do
- rotors[i].state = state[i] - 1
- end
- end
-
---[[ichd--
-\startparagraph
-When \modulename{Enigma} is called from \TEX, the encoding
-proceeds character by character as we iterate one node at a time.
-\luafunction{encode_string} is a wrapper for use with strings, e.~g. in
-the mtx-script (\at{page}[sec:fun]).
-It handles iteration and extraction of successive characters from the
-sequence.
-\stopparagraph
---ichd]]--
- local encode_string = function (machine, str) --, pattern)
- local result = { }
- for char in utfcharacters(str) do
- local tmp = machine:encode(char)
- if tmp ~= false then
- if type(tmp) == "table" then
- for i=1, #tmp do
- result[#result+1] = tmp[i]
- end
- else
- result[#result+1] = tmp
- end
- end
- end
- machine:processed_chars()
- return tableconcat(result)
- end
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Initialization string parser]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\placetable[here][]{Initialization strings}{%
- \bTABLE
- \bTR
- \bTD Reflector \eTD
- \bTD[nc=3] Rotor \eTD
- \bTD[nc=3] Initial \eTD
- \bTD[nc=10] Plugboard wiring \eTD
- \eTR
- \eTR
- \bTR
- \bTD in slot \eTD
- \bTD[nc=3] setting \eTD
- \bTD[nc=3] rotor \eTD
- \eTR
- \bTR
- \bTD \eTD
- \bTD 1 \eTD\bTD 2 \eTD\bTD 3 \eTD
- \bTD 1 \eTD\bTD 2 \eTD\bTD 3 \eTD
- \bTD 1 \eTD\bTD 2 \eTD\bTD 3 \eTD\bTD 4 \eTD\bTD 5 \eTD
- \bTD 6 \eTD\bTD 7 \eTD\bTD 8 \eTD\bTD 9 \eTD\bTD 10 \eTD
- \eTR
- \bTR
- \bTD B \eTD
- \bTD I \eTD\bTD IV \eTD\bTD III \eTD
- \bTD 16 \eTD\bTD 26 \eTD\bTD 08 \eTD
- \bTD AD \eTD\bTD CN \eTD\bTD ET \eTD
- \bTD FL \eTD\bTD GI \eTD\bTD JV \eTD
- \bTD KZ \eTD\bTD PU \eTD\bTD QY \eTD
- \bTD WX \eTD
- \eTR
- \eTABLE
-}
---ichd]]--
- local roman_digits = {
- i = 1, I = 1,
- ii = 2, II = 2,
- iii = 3, III = 3,
- iv = 4, IV = 4,
- v = 5, V = 5,
- }
-
- local p_init = P{
- "init",
- init = V"whitespace"^-1 * Ct(V"do_init"),
- do_init = (V"reflector" * V"whitespace")^-1
- * V"rotors" * V"whitespace"
- * V"ring"
- * (V"whitespace" * V"plugboard")^-1
- ,
- reflector = Cg(C(R("ac","AC")) / stringlower, "reflector")
- ,
-
- rotors = Cg(Ct(V"rotor" * V"whitespace"
- * V"rotor" * V"whitespace"
- * V"rotor"),
- "rotors")
- ,
- rotor = Cs(V"roman_five" / roman_digits
- + V"roman_four" / roman_digits
- + V"roman_three" / roman_digits
- + V"roman_two" / roman_digits
- + V"roman_one" / roman_digits)
- ,
- roman_one = P"I" + P"i",
- roman_two = P"II" + P"ii",
- roman_three = P"III" + P"iii",
- roman_four = P"IV" + P"iv",
- roman_five = P"V" + P"v",
-
- ring = Cg(Ct(V"double_digit" * V"whitespace"
- * V"double_digit" * V"whitespace"
- * V"double_digit"),
- "ring")
- ,
- double_digit = C(R"02" * R"09"),
-
- plugboard = Cg(V"do_plugboard", "plugboard"),
- --- no need to enforce exactly ten substitutions
- --do_plugboard = Ct(V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination" * V"whitespace"
- -- * V"letter_combination")
- do_plugboard = Ct(V"letter_combination"
- * (V"whitespace" * V"letter_combination")^0)
- ,
- letter_combination = C(R("az", "AZ") * R("az", "AZ")),
-
- whitespace = S" \n\t\v"^1,
- }
-
-
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Initialization routines]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\startparagraph
-The plugboard is implemented as a pair of hash tables.
-\stopparagraph
---ichd]]--
- local get_plugboard_substitution = function (p)
- --- Plugboard wirings are symmetrical, thus we have one table for
- --- each direction.
- local tmp, result = { }, { }
- for _, str in next, p do
- local one, two = stringlower(stringsub(str, 1, 1)),
- stringlower(stringsub(str, 2))
- tmp[one] = two
- tmp[two] = one
- end
- local n_letters = 26
-
- local lv = letter_to_value
- for n=1, n_letters do
- local letter = value_to_letter[n]
- local sub = tmp[letter] or letter
- -- Map each char either to the plugboard substitution or itself.
- result[lv[letter]] = lv[sub or letter]
- end
- return result
- end
-
---[[ichd--
-\startparagraph
-Initialization of the rotors requires some precautions to be taken.
-The most obvious of which is adjusting the displacement of its wiring
-by the ring setting.
-\stopparagraph
-\startparagraph
-Another important task is to store the notch position in order for it
-to be retrievable by the rotation subroutine at a later point.
-\stopparagraph
-\startparagraph
-The actual bidirectional mapping is implemented as a pair of tables.
-The initial order of letters, before the ring shift is applied, is
-alphabetical on the input (right, “from”) side and, on the output
-(left, “to”) side taken by the hard wired correspondence as specified
-in the rotor wirings above.
-NB the descriptions in terms of “output” and “input” directions is
-misleading in so far as during any encoding step the electricity will
-pass through every rotor in both ways.
-Hence, the “input” (right, from) direction literally applies only to
-the first half of the encoding process between plugboard and reflector.
-\stopparagraph
-\startparagraph
-The function \luafunction{do_get_rotor} creates a single rotor instance
-and populates it with character mappings. The \identifier{from} and
-\identifier{to} subfields of its \identifier{wiring} field represent
-the wiring in the respective directions.
-This initital wiring was specified in the corresponding
-\identifier{raw_rotor_wiring} table; the ringshift is added modulo the
-alphabet size in order to get the correctly initialized rotor.
-\stopparagraph
---ichd]]--
- local do_get_rotor = function (raw, notch, ringshift)
- local rotor = {
- wiring = {
- from = { },
- to = { },
- },
- state = 0,
- notch = notch,
- }
- local w = rotor.wiring
- for from=1, 26 do
- local to = letter_to_value[stringsub(raw, from, from)]
- --- The shift needs to be added in both directions.
- to = (to + ringshift - 1) % 26 + 1
- from = (from + ringshift - 1) % 26 + 1
- rotor.wiring.from[from] = to
- rotor.wiring.to [to ] = from
- end
- --table.print(rotor, "rotor")
- return rotor
- end
-
---[[ichd--
-\startparagraph
-Rotors are initialized sequentially accordings to the initialization
-request.
-The function \luafunction{get_rotors} walks over the list of
-initialization instructions and calls \luafunction{do_get_rotor} for
-the actual generation of the rotor table. Each rotor generation request
-consists of three elements:
-\stopparagraph
-\startitemize[n]
- \item the choice of rotor (one of five),
- \item the notch position of said rotor, and
- \item the ring shift.
-\stopitemize
---ichd]]--
- local get_rotors = function (rotors, ring)
- local s, r = { }, { }
- for n=1, 3 do
- local nr = tonumber(rotors[n])
- local ni = tonumber(ring[n]) - 1 -- “1” means shift of zero
- r[n] = do_get_rotor(raw_rotor_wiring[nr], notches[nr], ni)
- s[n] = ni
- end
- return r, s
- end
-
- local decode_char = encode_char -- hooray for involutory ciphers
-
---[[ichd--
-\startparagraph
-The function \luafunction{encode_general} is an intermediate step for
-the actual single-character encoding / decoding routine
-\luafunction{enchode_char}.
-Its purpose is to ensure encodability of a given character before
-passing it along.
-Characters are first checked against the replacement table
-\identifier{pp_substitutions} (see \at{page}[listing:preproc]).
-For single-character replacements the function returns the encoded
-character (string).
-However, should the replacement turn out to consist of more than one
-character, each one will be encoded successively, yielding a list.
-\stopparagraph
---ichd]]--
- local encode_general = function (machine, chr)
- local chr = utf8lower(chr)
- local replacement
- = pp_substitutions[chr] or letter_to_value[chr] and chr
- if not replacement then
- if machine.other_chars then
- return chr
- else
- return false
- end
- end
-
- if utf8len(replacement) == 1 then
- return encode_char(machine, replacement)
- end
- local result = { }
- for new_chr in utfcharacters(replacement) do
- result[#result+1] = encode_char(machine, new_chr)
- end
- return result
- end
-
- local process_message_key
- local alpha = R"az"
- local alpha_dec = alpha / letter_to_value
- local whitespace = S" \n\t\v"
- local mkeypattern = Ct(alpha_dec * alpha_dec * alpha_dec)
- * whitespace^0
- * C(alpha * alpha *alpha)
- process_message_key = function (machine, message_key)
- message_key = stringlower(message_key)
- local init, three = lpegmatch(mkeypattern, message_key)
- -- to be implemented
- end
-
- local decode_string = function (machine, str, message_key)
- machine.kenngruppe, str = stringsub(str, 3, 5), stringsub(str, 6)
- machine:process_message_key(message_key)
- local decoded = encode_string(machine, str)
- return decoded
- end
-
- local testoptions = {
- size = 42,
-
- }
- local generate_header = function (options)
- end
-
- local processed_chars = function (machine)
- emit(1, pprint_machine_step, machine.step, machine.name)
- end
-
---[[ichd--
-\startparagraph
-The day key is entrusted to the function \luafunction{handle_day_key}.
-If the day key is the empty string or \type{nil}, it will ask for a key
-on the terminal. (Cf. below, \at{page}[listing:ask_for_day_key].)
-Lesson: don’t forget providing day keys in your setups when running in
-batch mode.
-\stopparagraph
---ichd]]--
- local handle_day_key handle_day_key = function (dk, name, old)
- local result
- if not dk or dk == "" then
- dk = ask_for_day_key(name, old)
- end
- result = lpegmatch(p_init, dk)
- result.reflector = result.reflector or "b"
- -- If we don’t like the key we’re going to ask again. And again....
- return result or handle_day_key(nil, name, dk)
- end
-
---[[ichd--
-\startparagraph
-The enigma encoding is restricted to an input -- and, naturally, output
--- alphabet of exactly twenty-seven characters. Obviously, this would
-severely limit the set of encryptable documents. For this reason the
-plain text would be \emph{preprocessed} prior to encoding, removing
-spaces and substituting a range of characters, e.\,g. punctuation, with
-placeholders (“X”) from the encodable spectrum. See above
-\at{page}[listing:preproc] for a comprehensive list of substitutions.
-\stopparagraph
-
-\startparagraph
-The above mentioned preprocessing, however, does not even nearly extend
-to the whole unicode range that modern day typesetting is expected to
-handle. Thus, sooner or later an Enigma machine will encounter
-non-preprocessable characters and it will have to decide what to do
-with them. The Enigma module offers two ways to handle this kind of
-situation: \emph{drop} those characters, possibly distorting the
-deciphered plain text, or to leave them in, leaving hints behind as to
-the structure of the encrypted text. None of these is optional, so it
-is nevertheless advisable to not include non-latin characters in the
-plain text in the first place. The settings key
-\identifier{other_chars} (type boolean) determines whether we will keep
-or drop offending characters.
-\stopparagraph
---ichd]]--
-
- new = function (name, args)
- local setup_string, pattern = args.day_key, args.rotor_setting
- local raw_settings = handle_day_key(setup_string, name)
- local rotors, ring =
- get_rotors(raw_settings.rotors, raw_settings.ring)
- local plugboard
- = raw_settings.plugboard
- and get_plugboard_substitution(raw_settings.plugboard)
- or get_plugboard_substitution{ }
- local machine = {
- name = name,
- step = 0, -- n characters encoded
- init = {
- rotors = raw_settings.rotors,
- ring = raw_settings.ring
- },
- rotors = rotors,
- ring = ring,
- state = init_state,
- other_chars = args.other_chars,
- spacing = args.spacing,
- ---> a>1, b>2, c>3
- reflector = letter_to_value[raw_settings.reflector],
- plugboard = plugboard,
- --- functionality
- rotate = rotate,
- --process_message_key = process_message_key,
- encode_string = encode_string,
- encode_char = encode_char,
- encode = encode_general,
- decode_string = decode_string,
- decode_char = decode_char,
- set_state = set_state,
- processed_chars = processed_chars,
- --- <badcodingstyle> -- hackish but occasionally useful
- __raw = raw_settings
- --- </badcodingstyle>
- } --- machine
- local init_state
- = pattern_to_state(pattern or get_random_pattern())
- emit(1, pprint_init, init_state)
- machine:set_state(init_state)
-
- --table.print(machine.rotors)
- emit(1, pprint_new_machine, machine)
- return machine
- end
-
-end
---[[ichd--
-\stopdocsection
---ichd]]--
-
-
---[[ichd--
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\startdocsection[title=Setup Argument Handling]
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
---ichd]]--
-do
---[[ichd--
-\startparagraph
-As the module is intended to work both with the Plain and \LATEX\
-formats as well as \CONTEXT, we can’t rely on format dependent setups.
-Hence the need for an argument parser. Should be more efficient anyways
-as all the functionality resides in Lua.
-\stopparagraph
---ichd]]--
-
- local p_args = P{
- "args",
- args = Cf(Ct"" * (V"kv_pair" + V"emptyline")^0, rawset),
- kv_pair = Cg(V"key"
- * V"separator"
- * (V"value" * V"final"
- + V"empty"))
- * V"rest_of_line"^-1
- ,
- key = V"whitespace"^0 * C(V"key_char"^1),
- key_char = (1 - V"whitespace" - V"eol" - V"equals")^1,
- separator = V"whitespace"^0 * V"equals" * V"whitespace"^0,
- empty = V"whitespace"^0 * V"comma" * V"rest_of_line"^-1
- * Cc(false)
- ,
- value = C((V"balanced" + (1 - V"final"))^1),
- final = V"whitespace"^0 * V"comma" + V"rest_of_string",
- rest_of_string = V"whitespace"^0
- * V"eol_comment"^-1
- * V"eol"^0
- * V"eof"
- ,
- rest_of_line = V"whitespace"^0 * V"eol_comment"^-1 * V"eol",
- eol_comment = V"comment_string" * (1 - (V"eol" + V"eof"))^0,
- comment_string = V"lua_comment" + V"TeX_comment",
- TeX_comment = V"percent",
- lua_comment = V"double_dash",
- emptyline = V"rest_of_line",
-
- balanced = V"balanced_brk" + V"balanced_brc",
- balanced_brk = V"lbrk"
- * (V"balanced" + (1 - V"rbrk"))^0
- * V"rbrk"
- ,
- balanced_brc = V"lbrc"
- * (V"balanced" + (1 - V"rbrc"))^0
- * V"rbrc"
- ,
- -- Terminals
- eol = P"\n\r" + P"\r\n" + P"\n" + P"\r",
- eof = -P(1),
- whitespace = S" \t\v",
- equals = P"=",
- dot = P".",
- comma = P",",
- dash = P"-", double_dash = V"dash" * V"dash",
- percent = P"%",
- lbrk = P"[", rbrk = P"]",
- lbrc = P"{", rbrc = P"}",
- }
-
-
---[[ichd--
-\startparagraph
-In the next step we process the arguments, check the input for sanity
-etc. The function \luafunction{parse_args} will test whether a value
-has a sanitizer routine and, if so, apply it to its value.
-\stopparagraph
---ichd]]--
-
- local boolean_synonyms = {
- ["1"] = true,
- doit = true,
- indeed = true,
- ok = true,
- ["⊤"] = true,
- ["true"] = true,
- yes = true,
- }
- local toboolean
- = function (value) return boolean_synonyms[value] or false end
- local alpha = R("az", "AZ")
- local digit = R"09"
- local space = S" \t\v"
- local ans = alpha + digit + space
- local p_ans = Cs((ans + (1 - ans / ""))^1)
- local alphanum_or_space = function (str)
- if type(str) ~= "string" then return nil end
- return lpegmatch(p_ans, str)
- end
- local ensure_int = function (n)
- n = tonumber(n)
- if not n then return 0 end
- return mathfloor(n + 0.5)
- end
- p_alpha = Cs((alpha + (1 - alpha / ""))^1)
- local ensure_alpha = function (s)
- s = tostring(s)
- return lpegmatch(p_alpha, s)
- end
-
- local sanitizers = {
- other_chars = toboolean, -- true = keep, false = drop
- spacing = toboolean,
- day_key = alphanum_or_space,
- rotor_setting = ensure_alpha,
- verbose = ensure_int,
- }
- enigma.parse_args = function (raw)
- local args = lpegmatch(p_args, raw)
- for k, v in next, args do
- local f = sanitizers[k]
- if f then
- args[k] = f(v)
- else
- -- OPTIONAL be fascist and permit only predefined args
- args[k] = v
- end
- end
- return args
- end
---[[ichd--
-\startparagraph
-If the machine setting lacks key settings then we’ll go ahead and ask
-\reference[listing:ask_for_day_key]{}%
-the user directly, hence the function \luafunction{ask_for_day_key}.
-We abort after three misses lest we annoy the user \dots
-\stopparagraph
---ichd]]--
- local max_tries = 3
- ask_for_day_key = function (name, old, try)
- if try == max_tries then
- iowrite[[
-Aborting. Entered invalid key three times.
-]]
- os.exit()
- end
- if old then
- emit(0, key_invalid)
- end
- emit(0, polite_key_request, name)
- local result = ioread()
- iowrite("\n")
- return alphanum_or_space(result) or
- ask_for_day_key(name, (try and try + 1 or 1))
- end
-end
-
---[[ichd--
-\stopdocsection
---ichd]]--
-
---[[ichd--
-\startdocsection[title=Callback]
-\startparagraph
-This is the interface to \TEX. We generate a new callback handler for
-each defined Enigma machine. \CONTEXT\ delivers the head as third
-argument of a callback only (...‽), so we’ll have to do some variable
-shuffling on the function side.
-\stopparagraph
-
-\startparagraph
-When grouping output into the traditional blocks of five letters we
-insert space nodes. As their properties depend on the font we need to
-recreate the space item for every paragraph. Also, as \CONTEXT\ does
-not preload a font we lack access to font metrics before
-\type{\starttext}. Thus creating the space earlier will result in an
-error.
-The function \luafunction{generate_space} will be called inside the
-callback in order to get an appropriate space glue.
-\stopparagraph
---ichd]]--
-
-local generate_space = function ( )
- local current_fontparms = font.getfont(font.current()).parameters
- local space_node = nodenew(GLUE_NODE)
- space_node.spec = nodenew(GLUE_SPEC_NODE)
- space_node.spec.width = current_fontparms.space
- space_node.spec.shrink = current_fontparms.space_shrink
- space_node.spec.stretch = current_fontparms.space_stretch
- return space_node
-end
-
---[[ichd--
-\startparagraph
-\useURL[khaled_hosny_texsx] [http://tex.stackexchange.com/a/11970]
- [] [tex.sx]
-Registering a callback (“node attribute”?, “node task”?, “task
-action”?) in \CONTEXT\ is not straightforward, let alone documented.
-The trick is to create, install and register a handler first in order
-to use it later on \dots\ many thanks to Khaled Hosny, who posted an
-answer to \from[khaled_hosny_texsx].
-\stopparagraph
---ichd]]--
-
-local new_callback = function (machine, name)
- enigma.machines [name] = machine
- local format_is_context = format_is_context
- local current_space_node
- local mod_5 = 0
-
- --- First we need to choose an insertion method. If autospacing is
- --- requested, a space will have to be inserted every five
- --- characters. The rationale behind using differend functions to
- --- implement each method is that it should be faster than branching
- --- for each character.
- local insert_encoded
-
- if machine.spacing then -- auto-group output
- insert_encoded = function (head, n, replacement)
- local insert_glyph = nodecopy(n)
- if replacement then -- inefficient but bulletproof
- insert_glyph.char = utf8byte(replacement)
- --print(utf8char(n.char), "=>", utf8char(insertion.char))
- end
- --- if we insert a space we need to return the
- --- glyph node in order to track positions when
- --- replacing multiple nodes at once (e.g. ligatures)
- local insertion = insert_glyph
- mod_5 = mod_5 + 1
- if mod_5 > 5 then
- mod_5 = 1
- insertion = nodecopy(current_space_node)
- insertion.next, insert_glyph.prev = insert_glyph, insertion
- end
- if head == n then --> replace head
- local succ = head.next
- if succ then
- insert_glyph.next, succ.prev = succ, insert_glyph
- end
- head = insertion
- else --> replace n
- local pred, succ = n.prev, n.next
- pred.next, insertion.prev = insertion, pred
- if succ then
- insert_glyph.next, succ.prev = succ, insert_glyph
- end
- end
-
- --- insertion becomes the new head
- return head, insert_glyph -- so we know where to insert
- end
- else
-
- insert_encoded = function (head, n, replacement)
- local insertion = nodecopy(n)
- if replacement then
- insertion.char = utf8byte(replacement)
- end
- if head == n then
- local succ = head.next
- if succ then
- insertion.next, succ.prev = succ, insertion
- end
- head = insertion
- else
- nodeinsert_before(head, n, insertion)
- noderemove(head, n)
- end
- return head, insertion
- end
- end
-
- --- The callback proper starts here.
- local aux aux = function (head, recurse)
- if recurse == nil then recurse = 0 end
- for n in nodetraverse(head) do
- local nid = n.id
- --print(utf8char(n.char), n)
- if nid == GLYPH_NODE then
- local chr = utf8char(n.char)
- --print(chr, n)
- local replacement = machine:encode(chr)
- --print(chr, replacement, n)
- local treplacement = replacement and type(replacement)
- --if replacement == false then
- if not replacement then
- noderemove(head, n)
- elseif treplacement == "string" then
- --print(head, n, replacement)
- head, _ = insert_encoded(head, n, replacement)
- elseif treplacement == "table" then
- local current = n
- for i=1, #replacement do
- head, current = insert_encoded(head, current, replacement[i])
- end
- end
- elseif nid == GLUE_NODE then
- if n.subtype ~= 15 then -- keeping the parfillskip
- noderemove(head, n)
- end
- elseif IGNORE_NODES[nid] then
- -- drop spaces and kerns
- noderemove(head, n)
- elseif nid == DISC_NODE then
- --- ligatures need to be resolved if they are characters
- local npre, npost = n.pre, n.post
- if nodeid(npre) == GLYPH_NODE and
- nodeid(npost) == GLYPH_NODE then
- if npre.char and npost.char then -- ligature between glyphs
- local replacement_pre = machine:encode(utf8char(npre.char))
- local replacement_post = machine:encode(utf8char(npost.char))
- insert_encoded(head, npre, replacement_pre)
- insert_encoded(head, npost, replacement_post)
- else -- hlists or whatever
- -- pass
- --noderemove(head, npre)
- --noderemove(head, npost)
- end
- end
- noderemove(head, n)
- elseif nid == HLIST_NODE or nid == VLIST_NODE then
- if nodelength(n.list) > 0 then
- n.list = aux(n.list, recurse + 1)
- end
--- else
--- -- TODO other node types
--- print(n)
- end
- end
- nodeslide(head)
- return head
- end -- callback auxiliary
-
- --- Context requires
- --- × argument shuffling; a properly registered “action” gets the
- --- head passed as its third argument
- --- × hacking our way around the coupling of pre_linebreak_filter
- --- and hpack_filter; background:
- --- http://www.ntg.nl/pipermail/ntg-context/2012/067779.html
- local cbk = function (a, _, c)
- local head
- current_space_node = generate_space ()
- mod_5 = 0
- if format_is_context == true then
- head = c
- local cbk_env = get_debug_info(4) -- no getenv in lua 5.2
- --inspect(cbk_env)
- if cbk_env.func == nodes.processors.pre_linebreak_filter then
- -- how weird is that?
- return aux(head)
- end
- return head
- end
- head = a
- return aux(head)
- end
-
- if format_is_context then
- local cbk_id = "enigma_" .. name
- enigma.callbacks[name] = nodesinstallattributehandler{
- name = cbk_id,
- namespace = thirddata.enigma,
- processor = cbk,
- }
- local cbk_location = "thirddata.enigma.callbacks." .. name
- nodestasksappendaction("processors",
- --"characters",
- --"finalizers",
- --- this one is tagged “for users”
- --- (cf. node-tsk.lua)
- "before",
- cbk_location)
- nodestasksdisableaction("processors", cbk_location)
- else
- enigma.callbacks[name] = cbk
- end
-end
-
---[[ichd--
-\startparagraph
-Enigma\reference[listing:retrieve]{} machines can be copied and derived
-from one another at will, cf. the \texmacro{defineenigma} on
-\at{page}[listing:define]. Two helper functions residing inside the
-\identifier{thirddata.enigma} namespace take care of these actions:
-\luafunction{save_raw_args} and \luafunction{retrieve_raw_args}. As
-soon as a machine is defined, we store its parsed options inside the
-table \identifier{configurations} for later reference. For further
-details on the machine derivation mechanism see
-\at{page}[listing:inherit].
-\stopparagraph
---ichd]]--
-local configurations = { }
-
-local save_raw_args = function (conf, name)
- local current = configurations[name] or { }
- for k, v in next, conf do
- current[k] = v
- end
- configurations[name] = current
-end
-
-local retrieve_raw_args = function (name)
- local cn = configurations[name]
- return cn and tablecopy(cn) or { }
-end
-
-enigma.save_raw_args = save_raw_args
-enigma.retrieve_raw_args = retrieve_raw_args
-
-
---[[ichd--
-\startparagraph
-The function \luafunction{new_machine} instantiates a table containing
-the complete specification of a workable \emph{Enigma} machine and
-other metadata. The result is intended to be handed over to the
-callback creation mechanism (\luafunction{new_callback}). However, the
-arguments table is usally stored away in the
-\identifier{thirddata.enigma} namespace anyway
-(\luafunction{save_raw_args}), so that the specification of any machine
-can be inherited by some new setup later on.
-\stopparagraph
---ichd]]--
-local new_machine = function (name)
- local args = configurations[name]
- --table.print(configurations)
- verbose_level = args and args.verbose or verbose_level
- local machine = new(name, args)
- return machine
-end
-
-enigma.new_machine = new_machine
-enigma.new_callback = new_callback
-
---[[ichd--
-\stopdocsection
---ichd]]--
-
--- vim:ft=lua:sw=2:ts=2:tw=71:expandtab