Makefile: add pickrand
[cmccabe-bin] / hexconv.c
1 #include <errno.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6
7 /*
8  * This program is useful for converting between hex strings and signed
9  * integers.  It can handle numbers of arbitrary bit sizes (provided they're
10  * between 2 and 63 inclusive.)
11  *
12  * Colin McCabe
13  */
14
15 #define EXIT_WITH_NUMBER 2
16
17 static int run_unit_tests(void);
18
19 static void usage(const char *argv0)
20 {
21     fprintf(stderr, "%s: convert a hex number to its decimal "
22             "representation.\n", argv0);
23     fprintf(stderr, "\n");
24     fprintf(stderr, "-b [bitsize]: bit size to assume (defaults to 32).\n");
25     fprintf(stderr, "-h: this help message.\n");
26     fprintf(stderr, "-n [number]: number to convert.  Must not be 0.\n");
27     fprintf(stderr, "-s: assume signed (default is unsigned).\n");
28     fprintf(stderr, "-u: run unit tests.\n");
29 }
30
31 static int process(char **argv, long long *result)
32 {
33     int c, i, argc = 0, bitsize = 32, is_signed = 0;
34     long long number = 0;
35     char *endptr;
36
37     for (i = 0; argv[i]; i++) {
38         argc++;
39     }
40     optind = 1;
41     while ((c = getopt(argc, argv, "b:hn:su")) != -1) {
42         switch (c) {
43         case 'b':
44             bitsize = atoi(optarg);
45             break;
46         case 'h':
47             usage(argv[0]);
48             return EXIT_SUCCESS;
49         case 'n':
50             if (strncmp(optarg, "0x", 2) != 0) {
51                 fprintf(stderr, "The number you pass must start with 0x.\n");
52                 return EXIT_FAILURE;
53             }
54             endptr = NULL;
55             errno = 0;
56             number = strtoull(optarg + 2, &endptr, 16);
57             if (errno) {
58                 int err = errno;
59                 fprintf(stderr, "strtoull failed: error %d (%s)\n",
60                         err, strerror(err));
61                 return EXIT_FAILURE;
62             }
63             if (*endptr != '\0') {
64                 fprintf(stderr, "Garbage at end of string, beginning "
65                         "with %s\n", endptr);
66                 return EXIT_FAILURE;
67             }
68             break;
69         case 's':
70             is_signed = 1;
71             break;
72         case 'u':
73             return run_unit_tests();
74         default:
75             fprintf (stderr, "getopt error.\n");
76             return EXIT_FAILURE;
77         }
78     }
79     if ((bitsize <= 1) || (bitsize >= 64)) {
80         fprintf(stderr, "Invalid bitsize of %d.  Bitsize must be "
81                 "between 2 and 63, inclusive.\n", bitsize);
82         return EXIT_FAILURE;
83     }
84     if (number == 0) {
85         fprintf(stderr, "You must specify a non-zero number to convert.  -h "
86                 "for help.\n");
87         return EXIT_FAILURE;
88     }
89     if (number > (1LL << bitsize)) {
90         fprintf(stderr, "Number %lld does not fit in %d bits.\n",
91                 number, bitsize);
92         return EXIT_FAILURE;
93     }
94     // Mask off unused bits.
95     if (is_signed) {
96         unsigned long long highest_neg = (1LLU << (bitsize - 1));
97         if ((unsigned long long)number >= highest_neg) {
98             unsigned long long full = (1LLU << bitsize);
99             number -= full;
100         }
101     } else {
102         number &= ((1LLU << bitsize) - 1LLU);
103     }
104     *result = number;
105     return EXIT_WITH_NUMBER;
106 }
107
108 static int run_unit_tests(void)
109 {
110     long long result;
111     char *help_args[] = { "hexconv", "-h", NULL };
112     char *simple_args[] = { "hexconv", "-n", "0xff", NULL };
113     char *invalid_1[] = { "hexconv", "-n", "123", NULL };
114     char *invalid_2[] = { "hexconv", "-n", "0x123", "-b", "65", NULL };
115     char *invalid_3[] = { "hexconv", "-n", "0xffff", "-b", "8", NULL };
116     char *neg_1[] = { "hexconv", "-n", "0x80", "-b", "8", "-s", NULL };
117     char *neg_2[] = { "hexconv", "-n", "0xff", "-b", "8", "-s", NULL };
118     char *neg_3[] = { "hexconv", "-n", "0xffff", "-b", "16", "-s", NULL };
119     char *neg_4[] = { "hexconv", "-n", "0xfffffe", "-b", "24", "-s", NULL };
120
121     fprintf(stderr, "*** testing -h ***\n");
122     if (EXIT_SUCCESS != process(help_args, NULL)) abort();
123     fprintf(stderr, "*** testing converting 0xff ***\n");
124     if (EXIT_WITH_NUMBER != process(simple_args, &result)) abort();
125     if (result != 255) abort();
126     fprintf(stderr, "*** testing invalid input 123 ***\n");
127     if (EXIT_FAILURE != process(invalid_1, &result)) abort();
128     fprintf(stderr, "*** testing invalid bitsize input ***\n");
129     if (EXIT_FAILURE != process(invalid_2, &result)) abort();
130     fprintf(stderr, "*** testing too-big input ***\n");
131     if (EXIT_FAILURE != process(invalid_3, &result)) abort();
132     fprintf(stderr, "*** testing converting 0x80 with in signed mode ***\n");
133     if (EXIT_WITH_NUMBER != process(neg_1, &result)) abort();
134     if (result != -128) abort();
135     fprintf(stderr, "*** testing converting 0xff with in signed mode ***\n");
136     if (EXIT_WITH_NUMBER != process(neg_2, &result)) abort();
137     if (result != -1) abort();
138     fprintf(stderr, "*** testing converting 0xffff with in signed mode ***\n");
139     if (EXIT_WITH_NUMBER != process(neg_3, &result)) abort();
140     if (result != -1) abort();
141     fprintf(stderr, "*** testing converting 0xfffffe with in signed mode ***\n");
142     if (EXIT_WITH_NUMBER != process(neg_4, &result)) abort();
143     if (result != -2) abort();
144     return EXIT_SUCCESS;
145 }
146
147 int main(int argc, char **argv)
148 {
149     long long result = argc; // avoid annoying "unused variable" warning
150     int ret = process(argv, &result);
151     switch (ret) {
152     case EXIT_SUCCESS:
153         return EXIT_SUCCESS;
154     case EXIT_WITH_NUMBER:
155         printf("%lld\n", result);
156         return EXIT_SUCCESS;
157     default:
158         return EXIT_FAILURE;
159     }
160 }