From: Colin Patrick Mccabe Date: Thu, 6 Nov 2014 04:12:48 +0000 (-0800) Subject: add hexconv.c, a tool for converting hex strings to signed decimals X-Git-Url: http://club.cc.cmu.edu/~cmccabe/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d2d0cdfe5ecbb42e9f35b291cad64c80479cce67;p=cmccabe-bin add hexconv.c, a tool for converting hex strings to signed decimals Signed-off-by: Colin McCabe --- diff --git a/.gitignore b/.gitignore index 6c30d1d..f0b2e2e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ simple_time vimstart show_default_sockopts print-code-points +hexconv # # Normal rules diff --git a/Makefile b/Makefile index 20180f3..b6bab15 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CFLAGS=-Wall -O2 -all: bytor errno_speak show_default_sockopts simple_time vimstart +all: bytor errno_speak show_default_sockopts simple_time vimstart hexconv bytor: go build bytor.go @@ -13,5 +13,7 @@ simple_time: simple_time.o vimstart: vimstart.o +hexconv: hexconv.o + clean: - rm -rf errno_speak show_default_sockopts simple_time vimstart *.o + rm -rf errno_speak show_default_sockopts simple_time vimstart hexconv *.o diff --git a/hexconv.c b/hexconv.c new file mode 100644 index 0000000..ba4dba3 --- /dev/null +++ b/hexconv.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include + +/* + * This program is useful for converting between hex strings and signed + * integers. It can handle numbers of arbitrary bit sizes (provided they're + * between 2 and 63 inclusive.) + * + * Colin McCabe + */ + +#define EXIT_WITH_NUMBER 2 + +static int run_unit_tests(void); + +static void usage(const char *argv0) +{ + fprintf(stderr, "%s: convert a hex number to its decimal " + "representation.\n", argv0); + fprintf(stderr, "\n"); + fprintf(stderr, "-b [bitsize]: bit size to assume (defaults to 32).\n"); + fprintf(stderr, "-h: this help message.\n"); + fprintf(stderr, "-n [number]: number to convert. Must not be 0.\n"); + fprintf(stderr, "-s: assume signed (default is unsigned).\n"); + fprintf(stderr, "-u: run unit tests.\n"); +} + +static int process(char **argv, long long *result) +{ + int c, i, argc = 0, bitsize = 32, is_signed = 0; + long long number = 0; + char *endptr; + + for (i = 0; argv[i]; i++) { + argc++; + } + optind = 1; + while ((c = getopt(argc, argv, "b:hn:su")) != -1) { + switch (c) { + case 'b': + bitsize = atoi(optarg); + break; + case 'h': + usage(argv[0]); + return EXIT_SUCCESS; + case 'n': + if (strncmp(optarg, "0x", 2) != 0) { + fprintf(stderr, "The number you pass must start with 0x.\n"); + return EXIT_FAILURE; + } + endptr = NULL; + errno = 0; + number = strtoull(optarg + 2, &endptr, 16); + if (errno) { + int err = errno; + fprintf(stderr, "strtoull failed: error %d (%s)\n", + err, strerror(err)); + return EXIT_FAILURE; + } + if (*endptr != '\0') { + fprintf(stderr, "Garbage at end of string, beginning " + "with %s\n", endptr); + return EXIT_FAILURE; + } + break; + case 's': + is_signed = 1; + break; + case 'u': + return run_unit_tests(); + default: + fprintf (stderr, "getopt error.\n"); + return EXIT_FAILURE; + } + } + if ((bitsize <= 1) || (bitsize >= 64)) { + fprintf(stderr, "Invalid bitsize of %d. Bitsize must be " + "between 2 and 63, inclusive.\n", bitsize); + return EXIT_FAILURE; + } + if (number == 0) { + fprintf(stderr, "You must specify a non-zero number to convert. -h " + "for help.\n"); + return EXIT_FAILURE; + } + if (number > (1LL << bitsize)) { + fprintf(stderr, "Number %lld does not fit in %d bits.\n", + number, bitsize); + return EXIT_FAILURE; + } + // Mask off unused bits. + if (is_signed) { + unsigned long long highest_neg = (1LLU << (bitsize - 1)); + if ((unsigned long long)number >= highest_neg) { + unsigned long long full = (1LLU << bitsize); + number -= full; + } + } else { + number &= ((1LLU << bitsize) - 1LLU); + } + *result = number; + return EXIT_WITH_NUMBER; +} + +static int run_unit_tests(void) +{ + long long result; + char *help_args[] = { "hexconv", "-h", NULL }; + char *simple_args[] = { "hexconv", "-n", "0xff", NULL }; + char *invalid_1[] = { "hexconv", "-n", "123", NULL }; + char *invalid_2[] = { "hexconv", "-n", "0x123", "-b", "65", NULL }; + char *invalid_3[] = { "hexconv", "-n", "0xffff", "-b", "8", NULL }; + char *neg_1[] = { "hexconv", "-n", "0x80", "-b", "8", "-s", NULL }; + char *neg_2[] = { "hexconv", "-n", "0xff", "-b", "8", "-s", NULL }; + char *neg_3[] = { "hexconv", "-n", "0xffff", "-b", "16", "-s", NULL }; + char *neg_4[] = { "hexconv", "-n", "0xfffffe", "-b", "24", "-s", NULL }; + + fprintf(stderr, "*** testing -h ***\n"); + if (EXIT_SUCCESS != process(help_args, NULL)) abort(); + fprintf(stderr, "*** testing converting 0xff ***\n"); + if (EXIT_WITH_NUMBER != process(simple_args, &result)) abort(); + if (result != 255) abort(); + fprintf(stderr, "*** testing invalid input 123 ***\n"); + if (EXIT_FAILURE != process(invalid_1, &result)) abort(); + fprintf(stderr, "*** testing invalid bitsize input ***\n"); + if (EXIT_FAILURE != process(invalid_2, &result)) abort(); + fprintf(stderr, "*** testing too-big input ***\n"); + if (EXIT_FAILURE != process(invalid_3, &result)) abort(); + fprintf(stderr, "*** testing converting 0x80 with in signed mode ***\n"); + if (EXIT_WITH_NUMBER != process(neg_1, &result)) abort(); + if (result != -128) abort(); + fprintf(stderr, "*** testing converting 0xff with in signed mode ***\n"); + if (EXIT_WITH_NUMBER != process(neg_2, &result)) abort(); + if (result != -1) abort(); + fprintf(stderr, "*** testing converting 0xffff with in signed mode ***\n"); + if (EXIT_WITH_NUMBER != process(neg_3, &result)) abort(); + if (result != -1) abort(); + fprintf(stderr, "*** testing converting 0xfffffe with in signed mode ***\n"); + if (EXIT_WITH_NUMBER != process(neg_4, &result)) abort(); + if (result != -2) abort(); + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + long long result = argc; // avoid annoying "unused variable" warning + int ret = process(argv, &result); + switch (ret) { + case EXIT_SUCCESS: + return EXIT_SUCCESS; + case EXIT_WITH_NUMBER: + printf("%lld\n", result); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } +}