tests: Test some corner cases of upkg_decode_index.
[upkg.git] / test / decodeindex.c
1 /*
2  * Tool for decoding compact index values for testing.
3  * Copyright © 2012 Nick Bowler
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23 #include <getopt.h>
24
25 #include <upkg.h>
26
27 #define PROGNAME "decodeindex"
28 static const char *progname = PROGNAME;
29 static const unsigned char sopts[] = "VH";
30 static const struct option lopts[] = {
31         { "version", 0, NULL, 'V' },
32         { "help",    0, NULL, 'H' },
33         { 0 }
34 };
35
36 /*
37  * Decode a hexadecimal string into a sequence of bytes.  If there are an
38  * odd number of nibbles, treat the first character as the least significant
39  * nibble of the first byte.  The result is written to the buffer specified by
40  * buf.  At most n bytes are written to the buffer.
41  *
42  * Returns the number of bytes that would be written provided that n was large
43  * enough, or (size_t)-1 if the input is not valid.
44  */
45 static size_t decode_hex(const char *hex, unsigned char *buf, size_t n)
46 {
47         size_t len, count = 0;
48         char tmp[] = "00";
49
50         for (len = 0; hex[len]; len++) {
51                 if (!isxdigit(hex[len]))
52                         return -1;
53         }
54
55         if (!len)
56                 return 0;
57
58         switch (len % 2) {
59                 while (len > 0) {
60                         case 0: tmp[0] = *hex++; len--;
61                         case 1: tmp[1] = *hex++; len--;
62
63                         if (count < n)
64                                 buf[count] = strtoul(tmp, NULL, 16);
65                         count++;
66                 }
67         }
68
69         return count;
70 }
71
72 static void print_usage(FILE *f)
73 {
74         fprintf(f, "Usage: %s [options] index [index ...]\n", progname);
75 }
76
77 static void print_version(void)
78 {
79         printf("%s (%s) %s\n", PROGNAME, PACKAGE_NAME, PACKAGE_VERSION);
80         puts("Copyright (C) 2012 Nick Bowler.");
81         puts("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.");
82         puts("This is free software: you are free to change and redistribute it.");
83         puts("There is NO WARRANTY, to the extent permitted by law.");
84 }
85
86 static void print_bytes(FILE *f, int indent, void *buf, size_t n)
87 {
88         fprintf(f, "%*s", indent, "");
89         for (size_t i = 0; i < n; i++)
90                 fprintf(f, "%*s%.2hhx", i != 0, "", ((unsigned char *)buf)[i]);
91         putc('\n', f);
92 }
93
94 static int print_index(const char *hex)
95 {
96         unsigned char buf[32];
97         long index = 0;
98         size_t n, rc;
99         int ret = -1;
100
101         n = decode_hex(hex, buf, sizeof buf);
102         if (n == -1) {
103                 fprintf(stderr, "%s: invalid hex sequence: %s\n",
104                                 progname, hex);
105                 goto out;
106         } else if (n == 0) {
107                 fprintf(stderr, "%s: empty argument\n", progname);
108                 goto out;
109         } else if (n > sizeof buf) {
110                 fprintf(stderr, "%s: hex sequence too long: %s\n",
111                                 progname, hex);
112                 goto out;
113         }
114                 
115         rc = upkg_decode_index(&index, buf, n);
116         if (rc == 0) {
117                 fprintf(stderr, "%s: invalid index encoding:\n", progname);
118                 print_bytes(stderr, 4, buf, n);
119                 goto out;
120         } else if (rc < n) {
121                 fprintf(stderr, "%s: trailing bytes in argument:\n", progname);
122                 print_bytes(stderr, 4, buf+rc, n-rc);
123                 /* Non-fatal */
124         }
125
126         ret = 0;
127 out:
128         printf("%ld\n", index);
129         return ret;
130 }
131
132 int main(int argc, char **argv)
133 {
134         int opt, ret = EXIT_SUCCESS;
135
136         if (argc > 0)
137                 progname = argv[0];
138
139         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
140                 switch (opt) {
141                 case 'V':
142                         print_version();
143                         return EXIT_SUCCESS;
144                 case 'H':
145                         print_usage(stdout);
146                         return EXIT_SUCCESS;
147                 default:
148                         print_usage(stderr);
149                         return EXIT_FAILURE;
150                 }
151         }
152
153         if (!argv[optind]) {
154                 print_usage(stderr);
155                 return EXIT_FAILURE;
156         }
157
158         for (int i = optind; i < argc; i++) {
159                 if (print_index(argv[i]) != 0) {
160                         ret = EXIT_FAILURE;
161                 }
162         }
163
164         return ret;
165 }