dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / bart / compare.c
blob4b62f6372bf6c76dae19265dba045f314deadcad
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <unistd.h>
28 #include "bart.h"
30 static int compare_manifests(FILE *rulesfile, char *control, char *test,
31 boolean_t prog_fmt, uint_t flags);
32 static void extract_fname_ftype(char *line, char *fname, char *type);
33 static int report_add(char *fname, char *type);
34 static int report_delete(char *fname, char *type);
35 static int evaluate_differences(char *control_line, char *test_line,
36 boolean_t prog_fmt, int flags);
37 static void report_error(char *fname, char *type, char *ctrl_val,
38 char *test_val, boolean_t prog_fmt);
39 static int read_manifest_line(FILE *fd, char *buf, int buf_size, int start_pos,
40 char **line, char *fname);
41 static void parse_line(char *line, char *fname, char *type, char *size,
42 char *mode, char *acl, char *mtime, char *uid, char *gid, char *contents,
43 char *devnode, char *dest);
44 static void init_default_flags(uint_t *flags);
45 static void get_token(char *line, int *curr_pos, int line_len, char *buf,
46 int buf_size);
48 int
49 bart_compare(int argc, char **argv)
51 char *control_fname, *test_fname;
52 int c;
53 FILE *rules_fd = NULL;
54 uint_t glob_flags;
55 boolean_t prog_fmt = B_FALSE;
57 init_default_flags(&glob_flags);
59 while ((c = getopt(argc, argv, "pr:i:")) != EOF) {
60 switch (c) {
61 case 'p':
62 prog_fmt = B_TRUE;
63 break;
65 case 'r':
66 if (optarg == NULL)
67 usage();
69 if (strcmp(optarg, "-") == 0)
70 rules_fd = stdin;
71 else
72 rules_fd = fopen(optarg, "r");
73 if (rules_fd == NULL) {
74 perror(optarg);
75 usage();
77 break;
79 case 'i':
80 process_glob_ignores(optarg, &glob_flags);
81 break;
83 case '?':
84 default:
85 usage();
89 /* Make sure we have the right number of args */
90 if ((optind + 2) != argc)
91 usage();
92 argv += optind;
93 control_fname = argv[0];
94 test_fname = argv[1];
95 /* At this point, the filenames are sane, so do the comparison */
96 return (compare_manifests(rules_fd, control_fname, test_fname,
97 prog_fmt, glob_flags));
100 static int
101 compare_manifests(FILE *rulesfile, char *control, char *test,
102 boolean_t prog_fmt, uint_t flags)
104 FILE *control_fd, *test_fd;
105 char *control_line, *test_line, control_buf[BUF_SIZE],
106 test_buf[BUF_SIZE], control_fname[PATH_MAX],
107 control_type[TYPE_SIZE], test_fname[PATH_MAX],
108 test_type[TYPE_SIZE];
109 int control_pos, test_pos, ret, fname_cmp, return_status;
111 return_status = EXIT;
113 return_status = read_rules(rulesfile, "", flags, 0);
115 control_fd = fopen(control, "r");
116 if (control_fd == NULL) {
117 perror(control);
118 return (FATAL_EXIT);
121 test_fd = fopen(test, "r");
122 if (test_fd == NULL) {
123 perror(test);
124 return (FATAL_EXIT);
127 control_pos = read_manifest_line(control_fd, control_buf,
128 BUF_SIZE, 0, &control_line, control);
129 test_pos = read_manifest_line(test_fd, test_buf, BUF_SIZE, 0,
130 &test_line, test);
132 while ((control_pos != -1) && (test_pos != -1)) {
133 ret = strcmp(control_line, test_line);
134 if (ret == 0) {
135 /* Lines compare OK, just read the next lines.... */
136 control_pos = read_manifest_line(control_fd,
137 control_buf, BUF_SIZE, control_pos, &control_line,
138 control);
139 test_pos = read_manifest_line(test_fd, test_buf,
140 BUF_SIZE, test_pos, &test_line, test);
141 continue;
145 * Something didn't compare properly.
147 extract_fname_ftype(control_line, control_fname, control_type);
148 extract_fname_ftype(test_line, test_fname, test_type);
149 fname_cmp = strcmp(control_fname, test_fname);
151 if (fname_cmp == 0) {
153 * Filenames were the same, see what was
154 * different and continue.
156 if (evaluate_differences(control_line, test_line,
157 prog_fmt, flags) != 0)
158 return_status = WARNING_EXIT;
160 control_pos = read_manifest_line(control_fd,
161 control_buf, BUF_SIZE, control_pos, &control_line,
162 control);
163 test_pos = read_manifest_line(test_fd, test_buf,
164 BUF_SIZE, test_pos, &test_line, test);
165 } else if (fname_cmp > 0) {
166 /* Filenames were different, a files was ADDED */
167 if (report_add(test_fname, test_type)) {
168 report_error(test_fname, ADD_KEYWORD, NULL,
169 NULL, prog_fmt);
170 return_status = WARNING_EXIT;
172 test_pos = read_manifest_line(test_fd, test_buf,
173 BUF_SIZE, test_pos, &test_line, test);
174 } else if (fname_cmp < 0) {
175 /* Filenames were different, a files was DELETED */
176 if (report_delete(control_fname, control_type)) {
177 report_error(control_fname, DELETE_KEYWORD,
178 NULL, NULL, prog_fmt);
179 return_status = WARNING_EXIT;
181 control_pos = read_manifest_line(control_fd,
182 control_buf, BUF_SIZE, control_pos, &control_line,
183 control);
188 * Entering this while loop means files were DELETED from the test
189 * manifest.
191 while (control_pos != -1) {
192 (void) sscanf(control_line, "%1023s", control_fname);
193 if (report_delete(control_fname, control_type)) {
194 report_error(control_fname, DELETE_KEYWORD, NULL,
195 NULL, prog_fmt);
196 return_status = WARNING_EXIT;
198 control_pos = read_manifest_line(control_fd, control_buf,
199 BUF_SIZE, control_pos, &control_line, control);
203 * Entering this while loop means files were ADDED to the test
204 * manifest.
206 while (test_pos != -1) {
207 (void) sscanf(test_line, "%1023s", test_fname);
208 if (report_add(test_fname, test_type)) {
209 report_error(test_fname, ADD_KEYWORD, NULL,
210 NULL, prog_fmt);
211 return_status = WARNING_EXIT;
213 test_pos = read_manifest_line(test_fd, test_buf,
214 BUF_SIZE, test_pos, &test_line, test);
217 (void) fclose(control_fd);
218 (void) fclose(test_fd);
220 /* For programmatic mode, add a newline for cosmetic reasons */
221 if (prog_fmt && (return_status != 0))
222 (void) printf("\n");
224 return (return_status);
227 static void
228 parse_line(char *line, char *fname, char *type, char *size, char *mode,
229 char *acl, char *mtime, char *uid, char *gid, char *contents, char *devnode,
230 char *dest)
232 int pos, line_len;
234 line_len = strlen(line);
235 pos = 0;
237 get_token(line, &pos, line_len, fname, PATH_MAX);
238 get_token(line, &pos, line_len, type, TYPE_SIZE);
239 get_token(line, &pos, line_len, size, MISC_SIZE);
240 get_token(line, &pos, line_len, mode, MISC_SIZE);
241 get_token(line, &pos, line_len, acl, ACL_SIZE);
242 get_token(line, &pos, line_len, mtime, MISC_SIZE);
243 get_token(line, &pos, line_len, uid, MISC_SIZE);
244 get_token(line, &pos, line_len, gid, MISC_SIZE);
246 /* Reset these fields... */
248 *contents = '\0';
249 *devnode = '\0';
250 *dest = '\0';
252 /* Handle filetypes which have a last field..... */
253 if (type[0] == 'F')
254 get_token(line, &pos, line_len, contents, PATH_MAX);
255 else if ((type[0] == 'B') || (type[0] == 'C'))
256 get_token(line, &pos, line_len, devnode, PATH_MAX);
257 else if (type[0] == 'L')
258 get_token(line, &pos, line_len, dest, PATH_MAX);
261 static void
262 get_token(char *line, int *curr_pos, int line_len, char *buf, int buf_size)
264 int cnt = 0;
266 while (isspace(line[*curr_pos]) && (*curr_pos < line_len))
267 (*curr_pos)++;
269 while (!isspace(line[*curr_pos]) &&
270 (*curr_pos < line_len) && (cnt < (buf_size-1))) {
271 buf[cnt] = line[*curr_pos];
272 (*curr_pos)++;
273 cnt++;
275 buf[cnt] = '\0';
279 * Utility function: extract fname and type from this line
281 static void
282 extract_fname_ftype(char *line, char *fname, char *type)
284 int line_len, pos;
286 pos = 0;
287 line_len = strlen(line);
289 get_token(line, &pos, line_len, fname, PATH_MAX);
290 get_token(line, &pos, line_len, type, TYPE_SIZE);
294 * Utility function: tells us whether or not this addition should be reported
296 * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
297 * reported.
299 static int
300 report_add(char *fname, char *type)
302 struct rule *rule_ptr;
304 rule_ptr = check_rules(fname, type[0]);
305 if ((rule_ptr != NULL) && (rule_ptr->attr_list & ATTR_ADD))
306 return (1);
307 else
308 return (0);
312 * Utility function: tells us whether or not this deletion should be reported
314 * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
315 * reported.
317 static int
318 report_delete(char *fname, char *type)
320 struct rule *rule_ptr;
322 rule_ptr = check_rules(fname, type[0]);
324 if ((rule_ptr != NULL) && (rule_ptr->attr_list & ATTR_DELETE))
325 return (1);
326 else
327 return (0);
331 * This function takes in the two entries, which have been flagged as
332 * different, breaks them up and reports discrepancies. Note, discrepancies
333 * are affected by the 'CHECK' and 'IGNORE' stanzas which may apply to
334 * these entries.
336 * Returns the number of discrepancies reported.
338 static int
339 evaluate_differences(char *control_line, char *test_line,
340 boolean_t prog_fmt, int flags)
342 char ctrl_fname[PATH_MAX], test_fname[PATH_MAX],
343 ctrl_type[TYPE_SIZE], test_type[TYPE_SIZE],
344 ctrl_size[MISC_SIZE], ctrl_mode[MISC_SIZE],
345 ctrl_acl[ACL_SIZE], ctrl_mtime[MISC_SIZE],
346 ctrl_uid[MISC_SIZE], ctrl_gid[MISC_SIZE],
347 ctrl_dest[PATH_MAX], ctrl_contents[PATH_MAX],
348 ctrl_devnode[PATH_MAX], test_size[MISC_SIZE],
349 test_mode[MISC_SIZE], test_acl[ACL_SIZE],
350 test_mtime[MISC_SIZE], test_uid[MISC_SIZE],
351 test_gid[MISC_SIZE], test_dest[PATH_MAX],
352 test_contents[PATH_MAX], test_devnode[PATH_MAX],
353 *tag;
354 int ret_val;
355 struct rule *rule_ptr;
357 ret_val = 0;
359 parse_line(control_line, ctrl_fname, ctrl_type, ctrl_size, ctrl_mode,
360 ctrl_acl, ctrl_mtime, ctrl_uid, ctrl_gid, ctrl_contents,
361 ctrl_devnode, ctrl_dest);
364 * Now we know the fname and type, let's get the rule that matches this
365 * manifest entry. If there is a match, make sure to setup the
366 * correct reporting flags.
368 rule_ptr = check_rules(ctrl_fname, ctrl_type[0]);
369 if (rule_ptr != NULL)
370 flags = rule_ptr->attr_list;
372 parse_line(test_line, test_fname, test_type, test_size, test_mode,
373 test_acl, test_mtime, test_uid, test_gid, test_contents,
374 test_devnode, test_dest);
377 * Report the errors based upon which keywords have been set by
378 * the user.
380 if ((flags & ATTR_TYPE) && (ctrl_type[0] != test_type[0])) {
381 report_error(ctrl_fname, TYPE_KEYWORD, ctrl_type,
382 test_type, prog_fmt);
383 ret_val++;
386 if ((flags & ATTR_SIZE) && (strcmp(ctrl_size, test_size) != 0)) {
387 report_error(ctrl_fname, SIZE_KEYWORD, ctrl_size,
388 test_size, prog_fmt);
389 ret_val++;
392 if ((flags & ATTR_MODE) && (strcmp(ctrl_mode, test_mode) != 0)) {
393 report_error(ctrl_fname, MODE_KEYWORD, ctrl_mode,
394 test_mode, prog_fmt);
395 ret_val++;
398 if ((flags & ATTR_ACL) && (strcmp(ctrl_acl, test_acl) != 0)) {
399 report_error(ctrl_fname, ACL_KEYWORD, ctrl_acl,
400 test_acl, prog_fmt);
401 ret_val++;
404 if ((flags & ATTR_MTIME) && (ctrl_type[0] == test_type[0])) {
405 if (strcmp(ctrl_mtime, test_mtime) != 0) {
406 switch (ctrl_type[0]) {
407 case 'D':
408 tag = "dirmtime";
409 break;
410 case 'L':
411 tag = "lnmtime";
412 break;
413 default:
414 tag = "mtime";
415 break;
417 if (flags == 0) {
418 report_error(ctrl_fname, tag, ctrl_mtime,
419 test_mtime, prog_fmt);
420 ret_val++;
424 if ((ctrl_type[0] == 'F') && (flags & ATTR_MTIME) &&
425 (strcmp(ctrl_mtime, test_mtime) != 0)) {
426 report_error(ctrl_fname, MTIME_KEYWORD, ctrl_mtime, test_mtime,
427 prog_fmt);
428 ret_val++;
431 if ((ctrl_type[0] == 'D') && (flags & ATTR_DIRMTIME) &&
432 (strcmp(ctrl_mtime, test_mtime) != 0)) {
433 report_error(ctrl_fname, DIRMTIME_KEYWORD, ctrl_mtime,
434 test_mtime, prog_fmt);
435 ret_val++;
438 if ((ctrl_type[0] == 'L') && (flags & ATTR_LNMTIME) &&
439 (strcmp(ctrl_mtime, test_mtime) != 0)) {
440 report_error(ctrl_fname, LNMTIME_KEYWORD, ctrl_mtime,
441 test_mtime, prog_fmt);
442 ret_val++;
444 } else if ((flags & ATTR_MTIME) &&
445 (strcmp(ctrl_mtime, test_mtime) != 0)) {
446 report_error(ctrl_fname, MTIME_KEYWORD, ctrl_mtime,
447 test_mtime, prog_fmt);
448 ret_val++;
451 if ((flags & ATTR_UID) && (strcmp(ctrl_uid, test_uid) != 0)) {
452 report_error(ctrl_fname, UID_KEYWORD, ctrl_uid,
453 test_uid, prog_fmt);
454 ret_val++;
457 if ((flags & ATTR_GID) && (strcmp(ctrl_gid, test_gid) != 0)) {
458 report_error(ctrl_fname, GID_KEYWORD, ctrl_gid,
459 test_gid, prog_fmt);
460 ret_val++;
463 if ((flags & ATTR_DEVNODE) &&
464 (strcmp(ctrl_devnode, test_devnode) != 0)) {
465 report_error(ctrl_fname, DEVNODE_KEYWORD, ctrl_devnode,
466 test_devnode, prog_fmt);
467 ret_val++;
470 if ((flags & ATTR_DEST) && (strcmp(ctrl_dest, test_dest) != 0)) {
471 report_error(ctrl_fname, DEST_KEYWORD, ctrl_dest,
472 test_dest, prog_fmt);
473 ret_val++;
476 if ((flags & ATTR_CONTENTS) &&
477 (strcmp(ctrl_contents, test_contents)) != 0) {
478 report_error(ctrl_fname, CONTENTS_KEYWORD, ctrl_contents,
479 test_contents, prog_fmt);
480 ret_val++;
483 return (ret_val);
487 * Function responsible for reporting errors.
489 static void
490 report_error(char *fname, char *type, char *ctrl_val, char *test_val,
491 boolean_t prog_fmt)
493 static char last_fname[PATH_MAX] = "";
495 if (!prog_fmt) {
496 /* Verbose mode */
497 if (strcmp(fname, last_fname) != 0) {
498 (void) printf("%s:\n", fname);
499 (void) strlcpy(last_fname, fname, sizeof (last_fname));
502 if (strcmp(type, ADD_KEYWORD) == 0 ||
503 strcmp(type, DELETE_KEYWORD) == 0)
504 (void) printf(" %s\n", type);
505 else
506 (void) printf(" %s control:%s test:%s\n", type,
507 ctrl_val, test_val);
508 } else {
509 /* Programmatic mode */
510 if (strcmp(fname, last_fname) != 0) {
511 /* Ensure a line is not printed for the initial case */
512 if (strlen(last_fname) != 0)
513 (void) printf("\n");
514 (void) strlcpy(last_fname, fname, sizeof (last_fname));
515 (void) printf("%s ", fname);
518 (void) printf("%s ", type);
519 if (strcmp(type, ADD_KEYWORD) != 0 &&
520 strcmp(type, DELETE_KEYWORD) != 0) {
521 (void) printf("%s ", ctrl_val);
522 (void) printf("%s ", test_val);
528 * Function responsible for reading in a line from the manifest.
529 * Takes in the file ptr and a buffer, parses the buffer and sets the 'line'
530 * ptr correctly. In the case when the buffer is fully parsed, this function
531 * reads more data from the file ptr and refills the buffer.
533 static int
534 read_manifest_line(FILE *fd, char *buf, int buf_size, int start_pos,
535 char **line, char *fname)
537 int end_pos, len, iscomment = 0, filepos;
540 * Initialization case: make sure the manifest version is OK
542 if (start_pos == 0) {
543 end_pos = 0;
544 buf[0] = '\0';
545 filepos = ftell(fd);
546 (void) fread((void *) buf, (size_t)buf_size, (size_t)1, fd);
548 *line = buf;
550 if (filepos == 0) {
551 if (strncmp(buf, MANIFEST_VER,
552 strlen(MANIFEST_VER)) != 0)
553 (void) fprintf(stderr, MISSING_VER, fname);
554 if ((*line[0] == '!') || (*line[0] == '#'))
555 iscomment++;
557 while (iscomment) {
558 while ((buf[end_pos] != '\n') &&
559 (buf[end_pos] != '\0') &&
560 (end_pos < buf_size))
561 end_pos++;
563 if (end_pos >= buf_size)
564 return (-1);
566 end_pos++;
567 *line = &(buf[end_pos]);
568 iscomment = 0;
569 if ((*line[0] == '!') || (*line[0] == '#'))
570 iscomment++;
574 while ((buf[end_pos] != '\n') && (buf[end_pos] != '\0') &&
575 (end_pos < buf_size))
576 end_pos++;
578 if (end_pos < buf_size) {
579 if (buf[end_pos] == '\n') {
580 buf[end_pos] = '\0';
581 return (end_pos);
584 if (buf[end_pos] == '\0')
585 return (-1);
588 (void) fprintf(stderr, MANIFEST_ERR);
589 exit(FATAL_EXIT);
592 end_pos = (start_pos+1);
593 *line = &(buf[end_pos]);
595 /* Read the buffer until EOL or the buffer is empty */
596 while ((buf[end_pos] != '\n') && (buf[end_pos] != '\0') &&
597 (end_pos < buf_size))
598 end_pos++;
600 if (end_pos < buf_size) {
601 /* Found the end of the line, normal exit */
602 if (buf[end_pos] == '\n') {
603 buf[end_pos] = '\0';
604 return (end_pos);
607 /* No more input to read */
608 if (buf[end_pos] == '\0')
609 return (-1);
613 * The following code takes the remainder of the buffer and
614 * puts it at the beginning. The space after the remainder, which
615 * is now at the beginning, is blanked.
616 * At this point, read in more data and continue to find the EOL....
618 len = end_pos - (start_pos + 1);
619 (void) memcpy(buf, &(buf[start_pos+1]), (size_t)len);
620 (void) memset(&buf[len], '\0', (buf_size - len));
621 (void) fread((void *) &buf[len], (size_t)(buf_size-len), (size_t)1, fd);
622 *line = buf;
623 end_pos = len;
625 /* Read the buffer until EOL or the buffer is empty */
626 while ((buf[end_pos] != '\n') && (buf[end_pos] != '\0') &&
627 (end_pos < buf_size))
628 end_pos++;
630 if (end_pos < buf_size) {
631 /* Found the end of the line, normal exit */
632 if (buf[end_pos] == '\n') {
633 buf[end_pos] = '\0';
634 return (end_pos);
637 /* No more input to read */
638 if (buf[end_pos] == '\0')
639 return (-1);
642 (void) fprintf(stderr, MANIFEST_ERR);
643 exit(FATAL_EXIT);
645 /* NOTREACHED */
648 static void
649 init_default_flags(uint_t *flags)
651 /* Default behavior: everything is checked *except* dirmtime */
652 *flags = ATTR_ALL & ~(ATTR_DIRMTIME);