Wednesday, January 25, 2012
“A big ol' slab of beef!”
Once more into the breach, but I remembered last time this happened, and acted accordingly. But I needn't worry—while we were swarmed with men armed with huge chunks of roast critter, this time, the restaurant was way more crowded and thus, we weren't swarmed quite as heavily.
Also, amusingly, on the far wall from where I was sitting was a large wide screen television showing closeups of grass, of all things. And it wasn't made up like a window either—the grasses would change every so often. And yes, it was video of grass, not static images of grass.
Very odd.
And the food was again, great.
And yes, expense accounts rock!
99 ways to program a hex, Part 17: Lua, recursion, runtime type checking
Since Lua is a dynamically typed language (“values have types, not variables”) we can check the type of a variable at runtime and behave accordingly. Before, we were restricted with just dumping a file, but we could also dump strings (which in Lua can be pure binary data). So today's version checks what type the input is; if it's a file, we read data from there, otherwise if the input is a string, we pull the next blob of data out of it.
Granted, we don't actually use that feature here, but we can
more easily reuse do_dump() elsewhere.
#!/usr/bin/env lua
-- ***************************************************************
--
-- Copyright 2010 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, runtime type checking
function do_dump(fpin,fpout,offset)
local line
if type(fpin) == 'string' then
if offset > string.len(fpin) then return end
line = fpin:sub(offset + 1,offset + 16)
else
line = fpin:read(16)
if line == nil then return end
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)
Tuesday, January 24, 2012
99 ways to program a hex, Part 16: Lua, recursion
I'm continuing with Lua, 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.”
In this case, you can think of the tail call as a form of goto,
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 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 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.
Monday, January 23, 2012
99 ways to program a hex, Part 15: Lua
I'm still taking a break from C, and today's version is in Lua.
#!/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
function do_dump(fpin,fpout)
local offset = 0
while true do
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"
)
offset = offset + 16
end
end
-- **********************************************************************
if #arg == 0 then
print("-----stdin-----")
do_dump(io.stdin,io.stdout)
else
for i = 1 , #arg do
local f = io.open(arg[1],"r")
io.stdout:write("-----",arg[1],"-----","\n")
do_dump(f,io.stdout)
f:close()
end
end
os.exit(0)
What I'm noticing (besides my text editor's horrible attempts at syntax highlighting in this entry) is that the non-C versions are quite a bit shorter than the C versions. I'm sure part of that reason is the high level of abstraction obtained by not using C. For instance, in this version, the code to dump the data is easily half the length of the shortest C version, thanks to the clever string.gsub() routine in Lua.
A disconnect
R stops by my desk and drops off a new toy unit to play
with test. It's a network device you can plug a POTS line into and make calls over the
Internet. I guess we're testing the toy to play with unit to see
if our phone network features work with it.
It's a nice looking device and as R hands it to me, I see that's it still
on (yes, it comes with both an internal battery and a wall-wart). The tests
aren't complicated, but I do need to read the manual to figure out how to
run a few of them (involving conference calling, and forwarding phone
calls elsewhere). R also hands me the box the toy to play with
unit came in.
As I searched through the box for the manual, I come across a USB cable, still in it plastic wrap.
Okay, the unit comes with a USB port; what doesn't these days? I then find the manual
and start flipping through it. There's the diagram of the toy to play
with unit with a description of each port and button on it. I notice
the USB port has a note:
NOTE: Never place a USB-based device into the USB port of the XXXX XXXXX XXXXXXX under any circumstances. Doing so may damage the device and negate its warranty. The port was designed for diagnostic purposes only; it is not intended for customer use.
So, not only is there no sticker over the USB port saying “removal of this sticket voids warranty” but they give you a USB cable not to plug into it!
Methinks there is a disconnect between manufactoring and packaging at the
factory that makes these toys to play with units.
Sunday, January 22, 2012
99 ways to program a hex, Part 14: COLOR COMPUTER BASIC
Yesterday's code was labeled “COLOR COMPUTER BASIC, EASY” not because it was easy to write (it was somewhat easy—seeing how I didn't have to run it, and going off referenece material for a language I haven't used in over twenty years) but because it was relatively “easy” to read.
I'm being serious.
I never saw any published BASIC code look that nice. No, it would usually be presented as this (but probably without the blank lines):
1 '*****************************
2 '* COPYRIGHT 2012 SEAN CONNER
3 '*
4 '* THIS PROGRAM RELEASED UNDER
5 '* THE GNU LICENSE, VERSION 2
6 '* OF THE LICENSE, OR (AT YOUR
7 '* OPTION) ANY LATER VERSION.
8 '*
9 '* SEE THE GNU GENERAL PUBLIC
10 '* LICENCE FOR MORE DETAILS.
11 '***************************
12 '
13 'COLOR COMPUTER BASIC
14 'REQUIRES EXTENDED AND
15 'DOS BASIC TO RUN.
16'
20 PRINT "INPUT FILE NAME:";:INP
UT FN$:OPEN"I",#1,FN$:I=0:Y=0:FO
R S=1TOLOC(2):GET#1,S:D$=INPUT#1
:FOR B=1TO256STEP8:DD$=MID$(D$,B
,16):I$=HEX$(I):IF LEN(I$)<>4 TH
EN I$ = MID$("000",1,4-LEN(I$))+
I$
21 H$="":A$="":FOR C=1TO8:B$=MID
$(H$,C,1):IF CHR$(B$)<32 OR CHR$
(B$)>1THEN B$="."
22 A$=A$+B$:T$=HEX$(B$):IF LEN(T
$)=1THENT$="0"+T$
23 H$=H$+T$:NEXT C:PRINTI$;": ";
H$;" ";A$:I=I+8:Y=Y+1:IF Y=15 TH
EN INPUT T$:Y=1
24 NEXT B:NEXT S:CLOSE#1:END
Line 20 here covers lines 20 through 103 of yesterday's code, and I only broke it there because of the IF statement, which ends at the end of a number statement. Otherwise, it could have been longer, up to 255 characters in length. All due to memory constraints—4,096 bytes, 16,384 bytes or 32,768 bytes of RAM to fit both the program and data (and if you want high resolution graphics, you give up 6,144 bytes; 12,288 bytes if you want double-buffered high resolution graphics—and by “high resolution graphics” I mean 256×192 pixels, two colors).
Yes kids, this is how we used to write programs. And part of the reason why BASIC has the terrible reputation that it does.
Saturday, January 21, 2012
99 ways to program a hex, Part 13: COLOR COMPUTER BASIC, EASY
I decided to take a break with the C versions for a few days, given that a) I'm hopelessly behind on posting, and b) the next few versions are interesting and I want to make sure I have plenty of time for the write-ups. So I went back to my programming roots, to the first computer I ever owned, a Tandy Color Computer 2, and figured, why not do a hex dump program for it? In its version of BASIC.
One of the rules I set for myself is that the output of each version should match if at all possible, and I'm afraid that this version (and the next one) fall under those weasle words—the output won't be exactly the same. It's not hard to understand why though, when you realize that the text screen of the Color Computer is 32×16. Yes, it's only sixteen lines of 32 characters each. Yeah, this:
00000170: 44 4F 53 20 42 41 53 49 43 20 54 4F 20 52 55 4E DOS BASIC TO RUN 00000180: 2E 0A 31 36 27 0A 32 30 20 50 52 49 4E 54 20 22 ..16'.20 PRINT " 00000190: 49 4E 50 55 54 20 46 49 4C 45 20 4E 41 4D 45 3A INPUT FILE NAME: 000001A0: 22 3B 0A 32 31 20 49 4E 50 55 54 20 46 4E 24 0A ";.21 INPUT FN$. 000001B0: 32 32 20 4F 50 45 4E 22 49 22 2C 23 31 2C 46 4E 22 OPEN"I",#1,FN
ain't gonna fit!
This, however:
0170 444F532042415349 DOS BASI 0178 4320544F2052554E C TO RUN 0180 2E0A3136270A3230 ..16'.20 0188 205052494E542022 PRINT " 0190 494E505554204649 INPUT FI 0198 4C45204E414D453A LE NAME: 01A0 223B0A323120494E ";.21 IN 01A8 50555420464E240A PUT FN$. 01B0 3232204F50454E22 22 OPEN" 01B8 49222C23312C464E I",#1,FN
would. And we'll be going with that.
And I'm presenting the code as it would (or less) appear on the Color Computer, wrapped at 32 columns (more accurate would be a mid-range green background with a really crappy 8×12 pixel font).
1 '*****************************
2 '* COPYRIGHT 2012 SEAN CONNER
3 '*
4 '* THIS PROGRAM RELEASED UNDER
5 '* THE GNU LICENSE, VERSION 2
6 '* OF THE LICENSE, OR (AT YOUR
7 '* OPTION) ANY LATER VERSION.
8 '*
9 '* SEE THE GNU GENERAL PUBLIC
10 '* LICENCE FOR MORE DETAILS.
11 '***************************
12 '
13 'COLOR COMPUTER BASIC, EASY
14 'REQUIRES EXTENDED AND
15 'DOS BASIC TO RUN.
16'
20 PRINT "INPUT FILE NAME:";
21 INPUT FN$
22 OPEN"I",#1,FN$
23 I=0
24 Y=0
30 FOR S=1TOLOC(2)
31 GET#1,S
32 D$=INPUT#1
100 FOR B = 1 TO 256 STEP 8
101 DD$ = MID$(D$,B,16)
102 I$=HEX$(I)
103 IF LEN(I$)<>4 THEN I$ = MID$
("000",1,4-LEN(I$))+I$
104 H$ = ""
105 A$ = ""
106 FOR C = 1 TO 8
107 B$ = MID$(H$,C,1)
108 IF CHR$(B$)<32 OR CHR$(B$)>1
26 THEN B$="."
109 A$=A$+B$
110 T$=HEX$(B$)
111 IF LEN(T$)=1 THEN T$="0"+T$
112 H$=H$+T$
113 NEXT C
114 PRINTI$;": ";H$;" ";A$
115 I=I+8
116 Y=Y+1
117 IF Y=15 THEN INPUT T$:Y=1
118 NEXT B
119 NEXT S
120 CLOSE#1
130 END
Now, I don't know if the code actually works (unlike the previous twelve versions). I still have my first computer, but I haven't turned it on in years, and in fact, I would have to dig it out of storage, find a TV that worked to hook it up to (for a video display), then type in the program, debug it, then … um … type it in again, since I don't have any easy way of transferring data off the Color Computer (that would require more digging to piles of cables to find the right set of cables and adaptors, going from a 4-pin DIN to USB with some form of null-modem cable thrown in). So in theory the code works, but in practice …
The main problem is that the DOS BASIC commands are geared towards record based files (both sequential and random access) and not the more modern “stream of bytes” paradigm in use today. I'm reading what I hope are 256 byte binary records. That's where my biggest concern lies really.
Now, I could read in binary data directly; there is a command to do that, but it reads in a raw sector from the disk; I would have to write code to decode the actual file structure. It's not a complicated structure, but it's a bit more effort than I want do go into right now, and would distract from a “simple” program.
So I can only hope that the program presented above works.
- Part 12: C99, const and restrict correctness, assertive, GCC extensions
- Part 14: COLOR COMPUTER BASIC
Friday, January 20, 2012
99 ways to program a hex, Part 12: C99, const and restrict correctness, assertive, GCC extenstions
And today's code is identical to part 10 save again for one line—it too uses a GCC extension per yesterday's code.
This also marks the end of the C versions for a couple of days.
/*************************************************************************
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Comments, questions and criticisms can be sent to: sean@conman.org
*
*************************************************************************/
/* Style: C99, const and restrict correctness, assertive, GCC extenstions */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#define LINESIZE 16
static void do_dump (FILE *const restrict,FILE *const restrict) __attribute__((nonnull,nothrow));
/****************************************************************/
int main(const int argc,char *const restrict argv[])
{
assert(argc >= 1);
assert(argv != NULL);
assert(argv[0] != NULL);
if (argc == 1)
do_dump(stdin,stdout);
else
{
for (int i = 1 ; i < argc ; i++)
{
FILE *fp = fopen(argv[i],"rb");
if (fp == NULL)
{
perror(argv[i]);
continue;
}
printf("-----%s-----\n",argv[i]);
do_dump(fp,stdout);
fclose(fp);
}
}
return EXIT_SUCCESS;
}
/******************************************************************/
static void do_dump(FILE *const restrict fpin,FILE *const restrict fpout)
{
unsigned char buffer[BUFSIZ];
size_t offset;
size_t bread;
assert(fpin != NULL);
assert(fpout != NULL);
offset = 0;
while((bread = fread(buffer,1,BUFSIZ,fpin)) > 0)
{
unsigned char *pbyte = buffer;
while (bread > 0)
{
char ascii[LINESIZE + 1];
fprintf(fpout,"%08lX: ",(unsigned long)offset);
size_t j = 0;
do
{
fprintf(fpout,"%02X ",*pbyte);
if (isprint(*pbyte))
ascii [j] = *pbyte;
else
ascii [j] = '.';
pbyte ++;
offset ++;
j ++;
bread --;
} while ((j < LINESIZE) && (bread > 0));
ascii [j] = '\0';
if (j < LINESIZE)
for (size_t i = j ; i < LINESIZE ; i++) fprintf(fpout," ");
fprintf(fpout,"%s\n",ascii);
}
if (fflush(fpout) == EOF)
{
perror("output");
exit(EXIT_FAILURE);
}
}
}
/***************************************************************/
Notes about an overheard conversation between a blogger and his lovely and talented copy editor
“I think you miscounted.”
“Here?”
“Yes. There are only two problems … cache invalidation … ”
“Uh huh.”
“Naming things … ”
“Yeah … ”
“and off-by-one errors … oh. Oh! D'oh!”
Thursday, January 19, 2012
Okay, what failed this time?
I'm running the regression tests for “Project: Wolowizard” and about half way through the tests (around the two hour mark or so) start failing. Sometimes expected results just aren't showing up. I'm freaking about a bit because of all the issues we've had in running these tests, only for it to start failing in yet a different way.
Now, a bit about how this all works—there are four computers involved; one runs the tests, injecting messages towards a mini-cluster of two machines, either of which (depending on which one gets the message) sends a message to the fourth machine, which does a bunch of processing (which may involve interaction with a simulated cell phone on the testing machine), then responds back to the mini-cluster, which then responds back to the testing machine.
Now, I can check the immedate results from the mini-cluster, but the
actual data I'm interested in is logged via syslog, so I have
that data forwarded to the testing machine and my code grovels through a log
file for the actual data I want. And it's that data (or part
thereof) that apparently isn't being logged, and thus, the tests are
failing.
Now, it just so happens that the part of the test that's failing is the part dealing with the mini-cluster, and it looks like about half the tests are failing (hmm …. ).
I log into each of the two computers comprising the mini-cluster, and
check /etc/syslog.conf, in the off chance that changed. Nope.
I then explain the problem to Bunny, standing (or rather, sitting) in as my
cardboard programmer
when it hits me—I should check to see if the program is running.
Rats. It is.
The tests are still failing, and my shoes began to squeak.
Okay, just because syslogd is running doesn't necessarily
mean it's running correctly. So I run logger -p local1.info
FOO on each machine and yes, one of the machines is failing to foward
the logs to the testing machine.
Ahah!
I restart syslogd on that system, and lo! The log entries
are getting through now.
You know, I expect there to be issues with the stuff I'm testing; what I don't expect is the stuff that we didn't write is having issues (the Protocol Stack From Hell™ notwithstanding).
Okay, reset everything and start the regression test over again …
Update in the wee-hours of the morning, Friday, January 20th, 2012
A bit over half-way through the regression tests, and the log files
rotate. Aaaaaaaaaah! Okay, reset all the data, and start from the last
failed test. That's easy, since I can specify which cases to run. That's
hard, because I have to specify nearly a 100 cases. That's easy, since I
can use the Unix command seq to list them. That's hard,
because the test cases aren't just numbers, but things like
“1.b.77” and “1.c.18”, and while the shell supports
command line expantion from a running program via the backtick (ala
for i in `seq 34 77`; do echo 1.b.$i; done) I need to nest two
such operations (echo `for i in `seq 34 77`;do echo 1.b.$i;
done`) to specify the test cases from the command line, and the
command line doesn't support that. Okay, I can create a temporary
file that lists the test cases …
99 ways to program a hex, Part 11: C89, const correctness, assertive, GCC extensions
Today's code is identical to part
9 save for one line—it uses a GCC
extention to notify the compiler that the do_dump()
function accepts NULL pointers, and that it won't throw any
exceptions.
/*************************************************************************
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Comments, questions and criticisms can be sent to: sean@conman.org
*
*************************************************************************/
/* Style: C89, const correctness, assertive, GCC extensions */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#define LINESIZE 16
static void do_dump (FILE *const,FILE *const) __attribute__((nonnull,nothrow));
/****************************************************************/
int main(const int argc,char *const argv[])
{
assert(argc >= 1);
assert(argv != NULL);
assert(argv[0] != NULL);
if (argc == 1)
do_dump(stdin,stdout);
else
{
int i;
for (i = 1 ; i < argc ; i++)
{
FILE *fp;
fp = fopen(argv[i],"rb");
if (fp == NULL)
{
perror(argv[i]);
continue;
}
printf("-----%s-----\n",argv[i]);
do_dump(fp,stdout);
fclose(fp);
}
}
return EXIT_SUCCESS;
}
/******************************************************************/
static void do_dump(FILE *const fpin,FILE *const fpout)
{
unsigned char buffer[BUFSIZ];
unsigned char *pbyte;
size_t offset;
size_t bread;
size_t j;
char ascii[LINESIZE + 1];
assert(fpin != NULL);
assert(fpout != NULL);
offset = 0;
while((bread = fread(buffer,1,BUFSIZ,fpin)) > 0)
{
pbyte = buffer;
while (bread > 0)
{
fprintf(fpout,"%08lX: ",(unsigned long)offset);
j = 0;
do
{
fprintf(fpout,"%02X ",*pbyte);
if (isprint(*pbyte))
ascii [j] = *pbyte;
else
ascii [j] = '.';
pbyte ++;
offset ++;
j ++;
bread --;
} while ((j < LINESIZE) && (bread > 0));
ascii [j] = '\0';
if (j < LINESIZE)
{
size_t i;
for (i = j ; i < LINESIZE ; i++) fprintf(fpout," ");
}
fprintf(fpout,"%s\n",ascii);
}
if (fflush(fpout) == EOF)
{
perror("output");
exit(EXIT_FAILURE);
}
}
}
/***************************************************************/
- Part 10: C99, const and restrict correctness, assertive
- Part 12: C99, const and restrict correctness, assertive, GCC extensions
They're hard problems for a reason …
I quoted this once before, but it bears repeating:
There are only two hard problems in Computer Science: cache invalidation, naming things, and off-by-one errors.
I got hit with one of those issues today, and it wasn't naming a thing, or an off-by-one error. It'a amazing how much time can be wasted by omitting a single command …
![An emulated Captain Napalm from an emulated camera on
an emulated cell phone [An emulated Captain Napalm from an emulated camera on
an emulated cell phone]](http://www.conman.org/people/spc/about/2012/0111.t.jpg)
![Buy this book—he's my bud [Inappropriate Behavior: short transgressions by Sean Hoade]](/banners/inappropriate-behavior.jpg)