| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <console.h> |
| #include <errno.h> |
| #include <syslinux/loadfile.h> |
| |
| /* Macros */ |
| #define ROWS_PER_PAGE 24 |
| #define COLS_PER_ROW 16 |
| #define BYTES_PER_PAGE (ROWS_PER_PAGE * COLS_PER_ROW) |
| |
| /* Functions declarations */ |
| static int usage(void); |
| static void eat_stdin(void); |
| static int do_page(void); |
| static void hexdump(const void *memory, size_t bytes); |
| |
| /* Objects */ |
| static const char *prog_name; |
| static int opt_page; |
| static int opt_no_buffer; |
| static int opt_extended_ascii; |
| |
| int main(int argc, char **argv) |
| { |
| int rc; |
| const char *filename; |
| int i; |
| void *file_data; |
| size_t file_sz; |
| FILE *f; |
| size_t len; |
| const char *cur_pos; |
| |
| /* Assume failure */ |
| rc = EXIT_FAILURE; |
| |
| /* Determine the program name, as invoked */ |
| if (argc < 1 || !argv || !argv[0]) { |
| fprintf(stderr, "argc or argv failure!\n"); |
| goto err_prog_name; |
| } |
| prog_name = argv[0]; |
| |
| /* Process arguments */ |
| filename = NULL; |
| for (i = 1; i < argc; ++i) { |
| if (!argv[i]) { |
| fprintf(stderr, "argc and argv mismatch!\n"); |
| goto err_argv; |
| } |
| |
| if (!strncmp(argv[i], "--page", sizeof "--page") || |
| !strncmp(argv[i], "-p", sizeof "-p")) { |
| opt_page = 1; |
| continue; |
| } |
| |
| if (!strncmp(argv[i], "--no-buffer", sizeof "--no-buffer")) { |
| opt_no_buffer = 1; |
| continue; |
| } |
| |
| if (!strncmp(argv[i], "--extended-ascii", sizeof "--extended-ascii")) { |
| opt_extended_ascii = 1; |
| continue; |
| } |
| |
| if (!strncmp(argv[i], "--help", sizeof "--help") || |
| !strncmp(argv[i], "-h", sizeof "-h") || |
| !strncmp(argv[i], "-?", sizeof "-?")) |
| return usage(); |
| |
| /* Otherwise, interpret as a filename, but only accept one */ |
| if (filename) |
| return usage(); |
| filename = argv[i]; |
| } |
| if (!filename) |
| return usage(); |
| fprintf(stdout, "Dumping file: %s\n", filename); |
| |
| /* Either fetch the whole file, or just allocate a buffer */ |
| f = NULL; |
| if (opt_no_buffer) { |
| errno = 0; |
| if (loadfile(filename, &file_data, &file_sz)) { |
| fprintf(stderr, "Couldn't load file. Error: %d\n", errno); |
| goto err_file_data; |
| } |
| } else { |
| file_sz = BYTES_PER_PAGE; |
| file_data = malloc(file_sz); |
| if (!file_data) { |
| fprintf(stderr, "Couldn't allocate file data buffer\n"); |
| goto err_file_data; |
| } |
| errno = 0; |
| f = fopen(filename, "r"); |
| if (!f) { |
| fprintf(stderr, "Couldn't open file. Error: %d\n", errno); |
| goto err_f; |
| } |
| } |
| |
| /* Dump the data */ |
| len = BYTES_PER_PAGE; |
| cur_pos = file_data; |
| do { |
| if (f) { |
| /* Buffered */ |
| len = fread(file_data, 1, file_sz, f); |
| cur_pos = file_data; |
| } else { |
| /* Non-buffered */ |
| if (file_sz < len) |
| len = file_sz; |
| } |
| if (!len) |
| break; |
| |
| hexdump(cur_pos, len); |
| |
| /* Pause, if requested */ |
| if (opt_page) { |
| /* The user might choose to quit */ |
| if (do_page()) |
| break; |
| } |
| |
| /* Reduce file_sz for non-buffered mode */ |
| if (!f) |
| file_sz -= len; |
| } while (cur_pos += len); |
| |
| rc = EXIT_SUCCESS; |
| |
| if (f) |
| fclose(f); |
| err_f: |
| |
| free(file_data); |
| err_file_data: |
| |
| err_argv: |
| |
| err_prog_name: |
| |
| return rc; |
| } |
| |
| static int usage(void) |
| { |
| static const char usage[] = |
| "Usage: %s [<option> [...]] <filename> [<option> [...]]\n" |
| "\n" |
| "Options: -p\n" |
| " --page . . . . . . . Pause output every 24 lines\n" |
| " --no-buffer . . . . Load the entire file before dumping\n" |
| " --extended-ascii . . Use extended ASCII chars in dump\n" |
| " -?\n" |
| " -h\n" |
| " --help . . . . . . Display this help\n"; |
| |
| fprintf(stderr, usage, prog_name); |
| return EXIT_FAILURE; |
| } |
| |
| static void eat_stdin(void) |
| { |
| int i; |
| |
| while (1) { |
| i = fgetc(stdin); |
| if (i == EOF || i == '\n') |
| return; |
| } |
| } |
| static int do_page(void) |
| { |
| int i; |
| |
| while (1) { |
| fprintf(stdout, "Continue? [Y|n]: "); |
| i = fgetc(stdin); |
| switch (i) { |
| case 'n': |
| case 'N': |
| eat_stdin(); |
| return 1; |
| |
| case EOF: |
| fprintf(stderr, "No response. Continuing...\n"); |
| /* Fall through to "yes" */ |
| |
| case 'y': |
| case 'Y': |
| eat_stdin(); |
| case '\n': |
| return 0; |
| |
| default: |
| fprintf(stderr, "Invalid choice\n"); |
| eat_stdin(); |
| } |
| } |
| } |
| |
| static void hexdump(const void *memory, size_t bytes) |
| { |
| const unsigned char *p, *q; |
| int i; |
| |
| p = memory; |
| while (bytes) { |
| q = p; |
| printf("%p: ", (void *) p); |
| for (i = 0; i < 16 && bytes; ++i) { |
| printf("%02X ", *p); |
| ++p; |
| --bytes; |
| } |
| bytes += i; |
| while (i < 16) { |
| printf("XX "); |
| ++i; |
| } |
| printf("| "); |
| p = q; |
| for (i = 0; i < 16 && bytes; ++i) { |
| printf("%c", isprint(*p) && !isspace(*p) ? *p : ' '); |
| ++p; |
| --bytes; |
| } |
| while (i < 16) { |
| printf(" "); |
| ++i; |
| } |
| printf("\n"); |
| } |
| return; |
| } |