diff options
Diffstat (limited to 'src/kmk/kmkbuiltin/cmp_util.c')
-rw-r--r-- | src/kmk/kmkbuiltin/cmp_util.c | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/src/kmk/kmkbuiltin/cmp_util.c b/src/kmk/kmkbuiltin/cmp_util.c new file mode 100644 index 0000000..0228f38 --- /dev/null +++ b/src/kmk/kmkbuiltin/cmp_util.c @@ -0,0 +1,562 @@ +/* $NetBSD: cmp.c,v 1.15 2006/01/19 20:44:57 garbled Exp $ */ +/* $NetBSD: misc.c,v 1.11 2007/08/22 16:59:19 christos Exp $ */ +/* $NetBSD: regular.c,v 1.20 2006/06/03 21:47:55 christos Exp $ */ +/* $NetBSD: special.c,v 1.12 2007/08/21 14:09:54 christos Exp $ */ + +/* + * Copyright (c) 1987, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*__COPYRIGHT("@(#) Copyright (c) 1987, 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n");*/ + +#ifdef _MSC_VER +# define MSC_DO_64_BIT_IO +#endif +#include "config.h" +#include <sys/types.h> +#include <sys/stat.h> +#if defined(__FreeBSD__) || defined(__NetBSD__) /** @todo more mmap capable OSes. */ +# define CMP_USE_MMAP +# include <sys/param.h> +# include <sys/mman.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef _MSC_VER +# include <unistd.h> +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#else +# include "mscfakes.h" +#endif +#include "err.h" + +#include "cmp_extern.h" + + +static int +errmsg(PKMKBUILTINCTX pCtx, const char *file, off_t byte, off_t line, int lflag) +{ + if (lflag) +#ifdef _MSC_VER + return err(pCtx, ERR_EXIT, "%s: char %I64d, line %lld", file, (__int64)byte, (long long)line); +#else + return err(pCtx, ERR_EXIT, "%s: char %lld, line %lld", file, (long long)byte, (long long)line); +#endif + return err(pCtx, ERR_EXIT, "%s", file); +} + + +static int +eofmsg(PKMKBUILTINCTX pCtx, const char *file, off_t byte, off_t line, int sflag, int lflag) +{ + if (!sflag) + { + if (!lflag) + warnx(pCtx, "EOF on %s", file); + else + { +#ifdef _MSC_VER + if (line > 0) + warnx(pCtx, "EOF on %s: char %I64d, line %I64d", file, (__int64)byte, (__int64)line); + else + warnx(pCtx, "EOF on %s: char %I64d", file, (__int64)byte); +#else + if (line > 0) + warnx(pCtx, "EOF on %s: char %lld, line %lld", file, (long long)byte, (long long)line); + else + warnx(pCtx, "EOF on %s: char %lld", file, (long long)byte); +#endif + } + } + return DIFF_EXIT; +} + + +static int +diffmsg(PKMKBUILTINCTX pCtx, const char *file1, const char *file2, off_t byte, off_t line, int sflag) +{ + if (!sflag) +#ifdef _MSC_VER + kmk_builtin_ctx_printf(pCtx, 0, "%s %s differ: char %I64d, line %I64d\n", + file1, file2, (__int64)byte, (__int64)line); +#else + kmk_builtin_ctx_printf(pCtx, 0, "%s %s differ: char %lld, line %lld\n", + file1, file2, (long long)byte, (long long)line); +#endif + return DIFF_EXIT; +} + + +/** + * Compares two files, where one or both are non-regular ones. + */ +static int +c_special(PKMKBUILTINCTX pCtx, int fd1, const char *file1, off_t skip1, + int fd2, const char *file2, off_t skip2, + int lflag, int sflag) +{ + int fd1dup, fd2dup; + FILE *fp1; + int rc; + + /* duplicate because fdopen+fclose will otherwise close the handle. */ + fd1dup = dup(fd1); + if (fd1 < 0) + return err(pCtx, ERR_EXIT, "%s", file1); + fp1 = fdopen(fd1dup, "rb"); + if (!fp1) + fp1 = fdopen(fd1dup, "r"); + if (!fp1) + { + err(pCtx, ERR_EXIT, "%s", file1); + close(fd1dup); + return ERR_EXIT; + } + + fd2dup = dup(fd2); + if (fd2dup >= 0) + { + FILE *fp2 = fdopen(fd2dup, "rb"); + if (!fp2) + fp2 = fdopen(fd2dup, "r"); + if (fp2) + { + off_t byte; + off_t line; + int ch1 = 0; + int ch2 = 0; + + /* skipping ahead */ + rc = OK_EXIT; + for (byte = line = 1; skip1--; byte++) + { + ch1 = getc(fp1); + if (ch1 == EOF) + break; + if (ch1 == '\n') + line++; + } + for (byte = line = 1; skip2--; byte++) + { + ch2 = getc(fp2); + if (ch2 == EOF) + break; + if (ch2 == '\n') + line++; + } + if (ch2 != EOF && ch1 != EOF) + { + /* compare byte by byte */ + for (byte = line = 1;; ++byte) + { + ch1 = getc(fp1); + ch2 = getc(fp2); + if (ch1 == EOF || ch2 == EOF) + break; + if (ch1 != ch2) + { + if (!lflag) + { + rc = diffmsg(pCtx, file1, file2, byte, line, sflag); + break; + } + rc = DIFF_EXIT; +#ifdef _MSC_VER + kmk_builtin_ctx_printf(pCtx, 0, "%6i64d %3o %3o\n", (__int64)byte, ch1, ch2); +#else + kmk_builtin_ctx_printf(pCtx, 0, "%6lld %3o %3o\n", (long long)byte, ch1, ch2); +#endif + } + if (ch1 == '\n') + ++line; + } + } + + /* Check for errors and length differences (EOF). */ + if (ferror(fp1) && rc != ERR_EXIT) + rc = errmsg(pCtx, file1, byte, line, lflag); + if (ferror(fp2) && rc != ERR_EXIT) + rc = errmsg(pCtx, file2, byte, line, lflag); + if (rc == OK_EXIT) + { + if (feof(fp1)) + { + if (!feof(fp2)) + rc = eofmsg(pCtx, file1, byte, line, sflag, lflag); + } + else if (feof(fp2)) + rc = eofmsg(pCtx, file2, byte, line, sflag, lflag); + } + + fclose(fp2); + } + else + { + rc = err(pCtx, ERR_EXIT, "%s", file2); + close(fd2dup); + } + } + else + rc = err(pCtx, ERR_EXIT, "%s", file2); + + fclose(fp1); + return rc; +} + + +#ifdef CMP_USE_MMAP +/** + * Compare two files using mmap. + */ +static int +c_regular(PKMKBUILTINCTX pCtx, int fd1, const char *file1, off_t skip1, off_t len1, + int fd2, const char *file2, off_t skip2, off_t len2, int sflag, int lflag) +{ + unsigned char ch, *p1, *p2, *b1, *b2; + off_t byte, length, line; + int dfound; + size_t blk_sz, blk_cnt; + + if (sflag && len1 != len2) + return DIFF_EXIT; + + if (skip1 > len1) + return eofmsg(pCtx, file1, len1 + 1, 0, sflag, lflag); + len1 -= skip1; + if (skip2 > len2) + return eofmsg(pCtx, file2, len2 + 1, 0, sflag, lflag); + len2 -= skip2; + + byte = line = 1; + dfound = 0; + length = len1 <= len2 ? len1 : len2; + for (blk_sz = 1024 * 1024; length != 0; length -= blk_sz) + { + if (blk_sz > length) + blk_sz = length; + b1 = p1 = mmap(NULL, blk_sz, PROT_READ, MAP_FILE | MAP_SHARED, fd1, skip1); + if (p1 == MAP_FAILED) + goto l_mmap_failed; + + b2 = p2 = mmap(NULL, blk_sz, PROT_READ, MAP_FILE | MAP_SHARED, fd2, skip2); + if (p2 == MAP_FAILED) + { + munmap(p1, blk_sz); + goto l_mmap_failed; + } + + blk_cnt = blk_sz; + for (; blk_cnt--; ++p1, ++p2, ++byte) + { + if ((ch = *p1) != *p2) + { + if (!lflag) + { + munmap(b1, blk_sz); + munmap(b2, blk_sz); + return diffmsg(pCtx, file1, file2, byte, line, sflag); + } + dfound = 1; +#ifdef _MSC_VER + kmk_builtin_ctx_printf(pCtx, 0, "%6I64d %3o %3o\n", (__int64)byte, ch, *p2); +#else + kmk_builtin_ctx_printf(pCtx, 0, "%6lld %3o %3o\n", (long long)byte, ch, *p2); +#endif + } + if (ch == '\n') + ++line; + } + munmap(p1 - blk_sz, blk_sz); + munmap(p2 - blk_sz, blk_sz); + skip1 += blk_sz; + skip2 += blk_sz; + } + + if (len1 != len2) + return eofmsg(pCtx, len1 > len2 ? file2 : file1, byte, line, sflag, lflag); + if (dfound) + return DIFF_EXIT; + return OK_EXIT; + +l_mmap_failed: + return c_special(pCtx, fd1, file1, skip1, fd2, file2, skip2, lflag, sflag); +} + +#else /* non-mmap c_regular: */ + +/** + * Compare two files without mmaping them. + */ +static int +c_regular(PKMKBUILTINCTX pCtx, int fd1, const char *file1, off_t skip1, off_t len1, + int fd2, const char *file2, off_t skip2, off_t len2, int sflag, int lflag) +{ + unsigned char ch, *p1, *p2, *b1 = 0, *b2 = 0; + off_t byte, length, line, bytes_read; + int dfound; + size_t blk_sz, blk_cnt; + + if (sflag && len1 != len2) + return DIFF_EXIT; + + if (skip1 > len1) + return eofmsg(pCtx, file1, len1 + 1, 0, sflag, lflag); + len1 -= skip1; + if (skip2 > len2) + return eofmsg(pCtx, file2, len2 + 1, 0, sflag, lflag); + len2 -= skip2; + + if (skip1 && lseek(fd1, skip1, SEEK_SET) < 0) + goto l_special; + if (skip2 && lseek(fd2, skip2, SEEK_SET) < 0) + { + if (skip1 && lseek(fd1, 0, SEEK_SET) < 0) + return err(pCtx, 1, "seek failed"); + goto l_special; + } + +#define CMP_BUF_SIZE (128*1024) + + b1 = malloc(CMP_BUF_SIZE); + b2 = malloc(CMP_BUF_SIZE); + if (!b1 || !b2) + goto l_malloc_failed; + + byte = line = 1; + dfound = 0; + length = len1; + if (length > len2) + length = len2; + for (blk_sz = CMP_BUF_SIZE; length != 0; length -= blk_sz) + { + if ((off_t)blk_sz > length) + blk_sz = (size_t)length; + + bytes_read = read(fd1, b1, blk_sz); + if (bytes_read != (off_t)blk_sz) + goto l_read_error; + + bytes_read = read(fd2, b2, blk_sz); + if (bytes_read != (off_t)blk_sz) + goto l_read_error; + + blk_cnt = blk_sz; + p1 = b1; + p2 = b2; + for (; blk_cnt--; ++p1, ++p2, ++byte) + { + if ((ch = *p1) != *p2) + { + if (!lflag) + { + free(b1); + free(b2); + return diffmsg(pCtx, file1, file2, byte, line, sflag); + } + dfound = 1; +#ifdef _MSC_VER + kmk_builtin_ctx_printf(pCtx, 0, "%6I64d %3o %3o\n", (__int64)byte, ch, *p2); +#else + kmk_builtin_ctx_printf(pCtx, 0, "%6lld %3o %3o\n", (long long)byte, ch, *p2); +#endif + } + if (ch == '\n') + ++line; + } + skip1 += blk_sz; + skip2 += blk_sz; + } + + if (len1 != len2) + return eofmsg(pCtx, len1 > len2 ? file2 : file1, byte, line, sflag, lflag); + if (dfound) + return DIFF_EXIT; + return OK_EXIT; + +l_read_error: + if ( lseek(fd1, 0, SEEK_SET) < 0 + || lseek(fd2, 0, SEEK_SET) < 0) + { + err(pCtx, 1, "seek failed"); + free(b1); + free(b2); + return 1; + } +l_malloc_failed: + free(b1); + free(b2); +l_special: + return c_special(pCtx, fd1, file1, skip1, fd2, file2, skip2, lflag, sflag); +} +#endif /* non-mmap c_regular */ + + +/** + * Compares two open files. + */ +int +cmp_fd_and_fd_ex(PKMKBUILTINCTX pCtx, int fd1, const char *file1, off_t skip1, + int fd2, const char *file2, off_t skip2, + int sflag, int lflag, int special) +{ + struct stat st1, st2; + int rc; + + if (fstat(fd1, &st1)) + return err(pCtx, ERR_EXIT, "%s", file1); + if (fstat(fd2, &st2)) + return err(pCtx, ERR_EXIT, "%s", file2); + + if ( !S_ISREG(st1.st_mode) + || !S_ISREG(st2.st_mode) + || special) + rc = c_special(pCtx, fd1, file1, skip1, + fd2, file2, skip2, sflag, lflag); + else + rc = c_regular(pCtx, fd1, file1, skip1, st1.st_size, + fd2, file2, skip2, st2.st_size, sflag, lflag); + return rc; +} + + +/** + * Compares two open files. + */ +int +cmp_fd_and_fd(PKMKBUILTINCTX pCtx, int fd1, const char *file1, + int fd2, const char *file2, + int sflag, int lflag, int special) +{ + return cmp_fd_and_fd_ex(pCtx, fd1, file1, 0, fd2, file2, 0, sflag, lflag, special); +} + + +/** + * Compares an open file with another that isn't open yet. + */ +int +cmp_fd_and_file_ex(PKMKBUILTINCTX pCtx, int fd1, const char *file1, off_t skip1, + const char *file2, off_t skip2, + int sflag, int lflag, int special) +{ + int rc; + int fd2; + + if (!strcmp(file2, "-")) + { + fd2 = 0 /* stdin */; + special = 1; + file2 = "stdin"; + } + else + fd2 = open(file2, O_RDONLY | O_BINARY | KMK_OPEN_NO_INHERIT, 0); + if (fd2 >= 0) + { + rc = cmp_fd_and_fd_ex(pCtx, fd1, file1, skip1, + fd2, file2, skip2, sflag, lflag, special); + close(fd2); + } + else + { + if (!sflag) + warn(pCtx, "%s", file2); + rc = ERR_EXIT; + } + return rc; +} + + +/** + * Compares an open file with another that isn't open yet. + */ +int +cmp_fd_and_file(PKMKBUILTINCTX pCtx, int fd1, const char *file1, + const char *file2, + int sflag, int lflag, int special) +{ + return cmp_fd_and_file_ex(pCtx, fd1, file1, 0, + file2, 0, sflag, lflag, special); +} + + +/** + * Opens and compare two files. + */ +int +cmp_file_and_file_ex(PKMKBUILTINCTX pCtx, const char *file1, off_t skip1, + const char *file2, off_t skip2, + int sflag, int lflag, int special) +{ + int fd1; + int rc; + + if (lflag && sflag) + return errx(pCtx, ERR_EXIT, "only one of -l and -s may be specified"); + + if (!strcmp(file1, "-")) + { + if (!strcmp(file2, "-")) + return errx(pCtx, ERR_EXIT, "standard input may only be specified once"); + file1 = "stdin"; + fd1 = 1; + special = 1; + } + else + fd1 = open(file1, O_RDONLY | O_BINARY | KMK_OPEN_NO_INHERIT, 0); + if (fd1 >= 0) + { + rc = cmp_fd_and_file_ex(pCtx, fd1, file1, skip1, + file2, skip2, sflag, lflag, special); + close(fd1); + } + else + { + if (!sflag) + warn(pCtx, "%s", file1); + rc = ERR_EXIT; + } + + return rc; +} + + +/** + * Opens and compare two files. + */ +int +cmp_file_and_file(PKMKBUILTINCTX pCtx, const char *file1, const char *file2, int sflag, int lflag, int special) +{ + return cmp_file_and_file_ex(pCtx, file1, 0, file2, 0, sflag, lflag, special); +} + |