[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

hexump.c

jacob navia

9/9/2011 8:54:00 PM

Got the idea of adding one more exercise to the tutorial.

The goal is to show a small hexdump utility without any bells and
whistles, and add a bunch of exercises to add those. Here it is.

It uses standard C. Please tell me if there could be any
portability problems.

I do not use putchar but fputc to make it easier to add an output
file later as one more argument.

Please tell me if you see any errors in it. Note that the manifest
constants will be replaced by #defines in the exercises, when they
are asked to increase the number of columns, etc.

------------------------------------------------------cut here
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
if (argc < 2) {
fprintf(stderr,"Usage: %s <file name>\n",argv[0]);
return EXIT_FAILURE;
}
FILE *file = fopen(argv[1],"rb");
if (file == NULL) {
fprintf(stderr,"Impossible to open %s for reading\n",argv[1]);
return EXIT_FAILURE;
}
int oneChar = fgetc(file);
int column = 0,line = 0;
unsigned char tab[16+1];
char *hex = "0123456789abcdef";
int address = 1;

while (oneChar != EOF) {
if (column == 0) {
memset(tab,'.',16);
fprintf(stdout,"[%d] ",address);
}
if (oneChar >= ' ' && oneChar <= 127) {
tab[column] = oneChar;
}
fputc(hex[(oneChar >> 4)&0xf],stdout);
fputc(hex[oneChar&0xf],stdout);
fputc(' ',stdout);
column++;
if (column == 16) {
fputc(' ',stdout);
tab[column]=0;
fputs(tab,stdout);
column = 0;
}
line++;
if (line == 16) {
fputc('\n',stdout);
line = 0;
}
oneChar = fgetc(file);
address++;
}
fclose(file);
address--;
if (column > 0 ) {
while (column < 16) {
fprintf(stdout," ");
tab[column]=' ';
column++;
}
tab[16]=0;
fprintf(stdout," %s\n[%d]\n",tab,address);
}
else fprintf(stdout,"[%d]\n",address);
return EXIT_SUCCESS;
}

----------------------------------------------------cut here
87 Answers

unknown

9/9/2011 9:07:00 PM

0

jacob navia writes:
> fprintf(stderr,"Usage: %s <file name>\n",argv[0]); return
fails if argv[0] is null

> unsigned char tab[16+1];

you should use char to store printable characters

> if (oneChar >= ' ' && oneChar <= 127) {
> tab[column] = oneChar;
> }

could lead to unprintable characters appearing on non-ascii systems

> fputc(hex[(oneChar >> 4)&0xf],stdout);

the mask is unnecessary unless you are targeting systems with CHAR_BIT>8

> fprintf(stdout," ");

you know there is a function called printf?

looks like your C code for trivial exercises is just as sloppy as the
code in your buggy and overpriced compiler jacob.

Ian Collins

9/9/2011 9:19:00 PM

0

On 09/10/11 08:53 AM, jacob navia wrote:
> Got the idea of adding one more exercise to the tutorial.
>
> The goal is to show a small hexdump utility without any bells and
> whistles, and add a bunch of exercises to add those. Here it is.
>
> It uses standard C. Please tell me if there could be any
> portability problems.
>
> I do not use putchar but fputc to make it easier to add an output
> file later as one more argument.
>
> Please tell me if you see any errors in it. Note that the manifest
> constants will be replaced by #defines in the exercises, when they
> are asked to increase the number of columns, etc.
>
> ------------------------------------------------------cut here
> #include<stdio.h>
> #include<stdlib.h>
> #include<string.h>
> int main(int argc,char *argv[])
> {
> if (argc< 2) {
> fprintf(stderr,"Usage: %s<file name>\n",argv[0]);
> return EXIT_FAILURE;
> }
> FILE *file = fopen(argv[1],"rb");
> if (file == NULL) {
> fprintf(stderr,"Impossible to open %s for reading\n",argv[1]);
> return EXIT_FAILURE;
> }
> int oneChar = fgetc(file);
> int column = 0,line = 0;
> unsigned char tab[16+1];

This should be char, not unsigned char.

> char *hex = "0123456789abcdef";

This should be const char*.

--
Ian Collins

jacob navia

9/9/2011 9:57:00 PM

0

Le 09/09/11 23:06, tea strainer a écrit :
> jacob navia writes:
>> fprintf(stderr,"Usage: %s<file name>\n",argv[0]); return
> fails if argv[0] is null
>
>> unsigned char tab[16+1];
>
> you should use char to store printable characters

OK
>
>> if (oneChar>= ' '&& oneChar<= 127) {
>> tab[column] = oneChar;
>> }
>
> could lead to unprintable characters appearing on non-ascii systems
>

Yes, I will replace this with isprint()


>> fputc(hex[(oneChar>> 4)&0xf],stdout);
>
> the mask is unnecessary unless you are targeting systems with CHAR_BIT>8
>

You are just wrong. If I would follow you I would risk a segment
violation.

>> fprintf(stdout," ");
>
> you know there is a function called printf?
>

I said in the introduction to the code that I did not want to use
implicit stdout since the output file will be changed as an exercise.

But yes, I did not know about printf, thanks


> looks like your C code for trivial exercises is just as sloppy as the
> code in your buggy and overpriced compiler jacob.

You are too coward to insult people openly, you hide behind a pseudonym.

Good bye "tea strainer".

jacob navia

9/9/2011 10:00:00 PM

0

Le 09/09/11 23:18, Ian Collins a écrit :
> On 09/10/11 08:53 AM, jacob navia wrote:

>> unsigned char tab[16+1];
>
> This should be char, not unsigned char.
>
>> char *hex = "0123456789abcdef";
>
> This should be const char*.
>

Right on both counts. Thanks Ian.

Ike Naar

9/9/2011 10:29:00 PM

0

On 2011-09-09, jacob navia <jacob@spamsink.net> wrote:
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> int main(int argc,char *argv[])
> {
> if (argc < 2) {
> fprintf(stderr,"Usage: %s <file name>\n",argv[0]);

Undefined behaviour in the (unlikely) case where argc = 0 and argv[0] = NULL.

> return EXIT_FAILURE;
> }
> FILE *file = fopen(argv[1],"rb");
> if (file == NULL) {
> fprintf(stderr,"Impossible to open %s for reading\n",argv[1]);
> return EXIT_FAILURE;
> }
> int oneChar = fgetc(file);
> int column = 0,line = 0;

The variables column and line serve the same purpose.
One of them can go.

> unsigned char tab[16+1];

Why unsigned?

> char *hex = "0123456789abcdef";
> int address = 1;
>
> while (oneChar != EOF) {
> if (column == 0) {
> memset(tab,'.',16);
> fprintf(stdout,"[%d] ",address);
> }
> if (oneChar >= ' ' && oneChar <= 127) {
> tab[column] = oneChar;
> }
> fputc(hex[(oneChar >> 4)&0xf],stdout);
> fputc(hex[oneChar&0xf],stdout);
> fputc(' ',stdout);
> column++;
> if (column == 16) {
> fputc(' ',stdout);
> tab[column]=0;
> fputs(tab,stdout);
> column = 0;
> }
> line++;
> if (line == 16) {
> fputc('\n',stdout);
> line = 0;
> }
> oneChar = fgetc(file);
> address++;
> }
> fclose(file);
> address--;
> if (column > 0 ) {
> while (column < 16) {
> fprintf(stdout," ");
> tab[column]=' ';
> column++;
> }
> tab[16]=0;
> fprintf(stdout," %s\n[%d]\n",tab,address);
> }
> else fprintf(stdout,"[%d]\n",address);
> return EXIT_SUCCESS;
> }

The output would look nicer (better aligned) if the address
were printed using a constant field width. Now it looks like this:

[1] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
[17] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
[33] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
[49] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
[65] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
[81] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
[97] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
[113] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 0a xxxxxxxxxxxxxxx.
[128]

Note the misalignment between first/second and seventh/eighth lines.

And personally I find the one-based addresses a bit odd, but
perhaps that's just me.

jacob navia

9/9/2011 10:39:00 PM

0

Le 10/09/11 00:29, Ike Naar a écrit :
> On 2011-09-09, jacob navia<jacob@spamsink.net> wrote:
>> #include<stdio.h>
>> #include<stdlib.h>
>> #include<string.h>
>> int main(int argc,char *argv[])
>> {
>> if (argc< 2) {
>> fprintf(stderr,"Usage: %s<file name>\n",argv[0]);
>
> Undefined behaviour in the (unlikely) case where argc = 0 and argv[0] = NULL.
>

Yes. But do you know a system where that happens?

Never found one.

>> return EXIT_FAILURE;
>> }
>> FILE *file = fopen(argv[1],"rb");
>> if (file == NULL) {
>> fprintf(stderr,"Impossible to open %s for reading\n",argv[1]);
>> return EXIT_FAILURE;
>> }
>> int oneChar = fgetc(file);
>> int column = 0,line = 0;
>
> The variables column and line serve the same purpose.
> One of them can go.
>

No, lines count the lines and columns the number of chars
read in that line of output.

>> unsigned char tab[16+1];
>
> Why unsigned?
>


Bug. Changed that to plain char.

>
> The output would look nicer (better aligned) if the address
> were printed using a constant field width. Now it looks like this:
>
> [1] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
> [17] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
> [33] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
> [49] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
> [65] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
> [81] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
> [97] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 xxxxxxxxxxxxxxxx
> [113] 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 0a xxxxxxxxxxxxxxx.
> [128]
>
> Note the misalignment between first/second and seventh/eighth lines.

Yes, that will be one exercise. But to solve it, you have to know the
size of the file in order to know how many places you need.

>
> And personally I find the one-based addresses a bit odd, but
> perhaps that's just me.

Many exercises will be to add options:

1) Option for zero based addresses in hex
2) Option for decimal dump instead of hexadecimal
3) Option for writing the output into a file instead of
stdout.
4) Option to print more/less than 16 characters
5) Option to limit the output to N text positions
6) Option to dump in 16/32 bit format instead of 8.

Thanks for your answer

jacob

Ike Naar

9/9/2011 10:54:00 PM

0

On 2011-09-09, jacob navia <jacob@spamsink.net> wrote:
> Le 10/09/11 00:29, Ike Naar a ?crit :
>> On 2011-09-09, jacob navia<jacob@spamsink.net> wrote:
>>> #include<stdio.h>
>>> #include<stdlib.h>
>>> #include<string.h>
>>> int main(int argc,char *argv[])
>>> {
>>> if (argc< 2) {
>>> fprintf(stderr,"Usage: %s<file name>\n",argv[0]);
>>
>> Undefined behaviour in the (unlikely) case where argc = 0 and argv[0] = NULL.
>
> Yes. But do you know a system where that happens?
> Never found one.

I agree that argc = 0 is unlikely. But the standard allows it.

>>> int column = 0,line = 0;
>>
>> The variables column and line serve the same purpose.
>> One of them can go.
>>
>
> No, lines count the lines and columns the number of chars
> read in that line of output.

Are you really really sure?
I get the impression that they both represent the number of columns,
and that line==column is an invariant of the
``while (oneChar != EOF)'' loop.

jacob navia

9/9/2011 10:58:00 PM

0

Le 10/09/11 00:53, Ike Naar a écrit :
>
> Are you really really sure?
> I get the impression that they both represent the number of columns,
> and that line==column is an invariant of the
> ``while (oneChar != EOF)'' loop.

A REAL BUG!

Thanks a lot. Of course, line should be incremented only when
column goes to zero.

Thanks!

jacob

gordonb.ik17p

9/9/2011 11:17:00 PM

0

>> Undefined behaviour in the (unlikely) case where argc = 0 and argv[0] = NULL.
>>
>
> Yes. But do you know a system where that happens?
>
> Never found one.

It's easy for a malicious invoker to do that with the UNIX execv()
or execl() call. I suppose it's possible to do it accidentally but
that's unlikely.

Morris Keesan

9/10/2011 12:34:00 AM

0

On Fri, 09 Sep 2011 16:53:37 -0400, jacob navia <jacob@spamsink.net> wrote:

> if (argc < 2) {
> fprintf(stderr,"Usage: %s <file name>\n",argv[0]);
> return EXIT_FAILURE;
> }

This misses the usage-error case where argc > 2.
Others have pointed out the possibility that argv[0] == NULL.
And, personally, as a long-time Unix programmer, I prefer to let any
file-processing program work as a filter, so I would have written
something like

FILE *file;
if (argc < 2) {
file = stdin;
} else if (argc > 2) {
fprintf(stderr,"Usage: %s <file name>\n",argv[0]);
return EXIT_FAILURE;
} else {
file = fopen(argv[1], "rb");
etc.
....


> fputc(hex[(oneChar >> 4)&0xf],stdout);
> fputc(hex[oneChar&0xf],stdout);
Assumes CHAR_BIT == 8. I've used systems where this is not true, where
this code will not show the entire value of each char.


--
Morris Keesan -- mkeesan@post.harvard.edu