--- /dev/null
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * 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;
+ }
+}