--[[ BonaLuna test and documentation generator Copyright (C) 2010-2011 Christophe Delord http://cdsoft.fr/bl/bonaluna.html BonaLuna is based on Lua 5.2 Copyright (C) 2010 Lua.org, PUC-Rio. Freely available under the terms of the Lua license. --]] do local bl_rst = "../doc/bonaluna.rst" function doc(txt) local f = assert(io.open(bl_rst, "a")) f:write(txt) f:write("\n") f:close() end os.remove(bl_rst) end function p(fmt, ...) io.stderr:write(string.format(fmt, ...).."\n") end BONALUNA_VERSION = assert(io.popen(arg[-1].." -v")):read("*l"):gsub("BonaLuna%s([%d%.]+).*", "%1") doc([[ .. BonaLuna .. Copyright (C) 2010-2011 Christophe Delord http://www.cdsoft.fr/bl/bonaluna.html .. BonaLuna is based on Lua 5.2 Copyright (C) 2010 Lua.org, PUC-Rio. .. Freely available under the terms of the Lua license. ================= |logo| BonaLuna ================= ------------------------- A compact Lua extension ------------------------- .. |logo| image:: bl.png .. |logo_lua| image:: http://www.andreas-rozek.de/Lua/Lua-Logo_64x64.png .. sidebar:: Based on `Lua 5.2 `__ |logo_lua| Copyright (C) 2010 `Lua.org `__, PUC-Rio. :Author: Christophe Delord :Contact: cdelord@cdsoft.fr :Web: http://cdsoft.fr/bl/bonaluna.html :License: | Copyright (C) 2010-2011 Christophe Delord, `CDSoft.fr `__ | Freely available under the terms of the `Lua license `__ | **Lua**: `Lua license `__ | **miniLZO**, **QuickLZ**: GPL v2 | **LZ4**: BSD | **libcurl**: `MIT/X derivate `__ :Download: http://cdsoft.fr/bl/bonaluna-]]..BONALUNA_VERSION..[[.tgz :Version: ]]..BONALUNA_VERSION..[[ :Abstract: BonaLuna is a Lua interpretor plus a few packages in a single executable. .. contents:: Table of Contents :depth: 2 .. sectnum:: :depth: 2 ]]) doc([[ Lua === The original Lua interpretor and documentation is available at http://www.lua.org. BonaLuna is based on `]].._VERSION..[[ `__. ]]) doc [[ Global functions ================ ]] doc [[ Iterators --------- **iter(sequence)** returns an iterator of `sequence` items (a table). **list(iterator)** returns a table of items generated by `iterator`. **reverse(iterator)** returns `iterator` in reverse order. **sort(iterator [, cmp])** returns `iterator` sorted using `cmp` (as `table.sort`) **map(f, ...)** applies `f` to iterators. **zip(...)** groups values of the same rank of several iterators. **filter(p, ...)** selects values according to the predicate `p`. **range(i [, j [, s] ])** returns the values of the range [`i`, `j`]. `s` is the step. `range(j)` is equivalent to `range(1, j)`. The default step is 1. **enum(iterator)** generates tuples `(i, x[i])` where `i` is the rank of the value `x[i]` for each value of `iterator`. **chain(...)** chains several iterators. ]] do -- iter local L1 = {"a", "b", "c"} local L2 = {} for x in iter(L1) do table.insert(L2, x) end assert(#L2 == 3) assert(L2[1] == L1[1]) assert(L2[2] == L1[2]) assert(L2[3] == L1[3]) -- list L2 = list(iter(L1)) assert(#L2 == 3) assert(L2[1] == L1[1]) assert(L2[2] == L1[2]) assert(L2[3] == L1[3]) -- reverse L2 = list(reverse(L1)) assert(#L2 == 3) assert(L2[1] == L1[3]) assert(L2[2] == L1[2]) assert(L2[3] == L1[1]) -- sort L2 = list(sort(reverse(L1))) assert(#L2 == 3) assert(L2[1] == L1[1]) assert(L2[2] == L1[2]) assert(L2[3] == L1[3]) -- map L2 = list(map(string.upper, L1)) assert(#L2 == 3) assert(L2[1] == L1[1]:upper()) assert(L2[2] == L1[2]:upper()) assert(L2[3] == L1[3]:upper()) -- zip for i, x, y in enum(zip(L1, {4, 5, 6})) do assert(x == L1[i] and y == i+3) end -- filter L2 = list(filter(function(x) return x:byte()%2==1 end, L1)) assert(#L2 == 2) assert(L2[1] == "a") assert(L2[2] == "c") -- range L2 = list(range(3, 10, 2)) assert(#L2 == 4) assert(L2[1] == 3) assert(L2[2] == 5) assert(L2[3] == 7) assert(L2[4] == 9) L2 = list(range(10, 3, -2)) assert(#L2 == 4) assert(L2[1] == 10) assert(L2[2] == 8) assert(L2[3] == 6) assert(L2[4] == 4) -- enum L2 = {} for i, x in enum(L1) do table.insert(L2, {i, x}) end assert(#L2 == 3) assert(L2[1][1] == 1 and L2[1][2] == "a") assert(L2[2][1] == 2 and L2[2][2] == "b") assert(L2[3][1] == 3 and L2[3][2] == "c") -- chain L2 = list(chain(L1, {"d", "e"})) assert(#L2 == 5) assert(L2[1] == "a") assert(L2[2] == "b") assert(L2[3] == "c") assert(L2[4] == "d") assert(L2[5] == "e") end doc [[ BonaLuna packages ================= Note about objects ------------------ Objects defined in these packages uses closures [#]_ to store internal data. No *self* is required and methods are called with a single dot (`.`), not with a colon (`:`). Example:: aes = crypt.AES("my key", 128) encrypted = aes.encrypt("some text") .. [#] See `Object Orientation Closure Approach `__. ]] doc [[ crypt: Cryptographic functions ------------------------------ The `crypt` package is a pure Lua package (i.e. not really fast). **crypt.hex.encode(data)** encodes `data` in hexa. **crypt.hex.decode(data)** decodes the hexa `data`. **crypt.base64.encode(data)** encodes `data` in base64. **crypt.base64.decode(data)** decodes the base64 `data`. **crypt.crc32(data)** computes the CRC32 of `data`. **crypt.shaXXX(data)** computes an SHA digest of `data`. `XXX` is 1, 224 or 256. **crypt.AES(password [,keylen [,mode] ])** returns an AES codec. `password` is the encryption/decryption key, `keylen` is the length of the key (128 (default), 192 or 256), `mode` is the encryption/decryption mode ("cbc" (default) or "ecb"). `crypt.AES` objects have two methods: `encrypt(data)` and `decrypt(data)`. **crypt.random(bits)** returns a string with `bits` random bits. ]] if crypt then local data = "The quick brown fox jumps over the lazy dog" -- hex assert(crypt.hex.encode(data) == "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67") assert(crypt.hex.decode("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67") == data) -- base64 assert(crypt.base64.encode(data) == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==") assert(crypt.base64.decode("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==") == data) -- crc32 assert(crypt.crc32(data) == 0x414FA339) -- sha1 assert(crypt.sha1(data) == "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12") -- sha224, sha256 assert(crypt.sha224(data) == "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525") assert(crypt.sha256(data) == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592") -- aes local keylen = {128, 192, 256} local method = {"ecb", "cbc"} for i = 1, #keylen+1 do for j = 1, #method+1 do if not (keylen[i] == nil and method[j] ~= nil) then local aes = crypt.AES("my key", keylen[i], method[j]) local aes2 = crypt.AES("your key", keylen[i], method[j]) assert(aes.encrypt(data) ~= data) assert(aes.decrypt(aes.encrypt(data)) == data) assert(aes2.encrypt(data) ~= data) assert(aes2.decrypt(aes2.encrypt(data)) == data) assert(aes2.encrypt(data) ~= aes.encrypt(data)) assert(not aes2.decrypt(aes.encrypt(data))) assert(not aes.decrypt(aes2.encrypt(data))) end end end -- random for size in iter{0, 8, 64, 128} do local r1 = crypt.random(size) local r2 = crypt.random(size) local r3 = crypt.random(size) assert(#r1 == math.max(size/8, 1)) assert(#r2 == math.max(size/8, 1)) assert(#r3 == math.max(size/8, 1)) assert(not (r1==r2 and r2==r3)) if z then -- random data shall not be compressible assert(#z.compress(r1) > #r1) assert(#z.compress(r2) > #r2) assert(#z.compress(r3) > #r3) end end end doc [[ curl: libcurl interface ----------------------- `libcurl `__ is multiprotocol file transfer library. This package is a simple Lua interface to libcurl. This package is based on `Lua-cURL `__ and provides the same API plus a few higher level objects. This package was introduced before `socket` which is based on `Lua Socket`. I recommend using `socket` instead of `curl`. **curl.FTP(url [, login, password])** creates an FTP object to connect to the FTP server at `url`. `login` and `password` are optional. Methods are: - `cd(path)` changes the *current working directory*. No connection is made, `path` is just stored internally for later connections. - `get(path)` retrieves `path`. - `put(path, data)` sends and stores the string `data` to the file `path`. - `rm(path)` deletes the file `path`. - `mkdir(path)` creates the directory `path`. - `rmdir(path)` deletes the directory `path`. - `list(path)` returns an iterator listing the directory `path`. FTP connections are made through the cURL easy interface, each request is in fact an entire connection (and deconnection). **curl.HTTP(url)** creates an HTTP object to connect to the HTTP server at `url`. Methods are: - `get(path)` retrieves `path`. - `save(path [, name])` retrieves `path` and saves it to `name`. The default value of `name` is the basename of `path`. ]] doc [[ fs: File System --------------- ]] doc [[ **fs.getcwd()** returns the current working directory. **fs.chdir(path)** changes the current directory to `path`. ]] function rm_rf(path) if fs.stat(path) then for name in reverse(fs.walk(path)) do assert(fs.remove(name)) end end end do rm_rf "foo" local old_path = assert(fs.getcwd()) assert(fs.mkdir "foo") assert(fs.chdir "foo") local new_path = assert(fs.getcwd()) assert(fs.chdir '..') assert(fs.getcwd() == old_path) assert(fs.chdir "foo") assert(fs.getcwd() == new_path) assert(fs.chdir(old_path)) assert(fs.getcwd() == old_path) rm_rf "foo" end doc [[ **fs.listdir([path])** returns the list of files and directories in `path` (the default path is the current directory). **fs.dir([path])** returns an iterator listing files and directories in `path` (the default path is the current directory). **fs.walk([path])** returns an iterator listing directory and file names in `path` and its subdirectories (the default path is the current directory). **fs.mkdir(path)** creates a new directory `path`. **fs.rename(old_name, new_name)** renames the file `old_name` to `new_name`. **fs.remove(name)** deletes the file `name`. ]] do rm_rf "foo" assert(fs.mkdir("foo")) io.open("foo/file1.c", "w"):close() assert(fs.mkdir("foo/bar")) io.open("foo/file2.lua", "w"):close() io.open("foo/bar/file3.lua", "w"):close() local function check_foo(dir, path) assert(#dir == 3) assert(dir[1]~=dir[2] and dir[1]~=dir[3] and dir[2]~=dir[3]) assert(dir[1]=="file1.c" or dir[1]=="file2.lua" or dir[1]=="bar", dir[1]) assert(dir[2]=="file1.c" or dir[2]=="file2.lua" or dir[2]=="bar", dir[2]) assert(dir[3]=="file1.c" or dir[3]=="file2.lua" or dir[3]=="bar", dir[3]) end local foo = assert(fs.listdir("foo")) check_foo(foo, "foo") foo = assert(list(fs.dir("foo"))) check_foo(foo, "foo") assert(fs.chdir("foo")) foo = assert(fs.listdir()) check_foo(foo, ".") foo = assert(list(fs.dir())) check_foo(foo, ".") assert(fs.chdir("..")) local function check_foo2(dir) assert(#dir == 2) assert(dir[1]~=dir[2]) assert(dir[1]=="file2.lua" or dir[1]=="bar2", dir[1]) assert(dir[2]=="file2.lua" or dir[2]=="bar2", dir[2]) end local names = { "foo", "foo/file1.c", "foo/file2.lua", "foo/bar", "foo/bar/file3.lua" } local i = 0 for name in fs.walk "foo" do i = i + 1 assert(name == names[i]:gsub("/", fs.sep)) end assert(fs.remove("foo/file1.c")) assert(fs.rename("foo/bar", "foo/bar2")) check_foo2(fs.listdir("foo")) check_foo2(list(fs.dir("foo"))) local function check_foo3(dir) assert(#dir == 1) assert(dir[1]=="file3.lua", dir[1]) end assert(fs.remove("foo/bar2/file3.lua")) assert(fs.remove("foo/bar2")) assert(fs.rename("foo/file2.lua", "foo/file3.lua")) check_foo3(fs.listdir("foo")) check_foo3(list(fs.dir("foo"))) assert(fs.remove("foo/file3.lua")) assert(#fs.listdir("foo")==0) rm_rf "foo" end doc [[ **fs.copy(source_name, target_name)** copies file `source_name` to `target_name`. The attributes and times are preserved. ]] do local content = string.rep( "I don't remember the question, but for sure, the answer is 42!\n", 42*1000) local f = assert(io.open("answer", "wb")) f:write(content) f:close() assert(fs.touch("answer", 42)) assert(fs.chmod("answer", fs.aR)) assert(fs.copy("answer", "answer-2")) f = assert(io.open("answer-2", "rb")) assert(f:read("*a") == content) f:close() assert(fs.stat("answer-2").mode == fs.stat("answer").mode) assert(fs.stat("answer-2").mtime == 42) if sys.platform == "Windows" then assert(fs.chmod("answer", fs.aR, fs.aW)) assert(fs.chmod("answer-2", fs.aR, fs.aW)) end fs.remove("answer") fs.remove("answer-2") end doc [[ **fs.stat(name)** reads attributes of the file `name`. Attributes are: - `name`: name - type: "file" or "directory" - `size`: size in bytes - `mtime`, `atime`, `ctime`: modification, access and creation times. - `mode`: file permissions - `uR`, `uW`, `uX`: user Read/Write/eXecute permissions - `gR`, `gW`, `gX`: group Read/Write/eXecute permissions - `oR`, `oW`, `oX`: other Read/Write/eXecute permissions **fs.inode(name)** reads device and inode attributes of the file `name`. Attributes are: - `dev`, `ino`: device and inode numbers ]] do local function check(name, attrs) local st = fs.stat(name) assert(st.name == attrs.name) if (st.type=="file") then assert(st.size == attrs.size) end assert(math.abs(st.mtime-attrs.mtime)<=1) assert(math.abs(st.atime-attrs.atime)<=1) assert(math.abs(st.ctime-attrs.ctime)<=1) assert(st.uR == attrs.uR) assert(st.uW == attrs.uW) assert(st.uX == attrs.uX) if sys.platform == 'Linux' then assert(st.gR == attrs.gR) assert(st.gW == attrs.gW) assert(st.gX == attrs.gX) assert(st.oR == attrs.oR) assert(st.oW == attrs.oW) assert(st.oX == attrs.oX) end end rm_rf "foo" fs.mkdir("foo") check("foo", { name="foo", type="directory", mtime=os.time(), atime=os.time(), ctime=os.time(), uR=true, uW=true, uX=true, gR=true, gW=false, gX=true, oR=true, oW=false, oX=true, }) rm_rf "foo" end doc [[ **fs.chmod(name, other_file_name)** sets file `name` permissions as file `other_file_name` (string containing the name of another file). **fs.chmod(name, bit1, ..., bitn)** sets file `name` permissions as `bit1` or ... or `bitn` (integers). ]] do local function check(name, mode, uR, uW, uX, gR, gW, gX, oR, oW, oX) if sys.platform == 'Windows' then uR = true uX = false end if type(mode) == 'string' then assert(fs.chmod(name, mode)) else assert(fs.chmod(name, table.unpack(mode))) end local st = fs.stat(name) assert(st.uR == uR) assert(st.uW == uW) assert(st.uX == uX) end fs.chmod("bar", fs.aR, fs.aW); fs.remove("bar") fs.remove("bar2") assert(io.open("bar", "w")):close() assert(io.open("bar2", "w")):close() check("bar", {fs.uX, fs.gW, fs.oR}, false, false, true, false, true, false, true, false, false) check("bar2", {fs.aR, fs.aW, fs.aX}, true, true, true, true, true, true, true, true, true) check("bar", "bar2", true, true, true, true, true, true, true, true, true) fs.remove("bar") fs.remove("bar2") end doc [[ **fs.touch(name)** sets the access time and the modification time of file `name` with the current time. **fs.touch(name, number)** sets the access time and the modification time of file `name` with `number`. **fs.touch(name, other_name)** sets the access time and the modification time of file `name` with the times of file `other_name`. ]] do local function check(name, mtime, atime) local st = fs.stat(name) assert(math.abs(st.mtime-mtime)<=1) assert(math.abs(st.atime-atime)<=1) end local t0 = os.time() io.open("bar", "w"):close() io.open("bar2", "w"):close() assert(fs.touch("bar", 42)) check("bar", 42, 42) assert(fs.touch("bar", "bar2")) check("bar", t0, t0) assert(fs.touch("bar", 42)) check("bar", 42, 42) local t1 = os.time() assert(fs.touch("bar")) check("bar", t1, t1) fs.remove("bar") fs.remove("bar2") end doc [[ **fs.basename(path)** return the last component of path. **fs.dirname(path)** return all but the last component of path. **fs.absname(path)** return the absolute path name of path. ]] do assert(fs.basename("/usr/bin/bash") == "bash") assert(fs.basename("/usr/bin/") == "bin") assert(fs.basename("C:\\bin\\bl.exe") == "bl.exe") assert(fs.basename("bl") == "bl") assert(fs.basename("/") == "") assert(fs.dirname("/usr/bin/bash") == "/usr/bin") assert(fs.dirname("/usr/bin/") == "/usr") assert(fs.dirname("C:\\bin\\bl.exe") == "C:\\bin") assert(fs.dirname("bl") == "") assert(fs.dirname("/") == "") assert(fs.absname("/usr/bin/bash") == "/usr/bin/bash") assert(fs.absname("C:\\foo.txt") == "C:\\foo.txt") assert(fs.absname("foo/bar") == fs.getcwd()..fs.sep.."foo/bar") end doc [[ **fs.sep** is the directory separator (/ or \\). **fs.uR, fs.uW, fs.uX** are the User Read/Write/eXecute mask for `fs.chmod`. **fs.gR, fs.gW, fs.gX** are the Group Read/Write/eXecute mask for `fs.chmod`. **fs.oR, fs.oW, fs.oX** are the Other Read/Write/eXecute mask for `fs.chmod`. **fs.aR, fs.aW, fs.aX** are All Read/Write/eXecute mask for `fs.chmod`. ]] doc [[ z, lzo, qlz, lz4, zlib, ucl, lzma: compression libraries -------------------------------------------------------- Compression libraries are based on: - `LZO `__ - `QuickLZ `__ - `LZ4 `__ - `ZLIB `__ - `UCL `__ - `XZ Utils `__ It's inspired by the `Lua Lzo module `__. Future versions of BonaLuna may remove or add some compression library. Currently, only zlib is used in the default BonaLuna distribution but you can change it in `setup`. ]] doc [[ **z.compress(data)** compresses `data` using the best compressor and returns the compressed string. **z.decompress(data)** decompresses `data` and returns the decompressed string. **minilzo.compress(data)** compresses `data` with miniLZO and returns the compressed string. **minilzo.decompress(data)** decompresses `data` with miniLZO and returns the decompressed string. **lzo.compress(data)** compresses `data` with LZO and returns the compressed string. **lzo.decompress(data)** decompresses `data` with LZO and returns the decompressed string. **qlz.compress(data)** compresses `data` with QLZ and returns the compressed string. **qlz.decompress(data)** decompresses `data` with QLZ and returns the decompressed string. **lz4.compress(data)** compresses `data` with LZ4 and returns the compressed string. **lz4.decompress(data)** decompresses `data` with LZ4 and returns the decompressed string. **zlib.compress(data)** compresses `data` with ZLIB and returns the compressed string. **zlib.decompress(data)** decompresses `data` with ZLIB and returns the decompressed string. **ucl.compress(data)** compresses `data` with UCL and returns the compressed string. **ucl.decompress(data)** decompresses `data` with UCL and returns the decompressed string. **lzma.compress(data)** compresses `data` with XZ Utils and returns the compressed string. **lzma.decompress(data)** decompresses `data` with XZ Utils and returns the decompressed string. ]] if z then local a = "This is a test string" local b = "And this is another test string" local big = string.rep("a lot of bytes; ", 100000) local libs = {"z", "minilzo", "lzo", "qlz", "lz4", "zlib", "ucl", "lzma"} for name in iter(libs) do local lib = _G[name] if lib then assert(lib.decompress(lib.compress(a)) == a) assert(lib.decompress(lib.compress(b)) == b) assert(lib.decompress(lib.compress(big)) == big) assert(#lib.compress(big) < #big) local ok, err = lib.decompress("not a compressed string") assert(ok == nil and err == name..": not a compressed string") end end end doc [[ ps: Processes ------------- **ps.sleep(n)** sleeps for `n` seconds. ]] do local function check(nsec, niter) local t0 = os.time() for i = 1, niter do ps.sleep(nsec) end local t1 = os.time() assert(math.abs((t1-t0)/niter / nsec - 1.0) <= 1e-3) end --check(2, 1) --check(0.1, 50) --check(0.01, 500) end doc [[ rl: readline ------------ The rl (readline) package was initially inspired by `ilua `_ and adapted for BonaLuna. **rl.read(prompt)** prints `prompt` and returns the string entered by the user. **rl.add(line)** adds `line` to the readline history (Linux only). ]] doc [[ socket: Lua Socket (and networking tools) ----------------------------------------- The socket package is based on `Lua Socket `__ and adapted for BonaLuna. The documentation of `Lua Socket` is available at the `Lua Socket documentation web site `_. This package also comes with the following functions. **FTP(url [, login, password])** creates an FTP object to connect to the FTP server at `url`. `login` and `password` are optional. Methods are: - `cd(path)` changes the current working directory. - `pwd()` returns the current working directory. - `get(path)` retrieves `path`. - `put(path, data)` sends and stores the string `data` to the file `path`. - `rm(path)` deletes the file `path`. - `mkdir(path)` creates the directory `path`. - `rmdir(path)` deletes the directory `path`. - `list(path)` returns an iterator listing the directory `path`. ]] doc [[ struct: (un)pack structures --------------------------- The struct package is taken from `Library for Converting Data to and from C Structs for Lua 5.1 `_ and adapted for BonaLuna. **struct.pack(fmt, d1, d2, ...)** returns a string containing the values `d1`, `d2`, etc. packed according to the format string `fmt`. **struct.unpack(fmt, s, [i])** returns the values packed in string `s` according to the format string `fmt`. An optional `i` marks where in `s` to start reading (default is 1). After the read values, this function also returns the index in `s` where it stopped reading, which is also where you should start to read the rest of the string. **struct.size(fmt)** returns the size of a string formatted according to the format string `fmt`. For obvious reasons, the format string cannot contain neither the option `s` nor the option `c0`. ]] do assert(struct.unpack("f", struct.pack("f", 1.0)) == 1.0) assert(struct.unpack("I4", struct.pack("f", 1.0)) == 0x3F800000) end doc [[ sys: System management ---------------------- ]] doc [[ **sys.hostname()** returns the host name. **sys.domainname()** returns the domain name. **sys.hostid()** returns the host id. ]] do if platform == 'Linux' then assert(sys.hostname() == io.popen("hostname"):read("*l")) assert(sys.domainname() == io.popen("domainname"):read("*l")) assert(sys.hostid() == tonumber(io.popen("hostid"):read("*l"), 16)) end end doc [[ **sys.platform** is `"Linux"` or `"Windows"` ]] doc [[ Self running scripts ==================== It is possible to add scripts to the BonaLuna interpretor to make a single executable file containing the interpretor and some BonaLuna scripts. This feature is inspired by `srlua `__. `pegar.lua` parameters ---------------------- **compile:on|off|min** turn compilation on, off or on when chunks are smaller than sources (`min` is the default value) **compress:on|off|min** turn compression on, off or on when chunks are smaller than sources (`min` is the default value) **read:original_interpretor** reads the initial interpretor **lua:script.lua** adds a script to be executed at runtime **lua:script.lua=realname.lua** as above but stored under a different name **str:name=value** creates a global variable holding a string **str:name=@filename** as above but the string is the content of a file **file:name** adds a file to be created at runtime (the file is not overwritten if it already exists) **file:name=realname** as above but stored under a different name **dir:name** creates a directory at runtime **write:new_executable** write a new executable containing the original interpretor and all the added items When a path starts with `:`, it is relative to the executable path otherwise it is relative to the current working directory. ]] do local stub = arg[-1] local compress, compile local big_file = string.rep("what a big file", 10000) local big_str = string.rep("what a big string", 10000) for compress in iter{'on', 'off', 'min'} do for compile in iter{'on', 'off', 'min'} do rm_rf "tmp" assert(fs.mkdir "tmp") local f = io.open("tmp/hello.lua", "w") f:write [[#! the shebang that loadstring doesn't like assert(fs.basename(arg[-1]) == "hello.exe") assert(arg[0] == "hello.lua") assert(arg[1] == "a") assert(arg[2] == "b") assert(arg[3] == "c") assert(big_str == string.rep("what a big string", 10000)) print(my_constant*14) ]] f:write("\n--"..string.rep("a big compressible and useless comment...", 10000).."\n") f:write("z = [["..string.rep("a big compressible and useless string...", 10000).."]]\n") f:close() f = io.open("tmp/exit.lua", "w") f:write [[ os.exit() ]] f:close() f = io.open("tmp/hello.flag", "w") f:write [[ hi ]] f:close() f = io.open("tmp/hello.big_file", "w") f:write(big_file) f:close() f = io.open("tmp/hello.big_str", "w") f:write(big_str) f:close() os.execute(stub.." ../tools/pegar.lua -q".. " compile:"..compile.. " compress:"..compress.. " read:"..stub.. " file::/hello.flag2=tmp/hello.flag".. " file::/hello.big_file2=tmp/hello.big_file".. " str:big_str=@tmp/hello.big_str".. " dir:tmp/hello.dir".. " str:my_constant=3".. " lua:hello.lua=tmp/hello.lua".. " lua:exit.lua=tmp/exit.lua".. " write:tmp/hello.exe") assert(fs.stat("tmp/hello.exe")) assert(tonumber(io.popen("tmp"..fs.sep.."hello.exe a b c"):read("*a")) == 42) f = io.open("tmp/hello.flag2", "rb") assert(f:read("*a") == [[ hi ]]) f:close() f = io.open("tmp/hello.big_file", "rb") assert(f:read("*a") == big_file) f:close() assert(fs.stat("tmp/hello.dir").type == "directory") end end rm_rf "tmp" end doc [[ Examples ======== This documentation has been generated by a BonaLuna script. `bonaluna.lua `__ also contains some tests. ]] print "BonaLuna tests passed"