I'm continuing with Lua [1], with today's version using recursion.
>
```
#!/usr/bin/env lua
-- ***************************************************************
--
-- Copyright 2012 by Sean Conner. All Rights Reserved.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
-- Comments, questions and criticisms can be sent to: sean@conman.org
--
-- ********************************************************************
-- Style: Lua 5.1, recursion
function do_dump(fpin,fpout,offset)
local line = fpin:read(16)
if line == nil then return end
fpout:write(
string.format("%08X: ",offset),
line:gsub(".",function(c) return string.format("%02X ",c:byte()) end),
string.rep(" ",3 * (16 - line:len())),
line:gsub("%c","."),
"\n"
)
return do_dump(fpin,fpout,offset + 16)
end
-- **********************************************************************
if #arg == 0 then
print("-----stdin-----")
do_dump(io.stdin,io.stdout,0)
else
for i = 1 , #arg do
local f = io.open(arg[1],"r")
io.stdout:write("-----",arg[1],"-----","\n")
do_dump(f,io.stdout,0)
f:close()
end
end
os.exit(0)
```
Here, we have the do_dump() function calling itself for each lines worth of data. If you don't have experience with recursion, this is a common technique of solving certain programming problems by having a function call itself with either a simpler case to solve, or, like in this example, by calling itself with more data. And it just works.
If you are familiar with recursion, you might be horrified at such a solution, since a very large file might cause the program to crash since with recursion, the program (behind the scenes) keeps track of everything it's already done and thus, could run out of memory.
But in this case, we don't have to worry. Lua takes advantage of what's called “tail call optmization [2].” In this case, you can think of the tail call as a form of goto [3], but this type of goto can also goto other functions, which is useful in implementing state machines. For example, a pseudocode version of the TFTP (Trivial File Transfer Protocol) [4] protocol, in Lua:
>
```
function server(conn)
remote,request = conn:read()
if request.opcode == 'read' then
info = open_read(request.file)
READ_DATA(remote,info)
elseif request.opcode == 'write' then
info = open_write(request.file)
SEND_ACK(remote,info)
end
return server(conn)
end
-- *******************************************************
function READ_DATA(remote,info)
remote:send(DATA,readblock(info.file,info.blocknum))
return RECEIVE_ACK(remote,info)
end
-- *******************************************************
function RECEIVE_ACK(remote,info)
ack = remote:read_ack()
if info.blocknum > 0 and ack.blocknum < info.blocknum then
return RECEIVE_ACK(remote,info)
end
if #info.data < 512 then
return --we're done
end
info.blocknum = info.blocknum + 1
return READ_DATA(remote,info)
end
-- *******************************************************
function SEND_ACK(remote,info)
remote:send(ACK,info.blocknum)
if info.blocknum > 0 and #info.data < 512 then
return -- we're done
else
return RECEIVE_DATA(remote,info)
end
end
-- *******************************************************
function RECEIVE_DATA(remote,info)
data = remote:read_data()
if data.blocknum < info.blocknum then
return SEND_ACK(remote,info)
end
info.blocknum = data.blocknum
writeblock(info.file,info.blocknum,data.data)
return SEND_ACK(remote,info)
end
```
I earlier said I wasn't a fan of tail call optimization [5] but then, I didn't see a use for it. Here I do, at least for state machines. But for a hex dump program, not so much, but it doesn't hurt all that much either—it's still a loop in this case.
[2] http://en.wikipedia.org/wiki/Tail_call
[3] http://en.wikipedia.org/wiki/Goto
[4] http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol