1 /* Selectively compare two ar archives.
2 * Usage:
3 * ardiff [-ni] [-t name] ar1 ar2
4 * Options:
5 * -c compare member content. (This implies -s)
6 * -n compare member name.
7 * -i compare member mtime.
8 * -l compare archive length (member count).
9 * -s compare member size.
10 * -t specify the test name.
11 *
12 * By default, it compares nothing and consider the test "not ok"
13 * iff it encounters errors while reading archive.
14 *
15 * $Id: ardiff.c 3366 2016-01-24 21:33:06Z jkoshy $
16 */
17
18 #include <archive.h>
19 #include <archive_entry.h>
20 #include <err.h>
21 #include <errno.h>
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #define COUNTER "/tmp/bsdar-test-total"
28 #define PASSED "/tmp/bsdar-test-passed"
29
30 static void usage(void);
31 static void filediff(const char *tc, const char *msg, const char *e);
32 static void filesame(const char *tc);
33 static void incct(const char *pathname);
34
35 int
main(int argc,char ** argv)36 main(int argc, char **argv)
37 {
38 struct archive *a1;
39 struct archive *a2;
40 struct archive_entry *e1;
41 struct archive_entry *e2;
42 const char *tc;
43 char *buf1;
44 char *buf2;
45 char checkcont;
46 char checklen;
47 char checkname;
48 char checksize;
49 char checktime;
50 char a1end;
51 size_t size1;
52 size_t size2;
53 int opt, r;
54
55 /*
56 * Parse command line options.
57 */
58 checkcont = 0;
59 checklen = 0;
60 checkname = 0;
61 checksize = 0;
62 checktime = 0;
63 tc = NULL;
64 while ((opt = getopt(argc, argv, "cilnst:")) != -1) {
65 switch(opt) {
66 case 'c':
67 checkcont = 1;
68 break;
69 case 'i':
70 checktime = 1;
71 break;
72 case 'l':
73 checklen = 1;
74 break;
75 case 'n':
76 checkname = 1;
77 break;
78 case 's':
79 checksize = 1;
80 case 't':
81 tc = optarg;
82 break;
83 default:
84 usage();
85 }
86 }
87
88 argc -= optind;
89 argv += optind;
90 if (argc != 2)
91 usage();
92
93 /* Open file 1 */
94 a1 = archive_read_new();
95 archive_read_support_format_ar(a1);
96 if (archive_read_open_filename(a1, argv[0],
97 1024*10)) {
98 warnx("%s", archive_error_string(a1));
99 filediff(tc, "archive open failed", NULL);
100 }
101
102 /* Open file 2 */
103 a2 = archive_read_new();
104 archive_read_support_format_ar(a2);
105 if (archive_read_open_filename(a2, argv[1],
106 1024*10)) {
107 warnx("%s", archive_error_string(a2));
108 filediff(tc, "archive open failed", NULL);
109 }
110
111 /* Main loop */
112 a1end = 0;
113 size1 = 0;
114 size2 = 0;
115 for (;;) {
116 /*
117 * Read header from each archive, compare length.
118 */
119 r = archive_read_next_header(a1, &e1);
120 if (r == ARCHIVE_EOF)
121 a1end = 1;
122 if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY ||
123 r == ARCHIVE_FATAL) {
124 warnx("%s", archive_error_string(a1));
125 filediff(tc, "archive data error", NULL);
126 }
127 r = archive_read_next_header(a2, &e2);
128 if (r == ARCHIVE_EOF) {
129 if (a1end > 0)
130 break;
131 else {
132 if (checklen)
133 filediff(tc, "length differ", NULL);
134 break;
135 }
136 }
137 if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY ||
138 r == ARCHIVE_FATAL) {
139 warnx("%s", archive_error_string(a2));
140 filediff(tc, "archive data error", NULL);
141 }
142 if (a1end > 0) {
143 if (checklen)
144 filediff(tc, "length differ", NULL);
145 break;
146 }
147
148 /*
149 * Check member name if required.
150 */
151 if (checkname) {
152 if (strcmp(archive_entry_pathname(e1),
153 archive_entry_pathname(e2)) != 0)
154 filediff(tc, "member name differ",
155 archive_entry_pathname(e1));
156 }
157
158 /*
159 * Compare time if required.
160 */
161 if (checktime) {
162 if (archive_entry_mtime(e1) !=
163 archive_entry_mtime(e2))
164 filediff(tc, "member mtime differ",
165 archive_entry_pathname(e1));
166 }
167
168 /*
169 * Compare member size if required.
170 */
171 if (checksize || checkcont) {
172 size1 = (size_t) archive_entry_size(e1);
173 size2 = (size_t) archive_entry_size(e2);
174 if (size1 != size2)
175 filediff(tc, "member size differ",
176 archive_entry_pathname(e1));
177 }
178
179 /*
180 * Compare member content if required.
181 */
182 if (checkcont) {
183 if ((buf1 = malloc(size1)) == NULL)
184 filediff(tc, "not enough memory", NULL);
185 if ((buf2 = malloc(size2)) == NULL)
186 filediff(tc, "not enough memory", NULL);
187 if ((size_t) archive_read_data(a1, buf1, size1) !=
188 size1)
189 filediff(tc, "archive_read_data failed",
190 archive_entry_pathname(e1));
191 if ((size_t) archive_read_data(a2, buf2, size2) !=
192 size2)
193 filediff(tc, "archive_read_data failed",
194 archive_entry_pathname(e1));
195 if (memcmp(buf1, buf2, size1) != 0)
196 filediff(tc, "member content differ",
197 archive_entry_pathname(e1));
198 free(buf1);
199 free(buf2);
200 }
201
202 /* Proceed to next header. */
203 }
204
205 /* Passed! */
206 filesame(tc);
207 exit(EXIT_SUCCESS);
208 }
209
210 static void
filediff(const char * tc,const char * msg,const char * e)211 filediff(const char *tc, const char *msg, const char *e)
212 {
213 if (e != NULL)
214 fprintf(stdout, "%s - archive diff not ok (%s (entry: %s))\n",
215 tc, msg, e);
216 else
217 fprintf(stdout, "%s - archive diff not ok (%s)\n", tc, msg);
218
219 incct(COUNTER);
220 exit(EXIT_SUCCESS);
221 }
222
223 static void
filesame(const char * tc)224 filesame(const char *tc)
225 {
226 fprintf(stdout, "%s - archive diff ok\n", tc);
227 incct(COUNTER);
228 incct(PASSED);
229 }
230
231 static void
incct(const char * pathname)232 incct(const char *pathname)
233 {
234 FILE *fp;
235 char buf[10];
236
237 if ((fp = fopen(pathname, "r")) != NULL) {
238 if (fgets(buf, 10, fp) != buf)
239 perror("fgets");
240 snprintf(buf, 10, "%d\n", atoi(buf) + 1);
241 fclose(fp);
242 }
243 if ((fp = fopen(pathname, "w")) != NULL) {
244 fputs(buf, fp);
245 fclose(fp);
246 }
247 }
248
249 static void
usage(void)250 usage(void)
251 {
252 fprintf(stderr, "usage: ardiff archive1 archive2\n");
253 exit(EXIT_FAILURE);
254 }
255