Last active
March 17, 2022 23:30
-
-
Save maxrt101/8bb4760651453e66fa08c8ea71f494e4 to your computer and use it in GitHub Desktop.
Hexdump
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* hexdump.c by maxrt101 */ | |
#include <stdlib.h> | |
#include <stddef.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <ctype.h> | |
#define VERSION "0.50" | |
#define FORMAT_HEX_UPPER "%02X" | |
#define FORMAT_HEX_LOWER "%02x" | |
enum mode { | |
MODE_DEFAULT, | |
MODE_BINARY, | |
MODE_PLAIN, | |
MODE_CARRAY | |
}; | |
typedef struct { | |
FILE* fp; | |
char* data; | |
size_t size; | |
size_t count; | |
size_t start; | |
size_t end; | |
char* fmt; | |
int cols; | |
} state; | |
void die(const char* fmt, ...) { | |
va_list args; | |
va_start(args, fmt); | |
fprintf(stderr, "Error: "); | |
vfprintf(stderr, fmt, args); | |
fprintf(stderr, "\n"); | |
va_end(args); | |
exit(EXIT_FAILURE); | |
} | |
void state_init(state* s, const char* filename) { | |
if (!s) { | |
die("Error: filename is null"); | |
} | |
s->fp = fopen(filename, "rb"); | |
if (!s->fp) { | |
if (errno == 2) { | |
die("Error: no such file or directory"); | |
} else { | |
die("Error: failed to open a file(errno=%d)", errno); | |
} | |
} | |
fseek(s->fp, 0, SEEK_END); | |
s->size = ftell(s->fp); | |
rewind(s->fp); | |
if (s->size <= 0) { | |
die("Error: file size <= 0"); | |
} | |
s->data = (char*)malloc(s->size); | |
if (!s->data) { | |
die("Error: malloc failed (errno=%d)", errno); | |
} | |
if (fread(s->data, 1, s->size, s->fp) != s->size) { | |
free(s->data); | |
die("Error: couldn't read file (errno=%d)", errno); | |
} | |
s->count = 0; | |
s->cols = 16; | |
} | |
void state_cleanup(state* s) { | |
fclose(s->fp); | |
free(s->data); | |
} | |
void mode_default(state* s) { | |
int can_print = 1; | |
char* buf = (char*)malloc(s->cols); | |
while (can_print) { | |
memset(buf, '.', s->cols); | |
printf("%06zx |", s->count); | |
for (int i = 0; i < s->cols; i++) { | |
printf("%s", (i % (s->cols/2) == 0) ? " " : " "); | |
if (s->count < s->end) { | |
printf(s->fmt, (unsigned char)s->data[s->count]); | |
buf[s->count % s->cols] = s->data[s->count++]; | |
} else { | |
printf(" "); | |
can_print = 0; | |
} | |
} | |
printf(" | "); | |
for (int i = 0; i < s->cols; i++) { | |
printf("%c", isprint(buf[i]) ? buf[i] : '.'); | |
} | |
printf("\n"); | |
} | |
free(buf); | |
} | |
void mode_plain(state* s) { | |
while (s->count < s->end) { | |
printf(s->fmt, (unsigned char)s->data[s->count++]); | |
if (s->count % s->cols == 0) { | |
printf("\n"); | |
} | |
} | |
printf("\n"); | |
} | |
void mode_binary(state* s) { | |
int can_print = 1; | |
char* buf = (char*)malloc(s->cols); | |
while (can_print) { | |
memset(buf, '.', s->cols); | |
printf("%06zx |", s->count); | |
for (int i = 0; i < s->cols; i++) { | |
printf(" "); | |
if (s->count < s->end) { | |
unsigned char b = s->data[s->count]; | |
for (int i = 0; i < 8; i++) { | |
printf("%d", ((b >> i) & 0x1) ? 1 : 0); | |
} | |
buf[s->count % s->cols] = s->data[s->count++]; | |
} else { | |
printf(" "); | |
can_print = 0; | |
} | |
} | |
printf(" | "); | |
for (int i = 0; i < s->cols; i++) { | |
if (isprint(buf[i])) { | |
printf("%c", buf[i]); | |
} else { | |
printf("."); | |
} | |
} | |
printf("\n"); | |
} | |
free(buf); | |
} | |
void mode_carray(state* s) { | |
puts("unsigned char hex[] = {\n "); | |
while (s->count < s->end) { | |
printf("0x"); | |
printf(s->fmt, (unsigned char)s->data[s->count]); | |
printf(", "); | |
s->count++; | |
if (s->count % 10 == 0) { | |
printf("\n "); | |
} | |
} | |
printf("\n};\n"); | |
} | |
void set_cols(state* s, int cols, int default_cols) { | |
s->cols = cols ? cols : default_cols; | |
} | |
void usage(const char* prog_name) { | |
printf("Usage: %s FILE [OPTIONS]\n" | |
"Options:\n" | |
" -h Prints this message.\n" | |
" -v Print version string.\n" | |
" -b Binary mode.\n" | |
" -C C array mode.\n" | |
" -p Plain mode\n" | |
" -c cols Sets columns\n" | |
" -s off Offset from the beginning of the file.\n" | |
" -l len Length to read.\n" | |
" -U Uppercase hex formatting.\n" | |
" -L Lowercase hex formatting.\n", prog_name); | |
} | |
int main(int argc, char ** argv) { | |
char* filename = NULL; | |
char* fmt = FORMAT_HEX_UPPER; | |
enum mode mode = MODE_DEFAULT; | |
int start = 0, end = 0, cols = 0; | |
for (int i = 1; i < argc; i++) { | |
if (!strcmp("-h", argv[i])) { | |
usage(argv[0]); | |
return 0; | |
} else if (!strcmp("-v", argv[i])) { | |
printf("hexdump v%s by maxrt101\n", VERSION); | |
return 0; | |
} else if (!strcmp("-b", argv[i])) { | |
mode = MODE_BINARY; | |
} else if (!strcmp("-C", argv[i])) { | |
mode = MODE_CARRAY; | |
} else if (!strcmp("-p", argv[i])) { | |
mode = MODE_PLAIN; | |
} else if (!strcmp("-c", argv[i])) { | |
if (i+1 < argc) { | |
cols = atoi(argv[++i]); | |
} else { | |
usage(argv[0]); | |
} | |
} else if (!strcmp("-s", argv[i])) { | |
if (i+1 < argc) { | |
start = atoi(argv[++i]); | |
} else { | |
usage(argv[0]); | |
return 0; | |
} | |
} else if (!strcmp("-l", argv[i])) { | |
if (i+1 < argc) { | |
end = atoi(argv[++i]); | |
} else { | |
usage(argv[0]); | |
return 0; | |
} | |
} else if (!strcmp("-U", argv[i])) { | |
fmt = FORMAT_HEX_UPPER; | |
} else if (!strcmp("-L", argv[i])) { | |
fmt = FORMAT_HEX_LOWER; | |
} else { | |
filename = argv[i]; | |
} | |
} | |
if (!filename) { | |
usage(argv[0]); | |
return 0; | |
} | |
state s; | |
state_init(&s, filename); | |
s.start = start; | |
s.end = end ? end : s.size; | |
s.fmt = fmt; | |
s.count = s.start; | |
switch (mode) { | |
case MODE_DEFAULT: | |
set_cols(&s, cols, 16); | |
mode_default(&s); | |
break; | |
case MODE_BINARY: | |
set_cols(&s, cols, 6); | |
mode_binary(&s); | |
break; | |
case MODE_CARRAY: | |
set_cols(&s, cols, 10); | |
mode_carray(&s); | |
break; | |
case MODE_PLAIN: | |
set_cols(&s, cols, 30); | |
mode_plain(&s); | |
break; | |
default: | |
printf("Unknown operation mode\n"); | |
} | |
state_cleanup(&s); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment