#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/mman.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "file.h"
#include "fcntl.h"
#include "mmap.h"
#include "os.h"
#define CMPMAP_ZERO (1<<0)
#define ADDR_SUM(vp, lp) ((void *)((char *)(vp) + lp))
static char *File1 = NULL;
static char *File2 = NULL;
static size_t Length = 0;
static os_off_t Offset = 0;
static int Opts = 0;
static void
print_usage(void)
{
printf("Usage: cmpmap [options] file1 [file2]\n");
printf("Valid options:\n");
printf("-l, --length=N - compare up to N bytes\n");
printf("-o, --offset=N - skip N bytes at start of the files\n");
printf("-z, --zero - compare bytes of the file1 to NUL\n");
printf("-h, --help - print this usage info\n");
}
static const struct option long_options[] = {
{"length", required_argument, NULL, 'l'},
{"offset", required_argument, NULL, 'o'},
{"zero", no_argument, NULL, 'z'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0 },
};
static int
parse_args(int argc, char *argv[])
{
int opt;
char *endptr;
os_off_t off;
ssize_t len;
while ((opt = getopt_long(argc, argv, "l:o:zh",
long_options, NULL)) != -1) {
switch (opt) {
case 'l':
errno = 0;
len = strtoll(optarg, &endptr, 0);
if ((endptr && *endptr != '\0') || errno || len < 0) {
fprintf(stderr, "'%s' -- invalid length",
optarg);
return -1;
}
Length = (size_t)len;
break;
case 'o':
errno = 0;
off = strtol(optarg, &endptr, 0);
if ((endptr && *endptr != '\0') || errno || off < 0) {
fprintf(stderr, "'%s' -- invalid offset",
optarg);
return -1;
}
Offset = off;
break;
case 'z':
Opts |= CMPMAP_ZERO;
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
default:
print_usage();
exit(EXIT_FAILURE);
}
}
if (optind < argc) {
File1 = argv[optind];
if (optind + 1 < argc)
File2 = argv[optind + 1];
} else {
print_usage();
exit(EXIT_FAILURE);
}
return 0;
}
static int
validate_args(void)
{
if (File1 == NULL) {
fprintf(stderr, "no file provided");
return -1;
} else if (File2 == NULL && Length == 0) {
fprintf(stderr, "length of the file has to be provided");
return -1;
}
return 0;
}
static int
do_cmpmap(void)
{
int ret = EXIT_SUCCESS;
int fd1;
int fd2;
size_t size1;
size_t size2;
if ((fd1 = os_open(File1, O_RDONLY)) < 0) {
fprintf(stderr, "opening %s failed, errno %d\n", File1, errno);
exit(EXIT_FAILURE);
}
ssize_t size_tmp = util_file_get_size(File1);
if (size_tmp < 0) {
fprintf(stderr, "getting size of %s failed, errno %d\n", File1,
errno);
ret = EXIT_FAILURE;
goto out_close1;
}
size1 = (size_t)size_tmp;
int flag = MAP_SHARED;
if (Opts & CMPMAP_ZERO) {
fd2 = -1;
size2 = (size_t)Offset + Length;
flag |= MAP_ANONYMOUS;
} else if (File2 != NULL) {
if ((fd2 = os_open(File2, O_RDONLY)) < 0) {
fprintf(stderr, "opening %s failed, errno %d\n",
File2, errno);
ret = EXIT_FAILURE;
goto out_close1;
}
size_tmp = util_file_get_size(File2);
if (size_tmp < 0) {
fprintf(stderr, "getting size of %s failed, errno %d\n",
File2, errno);
ret = EXIT_FAILURE;
goto out_close2;
}
size2 = (size_t)size_tmp;
size_t min_size = (size1 < size2) ? size1 : size2;
if ((size_t)Offset + Length > min_size) {
if (size1 != size2) {
fprintf(stdout, "%s %s differ in size: %zu"
" %zu\n", File1, File2, size1, size2);
ret = EXIT_FAILURE;
goto out_close2;
} else {
Length = min_size - (size_t)Offset;
}
}
} else {
assert(0);
}
util_init();
void *addr1;
if ((addr1 = util_map(fd1, size1, MAP_SHARED, 1, 0)) == MAP_FAILED) {
fprintf(stderr, "mmap failed, file %s, length %zu, offset 0,"
" errno %d\n", File1, size1, errno);
ret = EXIT_FAILURE;
goto out_close2;
}
void *addr2;
if ((addr2 = util_map(fd2, size2, flag, 1, 0)) == MAP_FAILED) {
fprintf(stderr, "mmap failed, file %s, length %zu, errno %d\n",
File2 ? File2 : "(anonymous)", size2, errno);
ret = EXIT_FAILURE;
goto out_unmap1;
}
if ((ret = memcmp(ADDR_SUM(addr1, Offset), ADDR_SUM(addr2, Offset),
Length))) {
if (Opts & CMPMAP_ZERO)
fprintf(stdout, "%s is not zeroed\n", File1);
else
fprintf(stdout, "%s %s differ\n", File1, File2);
ret = EXIT_FAILURE;
}
munmap(addr2, size2);
out_unmap1:
munmap(addr1, size1);
out_close2:
if (File2 != NULL)
(void) os_close(fd2);
out_close1:
(void) os_close(fd1);
exit(ret);
}
int
main(int argc, char *argv[])
{
if (parse_args(argc, argv))
exit(EXIT_FAILURE);
if (validate_args())
exit(EXIT_FAILURE);
do_cmpmap();
}