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