I want to write a hexdump of one char*
pointer into the other char*
.
To do that I took this code snippet:
#include <stdio.h>
void DumpHex(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
for (i = 0; i < size; ++i) {
printf("%02X ", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
printf(" ");
if ((i+1) % 16 == 0) {
printf("| %s \n", ascii);
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
printf(" ");
}
for (j = (i+1) % 16; j < 16; ++j) {
printf(" ");
}
printf("| %s \n", ascii);
}
}
}
}
And modified it like this:
#include <stdio.h>
char* DumpHex2(const void* data, size_t size) {
const int symbolSize = 100;
char* buffer = calloc(10*size, sizeof(char));
char* symbol = calloc(symbolSize, sizeof(char));
char ascii[17];
size_t i, j;
ascii[16] = '\0';
for (i = 0; i < size; ++i) {
snprintf(symbol, symbolSize, "%02X ", ((unsigned char*)data)[i]);
strcat(buffer, symbol);
memset(symbol,0,strlen(symbol));
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
strcat(buffer, " ");
if ((i+1) % 16 == 0) {
snprintf(symbol, symbolSize, "| %s \n", ascii);
strcat(buffer, symbol);
memset(symbol,0,strlen(symbol));
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
strcat(buffer, " ");
}
for (j = (i+1) % 16; j < 16; ++j) {
strcat(buffer, " ");
}
snprintf(symbol, symbolSize, "| %s \n", ascii);
strcat(buffer, symbol);
memset(symbol,0,strlen(symbol));
}
}
}
free(symbol);
return buffer;
}
It works and returns the same output:
int main(int argc, char **argv) {
char* text = "Hello World! é";
DumpHex(text, strlen(text));
char* dump = DumpHex2(text, strlen(text));
printf("%s", dump);
free(dump);
return EXIT_SUCCESS;
}
Output:
48 65 6C 6C 6F 20 57 6F 72 6C 64 21 20 C3 A9 | Hello World! ..
48 65 6C 6C 6F 20 57 6F 72 6C 64 21 20 C3 A9 | Hello World! ..
However my modifications, i.e.:
snprintf(symbol, symbolSize, "| %s \n", ascii);
strcat(buffer, symbol);
memset(symbol,0,strlen(symbol));
Look bad to me (I'm new to C). Is there a way to format and append string easier?
You cannot use strlen()
on uninitialized data:
char* buffer = malloc(1000000); memset(buffer,0,strlen(buffer));
There is no way for strlen()
to find out the size of the memory allocated since it relies on a terminating null-character (0
, '\0'
) which might or might not be somewhere in the memory pointed to by buffer. Either specify the size of the memory allocated in memset()
:
memset(buffer, 0, 1000000);
or use calloc()
which initializes the allocated memory with zeros:
char buffer = calloc(1000000, sizeof(char)); // or calloc(1000000, 1) since sizeof(char) is 1 by definition.
There might still be other problems in your code. For example you call DumpHex2()
twice in main()
but never free the memory that function allocates. The memory allocated for symbol
is also leaked.
It would be easier to answer if you updated your question to include the exact format of the text you want DumpHex2()
to produce.
You should use isprint()
to determine if a character is printable or not.
Shorter and IMHO easier to read and understand:
#include <ctype.h> // isprint()
#include <stddef.h> // size_t
#include <stdlib.h> // malloc(), free()
#include <string.h> // strcat()
#include <stdio.h> // sprintf()
enum {
DUMP_BYTES_PER_LINE = 16,
DUMP_BYTES_GROUP = 8,
DUMP_CHARS_PER_LINE = DUMP_BYTES_PER_LINE * 4 + DUMP_BYTES_PER_LINE / DUMP_BYTES_GROUP + 4
};
char* DumpHex(const void* data, size_t size)
{
size_t const num_lines = size / DUMP_BYTES_PER_LINE + ((size % DUMP_BYTES_PER_LINE) > 0);
size_t const result_length = num_lines * DUMP_CHARS_PER_LINE;
char *result = malloc((result_length + 1) * sizeof(*result));
if (!result)
return NULL;
memset(result, ' ', result_length);
result[result_length] = '\0';
char *dump_pos = result;
char *plain_pos = result + DUMP_BYTES_PER_LINE * 3 + DUMP_BYTES_PER_LINE / DUMP_BYTES_GROUP + 3;
char unsigned const *src = data;
for (size_t i = 0; i < size; ++i, dump_pos += 3, ++plain_pos) {
sprintf(dump_pos, "%02x ", (int)src[i]);
dump_pos[3] = ' ';
*plain_pos = isprint(src[i]) ? src[i] : '.';
if ((i + 1) % DUMP_BYTES_PER_LINE == 0 || i + 1 == size) {
*++plain_pos = '\n';
size_t const bytes_per_line_left = (i + 1) % DUMP_BYTES_PER_LINE;
plain_pos[bytes_per_line_left ? -(long long)bytes_per_line_left - 3 : -DUMP_BYTES_PER_LINE - 3] = '|';
dump_pos = plain_pos + 1 - 3;
plain_pos = dump_pos + DUMP_BYTES_PER_LINE * 3 + DUMP_BYTES_PER_LINE / DUMP_BYTES_GROUP + 5;
}
else if ((i + 1) % DUMP_BYTES_GROUP == 0) {
++dump_pos;
}
}
return result;
}