/* Copyright (c) 2000-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file showhex.c The showhex program. */ #include #include #if DK_HAVE_CTYPE_H #include #endif #if DK_HAVE_STDLIB_H #include #endif #if DK_HAVE_PROCESS_H #include #endif #include #include #include #include #include #include #include #include #include "dktools-version.h" #line 72 "showhex.ctr" /** Messages issued by the program, filled using string finder. */ static char *showhex_str[11]; /** String finder data. */ static dk_string_finder_t showhex_msg[] = { { "/m/00", &(showhex_str[0]), "Current configuration:"}, { "/m/01", &(showhex_str[1]), "show text"}, { "/m/02", &(showhex_str[2]), "show addresses"}, { "/m/03", &(showhex_str[3]), "on"}, { "/m/04", &(showhex_str[4]), "off"}, { "/m/05", &(showhex_str[5]), "input buffer size"}, { "/m/06", &(showhex_str[6]), "Unknown long option: \""}, { "/m/07", &(showhex_str[7]), "\"!"}, { "/m/08", &(showhex_str[8]), "Unknown option: \""}, { "/m/09", &(showhex_str[9]), "\"!"}, { "/m/10", &(showhex_str[10]), "Too many file names."}, { NULL, NULL, NULL } }; /** Application. */ static dk_app_t *app = NULL; /** System configuration directory. */ static char sysconfdir[] = { DK_SYSCONFDIR }; /** Application group name. */ static char packagename[] = { "dktools" }; /** Verion number. */ static char the_version_number[] = { VERSNUMB }; /** Flag: Show address. */ #define FLAG_SHOWADDRESS 1 /** Flag: Show text. */ #define FLAG_SHOWTEXT 2 /** Flag: Output octal. */ #define FLAG_OCTAL 4 /** Showhex job. */ typedef struct { dk_app_t *app; /**< Application. */ int flags; /**< Flags. */ size_t requested_buffer_size; /**< Requested buffer size. */ char *infilename; /**< Input file name. */ char *outfilename; /**< Output file name. */ /* input and output file */ FILE *input_file; /**< Input file. */ FILE *output_file; /**< Output file. */ /* input buffer */ char *ibuffer; /**< Input buffer. */ size_t ibsize; /**< Size of input buffer. */ /* output buffer */ char *obuffer; /**< Output buffer. */ size_t obsize; /**< Size of output buffer. */ /* address */ unsigned long laddr; /**< Left address. */ unsigned long raddr; /**< Right address. */ char *addrptr; /**< Address pointer. */ char *hexptr; /**< Pointer to hexadecimal values. */ char *textptr; /**< Pointer to text. */ char *nlptr; /**< Pointer to newline. */ unsigned char_in_line; /**< Number of current character in line. */ char *realinname; /**< Real input file name. */ char *realoutname; /**< Real output file name. */ } ShowhexCmd; /** Reset showhex job. @param cmd Showhex job. */ static void cmd_reset DK_P1(ShowhexCmd *,cmd) { if(cmd) { cmd->flags = 0; cmd->requested_buffer_size = 1024; } } /** Initialize showhex job. @param cmd Showhex job. */ static void cmd_init DK_P1(ShowhexCmd *,cmd) { cmd_reset(cmd); if(cmd) { cmd->app = NULL; cmd->infilename = NULL; cmd->outfilename = NULL; cmd->input_file = NULL; cmd->output_file = NULL; cmd->ibuffer = NULL; cmd->obuffer = NULL; cmd->ibsize = (size_t)0; cmd->obsize = (size_t)0; cmd->requested_buffer_size = (size_t)1024; cmd->laddr = 0UL; cmd->raddr = 0UL; cmd->addrptr = NULL; cmd->hexptr = NULL; cmd->textptr = NULL; } } /** Check whether to run silently. @param argc Number of command line arguments. @param argv Command line arguments array. @param rs Pointer to result variable (run silently). @param rf Pointer to result variable (run as filter). */ static void silence_check DK_P4(int,argc,char **,argv,int *,rs,int *,rf) { int i, filenames; char *ptr, **lfdptr; filenames = 0; lfdptr = argv; lfdptr++; i = 1; while(i < argc) { ptr = *lfdptr; if(*ptr == '-') { ptr++; if(*ptr == 's') { *rs = 1; } } else { filenames++; } i++; lfdptr++; } if(filenames < 2) { *rf = 1; } } /** Long options. */ static char *long_options[] = { /* 00 */ "h$elp", /* 01 */ "v$ersion", /* 02 */ "c$onfigure", /* 03 */ "u$nconfigure", /* 04 */ "sh$ow-configuration", /* 05 */ "r$eset", /* 06 */ "a$ddresses", /* 07 */ "t$ext", /* 08 */ "b$uffer", /* 09 */ "o$ctal", NULL }; /** Set or reset flag depending on option value. @param flags Original flags. @param newflag One flag to set/reset. @param str Text representation of flag value. @return Modified flag set. */ static int flags_adjust DK_P3(int, flags, int, newflag, char *,str) { int back; back = flags; if(str) { if(*str) { switch(*str) { case '+': { back |= newflag; } break; case '-': { back &= (~(newflag)); } break; default: { if(dkstr_is_on(str)) { back |= newflag; } else { back &= (~(newflag)); } } break; } } else { back |= newflag; } } else { back |= newflag; } return back; } /** License terms. */ static char *license_terms[] = { "Redistribution and use in source and binary forms, with or without", "modification, are permitted provided that the following conditions are met:", "* Redistributions of source code must retain the above copyright notice, this", " list of conditions and the following disclaimer.", "* Redistributions in binary form must reproduce the above copyright notice,", " this list of conditions and the following disclaimer in the documentation", " and/or other materials provided with the distribution.", "* Neither the name of the Dirk Krause nor the names of other contributors may", " be used to endorse or promote products derived from this software without", " specific prior written permission.", "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"", "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE", "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE", "ARE DISCLAIMED.", "IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY", "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES", "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;", "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND", "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS", "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", NULL }; /** Print version information. */ static void print_version DK_P0() { char **ptr; printf( "\nshowhex (part of the dktools collection, version %s)\n", the_version_number ); printf("Copyright (C) 2003-2010 - Dipl.-Ing. Dirk Krause\n"); printf("http://dktools.sourceforge.net/\n\n"); ptr = license_terms; while(*ptr) { printf("%s\n", *(ptr++)); } } /** Set app component of \a cmd. @param cmd Showhex job. @param app Application. */ static void cmd_set_app DK_P2(ShowhexCmd *,cmd,dk_app_t *,app) { if(cmd) { cmd->app = app; } } /** Preference key: Show text. */ static char pk_text[] = { "/flag/text" }; /** Preference key: Show addresses. */ static char pk_addr[] = { "/flag/addr" }; /** Preference key: Buffer size. */ static char pk_bufs[] = { "/buffer-size" }; /** Preference value: on. */ static char pv_on[] = { "on" }; /** Preference value: off. */ static char pv_off[] = { "off" }; /** String: Three spaces are used before and after the value. */ static char three_spaces[] = { " " }; /** Find screen width of largest string. @param cmd Showhex job. @param max Maximum found so far. @param str String to text. @return Maximum of \a max and screen length of \a str. */ static size_t correct_strsize DK_P3(ShowhexCmd *,cmd, size_t, max, char *,str) { size_t back, i; back = max; if(str) { i = dkapp_prlen(cmd->app, str); if(i > back) back = i; } return back; } /** Print string right aligned. @param cmd Showhex job. @param max Available space for text. @param str String to print. */ static void print_right_aligned DK_P3(ShowhexCmd *,cmd, size_t, max, char *, str) { size_t i; i = 0; if(str) i = dkapp_prlen(cmd->app, str); while(i++ < max) fputc(' ', stdout); if(str) dkapp_stdout(cmd->app, str); } /** Show configuration. @param cmd Showhex job. */ static void cmd_show_conf DK_P1(ShowhexCmd *,cmd) { size_t max; char buffer[32]; max = 0; sprintf(buffer, "%u", (unsigned)(cmd->requested_buffer_size)); max = correct_strsize(cmd, max, buffer); max = correct_strsize(cmd, max, showhex_str[3]); max = correct_strsize(cmd, max, showhex_str[4]); dkapp_stdout(cmd->app, showhex_str[0]); fputc('\n', stdout); dkapp_stdout(cmd->app, "-t"); dkapp_stdout(cmd->app, three_spaces); print_right_aligned( cmd, max, (((cmd->flags) & FLAG_SHOWTEXT) ? showhex_str[3] : showhex_str[4]) ); dkapp_stdout(cmd->app, three_spaces); dkapp_stdout(cmd->app, showhex_str[1]); fputc('\n', stdout); dkapp_stdout(cmd->app, "-a"); dkapp_stdout(cmd->app, three_spaces); print_right_aligned( cmd, max, (((cmd->flags) & FLAG_SHOWADDRESS) ? showhex_str[3] : showhex_str[4]) ); dkapp_stdout(cmd->app, three_spaces); dkapp_stdout(cmd->app, showhex_str[2]); fputc('\n', stdout); dkapp_stdout(cmd->app, "-b"); dkapp_stdout(cmd->app, three_spaces); print_right_aligned(cmd, max, buffer); dkapp_stdout(cmd->app, three_spaces); dkapp_stdout(cmd->app, showhex_str[5]); fputc('\n', stdout); } /** Correct number to fit into specified range. @param u Original number. @param min Destination range minimum. @param max Destination range maximum. @return Corrected number. */ static unsigned correct_range DK_P3(unsigned, u, unsigned, min, unsigned, max) { unsigned back = u; if(back < min) back = min; if(back > max) back = max; return back; } /** Get default configuration from preferences. @param cmd Showhex job. */ static void cmd_get_conf DK_P1(ShowhexCmd *,cmd) { char buffer[32]; if(dkapp_get_pref(cmd->app, pk_text, buffer, sizeof(buffer), 0)) { if(dkstr_is_on(buffer)) { cmd->flags |= FLAG_SHOWTEXT; } else { cmd->flags &= (~(FLAG_SHOWTEXT)); } } if(dkapp_get_pref(cmd->app, pk_addr, buffer, sizeof(buffer), 0)) { if(dkstr_is_on(buffer)) { cmd->flags |= FLAG_SHOWADDRESS; } else { cmd->flags &= (~(FLAG_SHOWADDRESS)); } } if(dkapp_get_pref(cmd->app, pk_bufs, buffer, sizeof(buffer), 0)) { unsigned u; if(sscanf(buffer, "%u", &u) == 1) { u = correct_range(u, 128, 32768); cmd->requested_buffer_size = (size_t)u; } } } /** Save current configuration to preferences. @param cmd Showhex job. */ static void cmd_save_conf DK_P1(ShowhexCmd *,cmd) { char buffer[32]; dkapp_set_pref( cmd->app, pk_text, (((cmd->flags) & FLAG_SHOWTEXT) ? pv_on : pv_off) ); dkapp_set_pref( cmd->app, pk_addr, (((cmd->flags) & FLAG_SHOWADDRESS) ? pv_on : pv_off) ); sprintf(buffer, "%u", (unsigned)(cmd->requested_buffer_size)); dkapp_set_pref( cmd->app, pk_bufs, buffer ); } /** Initialize output line to show another 16 bytes. @param cmd Showhex job. */ static void init_output_line DK_P1(ShowhexCmd *, cmd) { #if DK_HAVE_MEMSET memset((cmd->obuffer), ' ', ((cmd->obsize) -1)); #else char *ptr; size_t i; ptr = cmd->obuffer; for(i = 0; i < cmd->obsize; i++) *(ptr++) = ' '; #endif (cmd->obuffer)[cmd->obsize] = '\0'; if(cmd->addrptr) { sprintf(cmd->addrptr, "%08lu:%08lu", ((cmd->laddr) & 0xFFFFFFFFUL), ((cmd->raddr) & 0xFFFFFFFFUL) ); (cmd->addrptr)[17] = ' '; cmd->raddr += 16UL; if(!((cmd->raddr) & 0xFFFFFFFFUL)) { cmd->raddr = 0UL; cmd->laddr += 1UL; } } cmd->char_in_line = 0; } /** End of line. */ static char end_of_line[] = { "\n\0" }; /** Flush output line. @param cmd Showhex job. */ static void flush_output_line DK_P1(ShowhexCmd *,cmd) { if(cmd->char_in_line) { if(cmd->nlptr) { strcpy(cmd->nlptr, end_of_line); fputs(cmd->obuffer, cmd->output_file); } init_output_line(cmd); } } /** Hexadecimal digits. */ static char hexdigits[] = { "0123456789ABCDEF" }; /** Add one character to output. @param cmd Showhex job. @param c Character to add. */ static void add_output_char DK_P2(ShowhexCmd *,cmd, char, c) { unsigned u1, u2; char buffer[16]; if((cmd->flags) & FLAG_OCTAL) { sprintf(buffer, "%03o", (unsigned char)c); (cmd->hexptr)[4 * (cmd->char_in_line)] = buffer[0]; (cmd->hexptr)[4 * (cmd->char_in_line) + 1] = buffer[1]; (cmd->hexptr)[4 * (cmd->char_in_line) + 2] = buffer[2]; } else { u1 = (unsigned)((unsigned char)c & 0x0F); u2 = (unsigned)(((unsigned char)c >> 4) & 0x0F); (cmd->hexptr)[3 * (cmd->char_in_line)] = hexdigits[u2]; (cmd->hexptr)[3 * (cmd->char_in_line) + 1] = hexdigits[u1]; } if(cmd->textptr) { if(isprint(c) && isascii(c)) { (cmd->textptr)[cmd->char_in_line] = c; } else { (cmd->textptr)[cmd->char_in_line] = '.'; } } cmd->char_in_line += 1; if(((cmd->char_in_line) >= 16) || (((cmd->flags) & FLAG_OCTAL) && ((cmd->char_in_line) >= 8))) { flush_output_line(cmd); } } /** Process all input. @param cmd Showhex job. @return 1 on success, 0 on error. */ static int run_with_buffers DK_P1(ShowhexCmd *, cmd) { int back, cc; size_t rdbytes, i; char *ptr; cc = 1; back = 0; cmd->laddr = cmd->raddr = 0UL; cmd->addrptr = NULL; cmd->hexptr = NULL; cmd->textptr = NULL; cmd->nlptr = NULL; /* set the pointers */ if((cmd->flags) & FLAG_OCTAL) { if((cmd->flags) & FLAG_SHOWADDRESS) { cmd->addrptr = &((cmd->obuffer)[0]); cmd->hexptr = &((cmd->obuffer)[20]); cmd->nlptr = &((cmd->obuffer)[51]); if((cmd->flags) & FLAG_SHOWTEXT) { cmd->textptr = &((cmd->obuffer)[54]); cmd->nlptr = &((cmd->obuffer)[62]); } } else { cmd->hexptr = &((cmd->obuffer)[0]); cmd->nlptr = &((cmd->obuffer)[31]); if((cmd->flags) & FLAG_SHOWTEXT) { cmd->textptr = &((cmd->obuffer)[34]); cmd->nlptr = &((cmd->obuffer)[42]); } } } else { if((cmd->flags) & FLAG_SHOWADDRESS) { cmd->addrptr = &((cmd->obuffer)[0]); cmd->hexptr = &((cmd->obuffer)[20]); cmd->nlptr = &((cmd->obuffer)[67]); if((cmd->flags) & FLAG_SHOWTEXT) { cmd->textptr = &((cmd->obuffer)[70]); cmd->nlptr = &((cmd->obuffer)[86]); } } else { cmd->hexptr = &((cmd->obuffer)[0]); cmd->nlptr = &((cmd->obuffer)[47]); if((cmd->flags) & FLAG_SHOWTEXT) { cmd->textptr = &((cmd->obuffer)[50]); cmd->nlptr = &((cmd->obuffer)[66]); } } } init_output_line(cmd); while(cc) { rdbytes = fread((void *)(cmd->ibuffer), 1, cmd->ibsize, cmd->input_file); if(rdbytes > 0) { back = 1; ptr = cmd->ibuffer; i = rdbytes; while(i--) add_output_char(cmd, *(ptr++)); } else { cc = 0; } } flush_output_line(cmd); return back; } /** Allocate buffers and process. @param cmd Showhex job. @return 1 on success, 0 on error. */ static int run_for_files DK_P1(ShowhexCmd *, cmd) { int back; size_t u; char my_input[128]; char output_line[128]; char *myptr; back = 0; cmd->obuffer = output_line; cmd->obsize = sizeof(output_line); u = cmd->requested_buffer_size; myptr = dk_new(char,u); if(myptr) { cmd->ibuffer = myptr; cmd->ibsize = cmd->requested_buffer_size; back = run_with_buffers(cmd); dk_delete(myptr); } else { cmd->ibuffer = my_input; cmd->ibsize = sizeof(my_input); back = run_with_buffers(cmd); } cmd->ibuffer = cmd->obuffer = NULL; cmd->ibsize = cmd->obsize = (size_t)0; return back; } /** Open input file and process. @param cmd Showhex job. @return 1 on success, 0 on error. */ static int run_for_output_file DK_P1(ShowhexCmd *,cmd) { int back = 0; dk_fne_t *fne; int found; char *the_new_name = NULL; if(cmd->infilename) { dksf_correct_fnsep(cmd->infilename); if(dksf_must_expand_filename(cmd->infilename)) { fne = dkfne_open(cmd->infilename, 1, 0); if(fne) { found = 0; while(dkfne_next(fne)) { the_new_name = dkfne_get_fullname(fne); if(the_new_name) { found = 1; cmd->input_file = dkapp_fopen(app, the_new_name, "rb"); if(cmd->input_file) { back = run_for_files(cmd); fclose(cmd->input_file); } else { dkapp_err_fopenr(app, the_new_name); } } } if(!found) { dkapp_err_matchfile(app, cmd->infilename); } dkfne_close(fne); } else { dkapp_err_matchfile(app, cmd->infilename); } } else { cmd->input_file = dkapp_fopen(app, cmd->infilename, "rb"); if(cmd->input_file) { back = run_for_files(cmd); fclose(cmd->input_file); } else { dkapp_err_fopenr(app, cmd->infilename); } } } else { cmd->input_file = stdin; back = run_for_files(cmd); } return back; } /** Open input and output file and process. @param cmd Showhex job. @return 1 on success, 0 on error. */ static int run_for_cmd DK_P1(ShowhexCmd *,cmd) { int back = 0; dk_fne_t *fne; char *cptr; if(cmd->outfilename) { dksf_correct_fnsep(cmd->outfilename); if(dksf_must_expand_filename(cmd->outfilename)) { fne = dkfne_open(cmd->outfilename, 1, 0); if(fne) { cptr = dkapp_fne_one(app, fne, cmd->outfilename); if(cptr) { cmd->output_file = dkapp_fopen(app, cptr, "w"); if(cmd->output_file) { back = run_for_output_file(cmd); fclose(cmd->output_file); } else { dkapp_err_fopenw(app, cptr); } dk_delete(cptr); } dkfne_close(fne); } else { dkapp_err_matchfile(app, cmd->outfilename); } } else { cmd->output_file = dkapp_fopen(app, cmd->outfilename, "w"); if(cmd->output_file) { back = run_for_output_file(cmd); fclose(cmd->output_file); } else { dkapp_err_fopenw(app, cmd->outfilename); } } } else { cmd->output_file = stdout; back = run_for_output_file(cmd); } #ifdef FIRST_APPROACH if(cmd->outfilename) { } else { /* cmd->input_file = stdin; cmd->output_file = stdout; back = run_for_files(cmd); */ } #endif return back; } /** Default help text, printed if help text file is not found. */ static char *help_text[] = { "showhex