aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThijs Schreijer <thijs@thijsschreijer.nl>2022-03-23 16:01:50 +0100
committerThijs Schreijer <thijs@thijsschreijer.nl>2022-03-29 13:33:00 +0200
commit7187be8b76452aa968726180af24deaaa545431d (patch)
tree201997c2b60c3f882fd845ff04828da8428aceba
parent0fc0122df8050db8cbb75f75927ab3ace0fc60bd (diff)
downloadluasocket-7187be8b76452aa968726180af24deaaa545431d.tar.gz
luasocket-7187be8b76452aa968726180af24deaaa545431d.tar.bz2
luasocket-7187be8b76452aa968726180af24deaaa545431d.zip
cleanup; delete the ./gem folder
-rw-r--r--gem/ex1.lua4
-rw-r--r--gem/ex10.lua17
-rw-r--r--gem/ex11.lua7
-rw-r--r--gem/ex12.lua34
-rw-r--r--gem/ex2.lua11
-rw-r--r--gem/ex3.lua15
-rw-r--r--gem/ex4.lua5
-rw-r--r--gem/ex5.lua15
-rw-r--r--gem/ex6.lua14
-rw-r--r--gem/ex7.lua16
-rw-r--r--gem/ex8.lua5
-rw-r--r--gem/ex9.lua3
-rw-r--r--gem/gem.c54
-rw-r--r--gem/gt.b64206
-rw-r--r--gem/input.binbin11732 -> 0 bytes
-rw-r--r--gem/ltn012.tex695
-rw-r--r--gem/luasocket.pngbin11732 -> 0 bytes
-rw-r--r--gem/makefile14
-rwxr-xr-xgem/myps2pdf113
-rw-r--r--gem/t1.lua25
-rw-r--r--gem/t1lf.txt5
-rw-r--r--gem/t2.lua36
-rw-r--r--gem/t2.txt4
-rw-r--r--gem/t2gt.qp5
-rw-r--r--gem/t3.lua25
-rw-r--r--gem/t4.lua10
-rw-r--r--gem/t5.lua30
-rw-r--r--gem/test.lua46
28 files changed, 0 insertions, 1414 deletions
diff --git a/gem/ex1.lua b/gem/ex1.lua
deleted file mode 100644
index 327a542..0000000
--- a/gem/ex1.lua
+++ /dev/null
@@ -1,4 +0,0 @@
1local CRLF = "\013\010"
2local input = source.chain(source.file(io.stdin), normalize(CRLF))
3local output = sink.file(io.stdout)
4pump.all(input, output)
diff --git a/gem/ex10.lua b/gem/ex10.lua
deleted file mode 100644
index 2b1b98f..0000000
--- a/gem/ex10.lua
+++ /dev/null
@@ -1,17 +0,0 @@
1function pump.step(src, snk)
2 local chunk, src_err = src()
3 local ret, snk_err = snk(chunk, src_err)
4 if chunk and ret then return 1
5 else return nil, src_err or snk_err end
6end
7
8function pump.all(src, snk, step)
9 step = step or pump.step
10 while true do
11 local ret, err = step(src, snk)
12 if not ret then
13 if err then return nil, err
14 else return 1 end
15 end
16 end
17end
diff --git a/gem/ex11.lua b/gem/ex11.lua
deleted file mode 100644
index 79c99af..0000000
--- a/gem/ex11.lua
+++ /dev/null
@@ -1,7 +0,0 @@
1local input = source.chain(
2 source.file(io.open("input.bin", "rb")),
3 encode("base64"))
4local output = sink.chain(
5 wrap(76),
6 sink.file(io.open("output.b64", "w")))
7pump.all(input, output)
diff --git a/gem/ex12.lua b/gem/ex12.lua
deleted file mode 100644
index de17d76..0000000
--- a/gem/ex12.lua
+++ /dev/null
@@ -1,34 +0,0 @@
1local smtp = require"socket.smtp"
2local mime = require"mime"
3local ltn12 = require"ltn12"
4
5CRLF = "\013\010"
6
7local message = smtp.message{
8 headers = {
9 from = "Sicrano <sicrano@example.com>",
10 to = "Fulano <fulano@example.com>",
11 subject = "A message with an attachment"},
12 body = {
13 preamble = "Hope you can see the attachment" .. CRLF,
14 [1] = {
15 body = "Here is our logo" .. CRLF},
16 [2] = {
17 headers = {
18 ["content-type"] = 'image/png; name="luasocket.png"',
19 ["content-disposition"] =
20 'attachment; filename="luasocket.png"',
21 ["content-description"] = 'LuaSocket logo',
22 ["content-transfer-encoding"] = "BASE64"},
23 body = ltn12.source.chain(
24 ltn12.source.file(io.open("luasocket.png", "rb")),
25 ltn12.filter.chain(
26 mime.encode("base64"),
27 mime.wrap()))}}}
28
29assert(smtp.send{
30 rcpt = "<diego@cs.princeton.edu>",
31 from = "<diego@cs.princeton.edu>",
32 server = "localhost",
33 port = 2525,
34 source = message})
diff --git a/gem/ex2.lua b/gem/ex2.lua
deleted file mode 100644
index 94bde66..0000000
--- a/gem/ex2.lua
+++ /dev/null
@@ -1,11 +0,0 @@
1function filter.cycle(lowlevel, context, extra)
2 return function(chunk)
3 local ret
4 ret, context = lowlevel(context, chunk, extra)
5 return ret
6 end
7end
8
9function normalize(marker)
10 return filter.cycle(eol, 0, marker)
11end
diff --git a/gem/ex3.lua b/gem/ex3.lua
deleted file mode 100644
index 60b4423..0000000
--- a/gem/ex3.lua
+++ /dev/null
@@ -1,15 +0,0 @@
1local function chainpair(f1, f2)
2 return function(chunk)
3 local ret = f2(f1(chunk))
4 if chunk then return ret
5 else return (ret or "") .. (f2() or "") end
6 end
7end
8
9function filter.chain(...)
10 local f = select(1, ...)
11 for i = 2, select('#', ...) do
12 f = chainpair(f, select(i, ...))
13 end
14 return f
15end
diff --git a/gem/ex4.lua b/gem/ex4.lua
deleted file mode 100644
index c48b77e..0000000
--- a/gem/ex4.lua
+++ /dev/null
@@ -1,5 +0,0 @@
1local qp = filter.chain(normalize(CRLF), encode("quoted-printable"),
2 wrap("quoted-printable"))
3local input = source.chain(source.file(io.stdin), qp)
4local output = sink.file(io.stdout)
5pump.all(input, output)
diff --git a/gem/ex5.lua b/gem/ex5.lua
deleted file mode 100644
index 196b30a..0000000
--- a/gem/ex5.lua
+++ /dev/null
@@ -1,15 +0,0 @@
1function source.empty(err)
2 return function()
3 return nil, err
4 end
5end
6
7function source.file(handle, io_err)
8 if handle then
9 return function()
10 local chunk = handle:read(20)
11 if not chunk then handle:close() end
12 return chunk
13 end
14 else return source.empty(io_err or "unable to open file") end
15end
diff --git a/gem/ex6.lua b/gem/ex6.lua
deleted file mode 100644
index a3fdca0..0000000
--- a/gem/ex6.lua
+++ /dev/null
@@ -1,14 +0,0 @@
1function source.chain(src, f)
2 return function()
3 if not src then
4 return nil
5 end
6 local chunk, err = src()
7 if not chunk then
8 src = nil
9 return f(nil)
10 else
11 return f(chunk)
12 end
13 end
14end
diff --git a/gem/ex7.lua b/gem/ex7.lua
deleted file mode 100644
index c766988..0000000
--- a/gem/ex7.lua
+++ /dev/null
@@ -1,16 +0,0 @@
1function sink.table(t)
2 t = t or {}
3 local f = function(chunk, err)
4 if chunk then table.insert(t, chunk) end
5 return 1
6 end
7 return f, t
8end
9
10local function null()
11 return 1
12end
13
14function sink.null()
15 return null
16end
diff --git a/gem/ex8.lua b/gem/ex8.lua
deleted file mode 100644
index 81e288c..0000000
--- a/gem/ex8.lua
+++ /dev/null
@@ -1,5 +0,0 @@
1local input = source.file(io.stdin)
2local output, t = sink.table()
3output = sink.chain(normalize(CRLF), output)
4pump.all(input, output)
5io.write(table.concat(t))
diff --git a/gem/ex9.lua b/gem/ex9.lua
deleted file mode 100644
index b857698..0000000
--- a/gem/ex9.lua
+++ /dev/null
@@ -1,3 +0,0 @@
1for chunk in source.file(io.stdin) do
2 io.write(chunk)
3end
diff --git a/gem/gem.c b/gem/gem.c
deleted file mode 100644
index 976f74d..0000000
--- a/gem/gem.c
+++ /dev/null
@@ -1,54 +0,0 @@
1#include "lua.h"
2#include "lauxlib.h"
3
4#define CR '\xD'
5#define LF '\xA'
6#define CRLF "\xD\xA"
7
8#define candidate(c) (c == CR || c == LF)
9static int pushchar(int c, int last, const char *marker,
10 luaL_Buffer *buffer) {
11 if (candidate(c)) {
12 if (candidate(last)) {
13 if (c == last)
14 luaL_addstring(buffer, marker);
15 return 0;
16 } else {
17 luaL_addstring(buffer, marker);
18 return c;
19 }
20 } else {
21 luaL_putchar(buffer, c);
22 return 0;
23 }
24}
25
26static int eol(lua_State *L) {
27 int context = luaL_checkint(L, 1);
28 size_t isize = 0;
29 const char *input = luaL_optlstring(L, 2, NULL, &isize);
30 const char *last = input + isize;
31 const char *marker = luaL_optstring(L, 3, CRLF);
32 luaL_Buffer buffer;
33 luaL_buffinit(L, &buffer);
34 if (!input) {
35 lua_pushnil(L);
36 lua_pushnumber(L, 0);
37 return 2;
38 }
39 while (input < last)
40 context = pushchar(*input++, context, marker, &buffer);
41 luaL_pushresult(&buffer);
42 lua_pushnumber(L, context);
43 return 2;
44}
45
46static luaL_reg func[] = {
47 { "eol", eol },
48 { NULL, NULL }
49};
50
51int luaopen_gem(lua_State *L) {
52 luaL_openlib(L, "gem", func, 0);
53 return 0;
54}
diff --git a/gem/gt.b64 b/gem/gt.b64
deleted file mode 100644
index a74c0b3..0000000
--- a/gem/gt.b64
+++ /dev/null
@@ -1,206 +0,0 @@
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
161aWlpFoslPz9/xIgRJpMJPOjg3ZYB4NRwPv7448rKymeffRa8pvAh5ylVkoT/9Kfs5577nBAjY7jv
162y8n5PkqEMMac0vaXX57/hz9M91SCXjipjh07lpWVtWbNGrWcoSzLe/bsSU9PDw4OppS6HMbuAt07
163yE3b0J133nnlirfsQO39hICi0N/9Lj0kxLRixQ7OMSG6W1I35RgTzmVK6TvvLF65cqIn7KuGkKqq
164qoiICL1eL8syxlhUNTlx4oSiKPfee2+X7+v5ESYWi2Xw4MEuEqlTd42TBliW6eOPj//yy0eCg42U
165tkuScKrcImJZJPITxtoDAgyff7505cqJskxdsK8OkznPlIuKiqqoqGhtbRWRz4qi6HS66dOni6TU
166Ls9w7DYBtL1Rjy1xiezw9IgkYVmmc+cmHD/+1MSJgxWlhRDo6flcvYx9jDEhoCgtY8bEHj/+5F13
167JQudx9MACSFiso8dO9ZsNn/44YeiUqu48/Lly2qCu/cXd1sLguvLMoovly5dUhTFZDJ5OstE02+s
168KCwkxLRs2Vi7nWRnn+dcIUTv9Oj2PUfqyBdjzME5Xb165iefLB440F/oPNrxav2INpvt8OHDR48e
169tVqt0dHRI0eOzM7OPnjwoF6vlyQpNzf38OHD9913X1BQkJcM347GbySpSDWUbtiwwWq1Pv300ypt
170vItlcWCLOI9lxYrdp0+XI2TEWHKu674hA3dGSimc21JSYtetu+v6s9w6hgiaEAWEUGNj4/r16yml
171AwcOLCsrE5bnkJCQL7/8sqCgQJZlPz+/u+++e8SIEVor6U0hgIrQq1evvvHGG3FxcUuXLgXPSpH2
172EVU1stuVd9459uqr+5uaGvuKDNeh3mIJfP756atWpQkPl/ASg5PBqtNfDeh8//33CSHLly8HgLa2
173to8//ri0tHTVqlXh4eF2u729vT0gIEA1gnYZ5dgTFnQdARFijBmNxmHDhu3Zs+fixYujR4/2/mIt
174OxJG3alTY5ctGwugP3WqzmazAiCMJe8FYHqGdwDkFKoK5+1+fuaVK9O3bFk8b16SKJWrMn2xshlj
175Fy9erK2t9fPz0+v1CKGmpqY9e/YsWrQoKCiIUmowGMaNG1dcXHzmzJlJkyYRQoxGI3Kecuc9lkfA
176jZ4nrHY0PDx8+fLl3377rcPhMBgM4HUdqNNKnISgKCwszO8vf7n9179Oe++9vPXr86qr6wEAQC8E
177XbdOse3sdcI9KU4HdQBARMSARx8dt2LFhOhoC2PcRdcUgyopKdm5c6fVahWCbfHixampqeJXNW1L
178WPx/+tOfrl+/vq6uLjw8XCj+XmoL9DIBtNSOj49ftmyZOgzBSbyXOVBrjgosRET4v/TSjDVrpmRm
179nvvoo5P795e1tVkBAEAHIKk4UvPcPaFbcA6V0XGuUKoAcJMpYNq05IcfHn333UNFlqTgOcLCIxoU
1806M7Pz//kk09mzJiRnp5OCMnMzBTFZgIDA+Pi4r766qvhw4cTQhRFAYCgoCBCiN1uB429wUffU68d
1816KyuXK28cr/i4XEQfFk9XlkMoLraundvyZ49JTk5FysrmwDEKWDCQyk+1zXpNHIw50ds9PRRUUFT
182pgyaNy9x7tzEmJiOoGj1CGn3GOnGxsa//OUv99xzT1pamjYmU8yn+vr6N998MyEh4cEHH9TpdAih
183L7/88uTJk7/97W99n/i9TACVDNfaRSgvLy8iIiI6OrrL7bg7ISnlCF07q6u9Xf7hh4a8vOrvv68r
184LKyvrLx66VKr1eqQZVlzJh4CwDqd5O+vHzjQLybGMmxYWGpq+PjxUcOGDTCZdFoFzNP5aoKlZGdn
185Hzhw4He/+506lxFCLS0ttbW1JpMpOjq6srLygw8+UBRlxIgRjY2NFy9efOSRR4YMGeLLIeIu0Jtn
186yrsYab///vtt27YtW7YsJSVFXQq+tAAA6lmaooSM0SiNGRMxenQ4dIh93txsa262NzfbbDZFVKrQ
1876bDRKAUGGi0WQ1CQ0WVqi7P7xKmFWut8px0wGAytra1NTU2hoaGKopSXlx85cqS4uNhms1FKp0yZ
188snjx4meeeSY3N/f8+fMhISH33nvvwIEDuQ8ZXZ0MuRdXgArq8L744ouvvvrqjjvumD17ttejNzy1
189I8JAROHBDtYv+IYXh6jTRX7tLFRN8lAXJdWdC679jTfeYIwlJiaWl5c3NDRERUVNmDBhyJAhZWVl
19027dv/8UvfjF27NgunS39QwAt98cYnzlzZvPmzUuWLBk1apSWn/asu2pvPVVkVaN3tP92t32EUHV1
191dWZmZnNzc0JCwsSJE0U0lfhp3bp1gYGBS5YsURRF3eX2gPvfLAK406ClpcVgMOh0Og361KolXWvK
192fQlaa4/LF+HVkiTp7bffjo6OzsjIELLtBvvfwyPNvYM6u4Uyqk2yFIYUdffgyX7Xl6BqONq9K3cm
1931MmyzJ1nF0qSdOjQocrKysmTJ4NTON/g7OlNIawFtVtaHU5c+eijjzDGGRkZAwYM8FE43yTQmnVB
194M+XVBVpXV/fBBx/Mnj07NTX16tWr+/bty8vLe+CBByIjIz2dpNZtRPXZ7FOXc2lp6RdffFFRUTF2
1957NhZs2aJBNjr+tQj8dDdzqjTXFWRtdtGZ2CHsmvXrtzcXJPJpChKWFhYRkZGbGyslwOsuwt9vfxV
196Mpw9e3bHjh1JSUmLFi1y2eyoJtxep4SLyFH/LS8vz8zMHD16dHp6urtuc+nSpbq6uuDg4KioKME5
197u9xa3ooEUMejVmJUFEVRFJEuK8Zjs9lUY1ZH/9yQ1bP3goa0Ku7sdntOTk5+fn59fX1CQsIdd9wR
198FxenfbX7svDdyuYj3CwZ4A7qNk0MQARTqmfNAYDNZlu7dq3FYpkwYUJSUpI4ckKrh2hnnIvBw9O7
199tPeD2ykuIm8rMTHxoYceEjsp7SMuEkIVxb27KPtHA3HX9gTDPXv27MmTJ8+fP2+1WtPS0jIyMnqw
200uXdRIgU0NzdXVlYWFhaOHz8+ISFBZXoqu+uyQupNgr5bAVpwd2oCgCRJw4YNGz58uKIo586dcxED
201R44cqampGTRoUGBgoMViCQ4OFhsLLaIZY4qiUEpFjSN1J7hjxw5ZlgkhgYGBqampLj1RVaA+EP6d
202oKJ/dXABWg4LTkah5d0iSe3YsWMOh8Nms8myvHLlyujoaDGR29vb169f39LSIqwI4eHhK1euBKdh
203ubq6uqioaMiQIZGRkULegJvZqh93grcEAQRop7N2q6xlVoyx1tZWq9U6YMAAbSDU8ePHEULiANOg
204oKDY2FithHCRFv0y0z3BLUQAT6C6d7TaIfiAR5c9bZcBA/0C/wEEEKDtZ6duHy1a3Wtk37LwH0OA
205/1fhphjjfgTf4f8C4VLHz/5KLxoAAAA8dEVYdGNvbW1lbnQAIEltYWdlIGdlbmVyYXRlZCBieSBH
206TlUgR2hvc3RzY3JpcHQgKGRldmljZT1wbm1yYXcpCvqLFvMAAAAASUVORK5CYII=
diff --git a/gem/input.bin b/gem/input.bin
deleted file mode 100644
index d24a954..0000000
--- a/gem/input.bin
+++ /dev/null
Binary files differ
diff --git a/gem/ltn012.tex b/gem/ltn012.tex
deleted file mode 100644
index 8027ecc..0000000
--- a/gem/ltn012.tex
+++ /dev/null
@@ -1,695 +0,0 @@
1\documentclass[10pt]{article}
2\usepackage{fancyvrb}
3\usepackage{url}
4\DefineVerbatimEnvironment{lua}{Verbatim}{fontsize=\small,commandchars=\@\#\%}
5\DefineVerbatimEnvironment{C}{Verbatim}{fontsize=\small,commandchars=\@\#\%}
6\DefineVerbatimEnvironment{mime}{Verbatim}{fontsize=\small,commandchars=\$\#\%}
7\newcommand{\stick}[1]{\vbox{\setlength{\parskip}{0pt}#1}}
8\newcommand{\bl}{\ensuremath{\mathtt{\backslash}}}
9\newcommand{\CR}{\texttt{CR}}
10\newcommand{\LF}{\texttt{LF}}
11\newcommand{\CRLF}{\texttt{CR~LF}}
12\newcommand{\nil}{\texttt{nil}}
13
14\title{Filters, sources, sinks, and pumps\\
15 {\large or Functional programming for the rest of us}}
16\author{Diego Nehab}
17
18\begin{document}
19
20\maketitle
21
22\begin{abstract}
23Certain data processing operations can be implemented in the
24form of filters. A filter is a function that can process
25data received in consecutive invocations, returning partial
26results each time it is called. Examples of operations that
27can be implemented as filters include the end-of-line
28normalization for text, Base64 and Quoted-Printable transfer
29content encodings, the breaking of text into lines, SMTP
30dot-stuffing, and there are many others. Filters become
31even more powerful when we allow them to be chained together
32to create composite filters. In this context, filters can be
33seen as the internal links in a chain of data transformations.
34Sources and sinks are the corresponding end points in these
35chains. A source is a function that produces data, chunk by
36chunk, and a sink is a function that takes data, chunk by
37chunk. Finally, pumps are procedures that actively drive
38data from a source to a sink, and indirectly through all
39intervening filters. In this article, we describe the design of an
40elegant interface for filters, sources, sinks, chains, and
41pumps, and we illustrate each step with concrete examples.
42\end{abstract}
43
44\section{Introduction}
45
46Within the realm of networking applications, we are often
47required to apply transformations to streams of data. Examples
48include the end-of-line normalization for text, Base64 and
49Quoted-Printable transfer content encodings, breaking text
50into lines with a maximum number of columns, SMTP
51dot-stuffing, \texttt{gzip} compression, HTTP chunked
52transfer coding, and the list goes on.
53
54Many complex tasks require a combination of two or more such
55transformations, and therefore a general mechanism for
56promoting reuse is desirable. In the process of designing
57\texttt{LuaSocket~2.0}, we repeatedly faced this problem.
58The solution we reached proved to be very general and
59convenient. It is based on the concepts of filters, sources,
60sinks, and pumps, which we introduce below.
61
62\emph{Filters} are functions that can be repeatedly invoked
63with chunks of input, successively returning processed
64chunks of output. Naturally, the result of
65concatenating all the output chunks must be the same as the
66result of applying the filter to the concatenation of all
67input chunks. In fancier language, filters \emph{commute}
68with the concatenation operator. More importantly, filters
69must handle input data correctly no matter how the stream
70has been split into chunks.
71
72A \emph{chain} is a function that transparently combines the
73effect of one or more filters. The interface of a chain is
74indistinguishable from the interface of its component
75filters. This allows a chained filter to be used wherever
76an atomic filter is accepted. In particular, chains can be
77themselves chained to create arbitrarily complex operations.
78
79Filters can be seen as internal nodes in a network through
80which data will flow, potentially being transformed many
81times along the way. Chains connect these nodes together.
82The initial and final nodes of the network are
83\emph{sources} and \emph{sinks}, respectively. Less
84abstractly, a source is a function that produces new chunks
85of data every time it is invoked. Conversely, sinks are
86functions that give a final destination to the chunks of
87data they receive in sucessive calls. Naturally, sources
88and sinks can also be chained with filters to produce
89filtered sources and sinks.
90
91Finally, filters, chains, sources, and sinks are all passive
92entities: they must be repeatedly invoked in order for
93anything to happen. \emph{Pumps} provide the driving force
94that pushes data through the network, from a source to a
95sink, and indirectly through all intervening filters.
96
97In the following sections, we start with a simplified
98interface, which we later refine. The evolution we present
99is not contrived: it recreates the steps we ourselves
100followed as we consolidated our understanding of these
101concepts within our application domain.
102
103\subsection{A simple example}
104
105The end-of-line normalization of text is a good
106example to motivate our initial filter interface.
107Assume we are given text in an unknown end-of-line
108convention (including possibly mixed conventions) out of the
109commonly found Unix (\LF), Mac OS (\CR), and
110DOS (\CRLF) conventions. We would like to be able to
111use the folowing code to normalize the end-of-line markers:
112\begin{quote}
113\begin{lua}
114@stick#
115local CRLF = "\013\010"
116local input = source.chain(source.file(io.stdin), normalize(CRLF))
117local output = sink.file(io.stdout)
118pump.all(input, output)
119%
120\end{lua}
121\end{quote}
122
123This program should read data from the standard input stream
124and normalize the end-of-line markers to the canonic
125\CRLF\ marker, as defined by the MIME standard.
126Finally, the normalized text should be sent to the standard output
127stream. We use a \emph{file source} that produces data from
128standard input, and chain it with a filter that normalizes
129the data. The pump then repeatedly obtains data from the
130source, and passes it to the \emph{file sink}, which sends
131it to the standard output.
132
133In the code above, the \texttt{normalize} \emph{factory} is a
134function that creates our normalization filter, which
135replaces any end-of-line marker with the canonic marker.
136The initial filter interface is
137trivial: a filter function receives a chunk of input data,
138and returns a chunk of processed data. When there are no
139more input data left, the caller notifies the filter by invoking
140it with a \nil\ chunk. The filter responds by returning
141the final chunk of processed data (which could of course be
142the empty string).
143
144Although the interface is extremely simple, the
145implementation is not so obvious. A normalization filter
146respecting this interface needs to keep some kind of context
147between calls. This is because a chunk boundary may lie between
148the \CR\ and \LF\ characters marking the end of a single line. This
149need for contextual storage motivates the use of
150factories: each time the factory is invoked, it returns a
151filter with its own context so that we can have several
152independent filters being used at the same time. For
153efficiency reasons, we must avoid the obvious solution of
154concatenating all the input into the context before
155producing any output chunks.
156
157To that end, we break the implementation into two parts:
158a low-level filter, and a factory of high-level filters. The
159low-level filter is implemented in C and does not maintain
160any context between function calls. The high-level filter
161factory, implemented in Lua, creates and returns a
162high-level filter that maintains whatever context the low-level
163filter needs, but isolates the user from its internal
164details. That way, we take advantage of C's efficiency to
165perform the hard work, and take advantage of Lua's
166simplicity for the bookkeeping.
167
168\subsection{The Lua part of the filter}
169
170Below is the complete implementation of the factory of high-level
171end-of-line normalization filters:
172\begin{quote}
173\begin{lua}
174@stick#
175function filter.cycle(lowlevel, context, extra)
176 return function(chunk)
177 local ret
178 ret, context = lowlevel(context, chunk, extra)
179 return ret
180 end
181end
182%
183
184@stick#
185function normalize(marker)
186 return filter.cycle(eol, 0, marker)
187end
188%
189\end{lua}
190\end{quote}
191
192The \texttt{normalize} factory simply calls a more generic
193factory, the \texttt{cycle}~factory, passing the low-level
194filter~\texttt{eol}. The \texttt{cycle}~factory receives a
195low-level filter, an initial context, and an extra
196parameter, and returns a new high-level filter. Each time
197the high-level filer is passed a new chunk, it invokes the
198low-level filter with the previous context, the new chunk,
199and the extra argument. It is the low-level filter that
200does all the work, producing the chunk of processed data and
201a new context. The high-level filter then replaces its
202internal context, and returns the processed chunk of data to
203the user. Notice that we take advantage of Lua's lexical
204scoping to store the context in a closure between function
205calls.
206
207\subsection{The C part of the filter}
208
209As for the low-level filter, we must first accept
210that there is no perfect solution to the end-of-line marker
211normalization problem. The difficulty comes from an
212inherent ambiguity in the definition of empty lines within
213mixed input. However, the following solution works well for
214any consistent input, as well as for non-empty lines in
215mixed input. It also does a reasonable job with empty lines
216and serves as a good example of how to implement a low-level
217filter.
218
219The idea is to consider both \CR\ and~\LF\ as end-of-line
220\emph{candidates}. We issue a single break if any candidate
221is seen alone, or if it is followed by a different
222candidate. In other words, \CR~\CR~and \LF~\LF\ each issue
223two end-of-line markers, whereas \CR~\LF~and \LF~\CR\ issue
224only one marker each. It is easy to see that this method
225correctly handles the most common end-of-line conventions.
226
227With this in mind, we divide the low-level filter into two
228simple functions. The inner function~\texttt{pushchar} performs the
229normalization itself. It takes each input character in turn,
230deciding what to output and how to modify the context. The
231context tells if the last processed character was an
232end-of-line candidate, and if so, which candidate it was.
233For efficiency, we use Lua's auxiliary library's buffer
234interface:
235\begin{quote}
236\begin{C}
237@stick#
238@#define candidate(c) (c == CR || c == LF)
239static int pushchar(int c, int last, const char *marker,
240 luaL_Buffer *buffer) {
241 if (candidate(c)) {
242 if (candidate(last)) {
243 if (c == last)
244 luaL_addstring(buffer, marker);
245 return 0;
246 } else {
247 luaL_addstring(buffer, marker);
248 return c;
249 }
250 } else {
251 luaL_pushchar(buffer, c);
252 return 0;
253 }
254}
255%
256\end{C}
257\end{quote}
258
259The outer function~\texttt{eol} simply interfaces with Lua.
260It receives the context and input chunk (as well as an
261optional custom end-of-line marker), and returns the
262transformed output chunk and the new context.
263Notice that if the input chunk is \nil, the operation
264is considered to be finished. In that case, the loop will
265not execute a single time and the context is reset to the
266initial state. This allows the filter to be reused many
267times:
268\begin{quote}
269\begin{C}
270@stick#
271static int eol(lua_State *L) {
272 int context = luaL_checkint(L, 1);
273 size_t isize = 0;
274 const char *input = luaL_optlstring(L, 2, NULL, &isize);
275 const char *last = input + isize;
276 const char *marker = luaL_optstring(L, 3, CRLF);
277 luaL_Buffer buffer;
278 luaL_buffinit(L, &buffer);
279 if (!input) {
280 lua_pushnil(L);
281 lua_pushnumber(L, 0);
282 return 2;
283 }
284 while (input < last)
285 context = pushchar(*input++, context, marker, &buffer);
286 luaL_pushresult(&buffer);
287 lua_pushnumber(L, context);
288 return 2;
289}
290%
291\end{C}
292\end{quote}
293
294When designing filters, the challenging part is usually
295deciding what to store in the context. For line breaking, for
296instance, it could be the number of bytes that still fit in the
297current line. For Base64 encoding, it could be a string
298with the bytes that remain after the division of the input
299into 3-byte atoms. The MIME module in the \texttt{LuaSocket}
300distribution has many other examples.
301
302\section{Filter chains}
303
304Chains greatly increase the power of filters. For example,
305according to the standard for Quoted-Printable encoding,
306text should be normalized to a canonic end-of-line marker
307prior to encoding. After encoding, the resulting text must
308be broken into lines of no more than 76 characters, with the
309use of soft line breaks (a line terminated by the \texttt{=}
310sign). To help specifying complex transformations like
311this, we define a chain factory that creates a composite
312filter from one or more filters. A chained filter passes
313data through all its components, and can be used wherever a
314primitive filter is accepted.
315
316The chaining factory is very simple. The auxiliary
317function~\texttt{chainpair} chains two filters together,
318taking special care if the chunk is the last. This is
319because the final \nil\ chunk notification has to be
320pushed through both filters in turn:
321\begin{quote}
322\begin{lua}
323@stick#
324local function chainpair(f1, f2)
325 return function(chunk)
326 local ret = f2(f1(chunk))
327 if chunk then return ret
328 else return ret .. f2() end
329 end
330end
331%
332
333@stick#
334function filter.chain(...)
335 local f = select(1, ...)
336 for i = 2, select('@#', ...) do
337 f = chainpair(f, select(i, ...))
338 end
339 return f
340end
341%
342\end{lua}
343\end{quote}
344
345Thanks to the chain factory, we can
346define the Quoted-Printable conversion as such:
347\begin{quote}
348\begin{lua}
349@stick#
350local qp = filter.chain(normalize(CRLF), encode("quoted-printable"),
351 wrap("quoted-printable"))
352local input = source.chain(source.file(io.stdin), qp)
353local output = sink.file(io.stdout)
354pump.all(input, output)
355%
356\end{lua}
357\end{quote}
358
359\section{Sources, sinks, and pumps}
360
361The filters we introduced so far act as the internal nodes
362in a network of transformations. Information flows from node
363to node (or rather from one filter to the next) and is
364transformed along the way. Chaining filters together is our
365way to connect nodes in this network. As the starting point
366for the network, we need a source node that produces the
367data. In the end of the network, we need a sink node that
368gives a final destination to the data.
369
370\subsection{Sources}
371
372A source returns the next chunk of data each time it is
373invoked. When there is no more data, it simply returns~\nil.
374In the event of an error, the source can inform the
375caller by returning \nil\ followed by the error message.
376
377Below are two simple source factories. The \texttt{empty} source
378returns no data, possibly returning an associated error
379message. The \texttt{file} source yields the contents of a file
380in a chunk by chunk fashion:
381\begin{quote}
382\begin{lua}
383@stick#
384function source.empty(err)
385 return function()
386 return nil, err
387 end
388end
389%
390
391@stick#
392function source.file(handle, io_err)
393 if handle then
394 return function()
395 local chunk = handle:read(2048)
396 if not chunk then handle:close() end
397 return chunk
398 end
399 else return source.empty(io_err or "unable to open file") end
400end
401%
402\end{lua}
403\end{quote}
404
405\subsection{Filtered sources}
406
407A filtered source passes its data through the
408associated filter before returning it to the caller.
409Filtered sources are useful when working with
410functions that get their input data from a source (such as
411the pumps in our examples). By chaining a source with one or
412more filters, such functions can be transparently provided
413with filtered data, with no need to change their interfaces.
414Here is a factory that does the job:
415\begin{quote}
416\begin{lua}
417@stick#
418function source.chain(src, f)
419 return function()
420 if not src then
421 return nil
422 end
423 local chunk, err = src()
424 if not chunk then
425 src = nil
426 return f(nil)
427 else
428 return f(chunk)
429 end
430 end
431end
432%
433\end{lua}
434\end{quote}
435
436\subsection{Sinks}
437
438Just as we defined an interface for a source of data, we can
439also define an interface for a data destination. We call
440any function respecting this interface a sink. In our first
441example, we used a file sink connected to the standard
442output.
443
444Sinks receive consecutive chunks of data, until the end of
445data is signaled by a \nil\ input chunk. A sink can be
446notified of an error with an optional extra argument that
447contains the error message, following a \nil\ chunk.
448If a sink detects an error itself, and
449wishes not to be called again, it can return \nil,
450followed by an error message. A return value that
451is not \nil\ means the sink will accept more data.
452
453Below are two useful sink factories.
454The table factory creates a sink that stores
455individual chunks into an array. The data can later be
456efficiently concatenated into a single string with Lua's
457\texttt{table.concat} library function. The \texttt{null} sink
458simply discards the chunks it receives:
459\begin{quote}
460\begin{lua}
461@stick#
462function sink.table(t)
463 t = t or {}
464 local f = function(chunk, err)
465 if chunk then table.insert(t, chunk) end
466 return 1
467 end
468 return f, t
469end
470%
471
472@stick#
473local function null()
474 return 1
475end
476
477function sink.null()
478 return null
479end
480%
481\end{lua}
482\end{quote}
483
484Naturally, filtered sinks are just as useful as filtered
485sources. A filtered sink passes each chunk it receives
486through the associated filter before handing it down to the
487original sink. In the following example, we use a source
488that reads from the standard input. The input chunks are
489sent to a table sink, which has been coupled with a
490normalization filter. The filtered chunks are then
491concatenated from the output array, and finally sent to
492standard out:
493\begin{quote}
494\begin{lua}
495@stick#
496local input = source.file(io.stdin)
497local output, t = sink.table()
498output = sink.chain(normalize(CRLF), output)
499pump.all(input, output)
500io.write(table.concat(t))
501%
502\end{lua}
503\end{quote}
504
505\subsection{Pumps}
506
507Although not on purpose, our interface for sources is
508compatible with Lua iterators. That is, a source can be
509neatly used in conjunction with \texttt{for} loops. Using
510our file source as an iterator, we can write the following
511code:
512\begin{quote}
513\begin{lua}
514@stick#
515for chunk in source.file(io.stdin) do
516 io.write(chunk)
517end
518%
519\end{lua}
520\end{quote}
521
522Loops like this will always be present because everything
523we designed so far is passive. Sources, sinks, filters: none
524of them can do anything on their own. The operation of
525pumping all data a source can provide into a sink is so
526common that it deserves its own function:
527\begin{quote}
528\begin{lua}
529@stick#
530function pump.step(src, snk)
531 local chunk, src_err = src()
532 local ret, snk_err = snk(chunk, src_err)
533 if chunk and ret then return 1
534 else return nil, src_err or snk_err end
535end
536%
537
538@stick#
539function pump.all(src, snk, step)
540 step = step or pump.step
541 while true do
542 local ret, err = step(src, snk)
543 if not ret then
544 if err then return nil, err
545 else return 1 end
546 end
547 end
548end
549%
550\end{lua}
551\end{quote}
552
553The \texttt{pump.step} function moves one chunk of data from
554the source to the sink. The \texttt{pump.all} function takes
555an optional \texttt{step} function and uses it to pump all the
556data from the source to the sink.
557Here is an example that uses the Base64 and the
558line wrapping filters from the \texttt{LuaSocket}
559distribution. The program reads a binary file from
560disk and stores it in another file, after encoding it to the
561Base64 transfer content encoding:
562\begin{quote}
563\begin{lua}
564@stick#
565local input = source.chain(
566 source.file(io.open("input.bin", "rb")),
567 encode("base64"))
568local output = sink.chain(
569 wrap(76),
570 sink.file(io.open("output.b64", "w")))
571pump.all(input, output)
572%
573\end{lua}
574\end{quote}
575
576The way we split the filters here is not intuitive, on
577purpose. Alternatively, we could have chained the Base64
578encode filter and the line-wrap filter together, and then
579chain the resulting filter with either the file source or
580the file sink. It doesn't really matter.
581
582\section{Exploding filters}
583
584Our current filter interface has one serious shortcoming.
585Consider for example a \texttt{gzip} decompression filter.
586During decompression, a small input chunk can be exploded
587into a huge amount of data. To address this problem, we
588decided to change the filter interface and allow exploding
589filters to return large quantities of output data in a chunk
590by chunk manner.
591
592More specifically, after passing each chunk of input to
593a filter, and collecting the first chunk of output, the
594user must now loop to receive other chunks from the filter until no
595filtered data is left. Within these secondary calls, the
596caller passes an empty string to the filter. The filter
597responds with an empty string when it is ready for the next
598input chunk. In the end, after the user passes a
599\nil\ chunk notifying the filter that there is no
600more input data, the filter might still have to produce too
601much output data to return in a single chunk. The user has
602to loop again, now passing \nil\ to the filter each time,
603until the filter itself returns \nil\ to notify the
604user it is finally done.
605
606Fortunately, it is very easy to modify a filter to respect
607the new interface. In fact, the end-of-line translation
608filter we presented earlier already conforms to it. The
609complexity is encapsulated within the chaining functions,
610which must now include a loop. Since these functions only
611have to be written once, the user is rarely affected.
612Interestingly, the modifications do not have a measurable
613negative impact in the performance of filters that do
614not need the added flexibility. On the other hand, for a
615small price in complexity, the changes make exploding
616filters practical.
617
618\section{A complex example}
619
620The LTN12 module in the \texttt{LuaSocket} distribution
621implements all the ideas we have described. The MIME
622and SMTP modules are tightly integrated with LTN12,
623and can be used to showcase the expressive power of filters,
624sources, sinks, and pumps. Below is an example
625of how a user would proceed to define and send a
626multipart message, with attachments, using \texttt{LuaSocket}:
627\begin{quote}
628\begin{mime}
629local smtp = require"socket.smtp"
630local mime = require"mime"
631local ltn12 = require"ltn12"
632
633local message = smtp.message{
634 headers = {
635 from = "Sicrano <sicrano@example.com>",
636 to = "Fulano <fulano@example.com>",
637 subject = "A message with an attachment"},
638 body = {
639 preamble = "Hope you can see the attachment" .. CRLF,
640 [1] = {
641 body = "Here is our logo" .. CRLF},
642 [2] = {
643 headers = {
644 ["content-type"] = 'image/png; name="luasocket.png"',
645 ["content-disposition"] =
646 'attachment; filename="luasocket.png"',
647 ["content-description"] = 'LuaSocket logo',
648 ["content-transfer-encoding"] = "BASE64"},
649 body = ltn12.source.chain(
650 ltn12.source.file(io.open("luasocket.png", "rb")),
651 ltn12.filter.chain(
652 mime.encode("base64"),
653 mime.wrap()))}}}
654
655assert(smtp.send{
656 rcpt = "<fulano@example.com>",
657 from = "<sicrano@example.com>",
658 source = message})
659\end{mime}
660\end{quote}
661
662The \texttt{smtp.message} function receives a table
663describing the message, and returns a source. The
664\texttt{smtp.send} function takes this source, chains it with the
665SMTP dot-stuffing filter, connects a socket sink
666with the server, and simply pumps the data. The message is never
667assembled in memory. Everything is produced on demand,
668transformed in small pieces, and sent to the server in chunks,
669including the file attachment which is loaded from disk and
670encoded on the fly. It just works.
671
672\section{Conclusions}
673
674In this article, we introduced the concepts of filters,
675sources, sinks, and pumps to the Lua language. These are
676useful tools for stream processing in general. Sources provide
677a simple abstraction for data acquisition. Sinks provide an
678abstraction for final data destinations. Filters define an
679interface for data transformations. The chaining of
680filters, sources and sinks provides an elegant way to create
681arbitrarily complex data transformations from simpler
682components. Pumps simply push the data through.
683
684\section{Acknowledgements}
685
686The concepts described in this text are the result of long
687discussions with David Burgess. A version of this text has
688been released on-line as the Lua Technical Note 012, hence
689the name of the corresponding LuaSocket module, LTN12. Wim
690Couwenberg contributed to the implementation of the module,
691and Adrian Sietsma was the first to notice the
692correspondence between sources and Lua iterators.
693
694
695\end{document}
diff --git a/gem/luasocket.png b/gem/luasocket.png
deleted file mode 100644
index d24a954..0000000
--- a/gem/luasocket.png
+++ /dev/null
Binary files differ
diff --git a/gem/makefile b/gem/makefile
deleted file mode 100644
index a4287c2..0000000
--- a/gem/makefile
+++ /dev/null
@@ -1,14 +0,0 @@
1ltn012.pdf: ltn012.ps
2 ./myps2pdf ltn012.ps
3
4ltn012.ps: ltn012.dvi
5 dvips -G0 -t letter -o ltn012.ps ltn012.dvi
6
7ltn012.dvi: ltn012.tex
8 latex ltn012
9
10clean:
11 rm -f *~ *.log *.aux *.bbl *.blg ltn012.pdf ltn012.ps ltn012.dvi ltn012.lof ltn012.toc ltn012.lot
12
13pdf: ltn012.pdf
14 open ltn012.pdf
diff --git a/gem/myps2pdf b/gem/myps2pdf
deleted file mode 100755
index 78c23e5..0000000
--- a/gem/myps2pdf
+++ /dev/null
@@ -1,113 +0,0 @@
1#!/bin/sh -
2do_opt=1
3best=0
4rot=0
5a4=0
6eps=0
7usage="Usage: $0 [-no_opt] [-best] [-rot] [-a4] [-eps] in.ps [out.pdf]"
8
9case "x$1" in
10"x-no_opt") do_opt=0 ; shift ;;
11esac
12
13case "x$1" in
14"x-best") best=1 ; shift ;;
15esac
16
17case "x$1" in
18"x-rot") rot=1 ; shift ;;
19esac
20
21case "x$1" in
22"x-a4") a4=1 ; shift ;;
23esac
24
25case "x$1" in
26"x-eps") eps=1 ; shift ;;
27esac
28
29case $# in
302) ifilename=$1 ; ofilename=$2 ;;
311) ifilename=$1
32 if `echo $1 | grep -i '\.e*ps$' > /dev/null`
33 then
34 ofilename=`echo $1 | sed 's/\..*$/.pdf/'`
35 else
36 echo "$usage" 1>&2
37 exit 1
38 fi ;;
39*) echo "$usage" 1>&2 ; exit 1 ;;
40esac
41
42if [ $best == 1 ]
43then
44 options="-dPDFSETTINGS=/prepress \
45 -r1200 \
46 -dMonoImageResolution=1200 \
47 -dGrayImageResolution=1200 \
48 -dColorImageResolution=1200 \
49 -dDownsampleMonoImages=false \
50 -dDownsampleGrayImages=false \
51 -dDownsampleColorImages=false \
52 -dAutoFilterMonoImages=false \
53 -dAutoFilterGrayImages=false \
54 -dAutoFilterColorImages=false \
55 -dMonoImageFilter=/FlateEncode \
56 -dGrayImageFilter=/FlateEncode \
57 -dColorImageFilter=/FlateEncode"
58else
59 options="-dPDFSETTINGS=/prepress \
60 -r600 \
61 -dDownsampleMonoImages=true \
62 -dDownsampleGrayImages=true \
63 -dDownsampleColorImages=true \
64 -dMonoImageDownsampleThreshold=2.0 \
65 -dGrayImageDownsampleThreshold=1.5 \
66 -dColorImageDownsampleThreshold=1.5 \
67 -dMonoImageResolution=600 \
68 -dGrayImageResolution=600 \
69 -dColorImageResolution=600 \
70 -dAutoFilterMonoImages=false \
71 -dMonoImageFilter=/FlateEncode \
72 -dAutoFilterGrayImages=true \
73 -dAutoFilterColorImages=true"
74fi
75
76if [ $rot == 1 ]
77then
78 options="$options -dAutoRotatePages=/PageByPage"
79fi
80
81if [ $eps == 1 ]
82then
83 options="$options -dEPSCrop"
84fi
85
86set -x
87
88if [ $a4 == 1 ]
89then
90 # Resize from A4 to letter size
91 psresize -Pa4 -pletter "$ifilename" myps2pdf.temp.ps
92 ifilename=myps2pdf.temp.ps
93fi
94
95gs -q -dSAFER -dNOPAUSE -dBATCH \
96 -sDEVICE=pdfwrite -sPAPERSIZE=letter -sOutputFile=myps2pdf.temp.pdf \
97 -dCompatibilityLevel=1.3 \
98 $options \
99 -dMaxSubsetPct=100 \
100 -dSubsetFonts=true \
101 -dEmbedAllFonts=true \
102 -dColorConversionStrategy=/LeaveColorUnchanged \
103 -dDoThumbnails=true \
104 -dPreserveEPSInfo=true \
105 -c .setpdfwrite -f "$ifilename"
106
107if [ $do_opt == 1 ]
108then
109 pdfopt myps2pdf.temp.pdf $ofilename
110else
111 mv myps2pdf.temp.pdf $ofilename
112fi
113rm -f myps2pdf.temp.pdf myps2pdf.temp.ps
diff --git a/gem/t1.lua b/gem/t1.lua
deleted file mode 100644
index 0c054c9..0000000
--- a/gem/t1.lua
+++ /dev/null
@@ -1,25 +0,0 @@
1source = {}
2sink = {}
3pump = {}
4filter = {}
5
6-- source.chain
7dofile("ex6.lua")
8
9-- source.file
10dofile("ex5.lua")
11
12-- normalize
13require"gem"
14eol = gem.eol
15dofile("ex2.lua")
16
17-- sink.file
18require"ltn12"
19sink.file = ltn12.sink.file
20
21-- pump.all
22dofile("ex10.lua")
23
24-- run test
25dofile("ex1.lua")
diff --git a/gem/t1lf.txt b/gem/t1lf.txt
deleted file mode 100644
index 8cddd1b..0000000
--- a/gem/t1lf.txt
+++ /dev/null
@@ -1,5 +0,0 @@
1this is a test file
2it should have been saved as lf eol
3but t1.lua will convert it to crlf eol
4otherwise it is broken!
5
diff --git a/gem/t2.lua b/gem/t2.lua
deleted file mode 100644
index a81ed73..0000000
--- a/gem/t2.lua
+++ /dev/null
@@ -1,36 +0,0 @@
1source = {}
2sink = {}
3pump = {}
4filter = {}
5
6-- filter.chain
7dofile("ex3.lua")
8
9-- normalize
10require"gem"
11eol = gem.eol
12dofile("ex2.lua")
13
14-- encode
15require"mime"
16encode = mime.encode
17
18-- wrap
19wrap = mime.wrap
20
21-- source.chain
22dofile("ex6.lua")
23
24-- source.file
25dofile("ex5.lua")
26
27-- sink.file
28require"ltn12"
29sink.file = ltn12.sink.file
30
31-- pump.all
32dofile("ex10.lua")
33
34-- run test
35CRLF = "\013\010"
36dofile("ex4.lua")
diff --git a/gem/t2.txt b/gem/t2.txt
deleted file mode 100644
index f484fe8..0000000
--- a/gem/t2.txt
+++ /dev/null
@@ -1,4 +0,0 @@
1esse é um texto com acentos
2quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de texto
3fora que as quebras de linhas têm que ser normalizadas
4vamos ver o que dá isso aqui
diff --git a/gem/t2gt.qp b/gem/t2gt.qp
deleted file mode 100644
index 355a845..0000000
--- a/gem/t2gt.qp
+++ /dev/null
@@ -1,5 +0,0 @@
1esse =E9 um texto com acentos
2quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de t=
3exto
4fora que as quebras de linhas t=EAm que ser normalizadas
5vamos ver o que d=E1 isso aqui
diff --git a/gem/t3.lua b/gem/t3.lua
deleted file mode 100644
index 4bb98ba..0000000
--- a/gem/t3.lua
+++ /dev/null
@@ -1,25 +0,0 @@
1source = {}
2sink = {}
3pump = {}
4filter = {}
5
6-- source.file
7dofile("ex5.lua")
8
9-- sink.table
10dofile("ex7.lua")
11
12-- sink.chain
13require"ltn12"
14sink.chain = ltn12.sink.chain
15
16-- normalize
17require"gem"
18eol = gem.eol
19dofile("ex2.lua")
20
21-- pump.all
22dofile("ex10.lua")
23
24-- run test
25dofile("ex8.lua")
diff --git a/gem/t4.lua b/gem/t4.lua
deleted file mode 100644
index 8b8071c..0000000
--- a/gem/t4.lua
+++ /dev/null
@@ -1,10 +0,0 @@
1source = {}
2sink = {}
3pump = {}
4filter = {}
5
6-- source.file
7dofile("ex5.lua")
8
9-- run test
10dofile("ex9.lua")
diff --git a/gem/t5.lua b/gem/t5.lua
deleted file mode 100644
index 7c569ea..0000000
--- a/gem/t5.lua
+++ /dev/null
@@ -1,30 +0,0 @@
1source = {}
2sink = {}
3pump = {}
4filter = {}
5
6-- source.chain
7dofile("ex6.lua")
8
9-- source.file
10dofile("ex5.lua")
11
12-- encode
13require"mime"
14encode = mime.encode
15
16-- sink.chain
17require"ltn12"
18sink.chain = ltn12.sink.chain
19
20-- wrap
21wrap = mime.wrap
22
23-- sink.file
24sink.file = ltn12.sink.file
25
26-- pump.all
27dofile("ex10.lua")
28
29-- run test
30dofile("ex11.lua")
diff --git a/gem/test.lua b/gem/test.lua
deleted file mode 100644
index a937b9a..0000000
--- a/gem/test.lua
+++ /dev/null
@@ -1,46 +0,0 @@
1function readfile(n)
2 local f = io.open(n, "rb")
3 local s = f:read("*a")
4 f:close()
5 return s
6end
7
8lf = readfile("t1lf.txt")
9os.remove("t1crlf.txt")
10os.execute("lua t1.lua < t1lf.txt > t1crlf.txt")
11crlf = readfile("t1crlf.txt")
12assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken")
13
14gt = readfile("t2gt.qp")
15os.remove("t2.qp")
16os.execute("lua t2.lua < t2.txt > t2.qp")
17t2 = readfile("t2.qp")
18assert(gt == t2, "broken")
19
20os.remove("t1crlf.txt")
21os.execute("lua t3.lua < t1lf.txt > t1crlf.txt")
22crlf = readfile("t1crlf.txt")
23assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken")
24
25t = readfile("test.lua")
26os.execute("lua t4.lua < test.lua > t")
27t2 = readfile("t")
28assert(t == t2, "broken")
29
30os.remove("output.b64")
31gt = readfile("gt.b64")
32os.execute("lua t5.lua")
33t5 = readfile("output.b64")
34assert(gt == t5, "failed")
35
36print("1 2 5 6 10 passed")
37print("2 3 4 5 6 10 passed")
38print("2 5 6 7 8 10 passed")
39print("5 9 passed")
40print("5 6 10 11 passed")
41
42os.remove("t")
43os.remove("t2.qp")
44os.remove("t1crlf.txt")
45os.remove("t11.b64")
46os.remove("output.b64")