diff options
author | mm <mm@FreeBSD.org> | 2011-07-17 21:33:15 +0000 |
---|---|---|
committer | mm <mm@FreeBSD.org> | 2011-07-17 21:33:15 +0000 |
commit | 867c8ddd4db0c581c7d6f04f5317cdc34c07dd5c (patch) | |
tree | 8e28f61596b4577fd91c1332f714c570089da33e /usr.bin/tar | |
parent | af1b78954b9a91ab8b481f90c8b8c3522f6ea0fa (diff) | |
download | FreeBSD-src-867c8ddd4db0c581c7d6f04f5317cdc34c07dd5c.zip FreeBSD-src-867c8ddd4db0c581c7d6f04f5317cdc34c07dd5c.tar.gz |
Update bsdtar to 2.8.4
Use common code from lib/libarchive/libarchive_fe
Approved by: kientzle
MFC after: 2 weeks
Diffstat (limited to 'usr.bin/tar')
36 files changed, 2615 insertions, 2163 deletions
diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile index 4b0d186..ef21e44 100644 --- a/usr.bin/tar/Makefile +++ b/usr.bin/tar/Makefile @@ -2,21 +2,24 @@ .include <bsd.own.mk> PROG= bsdtar -BSDTAR_VERSION_STRING=2.8.3 +BSDTAR_VERSION_STRING=2.8.4 SRCS= bsdtar.c \ cmdline.c \ - err.c \ getdate.c \ - line_reader.c \ - matching.c \ - pathmatch.c \ read.c \ subst.c \ tree.c \ util.c \ write.c -DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ} ${LIBMD} ${LIBLZMA} -LDADD= -larchive -lbz2 -lz -lmd -llzma + +.PATH: ${.CURDIR}/../../lib/libarchive/libarchive_fe +SRCS+= err.c \ + line_reader.c \ + matching.c \ + pathmatch.c + +DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ} ${LIBMD} ${LIBLZMA} ${LIBBSDXML} +LDADD= -larchive -lbz2 -lz -lmd -llzma -lbsdxml .if ${MK_OPENSSL} != "no" DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto @@ -24,6 +27,7 @@ LDADD+= -lcrypto CFLAGS+= -DBSDTAR_VERSION_STRING=\"${BSDTAR_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\" CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../../lib/libarchive +CFLAGS+= -I${.CURDIR}/../../lib/libarchive/libarchive_fe SYMLINKS= bsdtar ${BINDIR}/tar MLINKS= bsdtar.1 tar.1 DEBUG_FLAGS=-g diff --git a/usr.bin/tar/bsdtar.1 b/usr.bin/tar/bsdtar.1 index 81b3c41..14c77e9 100644 --- a/usr.bin/tar/bsdtar.1 +++ b/usr.bin/tar/bsdtar.1 @@ -50,8 +50,8 @@ .Sh DESCRIPTION .Nm creates and manipulates streaming archive files. -This implementation can extract from tar, pax, cpio, zip, jar, ar, -xar, and ISO 9660 cdrom images and can create tar, pax, cpio, ar, zip, +This implementation can extract from tar, pax, cpio, zip, jar, ar, xar, +rpm and ISO 9660 cdrom images and can create tar, pax, cpio, ar, zip, and shar archives. .Pp The first synopsis form shows a diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c index 00c6f45..0bb6a7f 100644 --- a/usr.bin/tar/bsdtar.c +++ b/usr.bin/tar/bsdtar.c @@ -63,6 +63,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_UNISTD_H #include <unistd.h> #endif +#if HAVE_ZLIB_H +#include <zlib.h> +#endif #include "bsdtar.h" #include "err.h" @@ -86,6 +89,8 @@ __FBSDID("$FreeBSD$"); int _CRT_glob = 0; /* Disable broken CRT globbing. */ #endif +static struct bsdtar *_bsdtar; + #if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1)) static volatile int siginfo_occurred; @@ -139,7 +144,7 @@ main(int argc, char **argv) * Use a pointer for consistency, but stack-allocated storage * for ease of cleanup. */ - bsdtar = &bsdtar_storage; + _bsdtar = bsdtar = &bsdtar_storage; memset(bsdtar, 0, sizeof(*bsdtar)); bsdtar->fd = -1; /* Mark as "unused" */ option_o = 0; @@ -152,36 +157,36 @@ main(int argc, char **argv) sa.sa_flags = 0; #ifdef SIGINFO if (sigaction(SIGINFO, &sa, NULL)) - bsdtar_errc(1, errno, "sigaction(SIGINFO) failed"); + lafe_errc(1, errno, "sigaction(SIGINFO) failed"); #endif #ifdef SIGUSR1 /* ... and treat SIGUSR1 the same way as SIGINFO. */ if (sigaction(SIGUSR1, &sa, NULL)) - bsdtar_errc(1, errno, "sigaction(SIGUSR1) failed"); + lafe_errc(1, errno, "sigaction(SIGUSR1) failed"); #endif } #endif - /* Need bsdtar_progname before calling bsdtar_warnc. */ + /* Need lafe_progname before calling lafe_warnc. */ if (*argv == NULL) - bsdtar_progname = "bsdtar"; + lafe_progname = "bsdtar"; else { #if defined(_WIN32) && !defined(__CYGWIN__) - bsdtar_progname = strrchr(*argv, '\\'); + lafe_progname = strrchr(*argv, '\\'); #else - bsdtar_progname = strrchr(*argv, '/'); + lafe_progname = strrchr(*argv, '/'); #endif - if (bsdtar_progname != NULL) - bsdtar_progname++; + if (lafe_progname != NULL) + lafe_progname++; else - bsdtar_progname = *argv; + lafe_progname = *argv; } time(&now); #if HAVE_SETLOCALE if (setlocale(LC_ALL, "") == NULL) - bsdtar_warnc(0, "Failed to set default locale"); + lafe_warnc(0, "Failed to set default locale"); #endif #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd'); @@ -233,7 +238,7 @@ main(int argc, char **argv) case 'b': /* SUSv2 */ t = atoi(bsdtar->optarg); if (t <= 0 || t > 8192) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Argument to -b is out of range (1..8192)"); bsdtar->bytes_per_block = 512 * t; break; @@ -251,7 +256,7 @@ main(int argc, char **argv) break; case OPTION_EXCLUDE: /* GNU tar */ if (lafe_exclude(&bsdtar->matching, bsdtar->optarg)) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Couldn't exclude %s\n", bsdtar->optarg); break; case OPTION_FORMAT: /* GNU tar, others */ @@ -297,20 +302,20 @@ main(int argc, char **argv) * when transforming archives. */ if (lafe_include(&bsdtar->matching, bsdtar->optarg)) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Failed to add %s to inclusion list", bsdtar->optarg); break; case 'j': /* GNU tar */ if (bsdtar->create_compression != '\0') - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; break; case 'J': /* GNU tar 1.21 and later */ if (bsdtar->create_compression != '\0') - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; @@ -330,7 +335,7 @@ main(int argc, char **argv) break; case OPTION_LZMA: if (bsdtar->create_compression != '\0') - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; @@ -355,7 +360,7 @@ main(int argc, char **argv) { struct stat st; if (stat(bsdtar->optarg, &st) != 0) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't open file %s", bsdtar->optarg); bsdtar->newer_ctime_sec = st.st_ctime; bsdtar->newer_ctime_nsec = @@ -369,7 +374,7 @@ main(int argc, char **argv) { struct stat st; if (stat(bsdtar->optarg, &st) != 0) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't open file %s", bsdtar->optarg); bsdtar->newer_mtime_sec = st.st_mtime; bsdtar->newer_mtime_nsec = @@ -440,7 +445,7 @@ main(int argc, char **argv) #if HAVE_REGEX_H add_substitution(bsdtar, bsdtar->optarg); #else - bsdtar_warnc(0, + lafe_warnc(0, "-s is not supported by this version of bsdtar"); usage(); #endif @@ -487,7 +492,7 @@ main(int argc, char **argv) break; case 'X': /* GNU tar */ if (lafe_exclude_from_file(&bsdtar->matching, bsdtar->optarg)) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "failed to process exclusions from file %s", bsdtar->optarg); break; @@ -496,21 +501,21 @@ main(int argc, char **argv) break; case 'y': /* FreeBSD version of GNU tar */ if (bsdtar->create_compression != '\0') - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; break; case 'Z': /* GNU tar */ if (bsdtar->create_compression != '\0') - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; break; case 'z': /* GNU tar, star, many others */ if (bsdtar->create_compression != '\0') - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; @@ -535,7 +540,7 @@ main(int argc, char **argv) /* Otherwise, a mode is required. */ if (bsdtar->mode == '\0') - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Must specify one of -c, -r, -t, -u, -x"); /* Check boolean options only permitted in certain modes. */ @@ -615,7 +620,7 @@ main(int argc, char **argv) #endif if (bsdtar->return_value != 0) - bsdtar_warnc(0, + lafe_warnc(0, "Error exit delayed from previous errors."); return (bsdtar->return_value); } @@ -624,7 +629,7 @@ static void set_mode(struct bsdtar *bsdtar, char opt) { if (bsdtar->mode != '\0' && bsdtar->mode != opt) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->mode); bsdtar->mode = opt; } @@ -636,7 +641,7 @@ static void only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) { if (strchr(valid_modes, bsdtar->mode) == NULL) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Option %s is not permitted in mode -%c", opt, bsdtar->mode); } @@ -647,7 +652,7 @@ usage(void) { const char *p; - p = bsdtar_progname; + p = lafe_progname; fprintf(stderr, "Usage:\n"); fprintf(stderr, " List: %s -tf <archive-filename>\n", p); @@ -707,7 +712,7 @@ long_help(void) const char *prog; const char *p; - prog = bsdtar_progname; + prog = lafe_progname; fflush(stderr); diff --git a/usr.bin/tar/cmdline.c b/usr.bin/tar/cmdline.c index dfe7cf6..ba3e8a1 100644 --- a/usr.bin/tar/cmdline.c +++ b/usr.bin/tar/cmdline.c @@ -221,7 +221,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) if (p[1] == ':') { bsdtar->optarg = *bsdtar->argv; if (bsdtar->optarg == NULL) { - bsdtar_warnc(0, + lafe_warnc(0, "Option %c requires an argument", opt); return ('?'); @@ -288,7 +288,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) /* Otherwise, pick up the next word. */ opt_word = *bsdtar->argv; if (opt_word == NULL) { - bsdtar_warnc(0, + lafe_warnc(0, "Option -%c requires an argument", opt); return ('?'); @@ -339,13 +339,13 @@ bsdtar_getopt(struct bsdtar *bsdtar) /* Fail if there wasn't a unique match. */ if (match == NULL) { - bsdtar_warnc(0, + lafe_warnc(0, "Option %s%s is not supported", long_prefix, opt_word); return ('?'); } if (match2 != NULL) { - bsdtar_warnc(0, + lafe_warnc(0, "Ambiguous option %s%s (matches --%s and --%s)", long_prefix, opt_word, match->name, match2->name); return ('?'); @@ -357,7 +357,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) if (bsdtar->optarg == NULL) { bsdtar->optarg = *bsdtar->argv; if (bsdtar->optarg == NULL) { - bsdtar_warnc(0, + lafe_warnc(0, "Option %s%s requires an argument", long_prefix, match->name); return ('?'); @@ -368,7 +368,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) } else { /* Argument forbidden: fail if there is one. */ if (bsdtar->optarg != NULL) { - bsdtar_warnc(0, + lafe_warnc(0, "Option %s%s does not allow an argument", long_prefix, match->name); return ('?'); diff --git a/usr.bin/tar/config_freebsd.h b/usr.bin/tar/config_freebsd.h index 8209758..4c8ec3a 100644 --- a/usr.bin/tar/config_freebsd.h +++ b/usr.bin/tar/config_freebsd.h @@ -44,6 +44,7 @@ #undef HAVE_LIBACL #define HAVE_LIBARCHIVE 1 #define HAVE_LIMITS_H 1 +#define HAVE_LINK 1 #undef HAVE_LINUX_EXT2_FS_H #undef HAVE_LINUX_FS_H #define HAVE_LOCALE_H 1 @@ -77,5 +78,5 @@ #define HAVE_TIME_H 1 #define HAVE_UNISTD_H 1 #define HAVE_WCTYPE_H 1 +#define HAVE_WCSCMP 1 #undef HAVE_WINDOWS_H - diff --git a/usr.bin/tar/err.c b/usr.bin/tar/err.c deleted file mode 100644 index 3770561..0000000 --- a/usr.bin/tar/err.c +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * Copyright (c) 2003-2010 Tim Kientzle - * 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 - * in this position and unchanged. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. - */ - -#include "bsdtar_platform.h" -__FBSDID("$FreeBSD$"); - -#ifdef HAVE_STDARG_H -#include <stdarg.h> -#endif -#include <stdio.h> -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif - -#include "err.h" - -const char *bsdtar_progname; - -static void -bsdtar_vwarnc(int code, const char *fmt, va_list ap) -{ - fprintf(stderr, "%s: ", bsdtar_progname); - vfprintf(stderr, fmt, ap); - if (code != 0) - fprintf(stderr, ": %s", strerror(code)); - fprintf(stderr, "\n"); -} - -void -bsdtar_warnc(int code, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - bsdtar_vwarnc(code, fmt, ap); - va_end(ap); -} - -void -bsdtar_errc(int eval, int code, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - bsdtar_vwarnc(code, fmt, ap); - va_end(ap); - exit(eval); -} diff --git a/usr.bin/tar/line_reader.c b/usr.bin/tar/line_reader.c deleted file mode 100644 index c9df1b0..0000000 --- a/usr.bin/tar/line_reader.c +++ /dev/null @@ -1,171 +0,0 @@ -/*- - * Copyright (c) 2008 Tim Kientzle - * 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 - * in this position and unchanged. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. - */ - -#include "bsdtar_platform.h" -__FBSDID("$FreeBSD$"); - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "err.h" -#include "line_reader.h" - -#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) -#define strdup _strdup -#endif - -/* - * Read lines from file and do something with each one. If option_null - * is set, lines are terminated with zero bytes; otherwise, they're - * terminated with newlines. - * - * This uses a self-sizing buffer to handle arbitrarily-long lines. - */ -struct lafe_line_reader { - FILE *f; - char *buff, *buff_end, *line_start, *line_end, *p; - char *pathname; - size_t buff_length; - int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */ - int ret; -}; - -struct lafe_line_reader * -lafe_line_reader(const char *pathname, int nullSeparator) -{ - struct lafe_line_reader *lr; - - lr = calloc(1, sizeof(*lr)); - if (lr == NULL) - bsdtar_errc(1, ENOMEM, "Can't open %s", pathname); - - lr->nullSeparator = nullSeparator; - lr->pathname = strdup(pathname); - - if (strcmp(pathname, "-") == 0) - lr->f = stdin; - else - lr->f = fopen(pathname, "r"); - if (lr->f == NULL) - bsdtar_errc(1, errno, "Couldn't open %s", pathname); - lr->buff_length = 8192; - lr->buff = malloc(lr->buff_length); - if (lr->buff == NULL) - bsdtar_errc(1, ENOMEM, "Can't read %s", pathname); - lr->line_start = lr->line_end = lr->buff_end = lr->buff; - - return (lr); -} - -const char * -lafe_line_reader_next(struct lafe_line_reader *lr) -{ - size_t bytes_wanted, bytes_read, new_buff_size; - char *line_start, *p; - - for (;;) { - /* If there's a line in the buffer, return it immediately. */ - while (lr->line_end < lr->buff_end) { - if (lr->nullSeparator) { - if (*lr->line_end == '\0') { - line_start = lr->line_start; - lr->line_start = lr->line_end + 1; - lr->line_end = lr->line_start; - return (line_start); - } - } else if (*lr->line_end == '\x0a' || *lr->line_end == '\x0d') { - *lr->line_end = '\0'; - line_start = lr->line_start; - lr->line_start = lr->line_end + 1; - lr->line_end = lr->line_start; - if (line_start[0] != '\0') - return (line_start); - } - lr->line_end++; - } - - /* If we're at end-of-file, process the final data. */ - if (lr->f == NULL) { - /* If there's more text, return one last line. */ - if (lr->line_end > lr->line_start) { - *lr->line_end = '\0'; - line_start = lr->line_start; - lr->line_start = lr->line_end + 1; - lr->line_end = lr->line_start; - return (line_start); - } - /* Otherwise, we're done. */ - return (NULL); - } - - /* Buffer only has part of a line. */ - if (lr->line_start > lr->buff) { - /* Move a leftover fractional line to the beginning. */ - memmove(lr->buff, lr->line_start, - lr->buff_end - lr->line_start); - lr->buff_end -= lr->line_start - lr->buff; - lr->line_end -= lr->line_start - lr->buff; - lr->line_start = lr->buff; - } else { - /* Line is too big; enlarge the buffer. */ - new_buff_size = lr->buff_length * 2; - if (new_buff_size <= lr->buff_length) - bsdtar_errc(1, ENOMEM, - "Line too long in %s", lr->pathname); - lr->buff_length = new_buff_size; - p = realloc(lr->buff, new_buff_size); - if (p == NULL) - bsdtar_errc(1, ENOMEM, - "Line too long in %s", lr->pathname); - lr->buff_end = p + (lr->buff_end - lr->buff); - lr->line_end = p + (lr->line_end - lr->buff); - lr->line_start = lr->buff = p; - } - - /* Get some more data into the buffer. */ - bytes_wanted = lr->buff + lr->buff_length - lr->buff_end; - bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f); - lr->buff_end += bytes_read; - - if (ferror(lr->f)) - bsdtar_errc(1, errno, "Can't read %s", lr->pathname); - if (feof(lr->f)) { - if (lr->f != stdin) - fclose(lr->f); - lr->f = NULL; - } - } -} - -void -lafe_line_reader_free(struct lafe_line_reader *lr) -{ - free(lr->buff); - free(lr->pathname); - free(lr); -} diff --git a/usr.bin/tar/line_reader.h b/usr.bin/tar/line_reader.h deleted file mode 100644 index e4c3729..0000000 --- a/usr.bin/tar/line_reader.h +++ /dev/null @@ -1,37 +0,0 @@ -/*- - * Copyright (c) 2009 Joerg Sonnenberger - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. - * - * $FreeBSD$ - */ - -#ifndef LAFE_LINE_READER_H -#define LAFE_LINE_READER_H - -struct lafe_line_reader; - -struct lafe_line_reader *lafe_line_reader(const char *, int nullSeparator); -const char *lafe_line_reader_next(struct lafe_line_reader *); -void lafe_line_reader_free(struct lafe_line_reader *); - -#endif diff --git a/usr.bin/tar/matching.c b/usr.bin/tar/matching.c deleted file mode 100644 index dc316b1..0000000 --- a/usr.bin/tar/matching.c +++ /dev/null @@ -1,281 +0,0 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. - */ - -#include "bsdtar_platform.h" -__FBSDID("$FreeBSD$"); - -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif - -#include "err.h" -#include "line_reader.h" -#include "matching.h" -#include "pathmatch.h" - -struct match { - struct match *next; - int matches; - char pattern[1]; -}; - -struct lafe_matching { - struct match *exclusions; - int exclusions_count; - struct match *inclusions; - int inclusions_count; - int inclusions_unmatched_count; -}; - -static void add_pattern(struct match **list, const char *pattern); -static void initialize_matching(struct lafe_matching **); -static int match_exclusion(struct match *, const char *pathname); -static int match_inclusion(struct match *, const char *pathname); - -/* - * The matching logic here needs to be re-thought. I started out to - * try to mimic gtar's matching logic, but it's not entirely - * consistent. In particular 'tar -t' and 'tar -x' interpret patterns - * on the command line as anchored, but --exclude doesn't. - */ - -/* - * Utility functions to manage exclusion/inclusion patterns - */ - -int -lafe_exclude(struct lafe_matching **matching, const char *pattern) -{ - - if (*matching == NULL) - initialize_matching(matching); - add_pattern(&((*matching)->exclusions), pattern); - (*matching)->exclusions_count++; - return (0); -} - -int -lafe_exclude_from_file(struct lafe_matching **matching, const char *pathname) -{ - struct lafe_line_reader *lr; - const char *p; - int ret = 0; - - lr = lafe_line_reader(pathname, 0); - while ((p = lafe_line_reader_next(lr)) != NULL) { - if (lafe_exclude(matching, p) != 0) - ret = -1; - } - lafe_line_reader_free(lr); - return (ret); -} - -int -lafe_include(struct lafe_matching **matching, const char *pattern) -{ - - if (*matching == NULL) - initialize_matching(matching); - add_pattern(&((*matching)->inclusions), pattern); - (*matching)->inclusions_count++; - (*matching)->inclusions_unmatched_count++; - return (0); -} - -int -lafe_include_from_file(struct lafe_matching **matching, const char *pathname, - int nullSeparator) -{ - struct lafe_line_reader *lr; - const char *p; - int ret = 0; - - lr = lafe_line_reader(pathname, nullSeparator); - while ((p = lafe_line_reader_next(lr)) != NULL) { - if (lafe_include(matching, p) != 0) - ret = -1; - } - lafe_line_reader_free(lr); - return (ret); -} - -static void -add_pattern(struct match **list, const char *pattern) -{ - struct match *match; - size_t len; - - len = strlen(pattern); - match = malloc(sizeof(*match) + len + 1); - if (match == NULL) - bsdtar_errc(1, errno, "Out of memory"); - strcpy(match->pattern, pattern); - /* Both "foo/" and "foo" should match "foo/bar". */ - if (len && match->pattern[len - 1] == '/') - match->pattern[strlen(match->pattern)-1] = '\0'; - match->next = *list; - *list = match; - match->matches = 0; -} - - -int -lafe_excluded(struct lafe_matching *matching, const char *pathname) -{ - struct match *match; - struct match *matched; - - if (matching == NULL) - return (0); - - /* Mark off any unmatched inclusions. */ - /* In particular, if a filename does appear in the archive and - * is explicitly included and excluded, then we don't report - * it as missing even though we don't extract it. - */ - matched = NULL; - for (match = matching->inclusions; match != NULL; match = match->next){ - if (match->matches == 0 - && match_inclusion(match, pathname)) { - matching->inclusions_unmatched_count--; - match->matches++; - matched = match; - } - } - - /* Exclusions take priority */ - for (match = matching->exclusions; match != NULL; match = match->next){ - if (match_exclusion(match, pathname)) - return (1); - } - - /* It's not excluded and we found an inclusion above, so it's included. */ - if (matched != NULL) - return (0); - - - /* We didn't find an unmatched inclusion, check the remaining ones. */ - for (match = matching->inclusions; match != NULL; match = match->next){ - /* We looked at previously-unmatched inclusions already. */ - if (match->matches > 0 - && match_inclusion(match, pathname)) { - match->matches++; - return (0); - } - } - - /* If there were inclusions, default is to exclude. */ - if (matching->inclusions != NULL) - return (1); - - /* No explicit inclusions, default is to match. */ - return (0); -} - -/* - * This is a little odd, but it matches the default behavior of - * gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar' - * - */ -static int -match_exclusion(struct match *match, const char *pathname) -{ - return (lafe_pathmatch(match->pattern, - pathname, - PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END)); -} - -/* - * Again, mimic gtar: inclusions are always anchored (have to match - * the beginning of the path) even though exclusions are not anchored. - */ -static int -match_inclusion(struct match *match, const char *pathname) -{ - return (lafe_pathmatch(match->pattern, pathname, PATHMATCH_NO_ANCHOR_END)); -} - -void -lafe_cleanup_exclusions(struct lafe_matching **matching) -{ - struct match *p, *q; - - if (*matching == NULL) - return; - - for (p = (*matching)->inclusions; p != NULL; ) { - q = p; - p = p->next; - free(q); - } - - for (p = (*matching)->exclusions; p != NULL; ) { - q = p; - p = p->next; - free(q); - } - - free(*matching); - *matching = NULL; -} - -static void -initialize_matching(struct lafe_matching **matching) -{ - *matching = calloc(sizeof(**matching), 1); - if (*matching == NULL) - bsdtar_errc(1, errno, "No memory"); -} - -int -lafe_unmatched_inclusions(struct lafe_matching *matching) -{ - - if (matching == NULL) - return (0); - return (matching->inclusions_unmatched_count); -} - -int -lafe_unmatched_inclusions_warn(struct lafe_matching *matching, const char *msg) -{ - struct match *p; - - if (matching == NULL) - return (0); - - for (p = matching->inclusions; p != NULL; p = p->next) { - if (p->matches == 0) - bsdtar_warnc(0, "%s: %s", p->pattern, msg); - } - - return (matching->inclusions_unmatched_count); -} diff --git a/usr.bin/tar/matching.h b/usr.bin/tar/matching.h deleted file mode 100644 index f4edebd..0000000 --- a/usr.bin/tar/matching.h +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle - * 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 - * in this position and unchanged. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. - * - * $FreeBSD$ - */ - -#ifndef MATCHING_H -#define MATCHING_H - -struct lafe_matching; - -int lafe_exclude(struct lafe_matching **matching, const char *pattern); -int lafe_exclude_from_file(struct lafe_matching **matching, - const char *pathname); -int lafe_include(struct lafe_matching **matching, const char *pattern); -int lafe_include_from_file(struct lafe_matching **matching, - const char *pathname, int nullSeparator); - -int lafe_excluded(struct lafe_matching *, const char *pathname); -void lafe_cleanup_exclusions(struct lafe_matching **); -int lafe_unmatched_inclusions(struct lafe_matching *); -int lafe_unmatched_inclusions_warn(struct lafe_matching *, const char *msg); - -#endif diff --git a/usr.bin/tar/pathmatch.c b/usr.bin/tar/pathmatch.c deleted file mode 100644 index a994d44..0000000 --- a/usr.bin/tar/pathmatch.c +++ /dev/null @@ -1,255 +0,0 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle - * 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 - * in this position and unchanged. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. - */ - -#include "bsdtar_platform.h" -__FBSDID("$FreeBSD$"); - -#ifdef HAVE_STRING_H -#include <string.h> -#endif - -#include "pathmatch.h" - -/* - * Check whether a character 'c' is matched by a list specification [...]: - * * Leading '!' or '^' negates the class. - * * <char>-<char> is a range of characters - * * \<char> removes any special meaning for <char> - * - * Some interesting boundary cases: - * a-d-e is one range (a-d) followed by two single characters - and e. - * \a-\d is same as a-d - * a\-d is three single characters: a, d, - - * Trailing - is not special (so [a-] is two characters a and -). - * Initial - is not special ([a-] is same as [-a] is same as [\\-a]) - * This function never sees a trailing \. - * [] always fails - * [!] always succeeds - */ -static int -pm_list(const char *start, const char *end, const char c, int flags) -{ - const char *p = start; - char rangeStart = '\0', nextRangeStart; - int match = 1, nomatch = 0; - - /* This will be used soon... */ - (void)flags; /* UNUSED */ - - /* If this is a negated class, return success for nomatch. */ - if ((*p == '!' || *p == '^') && p < end) { - match = 0; - nomatch = 1; - ++p; - } - - while (p < end) { - nextRangeStart = '\0'; - switch (*p) { - case '-': - /* Trailing or initial '-' is not special. */ - if ((rangeStart == '\0') || (p == end - 1)) { - if (*p == c) - return (match); - } else { - char rangeEnd = *++p; - if (rangeEnd == '\\') - rangeEnd = *++p; - if ((rangeStart <= c) && (c <= rangeEnd)) - return (match); - } - break; - case '\\': - ++p; - /* Fall through */ - default: - if (*p == c) - return (match); - nextRangeStart = *p; /* Possible start of range. */ - } - rangeStart = nextRangeStart; - ++p; - } - return (nomatch); -} - -/* - * If s is pointing to "./", ".//", "./././" or the like, skip it. - */ -static const char * -pm_slashskip(const char *s) { - while ((*s == '/') - || (s[0] == '.' && s[1] == '/') - || (s[0] == '.' && s[1] == '\0')) - ++s; - return (s); -} - -static int -pm(const char *p, const char *s, int flags) -{ - const char *end; - - /* - * Ignore leading './', './/', '././', etc. - */ - if (s[0] == '.' && s[1] == '/') - s = pm_slashskip(s + 1); - if (p[0] == '.' && p[1] == '/') - p = pm_slashskip(p + 1); - - for (;;) { - switch (*p) { - case '\0': - if (s[0] == '/') { - if (flags & PATHMATCH_NO_ANCHOR_END) - return (1); - /* "dir" == "dir/" == "dir/." */ - s = pm_slashskip(s); - } - return (*s == '\0'); - case '?': - /* ? always succeds, unless we hit end of 's' */ - if (*s == '\0') - return (0); - break; - case '*': - /* "*" == "**" == "***" ... */ - while (*p == '*') - ++p; - /* Trailing '*' always succeeds. */ - if (*p == '\0') - return (1); - while (*s) { - if (lafe_pathmatch(p, s, flags)) - return (1); - ++s; - } - return (0); - case '[': - /* - * Find the end of the [...] character class, - * ignoring \] that might occur within the class. - */ - end = p + 1; - while (*end != '\0' && *end != ']') { - if (*end == '\\' && end[1] != '\0') - ++end; - ++end; - } - if (*end == ']') { - /* We found [...], try to match it. */ - if (!pm_list(p + 1, end, *s, flags)) - return (0); - p = end; /* Jump to trailing ']' char. */ - break; - } else - /* No final ']', so just match '['. */ - if (*p != *s) - return (0); - break; - case '\\': - /* Trailing '\\' matches itself. */ - if (p[1] == '\0') { - if (*s != '\\') - return (0); - } else { - ++p; - if (*p != *s) - return (0); - } - break; - case '/': - if (*s != '/' && *s != '\0') - return (0); - /* Note: pattern "/\./" won't match "/"; - * pm_slashskip() correctly stops at backslash. */ - p = pm_slashskip(p); - s = pm_slashskip(s); - if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)) - return (1); - --p; /* Counteract the increment below. */ - --s; - break; - case '$': - /* '$' is special only at end of pattern and only - * if PATHMATCH_NO_ANCHOR_END is specified. */ - if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){ - /* "dir" == "dir/" == "dir/." */ - return (*pm_slashskip(s) == '\0'); - } - /* Otherwise, '$' is not special. */ - /* FALL THROUGH */ - default: - if (*p != *s) - return (0); - break; - } - ++p; - ++s; - } -} - -/* Main entry point. */ -int -lafe_pathmatch(const char *p, const char *s, int flags) -{ - /* Empty pattern only matches the empty string. */ - if (p == NULL || *p == '\0') - return (s == NULL || *s == '\0'); - - /* Leading '^' anchors the start of the pattern. */ - if (*p == '^') { - ++p; - flags &= ~PATHMATCH_NO_ANCHOR_START; - } - - if (*p == '/' && *s != '/') - return (0); - - /* Certain patterns and file names anchor implicitly. */ - if (*p == '*' || *p == '/' || *p == '/') { - while (*p == '/') - ++p; - while (*s == '/') - ++s; - return (pm(p, s, flags)); - } - - /* If start is unanchored, try to match start of each path element. */ - if (flags & PATHMATCH_NO_ANCHOR_START) { - for ( ; s != NULL; s = strchr(s, '/')) { - if (*s == '/') - s++; - if (pm(p, s, flags)) - return (1); - } - return (0); - } - - /* Default: Match from beginning. */ - return (pm(p, s, flags)); -} diff --git a/usr.bin/tar/pathmatch.h b/usr.bin/tar/pathmatch.h deleted file mode 100644 index a92f3ae..0000000 --- a/usr.bin/tar/pathmatch.h +++ /dev/null @@ -1,42 +0,0 @@ -/*- - * Copyright (c) 2003-2007 Tim Kientzle - * 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 - * in this position and unchanged. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. - * - * $FreeBSD$ - */ - -#ifndef LAFE_PATHMATCH_H -#define LAFE_PATHMATCH_H - -/* Don't anchor at beginning unless the pattern starts with "^" */ -#define PATHMATCH_NO_ANCHOR_START 1 -/* Don't anchor at end unless the pattern ends with "$" */ -#define PATHMATCH_NO_ANCHOR_END 2 - -/* Note that "^" and "$" are not special unless you set the corresponding - * flag above. */ - -int lafe_pathmatch(const char *p, const char *s, int flags); - -#endif diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c index 525daac..e41c0b5 100644 --- a/usr.bin/tar/read.c +++ b/usr.bin/tar/read.c @@ -23,7 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "bsdtar_platform.h" +#include "lafe_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H @@ -160,11 +160,11 @@ read_archive(struct bsdtar *bsdtar, char mode) archive_read_support_compression_all(a); archive_read_support_format_all(a); if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) - bsdtar_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_file(a, bsdtar->filename, bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : DEFAULT_BYTES_PER_BLOCK)) - bsdtar_errc(1, 0, "Error opening archive: %s", + lafe_errc(1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); @@ -180,9 +180,9 @@ read_archive(struct bsdtar *bsdtar, char mode) if (mode == 'x' && bsdtar->option_chroot) { #if HAVE_CHROOT if (chroot(".") != 0) - bsdtar_errc(1, errno, "Can't chroot to \".\""); + lafe_errc(1, errno, "Can't chroot to \".\""); #else - bsdtar_errc(1, 0, + lafe_errc(1, 0, "chroot isn't supported on this platform"); #endif } @@ -198,12 +198,12 @@ read_archive(struct bsdtar *bsdtar, char mode) if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) - bsdtar_warnc(0, "%s", archive_error_string(a)); + lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ - bsdtar_warnc(0, "Retrying..."); + lafe_warnc(0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) @@ -267,17 +267,17 @@ read_archive(struct bsdtar *bsdtar, char mode) r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); - bsdtar_warnc(0, "%s", + lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); - bsdtar_warnc(0, "%s", + lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); - bsdtar_warnc(0, "%s", + lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; @@ -329,7 +329,7 @@ read_archive(struct bsdtar *bsdtar, char mode) r = archive_read_close(a); if (r != ARCHIVE_OK) - bsdtar_warnc(0, "%s", archive_error_string(a)); + lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; diff --git a/usr.bin/tar/subst.c b/usr.bin/tar/subst.c index a217293..765f74b 100644 --- a/usr.bin/tar/subst.c +++ b/usr.bin/tar/subst.c @@ -58,7 +58,7 @@ init_substitution(struct bsdtar *bsdtar) bsdtar->substitution = subst = malloc(sizeof(*subst)); if (subst == NULL) - bsdtar_errc(1, errno, "Out of memory"); + lafe_errc(1, errno, "Out of memory"); subst->first_rule = subst->last_rule = NULL; } @@ -78,7 +78,7 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text) rule = malloc(sizeof(*rule)); if (rule == NULL) - bsdtar_errc(1, errno, "Out of memory"); + lafe_errc(1, errno, "Out of memory"); rule->next = NULL; if (subst->last_rule == NULL) @@ -88,32 +88,32 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text) subst->last_rule = rule; if (*rule_text == '\0') - bsdtar_errc(1, 0, "Empty replacement string"); + lafe_errc(1, 0, "Empty replacement string"); end_pattern = strchr(rule_text + 1, *rule_text); if (end_pattern == NULL) - bsdtar_errc(1, 0, "Invalid replacement string"); + lafe_errc(1, 0, "Invalid replacement string"); pattern = malloc(end_pattern - rule_text); if (pattern == NULL) - bsdtar_errc(1, errno, "Out of memory"); + lafe_errc(1, errno, "Out of memory"); memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1); pattern[end_pattern - rule_text - 1] = '\0'; if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) { char buf[80]; regerror(r, &rule->re, buf, sizeof(buf)); - bsdtar_errc(1, 0, "Invalid regular expression: %s", buf); + lafe_errc(1, 0, "Invalid regular expression: %s", buf); } free(pattern); start_subst = end_pattern + 1; end_pattern = strchr(start_subst, *rule_text); if (end_pattern == NULL) - bsdtar_errc(1, 0, "Invalid replacement string"); + lafe_errc(1, 0, "Invalid replacement string"); rule->result = malloc(end_pattern - start_subst + 1); if (rule->result == NULL) - bsdtar_errc(1, errno, "Out of memory"); + lafe_errc(1, errno, "Out of memory"); memcpy(rule->result, start_subst, end_pattern - start_subst); rule->result[end_pattern - start_subst] = '\0'; @@ -136,7 +136,7 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text) rule->symlink = 1; break; default: - bsdtar_errc(1, 0, "Invalid replacement flag %c", *end_pattern); + lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern); } } } @@ -154,7 +154,7 @@ realloc_strncat(char **str, const char *append, size_t len) new_str = malloc(old_len + len + 1); if (new_str == NULL) - bsdtar_errc(1, errno, "Out of memory"); + lafe_errc(1, errno, "Out of memory"); memcpy(new_str, *str, old_len); memcpy(new_str + old_len, append, len); new_str[old_len + len] = '\0'; @@ -175,7 +175,7 @@ realloc_strcat(char **str, const char *append) new_str = malloc(old_len + strlen(append) + 1); if (new_str == NULL) - bsdtar_errc(1, errno, "Out of memory"); + lafe_errc(1, errno, "Out of memory"); memcpy(new_str, *str, old_len); strcpy(new_str + old_len, append); free(*str); diff --git a/usr.bin/tar/test/Makefile b/usr.bin/tar/test/Makefile index 9babcfa..4342cdf 100644 --- a/usr.bin/tar/test/Makefile +++ b/usr.bin/tar/test/Makefile @@ -6,16 +6,18 @@ TAR_SRCDIR=${.CURDIR}/.. # Some tar sources are pulled in for white-box tests TAR_SRCS= \ - ../getdate.c + getdate.c TESTS= \ test_0.c \ test_basic.c \ test_copy.c \ + test_empty_mtree.c \ test_getdate.c \ test_help.c \ - test_option_T.c \ + test_option_T_upper.c \ test_option_q.c \ + test_option_r.c \ test_option_s.c \ test_patterns.c \ test_stdio.c \ diff --git a/usr.bin/tar/test/main.c b/usr.bin/tar/test/main.c index 041b7a0..bd1db1d 100644 --- a/usr.bin/tar/test/main.c +++ b/usr.bin/tar/test/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,12 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - * Various utility routines useful for test programs. - * Each test program is linked against this file. - */ #include "test.h" - #include <errno.h> #include <locale.h> #include <stdarg.h> @@ -38,148 +33,319 @@ * This same file is used pretty much verbatim for all test harnesses. * * The next few lines are the only differences. + * TODO: Move this into a separate configuration header, have all test + * suites share one copy of this file. */ -#define PROGRAM "bsdtar" /* Name of program being tested. */ -#define ENVBASE "BSDTAR" /* Prefix for environment variables. */ +__FBSDID("$FreeBSD$"); +#define KNOWNREF "test_patterns_2.tar.uu" +#define ENVBASE "BSDTAR" /* Prefix for environment variables. */ +#define PROGRAM "bsdtar" /* Name of program being tested. */ +#undef LIBRARY /* Not testing a library. */ #undef EXTRA_DUMP /* How to dump extra data */ /* How to generate extra version info. */ #define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") -__FBSDID("$FreeBSD$"); /* - * "list.h" is simply created by "grep DEFINE_TEST"; it has - * a line like - * DEFINE_TEST(test_function) - * for each test. - * Include it here with a suitable DEFINE_TEST to declare all of the - * test functions. + * + * Windows support routines + * + * Note: Configuration is a tricky issue. Using HAVE_* feature macros + * in the test harness is dangerous because they cover up + * configuration errors. The classic example of this is omitting a + * configure check. If libarchive and libarchive_test both look for + * the same feature macro, such errors are hard to detect. Platform + * macros (e.g., _WIN32 or __GNUC__) are a little better, but can + * easily lead to very messy code. It's best to limit yourself + * to only the most generic programming techniques in the test harness + * and thus avoid conditionals altogether. Where that's not possible, + * try to minimize conditionals by grouping platform-specific tests in + * one place (e.g., test_acl_freebsd) or by adding new assert() + * functions (e.g., assertMakeHardlink()) to cover up platform + * differences. Platform-specific coding in libarchive_test is often + * a symptom that some capability is missing from libarchive itself. */ -#undef DEFINE_TEST -#define DEFINE_TEST(name) void name(void); -#include "list.h" +#if defined(_WIN32) && !defined(__CYGWIN__) +#include <io.h> +#include <windows.h> +#ifndef F_OK +#define F_OK (0) +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) ((m) & _S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(m) ((m) & _S_IFREG) +#endif +#if !defined(__BORLANDC__) +#define access _access +#undef chdir +#define chdir _chdir +#endif +#ifndef fileno +#define fileno _fileno +#endif +/*#define fstat _fstat64*/ +#if !defined(__BORLANDC__) +#define getcwd _getcwd +#endif +#define lstat stat +/*#define lstat _stat64*/ +/*#define stat _stat64*/ +#define rmdir _rmdir +#if !defined(__BORLANDC__) +#define strdup _strdup +#define umask _umask +#endif +#define int64_t __int64 +#endif -/* Interix doesn't define these in a standard header. */ -#if __INTERIX__ -extern char *optarg; -extern int optind; +#if defined(HAVE__CrtSetReportMode) +# include <crtdbg.h> #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +void *GetFunctionKernel32(const char *name) +{ + static HINSTANCE lib; + static int set; + if (!set) { + set = 1; + lib = LoadLibrary("kernel32.dll"); + } + if (lib == NULL) { + fprintf(stderr, "Can't load kernel32.dll?!\n"); + exit(1); + } + return (void *)GetProcAddress(lib, name); +} + +static int +my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) +{ + static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); + static int set; + if (!set) { + set = 1; + f = GetFunctionKernel32("CreateSymbolicLinkA"); + } + return f == NULL ? 0 : (*f)(linkname, target, flags); +} + +static int +my_CreateHardLinkA(const char *linkname, const char *target) +{ + static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); + static int set; + if (!set) { + set = 1; + f = GetFunctionKernel32("CreateHardLinkA"); + } + return f == NULL ? 0 : (*f)(linkname, target, NULL); +} + +int +my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) +{ + HANDLE h; + int r; + + memset(bhfi, 0, sizeof(*bhfi)); + h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) + return (0); + r = GetFileInformationByHandle(h, bhfi); + CloseHandle(h); + return (r); +} +#endif + +#if defined(HAVE__CrtSetReportMode) +static void +invalid_parameter_handler(const wchar_t * expression, + const wchar_t * function, const wchar_t * file, + unsigned int line, uintptr_t pReserved) +{ + /* nop */ +} +#endif + +/* + * + * OPTIONS FLAGS + * + */ + /* Enable core dump on failure. */ static int dump_on_failure = 0; -/* Default is to remove temp dirs for successful tests. */ +/* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; -/* Default is to print some basic information about each test. */ -static int quiet_flag = 0; -/* Default is to summarize repeated failures. */ -static int verbose = 0; -/* Cumulative count of component failures. */ +/* Default is to just report pass/fail for each test. */ +static int verbosity = 0; +#define VERBOSITY_SUMMARY_ONLY -1 /* -q */ +#define VERBOSITY_PASSFAIL 0 /* Default */ +#define VERBOSITY_LIGHT_REPORT 1 /* -v */ +#define VERBOSITY_FULL 2 /* -vv */ +/* A few places generate even more output for verbosity > VERBOSITY_FULL, + * mostly for debugging the test harness itself. */ +/* Cumulative count of assertion failures. */ static int failures = 0; -/* Cumulative count of skipped component tests. */ +/* Cumulative count of reported skips. */ static int skips = 0; -/* Cumulative count of assertions. */ +/* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* - * My own implementation of the standard assert() macro emits the - * message in the same format as GCC (file:line: message). - * It also includes some additional useful information. - * This makes it a lot easier to skim through test failures in - * Emacs. ;-) - * - * It also supports a few special features specifically to simplify - * test harnesses: - * failure(fmt, args) -- Stores a text string that gets - * printed if the following assertion fails, good for - * explaining subtle tests. + * Report log information selectively to console and/or disk log. */ -static char msg[4096]; - -/* - * For each test source file, we remember how many times each - * failure was reported. - */ -static const char *failed_filename = NULL; -static struct line { - int line; - int count; -} failed_lines[1000]; - -/* - * Count this failure; return the number of previous failures. - */ -static int -previous_failures(const char *filename, int line) +static int log_console = 0; +static FILE *logfile; +static void +vlogprintf(const char *fmt, va_list ap) { - unsigned int i; - int count; +#ifdef va_copy + va_list lfap; + va_copy(lfap, ap); +#endif + if (log_console) + vfprintf(stdout, fmt, ap); + if (logfile != NULL) +#ifdef va_copy + vfprintf(logfile, fmt, lfap); + va_end(lfap); +#else + vfprintf(logfile, fmt, ap); +#endif +} - if (failed_filename == NULL || strcmp(failed_filename, filename) != 0) - memset(failed_lines, 0, sizeof(failed_lines)); - failed_filename = filename; +static void +logprintf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vlogprintf(fmt, ap); + va_end(ap); +} - for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { - if (failed_lines[i].line == line) { - count = failed_lines[i].count; - failed_lines[i].count++; - return (count); - } - if (failed_lines[i].line == 0) { - failed_lines[i].line = line; - failed_lines[i].count = 1; - return (0); - } - } - return (0); +/* Set up a message to display only if next assertion fails. */ +static char msgbuff[4096]; +static const char *msg, *nextmsg; +void +failure(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsprintf(msgbuff, fmt, ap); + va_end(ap); + nextmsg = msgbuff; } /* * Copy arguments into file-local variables. + * This was added to permit vararg assert() functions without needing + * variadic wrapper macros. Turns out that the vararg capability is almost + * never used, so almost all of the vararg assertions can be simplified + * by removing the vararg capability and reworking the wrapper macro to + * pass __FILE__, __LINE__ directly into the function instead of using + * this hook. I suspect this machinery is used so rarely that we + * would be better off just removing it entirely. That would simplify + * the code here noticably. */ static const char *test_filename; static int test_line; static void *test_extra; -void test_setup(const char *filename, int line) +void assertion_setup(const char *filename, int line) { test_filename = filename; test_line = line; } +/* Called at the beginning of each assert() function. */ +static void +assertion_count(const char *file, int line) +{ + (void)file; /* UNUSED */ + (void)line; /* UNUSED */ + ++assertions; + /* Proper handling of "failure()" message. */ + msg = nextmsg; + nextmsg = NULL; + /* Uncomment to print file:line after every assertion. + * Verbose, but occasionally useful in tracking down crashes. */ + /* printf("Checked %s:%d\n", file, line); */ +} + /* - * Inform user that we're skipping a test. + * For each test source file, we remember how many times each + * assertion was reported. Cleared before each new test, + * used by test_summarize(). */ -void -test_skipping(const char *fmt, ...) +static struct line { + int count; + int skip; +} failed_lines[10000]; + +/* Count this failure, setup up log destination and handle initial report. */ +static void +failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; - if (previous_failures(test_filename, test_line)) - return; + /* Record another failure for this line. */ + ++failures; + /* test_filename = filename; */ + failed_lines[line].count++; + + /* Determine whether to log header to console. */ + switch (verbosity) { + case VERBOSITY_FULL: + log_console = 1; + break; + case VERBOSITY_LIGHT_REPORT: + log_console = (failed_lines[line].count < 2); + break; + default: + log_console = 0; + } + /* Log file:line header for this failure */ va_start(ap, fmt); - fprintf(stderr, " *** SKIPPING: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); +#if _MSC_VER + logprintf("%s(%d): ", filename, line); +#else + logprintf("%s:%d: ", filename, line); +#endif + vlogprintf(fmt, ap); va_end(ap); - ++skips; + logprintf("\n"); + + if (msg != NULL && msg[0] != '\0') { + logprintf(" Description: %s\n", msg); + msg = NULL; + } + + /* Determine whether to log details to console. */ + if (verbosity == VERBOSITY_LIGHT_REPORT) + log_console = 0; } -/* Common handling of failed tests. */ +/* Complete reporting of failed tests. */ +/* + * The 'extra' hook here is used by libarchive to include libarchive + * error messages with assertion failures. It could also be used + * to add strerror() output, for example. Just define the EXTRA_DUMP() + * macro appropriately. + */ static void -report_failure(void *extra) +failure_finish(void *extra) { - if (msg[0] != '\0') { - fprintf(stderr, " Description: %s\n", msg); - msg[0] = '\0'; - } - + (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) - fprintf(stderr, " detail: %s\n", EXTRA_DUMP(extra)); -#else - (void)extra; /* UNUSED */ + logprintf(" detail: %s\n", EXTRA_DUMP(extra)); #endif if (dump_on_failure) { @@ -190,203 +356,170 @@ report_failure(void *extra) } } -/* - * Summarize repeated failures in the just-completed test file. - * The reports above suppress multiple failures from the same source - * line; this reports on any tests that did fail multiple times. - */ -static int -summarize_comparator(const void *a0, const void *b0) -{ - const struct line *a = a0, *b = b0; - if (a->line == 0 && b->line == 0) - return (0); - if (a->line == 0) - return (1); - if (b->line == 0) - return (-1); - return (a->line - b->line); -} - -static void -summarize(void) -{ - unsigned int i; - - qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]), - sizeof(failed_lines[0]), summarize_comparator); - for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { - if (failed_lines[i].line == 0) - break; - if (failed_lines[i].count > 1) - fprintf(stderr, "%s:%d: Failed %d times\n", - failed_filename, failed_lines[i].line, - failed_lines[i].count); - } - /* Clear the failure history for the next file. */ - memset(failed_lines, 0, sizeof(failed_lines)); -} - -/* Set up a message to display only after a test fails. */ +/* Inform user that we're skipping some checks. */ void -failure(const char *fmt, ...) +test_skipping(const char *fmt, ...) { + char buff[1024]; va_list ap; + va_start(ap, fmt); - vsprintf(msg, fmt, ap); + vsprintf(buff, fmt, ap); va_end(ap); + /* failure_start() isn't quite right, but is awfully convenient. */ + failure_start(test_filename, test_line, "SKIPPING: %s", buff); + --failures; /* Undo failures++ in failure_start() */ + /* Don't failure_finish() here. */ + /* Mark as skip, so doesn't count as failed test. */ + failed_lines[test_line].skip = 1; + ++skips; } +/* + * + * ASSERTIONS + * + */ + /* Generic assert() just displays the failed condition. */ int -test_assert(const char *file, int line, int value, const char *condition, void *extra) +assertion_assert(const char *file, int line, int value, + const char *condition, void *extra) { - ++assertions; - if (value) { - msg[0] = '\0'; - return (value); + assertion_count(file, line); + if (!value) { + failure_start(file, line, "Assertion failed: %s", condition); + failure_finish(extra); } - failures ++; - if (!verbose && previous_failures(file, line)) - return (value); - fprintf(stderr, "%s:%d: Assertion failed\n", file, line); - fprintf(stderr, " Condition: %s\n", condition); - report_failure(extra); return (value); } -/* assertEqualInt() displays the values of the two integers. */ +/* chdir() and report any errors */ int -test_assert_equal_int(const char *file, int line, - int v1, const char *e1, int v2, const char *e2, void *extra) +assertion_chdir(const char *file, int line, const char *pathname) { - ++assertions; - if (v1 == v2) { - msg[0] = '\0'; + assertion_count(file, line); + if (chdir(pathname) == 0) return (1); - } - failures ++; - if (!verbose && previous_failures(file, line)) - return (0); - fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n", - file, line); - fprintf(stderr, " %s=%d\n", e1, v1); - fprintf(stderr, " %s=%d\n", e2, v2); - report_failure(extra); + failure_start(file, line, "chdir(\"%s\")", pathname); + failure_finish(NULL); + return (0); + +} + +/* Verify two integers are equal. */ +int +assertion_equal_int(const char *file, int line, + long long v1, const char *e1, long long v2, const char *e2, void *extra) +{ + assertion_count(file, line); + if (v1 == v2) + return (1); + failure_start(file, line, "%s != %s", e1, e2); + logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); + logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); + failure_finish(extra); return (0); } -static void strdump(const char *p) +static void strdump(const char *e, const char *p) { + const char *q = p; + + logprintf(" %s = ", e); if (p == NULL) { - fprintf(stderr, "(null)"); + logprintf("NULL"); return; } - fprintf(stderr, "\""); + logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { - case '\a': fprintf(stderr, "\a"); break; - case '\b': fprintf(stderr, "\b"); break; - case '\n': fprintf(stderr, "\n"); break; - case '\r': fprintf(stderr, "\r"); break; + case '\a': printf("\a"); break; + case '\b': printf("\b"); break; + case '\n': printf("\n"); break; + case '\r': printf("\r"); break; default: if (c >= 32 && c < 127) - fprintf(stderr, "%c", c); + logprintf("%c", c); else - fprintf(stderr, "\\x%02X", c); + logprintf("\\x%02X", c); } } - fprintf(stderr, "\""); + logprintf("\""); + logprintf(" (length %d)\n", q == NULL ? -1 : (int)strlen(q)); } -/* assertEqualString() displays the values of the two strings. */ +/* Verify two strings are equal, dump them if not. */ int -test_assert_equal_string(const char *file, int line, +assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra) { - ++assertions; - if (v1 == NULL || v2 == NULL) { - if (v1 == v2) { - msg[0] = '\0'; - return (1); - } - } else if (strcmp(v1, v2) == 0) { - msg[0] = '\0'; + assertion_count(file, line); + if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); - } - failures ++; - if (!verbose && previous_failures(file, line)) - return (0); - fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n", - file, line); - fprintf(stderr, " %s = ", e1); - strdump(v1); - fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : (int)strlen(v1)); - fprintf(stderr, " %s = ", e2); - strdump(v2); - fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : (int)strlen(v2)); - report_failure(extra); + failure_start(file, line, "%s != %s", e1, e2); + strdump(e1, v1); + strdump(e2, v2); + failure_finish(extra); return (0); } -static void wcsdump(const wchar_t *w) +static void +wcsdump(const char *e, const wchar_t *w) { + logprintf(" %s = ", e); if (w == NULL) { - fprintf(stderr, "(null)"); + logprintf("(null)"); return; } - fprintf(stderr, "\""); + logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) - fprintf(stderr, "%c", c); + logprintf("%c", c); else if (c < 256) - fprintf(stderr, "\\x%02X", c); + logprintf("\\x%02X", c); else if (c < 0x10000) - fprintf(stderr, "\\u%04X", c); + logprintf("\\u%04X", c); else - fprintf(stderr, "\\U%08X", c); + logprintf("\\U%08X", c); } - fprintf(stderr, "\""); + logprintf("\"\n"); } -/* assertEqualWString() displays the values of the two strings. */ +#ifndef HAVE_WCSCMP +static int +wcscmp(const wchar_t *s1, const wchar_t *s2) +{ + + while (*s1 == *s2++) { + if (*s1++ == L'\0') + return 0; + } + if (*s1 > *--s2) + return 1; + else + return -1; +} +#endif + +/* Verify that two wide strings are equal, dump them if not. */ int -test_assert_equal_wstring(const char *file, int line, +assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { - ++assertions; - if (v1 == NULL) { - if (v2 == NULL) { - msg[0] = '\0'; - return (1); - } - } else if (v2 == NULL) { - if (v1 == NULL) { - msg[0] = '\0'; - return (1); - } - } else if (wcscmp(v1, v2) == 0) { - msg[0] = '\0'; + assertion_count(file, line); + if (v1 == v2 || wcscmp(v1, v2) == 0) return (1); - } - failures ++; - if (!verbose && previous_failures(file, line)) - return (0); - fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n", - file, line); - fprintf(stderr, " %s = ", e1); - wcsdump(v1); - fprintf(stderr, "\n"); - fprintf(stderr, " %s = ", e2); - wcsdump(v2); - fprintf(stderr, "\n"); - report_failure(extra); + failure_start(file, line, "%s != %s", e1, e2); + wcsdump(e1, v1); + wcsdump(e2, v2); + failure_finish(extra); return (0); } @@ -401,277 +534,1051 @@ hexdump(const char *p, const char *ref, size_t l, size_t offset) size_t i, j; char sep; + if (p == NULL) { + logprintf("(null)\n"); + return; + } for(i=0; i < l; i+=16) { - fprintf(stderr, "%04x", (int)(i + offset)); + logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; - fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]); + logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { - fprintf(stderr, "%c ", sep); + logprintf("%c ", sep); sep = ' '; } - fprintf(stderr, "%c", sep); + logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) - fprintf(stderr, "%c", c); + logprintf("%c", c); else - fprintf(stderr, "."); + logprintf("."); } - fprintf(stderr, "\n"); + logprintf("\n"); } } -/* assertEqualMem() displays the values of the two memory blocks. */ -/* TODO: For long blocks, hexdump the first bytes that actually differ. */ +/* Verify that two blocks of memory are the same, display the first + * block of differences if they're not. */ int -test_assert_equal_mem(const char *file, int line, - const char *v1, const char *e1, - const char *v2, const char *e2, +assertion_equal_mem(const char *file, int line, + const void *_v1, const char *e1, + const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { - ++assertions; - if (v1 == NULL || v2 == NULL) { - if (v1 == v2) { - msg[0] = '\0'; - return (1); - } - } else if (memcmp(v1, v2, l) == 0) { - msg[0] = '\0'; + const char *v1 = (const char *)_v1; + const char *v2 = (const char *)_v2; + size_t offset; + + assertion_count(file, line); + if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); + + failure_start(file, line, "%s != %s", e1, e2); + logprintf(" size %s = %d\n", ld, (int)l); + /* Dump 48 bytes (3 lines) so that the first difference is + * in the second line. */ + offset = 0; + while (l > 64 && memcmp(v1, v2, 32) == 0) { + /* Two lines agree, so step forward one line. */ + v1 += 16; + v2 += 16; + l -= 16; + offset += 16; } - failures ++; - if (!verbose && previous_failures(file, line)) - return (0); - fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n", - file, line); - fprintf(stderr, " size %s = %d\n", ld, (int)l); - fprintf(stderr, " Dump of %s\n", e1); - hexdump(v1, v2, l < 32 ? l : 32, 0); - fprintf(stderr, " Dump of %s\n", e2); - hexdump(v2, v1, l < 32 ? l : 32, 0); - fprintf(stderr, "\n"); - report_failure(extra); + logprintf(" Dump of %s\n", e1); + hexdump(v1, v2, l < 64 ? l : 64, offset); + logprintf(" Dump of %s\n", e2); + hexdump(v2, v1, l < 64 ? l : 64, offset); + logprintf("\n"); + failure_finish(extra); return (0); } +/* Verify that the named file exists and is empty. */ int -test_assert_empty_file(const char *f1fmt, ...) +assertion_empty_file(const char *f1fmt, ...) { char buff[1024]; char f1[1024]; struct stat st; va_list ap; ssize_t s; - int fd; - + FILE *f; + assertion_count(test_filename, test_line); va_start(ap, f1fmt); vsprintf(f1, f1fmt, ap); va_end(ap); if (stat(f1, &st) != 0) { - fprintf(stderr, "%s:%d: Could not stat: %s\n", - test_filename, test_line, f1); - failures ++; - report_failure(NULL); + failure_start(test_filename, test_line, "Stat failed: %s", f1); + failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); - failures ++; - if (!verbose && previous_failures(test_filename, test_line)) - return (0); - - fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1); - fprintf(stderr, " File size: %d\n", (int)st.st_size); - fprintf(stderr, " Contents:\n"); - fd = open(f1, O_RDONLY); - if (fd < 0) { - fprintf(stderr, " Unable to open %s\n", f1); + failure_start(test_filename, test_line, "File should be empty: %s", f1); + logprintf(" File size: %d\n", (int)st.st_size); + logprintf(" Contents:\n"); + f = fopen(f1, "rb"); + if (f == NULL) { + logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; - s = read(fd, buff, s); + s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); - close(fd); + fclose(f); } - report_failure(NULL); + failure_finish(NULL); return (0); } +/* Verify that the named file exists and is not empty. */ int -test_assert_non_empty_file(const char *f1fmt, ...) +assertion_non_empty_file(const char *f1fmt, ...) { char f1[1024]; struct stat st; va_list ap; - + assertion_count(test_filename, test_line); va_start(ap, f1fmt); vsprintf(f1, f1fmt, ap); va_end(ap); if (stat(f1, &st) != 0) { - fprintf(stderr, "%s:%d: Could not stat: %s\n", - test_filename, test_line, f1); - report_failure(NULL); - failures++; + failure_start(test_filename, test_line, "Stat failed: %s", f1); + failure_finish(NULL); return (0); } - if (st.st_size != 0) - return (1); - - failures ++; - if (!verbose && previous_failures(test_filename, test_line)) + if (st.st_size == 0) { + failure_start(test_filename, test_line, "File empty: %s", f1); + failure_finish(NULL); return (0); - - fprintf(stderr, "%s:%d: File empty: %s\n", - test_filename, test_line, f1); - report_failure(NULL); - return (0); + } + return (1); } -/* assertEqualFile() asserts that two files have the same contents. */ +/* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int -test_assert_equal_file(const char *f1, const char *f2pattern, ...) +assertion_equal_file(const char *fn1, const char *f2pattern, ...) { - char f2[1024]; + char fn2[1024]; va_list ap; char buff1[1024]; char buff2[1024]; - int fd1, fd2; + FILE *f1, *f2; int n1, n2; + assertion_count(test_filename, test_line); va_start(ap, f2pattern); - vsprintf(f2, f2pattern, ap); + vsprintf(fn2, f2pattern, ap); va_end(ap); - fd1 = open(f1, O_RDONLY); - fd2 = open(f2, O_RDONLY); + f1 = fopen(fn1, "rb"); + f2 = fopen(fn2, "rb"); for (;;) { - n1 = read(fd1, buff1, sizeof(buff1)); - n2 = read(fd2, buff2, sizeof(buff2)); + n1 = fread(buff1, 1, sizeof(buff1), f1); + n2 = fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { - close(fd1); - close(fd2); + fclose(f1); + fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } - close(fd1); - close(fd2); - failures ++; - if (!verbose && previous_failures(test_filename, test_line)) - return (0); - fprintf(stderr, "%s:%d: Files are not identical\n", - test_filename, test_line); - fprintf(stderr, " file1=\"%s\"\n", f1); - fprintf(stderr, " file2=\"%s\"\n", f2); - report_failure(test_extra); + fclose(f1); + fclose(f2); + failure_start(test_filename, test_line, "Files not identical"); + logprintf(" file1=\"%s\"\n", fn1); + logprintf(" file2=\"%s\"\n", fn2); + failure_finish(test_extra); return (0); } +/* Verify that the named file does exist. */ int -test_assert_file_exists(const char *fpattern, ...) +assertion_file_exists(const char *fpattern, ...) { char f[1024]; va_list ap; + assertion_count(test_filename, test_line); va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!_access(f, 0)) + return (1); +#else if (!access(f, F_OK)) return (1); - if (!previous_failures(test_filename, test_line)) { - fprintf(stderr, "%s:%d: File doesn't exist\n", - test_filename, test_line); - fprintf(stderr, " file=\"%s\"\n", f); - report_failure(test_extra); - } +#endif + failure_start(test_filename, test_line, "File should exist: %s", f); + failure_finish(test_extra); return (0); } +/* Verify that the named file doesn't exist. */ int -test_assert_file_not_exists(const char *fpattern, ...) +assertion_file_not_exists(const char *fpattern, ...) { char f[1024]; va_list ap; + assertion_count(test_filename, test_line); va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); +#if defined(_WIN32) && !defined(__CYGWIN__) + if (_access(f, 0)) + return (1); +#else if (access(f, F_OK)) return (1); - if (!previous_failures(test_filename, test_line)) { - fprintf(stderr, "%s:%d: File exists and shouldn't\n", - test_filename, test_line); - fprintf(stderr, " file=\"%s\"\n", f); - report_failure(test_extra); - } +#endif + failure_start(test_filename, test_line, "File should not exist: %s", f); + failure_finish(test_extra); return (0); } -/* assertFileContents() asserts the contents of a file. */ +/* Compare the contents of a file to a block of memory. */ int -test_assert_file_contents(const void *buff, int s, const char *fpattern, ...) +assertion_file_contents(const void *buff, int s, const char *fpattern, ...) { - char f[1024]; + char fn[1024]; va_list ap; char *contents; - int fd; + FILE *f; int n; + assertion_count(test_filename, test_line); va_start(ap, fpattern); - vsprintf(f, fpattern, ap); + vsprintf(fn, fpattern, ap); va_end(ap); - fd = open(f, O_RDONLY); - if (fd < 0) { - failures ++; - if (!previous_failures(test_filename, test_line)) { - fprintf(stderr, "%s:%d: File doesn't exist: %s\n", - test_filename, test_line, f); - report_failure(test_extra); - } + f = fopen(fn, "rb"); + if (f == NULL) { + failure_start(test_filename, test_line, + "File should exist: %s", fn); + failure_finish(test_extra); return (0); } contents = malloc(s * 2); - n = read(fd, contents, s * 2); - close(fd); + n = fread(contents, 1, s * 2, f); + fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } - failures ++; - if (!previous_failures(test_filename, test_line)) { - fprintf(stderr, "%s:%d: File contents don't match\n", - test_filename, test_line); - fprintf(stderr, " file=\"%s\"\n", f); - if (n > 0) - hexdump(contents, buff, n, 0); - else { - fprintf(stderr, " File empty, contents should be:\n"); - hexdump(buff, NULL, s, 0); + failure_start(test_filename, test_line, "File contents don't match"); + logprintf(" file=\"%s\"\n", fn); + if (n > 0) + hexdump(contents, buff, n > 512 ? 512 : n, 0); + else { + logprintf(" File empty, contents should be:\n"); + hexdump(buff, NULL, s > 512 ? 512 : s, 0); + } + failure_finish(test_extra); + free(contents); + return (0); +} + +/* Check the contents of a text file, being tolerant of line endings. */ +int +assertion_text_file_contents(const char *buff, const char *fn) +{ + char *contents; + const char *btxt, *ftxt; + FILE *f; + int n, s; + + assertion_count(test_filename, test_line); + f = fopen(fn, "r"); + s = strlen(buff); + contents = malloc(s * 2 + 128); + n = fread(contents, 1, s * 2 + 128 - 1, f); + if (n >= 0) + contents[n] = '\0'; + fclose(f); + /* Compare texts. */ + btxt = buff; + ftxt = (const char *)contents; + while (*btxt != '\0' && *ftxt != '\0') { + if (*btxt == *ftxt) { + ++btxt; + ++ftxt; + continue; } - report_failure(test_extra); + if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { + /* Pass over different new line characters. */ + ++btxt; + ftxt += 2; + continue; + } + break; + } + if (*btxt == '\0' && *ftxt == '\0') { + free(contents); + return (1); + } + failure_start(test_filename, test_line, "Contents don't match"); + logprintf(" file=\"%s\"\n", fn); + if (n > 0) + hexdump(contents, buff, n, 0); + else { + logprintf(" File empty, contents should be:\n"); + hexdump(buff, NULL, s, 0); } + failure_finish(test_extra); free(contents); return (0); } +/* Verify that a text file contains the specified lines, regardless of order */ +/* This could be more efficient if we sorted both sets of lines, etc, but + * since this is used only for testing and only ever deals with a dozen or so + * lines at a time, this relatively crude approach is just fine. */ +int +assertion_file_contains_lines_any_order(const char *file, int line, + const char *pathname, const char *lines[]) +{ + char *buff; + size_t buff_size; + size_t expected_count, actual_count, i, j; + char **expected; + char *p, **actual; + char c; + int expected_failure = 0, actual_failure = 0; + + assertion_count(file, line); + + buff = slurpfile(&buff_size, "%s", pathname); + if (buff == NULL) { + failure_start(pathname, line, "Can't read file: %s", pathname); + failure_finish(NULL); + return (0); + } + + // Make a copy of the provided lines and count up the expected file size. + expected_count = 0; + for (i = 0; lines[i] != NULL; ++i) { + } + expected_count = i; + expected = malloc(sizeof(char *) * expected_count); + for (i = 0; lines[i] != NULL; ++i) { + expected[i] = strdup(lines[i]); + } + + // Break the file into lines + actual_count = 0; + for (c = '\0', p = buff; p < buff + buff_size; ++p) { + if (*p == '\x0d' || *p == '\x0a') + *p = '\0'; + if (c == '\0' && *p != '\0') + ++actual_count; + c = *p; + } + actual = malloc(sizeof(char *) * actual_count); + for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { + if (*p != '\0') { + actual[j] = p; + ++j; + } + } + + // Erase matching lines from both lists + for (i = 0; i < expected_count; ++i) { + if (expected[i] == NULL) + continue; + for (j = 0; j < actual_count; ++j) { + if (actual[j] == NULL) + continue; + if (strcmp(expected[i], actual[j]) == 0) { + free(expected[i]); + expected[i] = NULL; + actual[j] = NULL; + break; + } + } + } + + // If there's anything left, it's a failure + for (i = 0; i < expected_count; ++i) { + if (expected[i] != NULL) + ++expected_failure; + } + for (j = 0; j < actual_count; ++j) { + if (actual[j] != NULL) + ++actual_failure; + } + if (expected_failure == 0 && actual_failure == 0) { + free(buff); + free(expected); + free(actual); + return (1); + } + failure_start(file, line, "File doesn't match: %s", pathname); + for (i = 0; i < expected_count; ++i) { + if (expected[i] != NULL) { + logprintf(" Expected but not present: %s\n", expected[i]); + free(expected[i]); + } + } + for (j = 0; j < actual_count; ++j) { + if (actual[j] != NULL) + logprintf(" Present but not expected: %s\n", actual[j]); + } + failure_finish(NULL); + free(buff); + free(expected); + free(actual); + return (0); +} + +/* Test that two paths point to the same file. */ +/* As a side-effect, asserts that both files exist. */ +static int +is_hardlink(const char *file, int line, + const char *path1, const char *path2) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; + int r; + + assertion_count(file, line); + r = my_GetFileInformationByName(path1, &bhfi1); + if (r == 0) { + failure_start(file, line, "File %s can't be inspected?", path1); + failure_finish(NULL); + return (0); + } + r = my_GetFileInformationByName(path2, &bhfi2); + if (r == 0) { + failure_start(file, line, "File %s can't be inspected?", path2); + failure_finish(NULL); + return (0); + } + return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber + && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh + && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); +#else + struct stat st1, st2; + int r; + + assertion_count(file, line); + r = lstat(path1, &st1); + if (r != 0) { + failure_start(file, line, "File should exist: %s", path1); + failure_finish(NULL); + return (0); + } + r = lstat(path2, &st2); + if (r != 0) { + failure_start(file, line, "File should exist: %s", path2); + failure_finish(NULL); + return (0); + } + return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); +#endif +} + +int +assertion_is_hardlink(const char *file, int line, + const char *path1, const char *path2) +{ + if (is_hardlink(file, line, path1, path2)) + return (1); + failure_start(file, line, + "Files %s and %s are not hardlinked", path1, path2); + failure_finish(NULL); + return (0); +} + +int +assertion_is_not_hardlink(const char *file, int line, + const char *path1, const char *path2) +{ + if (!is_hardlink(file, line, path1, path2)) + return (1); + failure_start(file, line, + "Files %s and %s should not be hardlinked", path1, path2); + failure_finish(NULL); + return (0); +} + +/* Verify a/b/mtime of 'pathname'. */ +/* If 'recent', verify that it's within last 10 seconds. */ +static int +assertion_file_time(const char *file, int line, + const char *pathname, long t, long nsec, char type, int recent) +{ + long long filet, filet_nsec; + int r; + +#if defined(_WIN32) && !defined(__CYGWIN__) +#define EPOC_TIME (116444736000000000ULL) + FILETIME ftime, fbirthtime, fatime, fmtime; + ULARGE_INTEGER wintm; + HANDLE h; + ftime.dwLowDateTime = 0; + ftime.dwHighDateTime = 0; + + assertion_count(file, line); + h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + failure_start(file, line, "Can't access %s\n", pathname); + failure_finish(NULL); + return (0); + } + r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); + switch (type) { + case 'a': ftime = fatime; break; + case 'b': ftime = fbirthtime; break; + case 'm': ftime = fmtime; break; + } + CloseHandle(h); + if (r == 0) { + failure_start(file, line, "Can't GetFileTime %s\n", pathname); + failure_finish(NULL); + return (0); + } + wintm.LowPart = ftime.dwLowDateTime; + wintm.HighPart = ftime.dwHighDateTime; + filet = (wintm.QuadPart - EPOC_TIME) / 10000000; + filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; + nsec = (nsec / 100) * 100; /* Round the request */ +#else + struct stat st; + + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0) { + failure_start(file, line, "Can't stat %s\n", pathname); + failure_finish(NULL); + return (0); + } + switch (type) { + case 'a': filet = st.st_atime; break; + case 'm': filet = st.st_mtime; break; + case 'b': filet = 0; break; + default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); + exit(1); + } +#if defined(__FreeBSD__) + switch (type) { + case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; + case 'b': filet = st.st_birthtime; + filet_nsec = st.st_birthtimespec.tv_nsec; break; + case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; + default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); + exit(1); + } + /* FreeBSD generally only stores to microsecond res, so round. */ + filet_nsec = (filet_nsec / 1000) * 1000; + nsec = (nsec / 1000) * 1000; +#else + filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ + if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ +#if defined(__HAIKU__) + if (type == 'a') return (1); /* Haiku doesn't have atime. */ +#endif +#endif +#endif + if (recent) { + /* Check that requested time is up-to-date. */ + time_t now = time(NULL); + if (filet < now - 10 || filet > now + 1) { + failure_start(file, line, + "File %s has %ctime %ld, %ld seconds ago\n", + pathname, type, filet, now - filet); + failure_finish(NULL); + return (0); + } + } else if (filet != t || filet_nsec != nsec) { + failure_start(file, line, + "File %s has %ctime %ld.%09ld, expected %ld.%09ld", + pathname, type, filet, filet_nsec, t, nsec); + failure_finish(NULL); + return (0); + } + return (1); +} + +/* Verify atime of 'pathname'. */ +int +assertion_file_atime(const char *file, int line, + const char *pathname, long t, long nsec) +{ + return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); +} + +/* Verify atime of 'pathname' is up-to-date. */ +int +assertion_file_atime_recent(const char *file, int line, const char *pathname) +{ + return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); +} + +/* Verify birthtime of 'pathname'. */ +int +assertion_file_birthtime(const char *file, int line, + const char *pathname, long t, long nsec) +{ + return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); +} + +/* Verify birthtime of 'pathname' is up-to-date. */ +int +assertion_file_birthtime_recent(const char *file, int line, + const char *pathname) +{ + return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); +} + +/* Verify mtime of 'pathname'. */ +int +assertion_file_mtime(const char *file, int line, + const char *pathname, long t, long nsec) +{ + return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); +} + +/* Verify mtime of 'pathname' is up-to-date. */ +int +assertion_file_mtime_recent(const char *file, int line, const char *pathname) +{ + return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); +} + +/* Verify number of links to 'pathname'. */ +int +assertion_file_nlinks(const char *file, int line, + const char *pathname, int nlinks) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + BY_HANDLE_FILE_INFORMATION bhfi; + int r; + + assertion_count(file, line); + r = my_GetFileInformationByName(pathname, &bhfi); + if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) + return (1); + failure_start(file, line, "File %s has %d links, expected %d", + pathname, bhfi.nNumberOfLinks, nlinks); + failure_finish(NULL); + return (0); +#else + struct stat st; + int r; + + assertion_count(file, line); + r = lstat(pathname, &st); + if (r == 0 && st.st_nlink == nlinks) + return (1); + failure_start(file, line, "File %s has %d links, expected %d", + pathname, st.st_nlink, nlinks); + failure_finish(NULL); + return (0); +#endif +} + +/* Verify size of 'pathname'. */ +int +assertion_file_size(const char *file, int line, const char *pathname, long size) +{ + int64_t filesize; + int r; + + assertion_count(file, line); +#if defined(_WIN32) && !defined(__CYGWIN__) + { + BY_HANDLE_FILE_INFORMATION bhfi; + r = !my_GetFileInformationByName(pathname, &bhfi); + filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; + } +#else + { + struct stat st; + r = lstat(pathname, &st); + filesize = st.st_size; + } +#endif + if (r == 0 && filesize == size) + return (1); + failure_start(file, line, "File %s has size %ld, expected %ld", + pathname, (long)filesize, (long)size); + failure_finish(NULL); + return (0); +} + +/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ +int +assertion_is_dir(const char *file, int line, const char *pathname, int mode) +{ + struct stat st; + int r; + +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)mode; /* UNUSED */ +#endif + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0) { + failure_start(file, line, "Dir should exist: %s", pathname); + failure_finish(NULL); + return (0); + } + if (!S_ISDIR(st.st_mode)) { + failure_start(file, line, "%s is not a dir", pathname); + failure_finish(NULL); + return (0); + } +#if !defined(_WIN32) || defined(__CYGWIN__) + /* Windows doesn't handle permissions the same way as POSIX, + * so just ignore the mode tests. */ + /* TODO: Can we do better here? */ + if (mode >= 0 && mode != (st.st_mode & 07777)) { + failure_start(file, line, "Dir %s has wrong mode", pathname); + logprintf(" Expected: 0%3o\n", mode); + logprintf(" Found: 0%3o\n", st.st_mode & 07777); + failure_finish(NULL); + return (0); + } +#endif + return (1); +} + +/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, + * verify that too. */ +int +assertion_is_reg(const char *file, int line, const char *pathname, int mode) +{ + struct stat st; + int r; + +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)mode; /* UNUSED */ +#endif + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0 || !S_ISREG(st.st_mode)) { + failure_start(file, line, "File should exist: %s", pathname); + failure_finish(NULL); + return (0); + } +#if !defined(_WIN32) || defined(__CYGWIN__) + /* Windows doesn't handle permissions the same way as POSIX, + * so just ignore the mode tests. */ + /* TODO: Can we do better here? */ + if (mode >= 0 && mode != (st.st_mode & 07777)) { + failure_start(file, line, "File %s has wrong mode", pathname); + logprintf(" Expected: 0%3o\n", mode); + logprintf(" Found: 0%3o\n", st.st_mode & 07777); + failure_finish(NULL); + return (0); + } +#endif + return (1); +} + +/* Check whether 'pathname' is a symbolic link. If 'contents' is + * non-NULL, verify that the symlink has those contents. */ +static int +is_symlink(const char *file, int line, + const char *pathname, const char *contents) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)pathname; /* UNUSED */ + (void)contents; /* UNUSED */ + assertion_count(file, line); + /* Windows sort-of has real symlinks, but they're only usable + * by privileged users and are crippled even then, so there's + * really not much point in bothering with this. */ + return (0); +#else + char buff[300]; + struct stat st; + ssize_t linklen; + int r; + + assertion_count(file, line); + r = lstat(pathname, &st); + if (r != 0) { + failure_start(file, line, + "Symlink should exist: %s", pathname); + failure_finish(NULL); + return (0); + } + if (!S_ISLNK(st.st_mode)) + return (0); + if (contents == NULL) + return (1); + linklen = readlink(pathname, buff, sizeof(buff)); + if (linklen < 0) { + failure_start(file, line, "Can't read symlink %s", pathname); + failure_finish(NULL); + return (0); + } + buff[linklen] = '\0'; + if (strcmp(buff, contents) != 0) + return (0); + return (1); +#endif +} + +/* Assert that path is a symlink that (optionally) contains contents. */ +int +assertion_is_symlink(const char *file, int line, + const char *path, const char *contents) +{ + if (is_symlink(file, line, path, contents)) + return (1); + if (contents) + failure_start(file, line, "File %s is not a symlink to %s", + path, contents); + else + failure_start(file, line, "File %s is not a symlink", path); + failure_finish(NULL); + return (0); +} + + +/* Create a directory and report any errors. */ +int +assertion_make_dir(const char *file, int line, const char *dirname, int mode) +{ + assertion_count(file, line); +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)mode; /* UNUSED */ + if (0 == _mkdir(dirname)) + return (1); +#else + if (0 == mkdir(dirname, mode)) + return (1); +#endif + failure_start(file, line, "Could not create directory %s", dirname); + failure_finish(NULL); + return(0); +} + +/* Create a file with the specified contents and report any failures. */ +int +assertion_make_file(const char *file, int line, + const char *path, int mode, const char *contents) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + /* TODO: Rework this to set file mode as well. */ + FILE *f; + (void)mode; /* UNUSED */ + assertion_count(file, line); + f = fopen(path, "wb"); + if (f == NULL) { + failure_start(file, line, "Could not create file %s", path); + failure_finish(NULL); + return (0); + } + if (contents != NULL) { + if (strlen(contents) + != fwrite(contents, 1, strlen(contents), f)) { + fclose(f); + failure_start(file, line, + "Could not write file %s", path); + failure_finish(NULL); + return (0); + } + } + fclose(f); + return (1); +#else + int fd; + assertion_count(file, line); + fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); + if (fd < 0) { + failure_start(file, line, "Could not create %s", path); + failure_finish(NULL); + return (0); + } + if (contents != NULL) { + if ((ssize_t)strlen(contents) + != write(fd, contents, strlen(contents))) { + close(fd); + failure_start(file, line, "Could not write to %s", path); + failure_finish(NULL); + return (0); + } + } + close(fd); + return (1); +#endif +} + +/* Create a hardlink and report any failures. */ +int +assertion_make_hardlink(const char *file, int line, + const char *newpath, const char *linkto) +{ + int succeeded; + + assertion_count(file, line); +#if defined(_WIN32) && !defined(__CYGWIN__) + succeeded = my_CreateHardLinkA(newpath, linkto); +#elif HAVE_LINK + succeeded = !link(linkto, newpath); +#else + succeeded = 0; +#endif + if (succeeded) + return (1); + failure_start(file, line, "Could not create hardlink"); + logprintf(" New link: %s\n", newpath); + logprintf(" Old name: %s\n", linkto); + failure_finish(NULL); + return(0); +} + +/* Create a symlink and report any failures. */ +int +assertion_make_symlink(const char *file, int line, + const char *newpath, const char *linkto) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + int targetIsDir = 0; /* TODO: Fix this */ + assertion_count(file, line); + if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) + return (1); +#elif HAVE_SYMLINK + assertion_count(file, line); + if (0 == symlink(linkto, newpath)) + return (1); +#endif + failure_start(file, line, "Could not create symlink"); + logprintf(" New link: %s\n", newpath); + logprintf(" Old name: %s\n", linkto); + failure_finish(NULL); + return(0); +} + +/* Set umask, report failures. */ +int +assertion_umask(const char *file, int line, int mask) +{ + assertion_count(file, line); + (void)file; /* UNUSED */ + (void)line; /* UNUSED */ + umask(mask); + return (1); +} + +/* + * + * UTILITIES for use by tests. + * + */ + +/* + * Check whether platform supports symlinks. This is intended + * for tests to use in deciding whether to bother testing symlink + * support; if the platform doesn't support symlinks, there's no point + * in checking whether the program being tested can create them. + * + * Note that the first time this test is called, we actually go out to + * disk to create and verify a symlink. This is necessary because + * symlink support is actually a property of a particular filesystem + * and can thus vary between directories on a single system. After + * the first call, this returns the cached result from memory, so it's + * safe to call it as often as you wish. + */ +int +canSymlink(void) +{ + /* Remember the test result */ + static int value = 0, tested = 0; + if (tested) + return (value); + + ++tested; + assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, "a"); + /* Note: Cygwin has its own symlink() emulation that does not + * use the Win32 CreateSymbolicLink() function. */ +#if defined(_WIN32) && !defined(__CYGWIN__) + value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) + && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); +#elif HAVE_SYMLINK + value = (0 == symlink("canSymlink.0", "canSymlink.1")) + && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); +#endif + return (value); +} + +/* + * Can this platform run the gzip program? + */ +/* Platform-dependent options for hiding the output of a subcommand. */ +#if defined(_WIN32) && !defined(__CYGWIN__) +static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ +#else +static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ +#endif +int +canGzip(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("gzip -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Can this platform run the gunzip program? + */ +int +canGunzip(void) +{ + static int tested = 0, value = 0; + if (!tested) { + tested = 1; + if (systemf("gunzip -V %s", redirectArgs) == 0) + value = 1; + } + return (value); +} + +/* + * Sleep as needed; useful for verifying disk timestamp changes by + * ensuring that the wall-clock time has actually changed before we + * go back to re-read something from disk. + */ +void +sleepUntilAfter(time_t t) +{ + while (t >= time(NULL)) +#if defined(_WIN32) && !defined(__CYGWIN__) + Sleep(500); +#else + sleep(1); +#endif +} + /* * Call standard system() call, but build up the command line using * sprintf() conventions. @@ -685,6 +1592,8 @@ systemf(const char *fmt, ...) va_start(ap, fmt); vsprintf(buff, fmt, ap); + if (verbosity > VERBOSITY_FULL) + logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); @@ -703,116 +1612,263 @@ slurpfile(size_t * sizep, const char *fmt, ...) va_list ap; char *p; ssize_t bytes_read; - int fd; + FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); - fd = open(filename, O_RDONLY); - if (fd < 0) { + f = fopen(filename, "rb"); + if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } - r = fstat(fd, &st); + r = fstat(fileno(f), &st); if (r != 0) { - fprintf(stderr, "Can't stat file %s\n", filename); - close(fd); + logprintf("Can't stat file %s\n", filename); + fclose(f); return (NULL); } - p = malloc(st.st_size + 1); + p = malloc((size_t)st.st_size + 1); if (p == NULL) { - fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); - close(fd); + logprintf("Can't allocate %ld bytes of memory to read file %s\n", + (long int)st.st_size, filename); + fclose(f); return (NULL); } - bytes_read = read(fd, p, st.st_size); + bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { - fprintf(stderr, "Can't read file %s\n", filename); - close(fd); + logprintf("Can't read file %s\n", filename); + fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; - close(fd); + fclose(f); return (p); } +/* Read a uuencoded file from the reference directory, decode, and + * write the result into the current directory. */ +#define UUDECODE(c) (((c) - 0x20) & 0x3f) +void +extract_reference_file(const char *name) +{ + char buff[1024]; + FILE *in, *out; + + sprintf(buff, "%s/%s.uu", refdir, name); + in = fopen(buff, "r"); + failure("Couldn't open reference file %s", buff); + assert(in != NULL); + if (in == NULL) + return; + /* Read up to and including the 'begin' line. */ + for (;;) { + if (fgets(buff, sizeof(buff), in) == NULL) { + /* TODO: This is a failure. */ + return; + } + if (memcmp(buff, "begin ", 6) == 0) + break; + } + /* Now, decode the rest and write it. */ + /* Not a lot of error checking here; the input better be right. */ + out = fopen(name, "wb"); + while (fgets(buff, sizeof(buff), in) != NULL) { + char *p = buff; + int bytes; + + if (memcmp(buff, "end", 3) == 0) + break; + + bytes = UUDECODE(*p++); + while (bytes > 0) { + int n = 0; + /* Write out 1-3 bytes from that. */ + if (bytes > 0) { + n = UUDECODE(*p++) << 18; + n |= UUDECODE(*p++) << 12; + fputc(n >> 16, out); + --bytes; + } + if (bytes > 0) { + n |= UUDECODE(*p++) << 6; + fputc((n >> 8) & 0xFF, out); + --bytes; + } + if (bytes > 0) { + n |= UUDECODE(*p++); + fputc(n & 0xFF, out); + --bytes; + } + } + } + fclose(out); + fclose(in); +} + +/* + * + * TEST management + * + */ + /* - * "list.h" is automatically generated; it just has a lot of lines like: - * DEFINE_TEST(function_name) - * It's used above to declare all of the test functions. - * We reuse it here to define a list of all tests (functions and names). + * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has + * a line like + * DEFINE_TEST(test_function) + * for each test. */ + +/* Use "list.h" to declare all of the test functions. */ +#undef DEFINE_TEST +#define DEFINE_TEST(name) void name(void); +#include "list.h" + +/* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST -#define DEFINE_TEST(n) { n, #n }, -struct { void (*func)(void); const char *name; } tests[] = { +#define DEFINE_TEST(n) { n, #n, 0 }, +struct { void (*func)(void); const char *name; int failures; } tests[] = { #include "list.h" }; /* - * Each test is run in a private work dir. Those work dirs - * do have consistent and predictable names, in case a group - * of tests need to collaborate. However, there is no provision - * for requiring that tests run in a certain order. + * Summarize repeated failures in the just-completed test. */ -static int test_run(int i, const char *tmpdir) +static void +test_summarize(const char *filename, int failed) { - int failures_before = failures; + unsigned int i; - if (!quiet_flag) { - printf("%d: %s\n", i, tests[i].name); + switch (verbosity) { + case VERBOSITY_SUMMARY_ONLY: + printf(failed ? "E" : "."); fflush(stdout); + break; + case VERBOSITY_PASSFAIL: + printf(failed ? "FAIL\n" : "ok\n"); + break; } - /* - * Always explicitly chdir() in case the last test moved us to - * a strange place. - */ - if (chdir(tmpdir)) { - fprintf(stderr, - "ERROR: Couldn't chdir to temp dir %s\n", - tmpdir); - exit(1); + log_console = (verbosity == VERBOSITY_LIGHT_REPORT); + + for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { + if (failed_lines[i].count > 1 && !failed_lines[i].skip) + logprintf("%s:%d: Summary: Failed %d times\n", + filename, i, failed_lines[i].count); + } + /* Clear the failure history for the next file. */ + memset(failed_lines, 0, sizeof(failed_lines)); +} + +/* + * Actually run a single test, with appropriate setup and cleanup. + */ +static int +test_run(int i, const char *tmpdir) +{ + char logfilename[64]; + int failures_before = failures; + int oldumask; + + switch (verbosity) { + case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ + break; + case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ + printf("%3d: %-50s", i, tests[i].name); + fflush(stdout); + break; + default: /* Title of test, details will follow */ + printf("%3d: %s\n", i, tests[i].name); } - /* Create a temp directory for this specific test. */ - if (mkdir(tests[i].name, 0755)) { + + /* Chdir to the top-level work directory. */ + if (!assertChdir(tmpdir)) { fprintf(stderr, - "ERROR: Couldn't create temp dir ``%s''\n", - tests[i].name); + "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } - /* Chdir() to that work directory. */ - if (chdir(tests[i].name)) { + /* Create a log file for this test. */ + sprintf(logfilename, "%s.log", tests[i].name); + logfile = fopen(logfilename, "w"); + fprintf(logfile, "%s\n\n", tests[i].name); + /* Chdir() to a work dir for this specific test. */ + if (!assertMakeDir(tests[i].name, 0755) + || !assertChdir(tests[i].name)) { fprintf(stderr, - "ERROR: Couldn't chdir to temp dir ``%s''\n", - tests[i].name); + "ERROR: Can't chdir to work dir %s/%s\n", + tmpdir, tests[i].name); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); - /* Run the actual test. */ + /* Record the umask before we run the test. */ + umask(oldumask = umask(0)); + /* + * Run the actual test. + */ (*tests[i].func)(); - /* Summarize the results of this test. */ - summarize(); - /* If there were no failures, we can remove the work dir. */ - if (failures == failures_before) { - if (!keep_temp_files && chdir(tmpdir) == 0) { + /* + * Clean up and report afterwards. + */ + /* Restore umask */ + umask(oldumask); + /* Reset locale. */ + setlocale(LC_ALL, "C"); + /* Reset directory. */ + if (!assertChdir(tmpdir)) { + fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", + tmpdir); + exit(1); + } + /* Report per-test summaries. */ + tests[i].failures = failures - failures_before; + test_summarize(test_filename, tests[i].failures); + /* Close the per-test log file. */ + fclose(logfile); + logfile = NULL; + /* If there were no failures, we can remove the work dir and logfile. */ + if (tests[i].failures == 0) { + if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) - systemf("rmdir /S /Q %s", tests[i].name); + /* Make sure not to leave empty directories. + * Sometimes a processing of closing files used by tests + * is not done, then rmdir will be failed and it will + * leave a empty test directory. So we should wait a few + * seconds and retry rmdir. */ + int r, t; + for (t = 0; t < 10; t++) { + if (t > 0) + Sleep(1000); + r = systemf("rmdir /S /Q %s", tests[i].name); + if (r == 0) + break; + } + systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); + systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ - return (failures == failures_before ? 0 : 1); + return (tests[i].failures); } -static void usage(const char *program) +/* + * + * + * MAIN and support routines. + * + * + */ + +static void +usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; @@ -838,87 +1894,101 @@ static void usage(const char *program) exit(1); } -#define UUDECODE(c) (((c) - 0x20) & 0x3f) - -void -extract_reference_file(const char *name) +static char * +get_refdir(const char *d) { - char buff[1024]; - FILE *in, *out; + char tried[512] = { '\0' }; + char buff[128]; + char *pwd, *p; + + /* If a dir was specified, try that */ + if (d != NULL) { + pwd = NULL; + snprintf(buff, sizeof(buff), "%s", d); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + goto failure; + } - sprintf(buff, "%s/%s.uu", refdir, name); - in = fopen(buff, "r"); - failure("Couldn't open reference file %s", buff); - assert(in != NULL); - if (in == NULL) - return; - /* Read up to and including the 'begin' line. */ - for (;;) { - if (fgets(buff, sizeof(buff), in) == NULL) { - /* TODO: This is a failure. */ - return; - } - if (memcmp(buff, "begin ", 6) == 0) - break; + /* Get the current dir. */ + pwd = getcwd(NULL, 0); + while (pwd[strlen(pwd) - 1] == '\n') + pwd[strlen(pwd) - 1] = '\0'; + + /* Look for a known file. */ + snprintf(buff, sizeof(buff), "%s", pwd); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + + snprintf(buff, sizeof(buff), "%s/test", pwd); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + +#if defined(LIBRARY) + snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); +#else + snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM); +#endif + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + + if (memcmp(pwd, "/usr/obj", 8) == 0) { + snprintf(buff, sizeof(buff), "%s", pwd + 8); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + + snprintf(buff, sizeof(buff), "%s/test", pwd + 8); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); } - /* Now, decode the rest and write it. */ - /* Not a lot of error checking here; the input better be right. */ - out = fopen(name, "w"); - while (fgets(buff, sizeof(buff), in) != NULL) { - char *p = buff; - int bytes; - if (memcmp(buff, "end", 3) == 0) - break; +failure: + printf("Unable to locate known reference file %s\n", KNOWNREF); + printf(" Checked following directories:\n%s\n", tried); +#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) + DebugBreak(); +#endif + exit(1); - bytes = UUDECODE(*p++); - while (bytes > 0) { - int n = 0; - /* Write out 1-3 bytes from that. */ - if (bytes > 0) { - n = UUDECODE(*p++) << 18; - n |= UUDECODE(*p++) << 12; - fputc(n >> 16, out); - --bytes; - } - if (bytes > 0) { - n |= UUDECODE(*p++) << 6; - fputc((n >> 8) & 0xFF, out); - --bytes; - } - if (bytes > 0) { - n |= UUDECODE(*p++); - fputc(n & 0xFF, out); - --bytes; - } - } - } - fclose(out); - fclose(in); +success: + free(p); + free(pwd); + return strdup(buff); } - -int main(int argc, char **argv) +int +main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); - int i, tests_run = 0, tests_failed = 0, opt; + int i, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; -#if defined(_WIN32) && !defined(__CYGWIN__) - char *testprg; -#endif - const char *opt_arg, *progname, *p; + const char *progname; + const char *tmp, *option_arg, *p; char tmpdir[256]; char tmpdir_timestamp[256]; (void)argc; /* UNUSED */ -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure open() function will be used with a binary mode. */ - /* on cygwin, we need something similar, but instead link against */ - /* a special startup object, binmode.o */ - _set_fmode(_O_BINARY); +#if defined(HAVE__CrtSetReportMode) + /* To stop to run the default invalid parameter handler. */ + _set_invalid_parameter_handler(invalid_parameter_handler); + /* Disable annoying assertion message box. */ + _CrtSetReportMode(_CRT_ASSERT, 0); #endif + /* * Name of this program, used to build root of our temp directory * tree. @@ -933,9 +2003,20 @@ int main(int argc, char **argv) #ifdef PROGRAM /* Get the target program from environment, if available. */ - testprog = getenv(ENVBASE); + testprogfile = getenv(ENVBASE); #endif + if (getenv("TMPDIR") != NULL) + tmp = getenv("TMPDIR"); + else if (getenv("TMP") != NULL) + tmp = getenv("TMP"); + else if (getenv("TEMP") != NULL) + tmp = getenv("TEMP"); + else if (getenv("TEMPDIR") != NULL) + tmp = getenv("TEMPDIR"); + else + tmp = "/tmp"; + /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; @@ -954,24 +2035,24 @@ int main(int argc, char **argv) p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { - opt = *p++; - opt_arg = NULL; + option = *p++; + option_arg = NULL; /* If 'opt' takes an argument, parse that. */ - if (opt == 'p' || opt == 'r') { + if (option == 'p' || option == 'r') { if (*p != '\0') - opt_arg = p; + option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", - opt); + option); usage(progname); } else - opt_arg = *argv++; + option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ - switch (opt) { + switch (option) { case 'd': dump_on_failure = 1; break; @@ -980,22 +2061,24 @@ int main(int argc, char **argv) break; case 'p': #ifdef PROGRAM - testprog = opt_arg; + testprogfile = option_arg; #else + fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': - quiet_flag++; + verbosity--; break; case 'r': - refdir = opt_arg; + refdir = option_arg; break; case 'v': - verbose = 1; + verbosity++; break; - case '?': default: + fprintf(stderr, "Unrecognized option '%c'\n", + option); usage(progname); } } @@ -1005,20 +2088,29 @@ int main(int argc, char **argv) * Sanity-check that our options make sense. */ #ifdef PROGRAM - if (testprog == NULL) + if (testprogfile == NULL) { + fprintf(stderr, "Program executable required\n"); usage(progname); -#endif + } + + { + char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) - /* - * command.com cannot accept the command used '/' with drive - * name such as c:/xxx/command.exe when use '|' pipe handling. - */ - testprg = strdup(testprog); - for (i = 0; testprg[i] != '\0'; i++) { - if (testprg[i] == '/') - testprg[i] = '\\'; + /* Command.com sometimes rejects '/' separators. */ + testprg = strdup(testprogfile); + for (i = 0; testprg[i] != '\0'; i++) { + if (testprg[i] == '/') + testprg[i] = '\\'; + } + testprogfile = testprg; +#endif + /* Quote the name that gets put into shell command lines. */ + testprg = malloc(strlen(testprogfile) + 3); + strcpy(testprg, "\""); + strcat(testprg, testprogfile); + strcat(testprg, "\""); + testprog = testprg; } - testprog = testprg; #endif /* @@ -1027,42 +2119,37 @@ int main(int argc, char **argv) * to make it easier to track the results of multiple tests. */ now = time(NULL); - for (i = 0; i < 1000; i++) { + for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); - sprintf(tmpdir, "/tmp/%s.%s-%03d", progname, tmpdir_timestamp, i); - if (mkdir(tmpdir,0755) == 0) + sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, + tmpdir_timestamp, i); + if (assertMakeDir(tmpdir,0755)) break; - if (errno == EEXIST) - continue; - fprintf(stderr, "ERROR: Unable to create temp directory %s\n", - tmpdir); - exit(1); + if (i >= 999) { + fprintf(stderr, + "ERROR: Unable to create temp directory %s\n", + tmpdir); + exit(1); + } } /* * If the user didn't specify a directory for locating - * reference files, use the current directory for that. + * reference files, try to find the reference files in + * the "usual places." */ - if (refdir == NULL) { - char *q; - systemf("/bin/pwd > %s/refdir", tmpdir); - q = slurpfile(NULL, "%s/refdir", tmpdir); - refdir = refdir_alloc = q; - q += strlen(refdir); - while (q[-1] == '\n') { - --q; - *q = '\0'; - } - systemf("rm %s/refdir", tmpdir); - } + refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ - if (!quiet_flag) { - printf("Running tests in: %s\n", tmpdir); + printf("\n"); + printf("If tests fail or crash, details will be in:\n"); + printf(" %s\n", tmpdir); + printf("\n"); + if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); @@ -1070,6 +2157,9 @@ int main(int argc, char **argv) printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); + } else { + printf("Running "); + fflush(stdout); } /* @@ -1084,15 +2174,30 @@ int main(int argc, char **argv) } } else { while (*(argv) != NULL) { - i = atoi(*argv); - if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) { - printf("*** INVALID Test %s\n", *argv); - usage(progname); + if (**argv >= '0' && **argv <= '9') { + i = atoi(*argv); + if (i < 0 || i >= limit) { + printf("*** INVALID Test %s\n", *argv); + free(refdir_alloc); + usage(progname); + /* usage() never returns */ + } } else { - if (test_run(i, tmpdir)) - tests_failed++; - tests_run++; + for (i = 0; i < limit; ++i) { + if (strcmp(*argv, tests[i].name) == 0) + break; + } + if (i >= limit) { + printf("*** INVALID Test ``%s''\n", + *argv); + free(refdir_alloc); + usage(progname); + /* usage() never returns */ + } } + if (test_run(i, tmpdir)) + tests_failed++; + tests_run++; argv++; } } @@ -1100,20 +2205,38 @@ int main(int argc, char **argv) /* * Report summary statistics. */ - if (!quiet_flag) { + if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); - printf("%d of %d tests reported failures\n", - tests_failed, tests_run); - printf(" Total of %d assertions checked.\n", assertions); - printf(" Total of %d assertions failed.\n", failures); - printf(" Total of %d assertions skipped.\n", skips); + printf("Totals:\n"); + printf(" Tests run: %8d\n", tests_run); + printf(" Tests failed: %8d\n", tests_failed); + printf(" Assertions checked:%8d\n", assertions); + printf(" Assertions failed: %8d\n", failures); + printf(" Skips reported: %8d\n", skips); + } + if (failures) { + printf("\n"); + printf("Failing tests:\n"); + for (i = 0; i < limit; ++i) { + if (tests[i].failures) + printf(" %d: %s (%d failures)\n", i, + tests[i].name, tests[i].failures); + } + printf("\n"); + printf("Details for failing tests: %s\n", tmpdir); + printf("\n"); + } else { + if (verbosity == VERBOSITY_SUMMARY_ONLY) + printf("\n"); + printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ + assertChdir(".."); rmdir(tmpdir); - return (tests_failed); + return (tests_failed ? 1 : 0); } diff --git a/usr.bin/tar/test/test.h b/usr.bin/tar/test/test.h index 6a1e1e8..a4a2461 100644 --- a/usr.bin/tar/test/test.h +++ b/usr.bin/tar/test/test.h @@ -45,37 +45,82 @@ #error Oops: No config.h and no pre-built configuration in test.h. #endif -#if !defined(_WIN32) || defined(__CYGWIN__) +#include <sys/types.h> /* Windows requires this before sys/stat.h */ +#include <sys/stat.h> + +#ifdef USE_DMALLOC +#include <dmalloc.h> +#endif +#if HAVE_DIRENT_H #include <dirent.h> -#else -#define dirent direct -#include "../bsdtar_windows.h" +#endif +#ifdef HAVE_DIRECT_H #include <direct.h> +#define dirent direct #endif #include <errno.h> #include <fcntl.h> +#ifdef HAVE_IO_H +#include <io.h> +#endif #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/stat.h> -#if !defined(_WIN32) || defined(__CYGWIN__) +#include <time.h> +#ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <wchar.h> - -#ifdef USE_DMALLOC -#include <dmalloc.h> +#ifdef HAVE_WINDOWS_H +#include <windows.h> #endif -#ifdef __FreeBSD__ -#include <sys/cdefs.h> /* For __FBSDID */ -#else -/* Some non-FreeBSD platforms such as newlib-derived ones like - * cygwin, have __FBSDID, so this definition must be guarded. +/* + * System-specific tweaks. We really want to minimize these + * as much as possible, since they make it harder to understand + * the mainline code. */ + +/* Windows (including Visual Studio and MinGW but not Cygwin) */ +#if defined(_WIN32) && !defined(__CYGWIN__) +#include "../bsdtar_windows.h" +#if !defined(__BORLANDC__) +#define strdup _strdup +#endif +#define LOCALE_DE "deu" +#else +#define LOCALE_DE "de_DE.UTF-8" +#endif + +/* Visual Studio */ +#ifdef _MSC_VER +#define snprintf sprintf_s +#endif + +/* Cygwin */ +#if defined(__CYGWIN__) +/* Cygwin-1.7.x is lazy about populating nlinks, so don't + * expect it to be accurate. */ +# define NLINKS_INACCURATE_FOR_DIRS +#endif + +#if defined(__HAIKU__) || defined(__QNXNTO__) +/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */ +#include <stdint.h> +#endif + +/* Get a real definition for __FBSDID if we can */ +#if HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif + +/* If not, define it so as to avoid dangling semicolons. */ #ifndef __FBSDID -#define __FBSDID(a) /* null */ +#define __FBSDID(a) struct _undefined_hack #endif + +#ifndef O_BINARY +#define O_BINARY 0 #endif /* @@ -85,41 +130,83 @@ #define DEFINE_TEST(name) void name(void); void name(void) /* An implementation of the standard assert() macro */ -#define assert(e) test_assert(__FILE__, __LINE__, (e), #e, NULL) - +#define assert(e) assertion_assert(__FILE__, __LINE__, (e), #e, NULL) +/* chdir() and error if it fails */ +#define assertChdir(path) \ + assertion_chdir(__FILE__, __LINE__, path) /* Assert two integers are the same. Reports value of each one if not. */ -#define assertEqualInt(v1,v2) \ - test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) - +#define assertEqualInt(v1,v2) \ + assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* Assert two strings are the same. Reports value of each one if not. */ #define assertEqualString(v1,v2) \ - test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) + assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* As above, but v1 and v2 are wchar_t * */ #define assertEqualWString(v1,v2) \ - test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) + assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) /* As above, but raw blocks of bytes. */ #define assertEqualMem(v1, v2, l) \ - test_assert_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) + assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL) /* Assert two files are the same; allow printf-style expansion of second name. * See below for comments about variable arguments here... */ #define assertEqualFile \ - test_setup(__FILE__, __LINE__);test_assert_equal_file + assertion_setup(__FILE__, __LINE__);assertion_equal_file /* Assert that a file is empty; supports printf-style arguments. */ #define assertEmptyFile \ - test_setup(__FILE__, __LINE__);test_assert_empty_file + assertion_setup(__FILE__, __LINE__);assertion_empty_file /* Assert that a file is not empty; supports printf-style arguments. */ #define assertNonEmptyFile \ - test_setup(__FILE__, __LINE__);test_assert_non_empty_file + assertion_setup(__FILE__, __LINE__);assertion_non_empty_file +#define assertFileAtime(pathname, sec, nsec) \ + assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec) +#define assertFileAtimeRecent(pathname) \ + assertion_file_atime_recent(__FILE__, __LINE__, pathname) +#define assertFileBirthtime(pathname, sec, nsec) \ + assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec) +#define assertFileBirthtimeRecent(pathname) \ + assertion_file_birthtime_recent(__FILE__, __LINE__, pathname) /* Assert that a file exists; supports printf-style arguments. */ #define assertFileExists \ - test_setup(__FILE__, __LINE__);test_assert_file_exists + assertion_setup(__FILE__, __LINE__);assertion_file_exists /* Assert that a file exists; supports printf-style arguments. */ #define assertFileNotExists \ - test_setup(__FILE__, __LINE__);test_assert_file_not_exists + assertion_setup(__FILE__, __LINE__);assertion_file_not_exists /* Assert that file contents match a string; supports printf-style arguments. */ #define assertFileContents \ - test_setup(__FILE__, __LINE__);test_assert_file_contents + assertion_setup(__FILE__, __LINE__);assertion_file_contents +#define assertFileMtime(pathname, sec, nsec) \ + assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec) +#define assertFileMtimeRecent(pathname) \ + assertion_file_mtime_recent(__FILE__, __LINE__, pathname) +#define assertFileNLinks(pathname, nlinks) \ + assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks) +#define assertFileSize(pathname, size) \ + assertion_file_size(__FILE__, __LINE__, pathname, size) +#define assertTextFileContents \ + assertion_setup(__FILE__, __LINE__);assertion_text_file_contents +#define assertFileContainsLinesAnyOrder(pathname, lines) \ + assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines) +#define assertIsDir(pathname, mode) \ + assertion_is_dir(__FILE__, __LINE__, pathname, mode) +#define assertIsHardlink(path1, path2) \ + assertion_is_hardlink(__FILE__, __LINE__, path1, path2) +#define assertIsNotHardlink(path1, path2) \ + assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) +#define assertIsReg(pathname, mode) \ + assertion_is_reg(__FILE__, __LINE__, pathname, mode) +#define assertIsSymlink(pathname, contents) \ + assertion_is_symlink(__FILE__, __LINE__, pathname, contents) +/* Create a directory, report error if it fails. */ +#define assertMakeDir(dirname, mode) \ + assertion_make_dir(__FILE__, __LINE__, dirname, mode) +#define assertMakeFile(path, mode, contents) \ + assertion_make_file(__FILE__, __LINE__, path, mode, contents) +#define assertMakeHardlink(newfile, oldfile) \ + assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) +#define assertMakeSymlink(newfile, linkto) \ + assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) +#define assertUmask(mask) \ + assertion_umask(__FILE__, __LINE__, mask) /* * This would be simple with C99 variadic macros, but I don't want to @@ -128,27 +215,61 @@ * but effective. */ #define skipping \ - test_setup(__FILE__, __LINE__);test_skipping + assertion_setup(__FILE__, __LINE__);test_skipping /* Function declarations. These are defined in test_utility.c. */ void failure(const char *fmt, ...); -void test_setup(const char *, int); +int assertion_assert(const char *, int, int, const char *, void *); +int assertion_chdir(const char *, int, const char *); +int assertion_empty_file(const char *, ...); +int assertion_equal_file(const char *, const char *, ...); +int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *); +int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *); +int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *); +int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); +int assertion_file_atime(const char *, int, const char *, long, long); +int assertion_file_atime_recent(const char *, int, const char *); +int assertion_file_birthtime(const char *, int, const char *, long, long); +int assertion_file_birthtime_recent(const char *, int, const char *); +int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **); +int assertion_file_contents(const void *, int, const char *, ...); +int assertion_file_exists(const char *, ...); +int assertion_file_mtime(const char *, int, const char *, long, long); +int assertion_file_mtime_recent(const char *, int, const char *); +int assertion_file_nlinks(const char *, int, const char *, int); +int assertion_file_not_exists(const char *, ...); +int assertion_file_size(const char *, int, const char *, long); +int assertion_is_dir(const char *, int, const char *, int); +int assertion_is_hardlink(const char *, int, const char *, const char *); +int assertion_is_not_hardlink(const char *, int, const char *, const char *); +int assertion_is_reg(const char *, int, const char *, int); +int assertion_is_symlink(const char *, int, const char *, const char *); +int assertion_make_dir(const char *, int, const char *, int); +int assertion_make_file(const char *, int, const char *, int, const char *); +int assertion_make_hardlink(const char *, int, const char *newpath, const char *); +int assertion_make_symlink(const char *, int, const char *newpath, const char *); +int assertion_non_empty_file(const char *, ...); +int assertion_text_file_contents(const char *buff, const char *f); +int assertion_umask(const char *, int, int); +void assertion_setup(const char *, int); + void test_skipping(const char *fmt, ...); -int test_assert(const char *, int, int, const char *, void *); -int test_assert_empty_file(const char *, ...); -int test_assert_non_empty_file(const char *, ...); -int test_assert_equal_file(const char *, const char *, ...); -int test_assert_equal_int(const char *, int, int, const char *, int, const char *, void *); -int test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *); -int test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *); -int test_assert_equal_mem(const char *, int, const char *, const char *, const char *, const char *, size_t, const char *, void *); -int test_assert_file_contents(const void *, int, const char *, ...); -int test_assert_file_exists(const char *, ...); -int test_assert_file_not_exists(const char *, ...); /* Like sprintf, then system() */ int systemf(const char * fmt, ...); +/* Delay until time() returns a value after this. */ +void sleepUntilAfter(time_t); + +/* Return true if this platform can create symlinks. */ +int canSymlink(void); + +/* Return true if this platform can run the "gzip" program. */ +int canGzip(void); + +/* Return true if this platform can run the "gunzip" program. */ +int canGunzip(void); + /* Suck file into string allocated via malloc(). Call free() when done. */ /* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */ char *slurpfile(size_t *, const char *fmt, ...); @@ -161,4 +282,7 @@ void extract_reference_file(const char *); */ /* Pathname of exe to be tested. */ +const char *testprogfile; +/* Name of exe to use in printf-formatted command strings. */ +/* On Windows, this includes leading/trailing quotes. */ const char *testprog; diff --git a/usr.bin/tar/test/test_0.c b/usr.bin/tar/test/test_0.c index d224daa..b97bd37 100644 --- a/usr.bin/tar/test/test_0.c +++ b/usr.bin/tar/test/test_0.c @@ -40,7 +40,7 @@ DEFINE_TEST(test_0) struct stat st; failure("File %s does not exist?!", testprog); - if (!assertEqualInt(0, stat(testprog, &st))) + if (!assertEqualInt(0, stat(testprogfile, &st))) exit(1); failure("%s is not executable?!", testprog); diff --git a/usr.bin/tar/test/test_basic.c b/usr.bin/tar/test/test_basic.c index 3fad754..1fa3341 100644 --- a/usr.bin/tar/test/test_basic.c +++ b/usr.bin/tar/test/test_basic.c @@ -30,24 +30,16 @@ static void basic_tar(const char *target, const char *pack_options, const char *unpack_options, const char *flist) { - struct stat st, st2; -#if !defined(_WIN32) || defined(__CYGWIN__) - char buff[128]; -#endif int r; - assertEqualInt(0, mkdir(target, 0775)); + assertMakeDir(target, 0775); /* Use the tar program to create an archive. */ -#if !defined(_WIN32) || defined(__CYGWIN__) - r = systemf("%s cf - %s `cat %s` >%s/archive 2>%s/pack.err", testprog, pack_options, flist, target, target); -#else r = systemf("%s cf - %s %s >%s/archive 2>%s/pack.err", testprog, pack_options, flist, target, target); -#endif failure("Error invoking %s cf -", testprog, pack_options); assertEqualInt(r, 0); - chdir(target); + assertChdir(target); /* Verify that nothing went to stderr. */ assertEmptyFile("pack.err"); @@ -67,119 +59,57 @@ basic_tar(const char *target, const char *pack_options, */ /* Regular file with 2 links. */ - r = lstat("file", &st); - failure("Failed to stat file %s/file, errno=%d", target, errno); - assertEqualInt(r, 0); - if (r == 0) { - assert(S_ISREG(st.st_mode)); -#if !defined(_WIN32) || defined(__CYGWIN__) - assertEqualInt(0644, st.st_mode & 0777); -#else - assertEqualInt(0600, st.st_mode & 0700); -#endif - assertEqualInt(10, st.st_size); - failure("file %s/file", target); - assertEqualInt(2, st.st_nlink); - } + assertIsReg("file", -1); + assertFileSize("file", 10); + failure("%s", target); + assertFileNLinks("file", 2); /* Another name for the same file. */ - r = lstat("linkfile", &st2); - failure("Failed to stat file %s/linkfile, errno=%d", target, errno); - assertEqualInt(r, 0); - if (r == 0) { - assert(S_ISREG(st2.st_mode)); -#if !defined(_WIN32) || defined(__CYGWIN__) - assertEqualInt(0644, st2.st_mode & 0777); -#else - assertEqualInt(0600, st2.st_mode & 0700); -#endif - assertEqualInt(10, st2.st_size); - failure("file %s/linkfile", target); - assertEqualInt(2, st2.st_nlink); - /* Verify that the two are really hardlinked. */ - assertEqualInt(st.st_dev, st2.st_dev); - failure("%s/linkfile and %s/file aren't really hardlinks", target, target); - assertEqualInt(st.st_ino, st2.st_ino); - } - -#if !defined(_WIN32) || defined(__CYGWIN__) + assertIsReg("linkfile", -1); + assertFileSize("linkfile", 10); + assertFileNLinks("linkfile", 2); + assertIsHardlink("file", "linkfile"); + /* Symlink */ - r = lstat("symlink", &st); - failure("Failed to stat file %s/symlink, errno=%d", target, errno); - assertEqualInt(r, 0); - if (r == 0) { - failure("symlink should be a symlink; actual mode is %o", - st.st_mode); - assert(S_ISLNK(st.st_mode)); - if (S_ISLNK(st.st_mode)) { - r = readlink("symlink", buff, sizeof(buff)); - assertEqualInt(r, 4); - buff[r] = '\0'; - assertEqualString(buff, "file"); - } - } -#endif + if (canSymlink()) + assertIsSymlink("symlink", "file"); /* dir */ - r = lstat("dir", &st); - if (r == 0) { - assertEqualInt(r, 0); - assert(S_ISDIR(st.st_mode)); -#if !defined(_WIN32) || defined(__CYGWIN__) - assertEqualInt(0775, st.st_mode & 0777); -#else - assertEqualInt(0700, st.st_mode & 0700); -#endif - } - - chdir(".."); + assertIsDir("dir", 0775); + assertChdir(".."); } DEFINE_TEST(test_basic) { - int fd; - int filelist; - int oldumask; + FILE *f; const char *flist; - oldumask = umask(0); - - /* - * Create an assortment of files on disk. - */ - filelist = open("filelist", O_CREAT | O_WRONLY, 0644); + assertUmask(0); /* File with 10 bytes content. */ - fd = open("file", O_CREAT | O_WRONLY, 0644); - assert(fd >= 0); - assertEqualInt(10, write(fd, "123456789", 10)); - close(fd); - write(filelist, "file\n", 5); + f = fopen("file", "wb"); + assert(f != NULL); + assertEqualInt(10, fwrite("123456789", 1, 10, f)); + fclose(f); /* hardlink to above file. */ - assertEqualInt(0, link("file", "linkfile")); - write(filelist, "linkfile\n", 9); + assertMakeHardlink("linkfile", "file"); + assertIsHardlink("file", "linkfile"); /* Symlink to above file. */ - assertEqualInt(0, symlink("file", "symlink")); - write(filelist, "symlink\n", 8); + if (canSymlink()) + assertMakeSymlink("symlink", "file"); /* Directory. */ - assertEqualInt(0, mkdir("dir", 0775)); - write(filelist, "dir\n", 4); - /* All done. */ - close(filelist); - -#if !defined(_WIN32) || defined(__CYGWIN__) - flist = "filelist"; -#else - flist = "file linkfile symlink dir"; -#endif + assertMakeDir("dir", 0775); + + if (canSymlink()) + flist = "file linkfile symlink dir"; + else + flist = "file linkfile dir"; /* Archive/dearchive with a variety of options. */ basic_tar("copy", "", "", flist); /* tar doesn't handle cpio symlinks correctly */ /* basic_tar("copy_odc", "--format=odc", ""); */ basic_tar("copy_ustar", "--format=ustar", "", flist); - - umask(oldumask); } diff --git a/usr.bin/tar/test/test_copy.c b/usr.bin/tar/test/test_copy.c index 120d9f3..5a1c4d0 100644 --- a/usr.bin/tar/test/test_copy.c +++ b/usr.bin/tar/test/test_copy.c @@ -25,7 +25,108 @@ #include "test.h" __FBSDID("$FreeBSD$"); -#define LOOP_MAX 170 +#if defined(__CYGWIN__) +# include <limits.h> +# include <sys/cygwin.h> +#endif + +/* + * Try to figure out how deep we can go in our tests. Assumes that + * the first call to this function has the longest starting cwd (which + * is currently "<testdir>/original"). This is mostly to work around + * limits in our Win32 support. + * + * Background: On Posix systems, PATH_MAX is merely a limit on the + * length of the string passed into a system call. By repeatedly + * calling chdir(), you can work with arbitrarily long paths on such + * systems. In contrast, Win32 APIs apply PATH_MAX limits to the full + * absolute path, so the permissible length of a system call argument + * varies with the cwd. Some APIs actually enforce limits + * significantly less than PATH_MAX to ensure that you can create + * files within the current working directory. The Win32 limits also + * apply to Cygwin before 1.7. + * + * Someday, I want to convert the Win32 support to use newer + * wide-character paths with '\\?\' prefix, which has a 32k PATH_MAX + * instead of the rather anemic 260 character limit of the older + * system calls. Then we can drop this mess (unless we want to + * continue to special-case Cygwin 1.5 and earlier). + */ +static int +compute_loop_max(void) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + static int LOOP_MAX = 0; + char buf[MAX_PATH]; + size_t cwdlen; + + if (LOOP_MAX == 0) { + assert(_getcwd(buf, MAX_PATH) != NULL); + cwdlen = strlen(buf); + /* 12 characters = length of 8.3 filename */ + /* 4 characters = length of "/../" used in symlink tests */ + /* 1 character = length of extra "/" separator */ + LOOP_MAX = MAX_PATH - (int)cwdlen - 12 - 4 - 1; + } + return LOOP_MAX; +#elif defined(__CYGWIN__) && !defined(HAVE_CYGWIN_CONV_PATH) + static int LOOP_MAX = 0; + if (LOOP_MAX == 0) { + char wbuf[PATH_MAX]; + char pbuf[PATH_MAX]; + size_t wcwdlen; + size_t pcwdlen; + size_t cwdlen; + assert(getcwd(pbuf, PATH_MAX) != NULL); + pcwdlen = strlen(pbuf); + cygwin_conv_to_full_win32_path(pbuf, wbuf); + wcwdlen = strlen(wbuf); + cwdlen = ((wcwdlen > pcwdlen) ? wcwdlen : pcwdlen); + /* Cygwin helper needs an extra few characters. */ + LOOP_MAX = PATH_MAX - (int)cwdlen - 12 - 4 - 4; + } + return LOOP_MAX; +#else + /* cygwin-1.7 ends up here, along with "normal" unix */ + return 200; /* restore pre-r278 depth */ +#endif +} + +/* filenames[i] is a distinctive filename of length i. */ +/* To simplify interpreting failures, each filename ends with a + * decimal integer which is the length of the filename. E.g., A + * filename ending in "_92" is 92 characters long. To detect errors + * which drop or misplace characters, the filenames use a repeating + * "abcdefghijklmnopqrstuvwxyz..." pattern. */ +static char *filenames[201]; + +static void +compute_filenames(void) +{ + char buff[250]; + size_t i,j; + + filenames[0] = strdup(""); + filenames[1] = strdup("1"); + filenames[2] = strdup("a2"); + for (i = 3; i < sizeof(filenames)/sizeof(filenames[0]); ++i) { + /* Fill with "abcdefghij..." */ + for (j = 0; j < i; ++j) + buff[j] = 'a' + (j % 26); + buff[j--] = '\0'; + /* Work from the end to fill in the number portion. */ + buff[j--] = '0' + (i % 10); + if (i > 9) { + buff[j--] = '0' + ((i / 10) % 10); + if (i > 99) + buff[j--] = '0' + (i / 100); + } + buff[j] = '_'; + /* Guard against obvious screwups in the above code. */ + assertEqualInt(strlen(buff), i); + filenames[i] = strdup(buff); + } +} static void create_tree(void) @@ -33,217 +134,145 @@ create_tree(void) char buff[260]; char buff2[260]; int i; - int fd; - - assertEqualInt(0, mkdir("original", 0775)); - chdir("original"); - assertEqualInt(0, mkdir("f", 0775)); - assertEqualInt(0, mkdir("l", 0775)); - assertEqualInt(0, mkdir("m", 0775)); - assertEqualInt(0, mkdir("s", 0775)); - assertEqualInt(0, mkdir("d", 0775)); - - for (i = 0; i < LOOP_MAX; i++) { - buff[0] = 'f'; - buff[1] = '/'; - /* Create a file named "f/abcdef..." */ - buff[i + 2] = 'a' + (i % 26); - buff[i + 3] = '\0'; - fd = open(buff, O_CREAT | O_WRONLY, 0644); - assert(fd >= 0); - assertEqualInt(i + 3, write(fd, buff, strlen(buff))); - close(fd); + int LOOP_MAX; + + compute_filenames(); + + /* Log that we'll be omitting some checks. */ + if (!canSymlink()) { + skipping("Symlink checks"); + } + + assertMakeDir("original", 0775); + assertEqualInt(0, chdir("original")); + LOOP_MAX = compute_loop_max(); + + assertMakeDir("f", 0775); + assertMakeDir("l", 0775); + assertMakeDir("m", 0775); + assertMakeDir("s", 0775); + assertMakeDir("d", 0775); + + for (i = 1; i < LOOP_MAX; i++) { + failure("Internal sanity check failed: i = %d", i); + assert(filenames[i] != NULL); + + sprintf(buff, "f/%s", filenames[i]); + assertMakeFile(buff, 0777, buff); /* Create a link named "l/abcdef..." to the above. */ - strcpy(buff2, buff); - buff2[0] = 'l'; - assertEqualInt(0, link(buff, buff2)); + sprintf(buff2, "l/%s", filenames[i]); + assertMakeHardlink(buff2, buff); /* Create a link named "m/abcdef..." to the above. */ - strcpy(buff2, buff); - buff2[0] = 'm'; - assertEqualInt(0, link(buff, buff2)); - -#if !defined(_WIN32) || defined(__CYGWIN__) - /* Create a symlink named "s/abcdef..." to the above. */ - strcpy(buff2 + 3, buff); - buff[0] = 's'; - buff2[0] = '.'; - buff2[1] = '.'; - buff2[2] = '/'; - assertEqualInt(0, symlink(buff2, buff)); -#else - skipping("create a symlink to the above"); -#endif + sprintf(buff2, "m/%s", filenames[i]); + assertMakeHardlink(buff2, buff); + + if (canSymlink()) { + /* Create a symlink named "s/abcdef..." to the above. */ + sprintf(buff, "s/%s", filenames[i]); + sprintf(buff2, "../f/%s", filenames[i]); + failure("buff=\"%s\" buff2=\"%s\"", buff, buff2); + assertMakeSymlink(buff, buff2); + } /* Create a dir named "d/abcdef...". */ buff[0] = 'd'; - assertEqualInt(0, mkdir(buff, 0775)); + failure("buff=\"%s\"", buff); + assertMakeDir(buff, 0775); } - chdir(".."); + assertEqualInt(0, chdir("..")); } -#define LIMIT_NONE 0 -#define LIMIT_USTAR 1 +#define LIMIT_NONE 200 +#define LIMIT_USTAR 100 static void -verify_tree(int limit) +verify_tree(size_t limit) { - struct stat st, st2; - char filename[260]; char name1[260]; char name2[260]; - char contents[260]; - int i, j, r; - int fd; - int len; - const char *p, *dp; - DIR *d; - struct dirent *de; + size_t i, LOOP_MAX; + + LOOP_MAX = compute_loop_max(); /* Generate the names we know should be there and verify them. */ for (i = 1; i < LOOP_MAX; i++) { - /* Generate a base name of the correct length. */ - for (j = 0; j < i; ++j) - filename[j] = 'a' + (j % 26); -#if 0 - for (n = i; n > 0; n /= 10) - filename[--j] = '0' + (n % 10); -#endif - filename[i] = '\0'; - /* Verify a file named "f/abcdef..." */ - strcpy(name1, "f/"); - strcat(name1, filename); - if (limit != LIMIT_USTAR || strlen(filename) <= 100) { - fd = open(name1, O_RDONLY); - failure("Couldn't open \"%s\": %s", - name1, strerror(errno)); - if (assert(fd >= 0)) { - len = read(fd, contents, i + 10); - close(fd); - assertEqualInt(len, i + 2); - /* Verify contents of 'contents' */ - contents[len] = '\0'; - failure("Each test file contains its own name"); - assertEqualString(name1, contents); - /* stat() for dev/ino for next check */ - assertEqualInt(0, lstat(name1, &st)); - } + sprintf(name1, "f/%s", filenames[i]); + if (i <= limit) { + assertFileExists(name1); + assertFileContents(name1, strlen(name1), name1); } - /* - * ustar allows 100 chars for links, and we have - * "original/" as part of the name, so the link - * names here can't exceed 91 chars. - */ - strcpy(name2, "l/"); - strcat(name2, filename); - if (limit != LIMIT_USTAR || strlen(name2) <= 100) { + sprintf(name2, "l/%s", filenames[i]); + if (i + 2 <= limit) { /* Verify hardlink "l/abcdef..." */ - assertEqualInt(0, (r = lstat(name2, &st2))); - if (r == 0) { - assertEqualInt(st2.st_dev, st.st_dev); - assertEqualInt(st2.st_ino, st.st_ino); - } - - /* Verify hardlink "m_abcdef..." */ + assertIsHardlink(name1, name2); + /* Verify hardlink "m/abcdef..." */ name2[0] = 'm'; - assertEqualInt(0, (r = lstat(name2, &st2))); - if (r == 0) { - assertEqualInt(st2.st_dev, st.st_dev); - assertEqualInt(st2.st_ino, st.st_ino); - } + assertIsHardlink(name1, name2); } -#if !defined(_WIN32) || defined(__CYGWIN__) - /* - * Symlink text doesn't include the 'original/' prefix, - * so the limit here is 100 characters. - */ - /* Verify symlink "s/abcdef..." */ - strcpy(name2, "../s/"); - strcat(name2, filename); - if (limit != LIMIT_USTAR || strlen(name2) <= 100) { - /* This is a symlink. */ - failure("Couldn't stat %s (length %d)", - filename, strlen(filename)); - if (assertEqualInt(0, lstat(name2 + 3, &st2))) { - assert(S_ISLNK(st2.st_mode)); - /* This is a symlink to the file above. */ - failure("Couldn't stat %s", name2 + 3); - if (assertEqualInt(0, stat(name2 + 3, &st2))) { - assertEqualInt(st2.st_dev, st.st_dev); - assertEqualInt(st2.st_ino, st.st_ino); - } - } + if (canSymlink()) { + /* Verify symlink "s/abcdef..." */ + sprintf(name1, "s/%s", filenames[i]); + sprintf(name2, "../f/%s", filenames[i]); + if (strlen(name2) <= limit) + assertIsSymlink(name1, name2); } -#else - skipping("verify symlink"); -#endif + /* Verify dir "d/abcdef...". */ - strcpy(name1, "d/"); - strcat(name1, filename); - if (limit != LIMIT_USTAR || strlen(filename) < 100) { - /* This is a dir. */ - failure("Couldn't stat %s (length %d)", - name1, strlen(filename)); - if (assertEqualInt(0, lstat(name1, &st2))) { - if (assert(S_ISDIR(st2.st_mode))) { - /* TODO: opendir/readdir this - * directory and make sure - * it's empty. - */ - } + sprintf(name1, "d/%s", filenames[i]); + if (i + 1 <= limit) { /* +1 for trailing slash */ + if (assertIsDir(name1, -1)) { + /* TODO: opendir/readdir this + * directory and make sure + * it's empty. + */ } } } - /* Now make sure nothing is there that shouldn't be. */ - for (dp = "dflms"; *dp != '\0'; ++dp) { - char dir[2]; - dir[0] = *dp; dir[1] = '\0'; - d = opendir(dir); - failure("Unable to open dir '%s'", dir); - if (!assert(d != NULL)) - continue; - while ((de = readdir(d)) != NULL) { - p = de->d_name; - switch(dp[0]) { - case 'l': case 'm': - if (limit == LIMIT_USTAR) { - failure("strlen(p) = %d", strlen(p)); - assert(strlen(p) <= 100); - } - case 'd': - if (limit == LIMIT_USTAR) { +#if !defined(_WIN32) || defined(__CYGWIN__) + { + const char *dp; + /* Now make sure nothing is there that shouldn't be. */ + for (dp = "dflms"; *dp != '\0'; ++dp) { + DIR *d; + struct dirent *de; + char dir[2]; + dir[0] = *dp; dir[1] = '\0'; + d = opendir(dir); + failure("Unable to open dir '%s'", dir); + if (!assert(d != NULL)) + continue; + while ((de = readdir(d)) != NULL) { + char *p = de->d_name; + if (p[0] == '.') + continue; + switch(dp[0]) { + case 'l': case 'm': case 'd': failure("strlen(p)=%d", strlen(p)); - assert(strlen(p) < 100); - } - case 'f': case 's': - if (limit == LIMIT_USTAR) { + assert(strlen(p) < limit); + assertEqualString(p, + filenames[strlen(p)]); + break; + case 'f': case 's': failure("strlen(p)=%d", strlen(p)); - assert(strlen(p) < 101); + assert(strlen(p) < limit + 1); + assertEqualString(p, + filenames[strlen(p)]); + break; + default: + failure("File %s shouldn't be here", p); + assert(0); } - /* Our files have very particular filename patterns. */ - if (p[0] != '.' || (p[1] != '.' && p[1] != '\0')) { - for (i = 0; p[i] != '\0' && i < LOOP_MAX; i++) { - failure("i=%d, p[i]='%c' 'a'+(i%%26)='%c'", i, p[i], 'a' + (i % 26)); - assertEqualInt(p[i], 'a' + (i % 26)); - } - assert(p[i] == '\0'); - } - break; - case '.': - assert(p[1] == '\0' || (p[1] == '.' && p[2] == '\0')); - break; - default: - failure("File %s shouldn't be here", p); - assert(0); } + closedir(d); } - closedir(d); } +#endif } static void @@ -251,7 +280,13 @@ copy_basic(void) { int r; - assertEqualInt(0, mkdir("plain", 0775)); + /* NOTE: for proper operation on cygwin-1.5 and windows, the + * length of the name of the directory below, "plain", must be + * less than or equal to the lengthe of the name of the original + * directory, "original" This restriction derives from the + * extremely limited pathname lengths on those platforms. + */ + assertMakeDir("plain", 0775); assertEqualInt(0, chdir("plain")); /* @@ -287,7 +322,13 @@ copy_ustar(void) const char *target = "ustar"; int r; - assertEqualInt(0, mkdir(target, 0775)); + /* NOTE: for proper operation on cygwin-1.5 and windows, the + * length of the name of the directory below, "ustar", must be + * less than or equal to the lengthe of the name of the original + * directory, "original" This restriction derives from the + * extremely limited pathname lengths on those platforms. + */ + assertMakeDir(target, 0775); assertEqualInt(0, chdir(target)); /* @@ -314,17 +355,13 @@ copy_ustar(void) assertEmptyFile("unpack.err"); assertEmptyFile("unpack.out"); - chdir("original"); verify_tree(LIMIT_USTAR); - chdir("../.."); + assertEqualInt(0, chdir("../..")); } DEFINE_TEST(test_copy) { - int oldumask; - - oldumask = umask(0); - + assertUmask(0); create_tree(); /* Create sample files in "original" dir. */ /* Test simple "tar -c | tar -x" pipeline copy. */ @@ -332,6 +369,4 @@ DEFINE_TEST(test_copy) /* Same, but constrain to ustar format. */ copy_ustar(); - - umask(oldumask); } diff --git a/usr.bin/tar/err.h b/usr.bin/tar/test/test_empty_mtree.c index 84ed3f4..6f8a5e9 100644 --- a/usr.bin/tar/err.h +++ b/usr.bin/tar/test/test_empty_mtree.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009 Joerg Sonnenberger + * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -21,23 +21,25 @@ * 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. - * - * $FreeBSD$ */ +#include "test.h" +__FBSDID("$FreeBSD$"); -#ifndef LAFE_ERR_H -#define LAFE_ERR_H - -#if defined(__GNUC__) && (__GNUC__ > 2 || \ - (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) -#define __LA_DEAD __attribute__((__noreturn__)) -#else -#define __LA_DEAD -#endif - -extern const char *bsdtar_progname; +/* + * Regression test: We used to get a bogus error message when we + * asked tar to copy entries out of an empty archive. See + * Issue 51 on libarchive.googlecode.com for details. + */ +DEFINE_TEST(test_empty_mtree) +{ + int r; -void bsdtar_warnc(int code, const char *fmt, ...); -void bsdtar_errc(int eval, int code, const char *fmt, ...) __LA_DEAD; + assertMakeFile("test1.mtree", 0777, "#mtree\n"); -#endif + r = systemf("%s cf test1.tar @test1.mtree >test1.out 2>test1.err", + testprog); + failure("Error invoking %s cf", testprog); + assertEqualInt(r, 0); + assertEmptyFile("test1.out"); + assertEmptyFile("test1.err"); +} diff --git a/usr.bin/tar/test/test_help.c b/usr.bin/tar/test/test_help.c index c547dbc..0e7d1c9 100644 --- a/usr.bin/tar/test/test_help.c +++ b/usr.bin/tar/test/test_help.c @@ -51,6 +51,7 @@ DEFINE_TEST(test_help) /* Exercise --help option. */ r = systemf("%s --help >help.stdout 2>help.stderr", testprog); + assertEqualInt(r, 0); failure("--help should generate nothing to stderr."); assertEmptyFile("help.stderr"); /* Help message should start with name of program. */ @@ -67,6 +68,7 @@ DEFINE_TEST(test_help) /* -h option should generate the same output. */ r = systemf("%s -h >h.stdout 2>h.stderr", testprog); + assertEqualInt(r, 0); failure("-h should generate nothing to stderr."); assertEmptyFile("h.stderr"); failure("stdout should be same for -h and --help"); @@ -74,6 +76,7 @@ DEFINE_TEST(test_help) /* -W help should be another synonym. */ r = systemf("%s -W help >Whelp.stdout 2>Whelp.stderr", testprog); + assertEqualInt(r, 0); failure("-W help should generate nothing to stderr."); assertEmptyFile("Whelp.stderr"); failure("stdout should be same for -W help and --help"); diff --git a/usr.bin/tar/test/test_option_T.c b/usr.bin/tar/test/test_option_T_upper.c index 72dcd54..87127cc 100644 --- a/usr.bin/tar/test/test_option_T.c +++ b/usr.bin/tar/test/test_option_T_upper.c @@ -26,110 +26,144 @@ __FBSDID("$FreeBSD$"); static int -touch(const char *fn) +touch(const char *fn, int fail) { - int fd = open(fn, O_RDWR | O_CREAT, 0644); - failure("Couldn't create file '%s', fd=%d, errno=%d (%s)\n", - fn, fd, errno, strerror(errno)); - if (!assert(fd > 0)) - return (0); /* Failure. */ - close(fd); + FILE *f = fopen(fn, "w"); + if (fail) { + failure("Couldn't create file '%s', errno=%d (%s)\n", + fn, errno, strerror(errno)); + if (!assert(f != NULL)) + return (0); /* Failure. */ + } else { + if (f == NULL) + return (0); /* Soft failure. */ + } + fclose(f); return (1); /* Success */ } -DEFINE_TEST(test_option_T) +DEFINE_TEST(test_option_T_upper) { FILE *f; int r; struct stat st; + int gnarlyFilesSupported; - /* Create a simple dir hierarchy; bail if anything fails. */ - if (!assertEqualInt(0, mkdir("d1", 0755))) return; - if (!assertEqualInt(0, mkdir("d1/d2", 0755))) return; - if (!touch("d1/f1")) return; - if (!touch("d1/f2")) return; - if (!touch("d1/d2/f3")) return; - if (!touch("d1/d2/f4")) return; - if (!touch("d1/d2/f5")) return; + /* Create a simple dir heirarchy; bail if anything fails. */ + if (!assertMakeDir("d1", 0755)) return; + if (!assertMakeDir("d1/d2", 0755)) return; + if (!touch("f", 1)) return; + if (!touch("d1/f1", 1)) return; + if (!touch("d1/f2", 1)) return; + if (!touch("d1/d2/f3", 1)) return; + if (!touch("d1/d2/f4", 1)) return; + if (!touch("d1/d2/f5", 1)) return; + if (!touch("d1/d2/f6", 1)) return; + /* Some platforms don't permit such things; just skip it. */ + gnarlyFilesSupported = touch("d1/d2/f\x0a", 0); /* Populate a file list */ f = fopen("filelist", "w+"); if (!assert(f != NULL)) return; - fprintf(f, "d1/f1\n"); - fprintf(f, "d1/d2/f4\n"); + /* Use a variety of text line endings. */ + fprintf(f, "f\x0d"); /* CR */ + fprintf(f, "d1/f1\x0d\x0a"); /* CRLF */ + fprintf(f, "d1/d2/f4\x0a"); /* NL */ + fprintf(f, "d1/d2/f6"); /* EOF */ fclose(f); /* Populate a second file list */ f = fopen("filelist2", "w+"); if (!assert(f != NULL)) return; - fprintf(f, "d1/d2/f3\n"); - fprintf(f, "d1/d2/f5\n"); + /* Use null-terminated names. */ + fprintf(f, "d1/d2/f3"); + fwrite("\0", 1, 1, f); + fprintf(f, "d1/d2/f5"); + fwrite("\0", 1, 1, f); + if (gnarlyFilesSupported) { + fprintf(f, "d1/d2/f\x0a"); + fwrite("\0", 1, 1, f); + } fclose(f); /* Use -c -T to archive up the files. */ r = systemf("%s -c -f test1.tar -T filelist > test1.out 2> test1.err", testprog); - failure("Failure here probably means that tar can't archive zero-length files without reading them"); assert(r == 0); assertEmptyFile("test1.out"); assertEmptyFile("test1.err"); /* Use -x -T to dearchive the files */ - if (!assertEqualInt(0, mkdir("test1", 0755))) return; + if (!assertMakeDir("test1", 0755)) return; systemf("%s -x -f test1.tar -T filelist -C test1" " > test1b.out 2> test1b.err", testprog); assertEmptyFile("test1b.out"); assertEmptyFile("test1b.err"); /* Verify the files were extracted. */ + assertFileExists("test1/f"); assertFileExists("test1/d1/f1"); assertFileNotExists("test1/d1/f2"); assertFileNotExists("test1/d1/d2/f3"); assertFileExists("test1/d1/d2/f4"); assertFileNotExists("test1/d1/d2/f5"); + assertFileExists("test1/d1/d2/f6"); + if (gnarlyFilesSupported) { + assertFileNotExists("test1/d1/d2/f\x0a"); + } /* Use -r -T to add more files to the archive. */ - systemf("%s -r -f test1.tar -T filelist2 > test2.out 2> test2.err", + systemf("%s -r -f test1.tar --null -T filelist2 > test2.out 2> test2.err", testprog); assertEmptyFile("test2.out"); assertEmptyFile("test2.err"); /* Use -x without -T to dearchive the files (ensure -r worked) */ - if (!assertEqualInt(0, mkdir("test3", 0755))) return; + if (!assertMakeDir("test3", 0755)) return; systemf("%s -x -f test1.tar -C test3" " > test3.out 2> test3.err", testprog); assertEmptyFile("test3.out"); assertEmptyFile("test3.err"); /* Verify the files were extracted.*/ + assertFileExists("test3/f"); assertFileExists("test3/d1/f1"); assertFileNotExists("test3/d1/f2"); assertFileExists("test3/d1/d2/f3"); assertFileExists("test3/d1/d2/f4"); assertFileExists("test3/d1/d2/f5"); + assertFileExists("test3/d1/d2/f6"); + if (gnarlyFilesSupported) { + assertFileExists("test3/d1/d2/f\x0a"); + } /* Use -x -T to dearchive the files (verify -x -T together) */ - if (!assertEqualInt(0, mkdir("test2", 0755))) return; + if (!assertMakeDir("test2", 0755)) return; systemf("%s -x -f test1.tar -T filelist -C test2" " > test2b.out 2> test2b.err", testprog); assertEmptyFile("test2b.out"); assertEmptyFile("test2b.err"); /* Verify the files were extracted.*/ + assertFileExists("test2/f"); assertFileExists("test2/d1/f1"); assertFileNotExists("test2/d1/f2"); assertFileNotExists("test2/d1/d2/f3"); assertFileExists("test2/d1/d2/f4"); assertFileNotExists("test2/d1/d2/f5"); + assertFileExists("test2/d1/d2/f6"); + if (gnarlyFilesSupported) { + assertFileNotExists("test2/d1/d2/f\x0a"); + } - assertEqualInt(0, mkdir("test4", 0755)); - assertEqualInt(0, mkdir("test4_out", 0755)); - assertEqualInt(0, mkdir("test4_out2", 0755)); - assertEqualInt(0, mkdir("test4/d1", 0755)); - assertEqualInt(1, touch("test4/d1/foo")); + assertMakeDir("test4", 0755); + assertMakeDir("test4_out", 0755); + assertMakeDir("test4_out2", 0755); + assertMakeDir("test4/d1", 0755); + assertEqualInt(1, touch("test4/d1/foo", 0)); /* Does bsdtar support -s option ? */ - systemf("%s -cf - -s /foo/bar/ test4/d1/foo > NUL 2> check.err", + systemf("%s -cf - -s /foo/bar/ test4/d1/foo > check.out 2> check.err", testprog); assertEqualInt(0, stat("check.err", &st)); if (st.st_size == 0) { @@ -148,6 +182,7 @@ DEFINE_TEST(test_option_T) } else { skipping("bsdtar does not support -s option on this platform"); } + /* TODO: Include some use of -C directory-changing within the filelist. */ /* I'm pretty sure -C within the filelist is broken on extract. */ } diff --git a/usr.bin/tar/test/test_option_q.c b/usr.bin/tar/test/test_option_q.c index 1d92dd5..c8e9540 100644 --- a/usr.bin/tar/test/test_option_q.c +++ b/usr.bin/tar/test/test_option_q.c @@ -27,7 +27,8 @@ __FBSDID("$FreeBSD$"); DEFINE_TEST(test_option_q) { - int fd; + FILE *f; + int r; /* * Create an archive with several different versions of the @@ -39,38 +40,38 @@ DEFINE_TEST(test_option_q) * what we use to build up the test archive. */ - fd = open("foo", O_CREAT | O_WRONLY, 0644); - assert(fd >= 0); - assertEqualInt(4, write(fd, "foo1", 4)); - close(fd); + f = fopen("foo", "w"); + assert(f != NULL); + fprintf(f, "foo1"); + fclose(f); assertEqualInt(0, systemf("%s -cf archive.tar foo", testprog)); - fd = open("foo", O_TRUNC | O_WRONLY, 0644); - assert(fd >= 0); - assertEqualInt(4, write(fd, "foo2", 4)); - close(fd); + f = fopen("foo", "w"); + assert(f != NULL); + fprintf(f, "foo2"); + fclose(f); assertEqualInt(0, systemf("%s -rf archive.tar foo", testprog)); - fd = open("bar", O_CREAT | O_WRONLY, 0644); - assert(fd >= 0); - assertEqualInt(4, write(fd, "bar1", 4)); - close(fd); + f = fopen("bar", "w"); + assert(f != NULL); + fprintf(f, "bar1"); + fclose(f); assertEqualInt(0, systemf("%s -rf archive.tar bar", testprog)); - fd = open("foo", O_TRUNC | O_WRONLY, 0644); - assert(fd >= 0); - assertEqualInt(4, write(fd, "foo3", 4)); - close(fd); + f = fopen("foo", "w"); + assert(f != NULL); + fprintf(f, "foo3"); + fclose(f); assertEqualInt(0, systemf("%s -rf archive.tar foo", testprog)); - fd = open("bar", O_TRUNC | O_WRONLY, 0644); - assert(fd >= 0); - assertEqualInt(4, write(fd, "bar2", 4)); - close(fd); + f = fopen("bar", "w"); + assert(f != NULL); + fprintf(f, "bar2"); + fclose(f); assertEqualInt(0, systemf("%s -rf archive.tar bar", testprog)); @@ -80,46 +81,49 @@ DEFINE_TEST(test_option_q) */ /* Test 1: -q foo should only extract the first foo. */ - assertEqualInt(0, mkdir("test1", 0755)); - assertEqualInt(0, chdir("test1")); - assertEqualInt(0, - systemf("%s -xf ../archive.tar -q foo >test.out 2>test.err", - testprog)); + assertMakeDir("test1", 0755); + assertChdir("test1"); + r = systemf("%s -xf ../archive.tar -q foo >test.out 2>test.err", + testprog); + failure("Fatal error trying to use -q option"); + if (!assertEqualInt(0, r)) + return; + assertFileContents("foo1", 4, "foo"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); - assertEqualInt(0, chdir("..")); + assertChdir(".."); /* Test 2: -q foo bar should extract up to the first bar. */ - assertEqualInt(0, mkdir("test2", 0755)); - assertEqualInt(0, chdir("test2")); + assertMakeDir("test2", 0755); + assertChdir("test2"); assertEqualInt(0, systemf("%s -xf ../archive.tar -q foo bar >test.out 2>test.err", testprog)); assertFileContents("foo2", 4, "foo"); assertFileContents("bar1", 4, "bar"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); - assertEqualInt(0, chdir("..")); + assertChdir(".."); /* Test 3: Same as test 2, but use --fast-read spelling. */ - assertEqualInt(0, mkdir("test3", 0755)); - assertEqualInt(0, chdir("test3")); + assertMakeDir("test3", 0755); + assertChdir("test3"); assertEqualInt(0, systemf("%s -xf ../archive.tar --fast-read foo bar >test.out 2>test.err", testprog)); assertFileContents("foo2", 4, "foo"); assertFileContents("bar1", 4, "bar"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); - assertEqualInt(0, chdir("..")); + assertChdir(".."); /* Test 4: Without -q, should extract everything. */ - assertEqualInt(0, mkdir("test4", 0755)); - assertEqualInt(0, chdir("test4")); + assertMakeDir("test4", 0755); + assertChdir("test4"); assertEqualInt(0, systemf("%s -xf ../archive.tar foo bar >test.out 2>test.err", testprog)); assertFileContents("foo3", 4, "foo"); assertFileContents("bar2", 4, "bar"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); - assertEqualInt(0, chdir("..")); + assertChdir(".."); } diff --git a/usr.bin/tar/test/test_option_r.c b/usr.bin/tar/test/test_option_r.c new file mode 100644 index 0000000..516a830 --- /dev/null +++ b/usr.bin/tar/test/test_option_r.c @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * Also see test_option_q for additional validation of -r support. + */ +DEFINE_TEST(test_option_r) +{ + char buff[15]; + char *p0, *p1; + size_t s; + FILE *f; + int r; + + /* Create a file */ + f = fopen("f1", "w"); + if (!assert(f != NULL)) + return; + assertEqualInt(3, fwrite("abc", 1, 3, f)); + fclose(f); + + /* Archive that one file. */ + r = systemf("%s cf archive.tar --format=ustar f1 >step1.out 2>step1.err", testprog); + failure("Error invoking %s cf archive.tar f1", testprog); + assertEqualInt(r, 0); + + /* Verify that nothing went to stdout or stderr. */ + assertEmptyFile("step1.out"); + assertEmptyFile("step1.err"); + + + /* Do some basic validation of the constructed archive. */ + p0 = slurpfile(&s, "archive.tar"); + if (!assert(p0 != NULL)) + return; + if (!assert(s >= 2048)) { + free(p0); + return; + } + assertEqualMem(p0 + 0, "f1", 3); + assertEqualMem(p0 + 512, "abc", 3); + assertEqualMem(p0 + 1024, "\0\0\0\0\0\0\0\0", 8); + assertEqualMem(p0 + 1536, "\0\0\0\0\0\0\0\0", 8); + + /* Edit that file */ + f = fopen("f1", "w"); + if (!assert(f != NULL)) + return; + assertEqualInt(3, fwrite("123", 1, 3, f)); + fclose(f); + + /* Update the archive. */ + r = systemf("%s rf archive.tar --format=ustar f1 >step2.out 2>step2.err", testprog); + failure("Error invoking %s rf archive.tar f1", testprog); + assertEqualInt(r, 0); + + /* Verify that nothing went to stdout or stderr. */ + assertEmptyFile("step2.out"); + assertEmptyFile("step2.err"); + + /* Do some basic validation of the constructed archive. */ + p1 = slurpfile(&s, "archive.tar"); + if (!assert(p1 != NULL)) { + free(p0); + return; + } + assert(s >= 3072); + /* Verify first entry is unchanged. */ + assertEqualMem(p0, p1, 1024); + /* Verify that second entry is correct. */ + assertEqualMem(p1 + 1024, "f1", 3); + assertEqualMem(p1 + 1536, "123", 3); + /* Verify end-of-archive marker. */ + assertEqualMem(p1 + 2048, "\0\0\0\0\0\0\0\0", 8); + assertEqualMem(p1 + 2560, "\0\0\0\0\0\0\0\0", 8); + free(p0); + free(p1); + + /* Unpack both items */ + assertMakeDir("step3", 0775); + assertChdir("step3"); + r = systemf("%s xf ../archive.tar", testprog); + failure("Error invoking %s xf archive.tar", testprog); + assertEqualInt(r, 0); + + /* Verify that the second one overwrote the first. */ + f = fopen("f1", "r"); + if (assert(f != NULL)) { + assertEqualInt(3, fread(buff, 1, 3, f)); + assertEqualMem(buff, "123", 3); + fclose(f); + } +} diff --git a/usr.bin/tar/test/test_option_s.c b/usr.bin/tar/test/test_option_s.c index c9a6899..94d6345 100644 --- a/usr.bin/tar/test/test_option_s.c +++ b/usr.bin/tar/test/test_option_s.c @@ -28,15 +28,15 @@ __FBSDID("$FreeBSD$"); static int mkfile(const char *fn, const char *contents) { - int fd = open(fn, O_RDWR | O_CREAT, 0644); - failure("Couldn't create file '%s', fd=%d, errno=%d (%s)\n", - fn, fd, errno, strerror(errno)); - if (!assert(fd > 0)) + FILE *f = fopen(fn, "w"); + failure("Couldn't create file '%s', errno=%d (%s)\n", + fn, errno, strerror(errno)); + if (!assert(f != NULL)) return (1); /* Failure. */ if (contents != NULL) assertEqualInt(strlen(contents), - write(fd, contents, strlen(contents))); - assertEqualInt(0, close(fd)); + fwrite(contents, 1, strlen(contents), f)); + assertEqualInt(0, fclose(f)); return (0); /* Success */ } @@ -44,9 +44,9 @@ DEFINE_TEST(test_option_s) { struct stat st; - /* Create a sample file hierarchy. */ - assertEqualInt(0, mkdir("in", 0755)); - assertEqualInt(0, mkdir("in/d1", 0755)); + /* Create a sample file heirarchy. */ + assertMakeDir("in", 0755); + assertMakeDir("in/d1", 0755); assertEqualInt(0, mkfile("in/d1/foo", "foo")); assertEqualInt(0, mkfile("in/d1/bar", "bar")); @@ -55,14 +55,15 @@ DEFINE_TEST(test_option_s) testprog); assertEqualInt(0, stat("check.err", &st)); if (st.st_size != 0) { - skipping("bsdtar does not support -s option on this platform"); + skipping("%s does not support -s option on this platform", + testprog); return; } /* * Test 1: Filename substitution when creating archives. */ - assertEqualInt(0, mkdir("test1", 0755)); + assertMakeDir("test1", 0755); systemf("%s -cf - -s /foo/bar/ in/d1/foo | %s -xf - -C test1", testprog, testprog); assertFileContents("foo", 3, "test1/in/d1/bar"); @@ -74,7 +75,7 @@ DEFINE_TEST(test_option_s) /* * Test 2: Basic substitution when extracting archive. */ - assertEqualInt(0, mkdir("test2", 0755)); + assertMakeDir("test2", 0755); systemf("%s -cf - in/d1/foo | %s -xf - -s /foo/bar/ -C test2", testprog, testprog); assertFileContents("foo", 3, "test2/in/d1/bar"); @@ -89,7 +90,7 @@ DEFINE_TEST(test_option_s) /* * Test 4: Multiple substitutions when extracting archive. */ - assertEqualInt(0, mkdir("test4", 0755)); + assertMakeDir("test4", 0755); systemf("%s -cf - in/d1/foo in/d1/bar | %s -xf - -s /foo/bar/ -s }bar}baz} -C test4", testprog, testprog); assertFileContents("foo", 3, "test4/in/d1/bar"); @@ -98,7 +99,7 @@ DEFINE_TEST(test_option_s) /* * Test 5: Name-switching substitutions when extracting archive. */ - assertEqualInt(0, mkdir("test5", 0755)); + assertMakeDir("test5", 0755); systemf("%s -cf - in/d1/foo in/d1/bar | %s -xf - -s /foo/bar/ -s }bar}foo} -C test5", testprog, testprog); assertFileContents("foo", 3, "test5/in/d1/bar"); diff --git a/usr.bin/tar/test/test_patterns.c b/usr.bin/tar/test/test_patterns.c index 66a4ecf..f909e27 100644 --- a/usr.bin/tar/test/test_patterns.c +++ b/usr.bin/tar/test/test_patterns.c @@ -28,11 +28,17 @@ __FBSDID("$FreeBSD$"); DEFINE_TEST(test_patterns) { - int fd, r; + FILE *f; + int r; const char *reffile2 = "test_patterns_2.tar"; const char *reffile3 = "test_patterns_3.tar"; const char *reffile4 = "test_patterns_4.tar"; - const char *p; + + const char *tar2aExpected[] = { + "/tmp/foo/bar/", + "/tmp/foo/bar/baz", + NULL + }; /* * Test basic command-line pattern handling. @@ -44,12 +50,12 @@ DEFINE_TEST(test_patterns) * * John Baldwin reported this problem in PR bin/121598 */ - fd = open("foo", O_CREAT | O_WRONLY, 0644); - assert(fd >= 0); - close(fd); + f = fopen("foo", "w"); + assert(f != NULL); + fclose(f); r = systemf("%s cfv tar1.tgz foo > tar1a.out 2> tar1a.err", testprog); assertEqualInt(r, 0); - r = systemf("%s xfv tar1.tgz foo bar > tar1b.out 2> tar1b.err", testprog); + r = systemf("%s xv --no-same-owner -f tar1.tgz foo bar > tar1b.out 2> tar1b.err", testprog); failure("tar should return non-zero because a file was given on the command line that's not in the archive"); assert(r != 0); @@ -61,12 +67,7 @@ DEFINE_TEST(test_patterns) r = systemf("%s tf %s /tmp/foo/bar > tar2a.out 2> tar2a.err", testprog, reffile2); assertEqualInt(r, 0); -#if !defined(_WIN32) || defined(__CYGWIN__) - p = "/tmp/foo/bar/\n/tmp/foo/bar/baz\n"; -#else - p = "/tmp/foo/bar/\r\n/tmp/foo/bar/baz\r\n"; -#endif - assertFileContents(p, strlen(p), "tar2a.out"); + assertFileContainsLinesAnyOrder("tar2a.out", tar2aExpected); assertEmptyFile("tar2a.err"); /* @@ -75,7 +76,7 @@ DEFINE_TEST(test_patterns) extract_reference_file(reffile3); /* Test 3a: Pattern tmp/foo/bar should not match /tmp/foo/bar */ - r = systemf("%s xf %s tmp/foo/bar > tar3a.out 2> tar3a.err", + r = systemf("%s x --no-same-owner -f %s tmp/foo/bar > tar3a.out 2> tar3a.err", testprog, reffile3); assert(r != 0); assertEmptyFile("tar3a.out"); @@ -83,26 +84,26 @@ DEFINE_TEST(test_patterns) /* Test 3b: Pattern /tmp/foo/baz should not match tmp/foo/baz */ assertNonEmptyFile("tar3a.err"); /* Again, with the '/' */ - r = systemf("%s xf %s /tmp/foo/baz > tar3b.out 2> tar3b.err", + r = systemf("%s x --no-same-owner -f %s /tmp/foo/baz > tar3b.out 2> tar3b.err", testprog, reffile3); assert(r != 0); assertEmptyFile("tar3b.out"); assertNonEmptyFile("tar3b.err"); /* Test 3c: ./tmp/foo/bar should not match /tmp/foo/bar */ - r = systemf("%s xf %s ./tmp/foo/bar > tar3c.out 2> tar3c.err", + r = systemf("%s x --no-same-owner -f %s ./tmp/foo/bar > tar3c.out 2> tar3c.err", testprog, reffile3); assert(r != 0); assertEmptyFile("tar3c.out"); assertNonEmptyFile("tar3c.err"); /* Test 3d: ./tmp/foo/baz should match tmp/foo/baz */ - r = systemf("%s xf %s ./tmp/foo/baz > tar3d.out 2> tar3d.err", + r = systemf("%s x --no-same-owner -f %s ./tmp/foo/baz > tar3d.out 2> tar3d.err", testprog, reffile3); assertEqualInt(r, 0); assertEmptyFile("tar3d.out"); assertEmptyFile("tar3d.err"); - assertEqualInt(0, access("tmp/foo/baz/bar", F_OK)); + assertFileExists("tmp/foo/baz/bar"); /* * Test 4 archive has some entries starting with windows drive letters @@ -110,7 +111,7 @@ DEFINE_TEST(test_patterns) */ extract_reference_file(reffile4); - r = systemf("%s xf %s -C tmp > tar4.out 2> tar4.err", + r = systemf("%s x --no-same-owner -f %s -C tmp > tar4.out 2> tar4.err", testprog, reffile4); assert(r != 0); assertEmptyFile("tar4.out"); @@ -167,16 +168,16 @@ DEFINE_TEST(test_patterns) * c:../..\file43 * \/?\UnC\../file54 */ - assertEqualInt(-1, access(filex, F_OK)); + assertFileNotExists(filex); filex = file_c; xsize = sizeof(file_c); filex[xsize-3] = '0' + r / 10; filex[xsize-2] = '0' + r % 10; - assertEqualInt(-1, access(filex, F_OK)); + assertFileNotExists(filex); break; default: /* Extracted patterns. */ - assertEqualInt(0, access(filex, F_OK)); + assertFileExists(filex); break; } } diff --git a/usr.bin/tar/test/test_patterns_2.tar.uu b/usr.bin/tar/test/test_patterns_2.tar.uu index 1ed9a7d..f3a0a75 100644 --- a/usr.bin/tar/test/test_patterns_2.tar.uu +++ b/usr.bin/tar/test/test_patterns_2.tar.uu @@ -1,4 +1,5 @@ $FreeBSD$ + begin 644 test_patterns_2.tar M+W1M<"]F;V\O```````````````````````````````````````````````` M```````````````````````````````````````````````````````````` diff --git a/usr.bin/tar/test/test_patterns_3.tar.uu b/usr.bin/tar/test/test_patterns_3.tar.uu index e8d487e..8a19f80 100644 --- a/usr.bin/tar/test/test_patterns_3.tar.uu +++ b/usr.bin/tar/test/test_patterns_3.tar.uu @@ -1,4 +1,5 @@ $FreeBSD$ + begin 644 test_patterns_3.tar M+W1M<"]F;V\O8F%R+P`````````````````````````````````````````` M```````````````````````````````````````````````````````````` diff --git a/usr.bin/tar/test/test_patterns_4.tar.uu b/usr.bin/tar/test/test_patterns_4.tar.uu index eb89518..3fc62e4 100644 --- a/usr.bin/tar/test/test_patterns_4.tar.uu +++ b/usr.bin/tar/test/test_patterns_4.tar.uu @@ -1,4 +1,5 @@ $FreeBSD$ + begin 644 test_patterns_4.tar M+V9I;&4P,0`````````````````````````````````````````````````` M```````````````````````````````````````````````````````````` diff --git a/usr.bin/tar/test/test_stdio.c b/usr.bin/tar/test/test_stdio.c index 2d24ae3..1780d96 100644 --- a/usr.bin/tar/test/test_stdio.c +++ b/usr.bin/tar/test/test_stdio.c @@ -27,27 +27,27 @@ __FBSDID("$FreeBSD$"); DEFINE_TEST(test_stdio) { - int fd; - int filelist; - int oldumask; + FILE *filelist; + char *p; + size_t s; int r; - oldumask = umask(0); + assertUmask(0); /* * Create a couple of files on disk. */ - filelist = open("filelist", O_CREAT | O_WRONLY, 0644); /* File */ - fd = open("f", O_CREAT | O_WRONLY, 0644); - assert(fd >= 0); - write(fd, "f\n", 2); - close(fd); - write(filelist, "f\n", 2); + assertMakeFile("f", 0755, "abc"); /* Link to above file. */ - assertEqualInt(0, link("f", "l")); - write(filelist, "l\n", 2); - close(filelist); + assertMakeHardlink("l", "f"); + + /* Create file list (text mode here) */ + filelist = fopen("filelist", "w"); + assert(filelist != NULL); + fprintf(filelist, "f\n"); + fprintf(filelist, "l\n"); + fclose(filelist); /* * Archive/dearchive with a variety of options, verifying @@ -111,7 +111,10 @@ DEFINE_TEST(test_stdio) /* 'xvOf' should generate list on stderr, file contents on stdout. */ r = systemf("%s xvOf archive >xvOf.out 2>xvOf.err", testprog); assertEqualInt(r, 0); - /* TODO: Verify xvOf.out */ + /* Verify xvOf.out is the file contents */ + p = slurpfile(&s, "xvOf.out"); + assert(s = 3); + assertEqualMem(p, "abc", 3); /* TODO: Verify xvf.err */ /* 'xvf -' should generate list on stderr, empty stdout. */ @@ -119,6 +122,4 @@ DEFINE_TEST(test_stdio) assertEqualInt(r, 0); assertEmptyFile("xvf-.out"); /* TODO: Verify xvf-.err */ - - umask(oldumask); } diff --git a/usr.bin/tar/test/test_strip_components.c b/usr.bin/tar/test/test_strip_components.c index 38e0e48..271f209 100644 --- a/usr.bin/tar/test/test_strip_components.c +++ b/usr.bin/tar/test/test_strip_components.c @@ -28,50 +28,49 @@ __FBSDID("$FreeBSD$"); static int touch(const char *fn) { - int fd = open(fn, O_RDWR | O_CREAT, 0644); - failure("Couldn't create file '%s', fd=%d, errno=%d (%s)\n", - fn, fd, errno, strerror(errno)); - if (!assert(fd > 0)) + FILE *f = fopen(fn, "w"); + failure("Couldn't create file '%s', errno=%d (%s)\n", + fn, errno, strerror(errno)); + if (!assert(f != NULL)) return (0); /* Failure. */ - close(fd); + fclose(f); return (1); /* Success */ } DEFINE_TEST(test_strip_components) { - struct stat st; - - assertEqualInt(0, mkdir("d0", 0755)); - assertEqualInt(0, chdir("d0")); - assertEqualInt(0, mkdir("d1", 0755)); - assertEqualInt(0, mkdir("d1/d2", 0755)); - assertEqualInt(0, mkdir("d1/d2/d3", 0755)); + assertMakeDir("d0", 0755); + assertChdir("d0"); + assertMakeDir("d1", 0755); + assertMakeDir("d1/d2", 0755); + assertMakeDir("d1/d2/d3", 0755); assertEqualInt(1, touch("d1/d2/f1")); - assertEqualInt(0, link("d1/d2/f1", "l1")); - assertEqualInt(0, link("d1/d2/f1", "d1/l2")); - assertEqualInt(0, symlink("d1/d2/f1", "s1")); - assertEqualInt(0, symlink("d2/f1", "d1/s2")); - assertEqualInt(0, chdir("..")); + assertMakeHardlink("l1", "d1/d2/f1"); + assertMakeHardlink("d1/l2", "d1/d2/f1"); + if (canSymlink()) { + assertMakeSymlink("s1", "d1/d2/f1"); + assertMakeSymlink("d1/s2", "d2/f1"); + } + assertChdir(".."); assertEqualInt(0, systemf("%s -cf test.tar d0", testprog)); - assertEqualInt(0, mkdir("target", 0755)); + assertMakeDir("target", 0755); assertEqualInt(0, systemf("%s -x -C target --strip-components 2 " "-f test.tar", testprog)); failure("d0/ is too short and should not get restored"); - assertEqualInt(-1, lstat("target/d0", &st)); + assertFileNotExists("target/d0"); failure("d0/d1/ is too short and should not get restored"); - assertEqualInt(-1, lstat("target/d1", &st)); + assertFileNotExists("target/d1"); failure("d0/d1/s2 is a symlink to something that won't be extracted"); -#if !defined(_WIN32) || defined(__CYGWIN__) - assertEqualInt(-1, stat("target/s2", &st)); -#else - skipping("symlink with stat()"); -#endif - assertEqualInt(0, lstat("target/s2", &st)); + /* If platform supports symlinks, target/s2 is a broken symlink. */ + /* If platform does not support symlink, target/s2 doesn't exist. */ + assertFileNotExists("target/s2"); + if (canSymlink()) + assertIsSymlink("target/s2", "d2/f1"); failure("d0/d1/d2 should be extracted"); - assertEqualInt(0, lstat("target/d2", &st)); + assertIsDir("target/d2", -1); /* * This next is a complicated case. d0/l1, d0/d1/l2, and @@ -102,9 +101,9 @@ DEFINE_TEST(test_strip_components) * parallel tests for cpio and newc formats. */ failure("d0/l1 is too short and should not get restored"); - assertEqualInt(-1, lstat("target/l1", &st)); + assertFileNotExists("target/l1"); failure("d0/d1/l2 is a hardlink to file whose name was too short"); - assertEqualInt(-1, lstat("target/l2", &st)); + assertFileNotExists("target/l2"); failure("d0/d1/d2/f1 is a hardlink to file whose name was too short"); - assertEqualInt(-1, lstat("target/d2/f1", &st)); + assertFileNotExists("target/d2/f1"); } diff --git a/usr.bin/tar/test/test_symlink_dir.c b/usr.bin/tar/test/test_symlink_dir.c index 356f17c..f0cfebb 100644 --- a/usr.bin/tar/test/test_symlink_dir.c +++ b/usr.bin/tar/test/test_symlink_dir.c @@ -32,40 +32,36 @@ __FBSDID("$FreeBSD$"); */ static int -mkfile(const char *name, int mode, const char *contents, ssize_t size) +mkfile(const char *name, int mode, const char *contents, size_t size) { - int fd = open(name, O_CREAT | O_WRONLY, mode); - if (fd < 0) + FILE *f = fopen(name, "wb"); + size_t written; + + (void)mode; /* UNUSED */ + if (f == NULL) return (-1); - if (size != write(fd, contents, size)) { - close(fd); + written = fwrite(contents, 1, size, f); + fclose(f); + if (size != written) return (-1); - } - close(fd); return (0); } DEFINE_TEST(test_symlink_dir) { - struct stat st; -#if !defined(_WIN32) || defined(__CYGWIN__) - struct stat st2; -#endif - int oldumask; - - oldumask = umask(0); + assertUmask(0); - assertEqualInt(0, mkdir("source", 0755)); + assertMakeDir("source", 0755); assertEqualInt(0, mkfile("source/file", 0755, "a", 1)); assertEqualInt(0, mkfile("source/file2", 0755, "ab", 2)); - assertEqualInt(0, mkdir("source/dir", 0755)); - assertEqualInt(0, mkdir("source/dir/d", 0755)); + assertMakeDir("source/dir", 0755); + assertMakeDir("source/dir/d", 0755); assertEqualInt(0, mkfile("source/dir/f", 0755, "abc", 3)); - assertEqualInt(0, mkdir("source/dir2", 0755)); - assertEqualInt(0, mkdir("source/dir2/d2", 0755)); + assertMakeDir("source/dir2", 0755); + assertMakeDir("source/dir2/d2", 0755); assertEqualInt(0, mkfile("source/dir2/f2", 0755, "abcd", 4)); - assertEqualInt(0, mkdir("source/dir3", 0755)); - assertEqualInt(0, mkdir("source/dir3/d3", 0755)); + assertMakeDir("source/dir3", 0755); + assertMakeDir("source/dir3/d3", 0755); assertEqualInt(0, mkfile("source/dir3/f3", 0755, "abcde", 5)); assertEqualInt(0, @@ -75,119 +71,90 @@ DEFINE_TEST(test_symlink_dir) /* * Extract with -x and without -P. */ - assertEqualInt(0, mkdir("dest1", 0755)); - /* "dir" is a symlink to an existing "real_dir" */ - assertEqualInt(0, mkdir("dest1/real_dir", 0755)); -#if !defined(_WIN32) || defined(__CYGWIN__) - assertEqualInt(0, symlink("real_dir", "dest1/dir")); - /* "dir2" is a symlink to a non-existing "real_dir2" */ - assertEqualInt(0, symlink("real_dir2", "dest1/dir2")); -#else - skipping("symlink does not work on this platform"); -#endif + assertMakeDir("dest1", 0755); + /* "dir" is a symlink to an existing "dest1/real_dir" */ + assertMakeDir("dest1/real_dir", 0755); + if (canSymlink()) { + assertMakeSymlink("dest1/dir", "real_dir"); + /* "dir2" is a symlink to a non-existing "real_dir2" */ + assertMakeSymlink("dest1/dir2", "real_dir2"); + } else { + skipping("some symlink checks"); + } /* "dir3" is a symlink to an existing "non_dir3" */ assertEqualInt(0, mkfile("dest1/non_dir3", 0755, "abcdef", 6)); - assertEqualInt(0, symlink("non_dir3", "dest1/dir3")); + if (canSymlink()) + assertMakeSymlink("dest1/dir3", "non_dir3"); /* "file" is a symlink to existing "real_file" */ assertEqualInt(0, mkfile("dest1/real_file", 0755, "abcdefg", 7)); - assertEqualInt(0, symlink("real_file", "dest1/file")); -#if !defined(_WIN32) || defined(__CYGWIN__) - /* "file2" is a symlink to non-existing "real_file2" */ - assertEqualInt(0, symlink("real_file2", "dest1/file2")); -#else - skipping("symlink does not work on this platform"); -#endif + if (canSymlink()) { + assertMakeSymlink("dest1/file", "real_file"); + /* "file2" is a symlink to non-existing "real_file2" */ + assertMakeSymlink("dest1/file2", "real_file2"); + } assertEqualInt(0, systemf("%s -xf test.tar -C dest1", testprog)); - /* dest1/dir symlink should be removed */ - assertEqualInt(0, lstat("dest1/dir", &st)); + /* dest1/dir symlink should be replaced */ failure("symlink to dir was followed when it shouldn't be"); - assert(S_ISDIR(st.st_mode)); - /* dest1/dir2 symlink should be removed */ - assertEqualInt(0, lstat("dest1/dir2", &st)); + assertIsDir("dest1/dir", -1); + /* dest1/dir2 symlink should be replaced */ failure("Broken symlink wasn't replaced with dir"); - assert(S_ISDIR(st.st_mode)); - /* dest1/dir3 symlink should be removed */ - assertEqualInt(0, lstat("dest1/dir3", &st)); + assertIsDir("dest1/dir2", -1); + /* dest1/dir3 symlink should be replaced */ failure("Symlink to non-dir wasn't replaced with dir"); - assert(S_ISDIR(st.st_mode)); - /* dest1/file symlink should be removed */ - assertEqualInt(0, lstat("dest1/file", &st)); - failure("Symlink to existing file should be removed"); - assert(S_ISREG(st.st_mode)); - /* dest1/file2 symlink should be removed */ - assertEqualInt(0, lstat("dest1/file2", &st)); - failure("Symlink to non-existing file should be removed"); - assert(S_ISREG(st.st_mode)); + assertIsDir("dest1/dir3", -1); + /* dest1/file symlink should be replaced */ + failure("Symlink to existing file should be replaced"); + assertIsReg("dest1/file", -1); + /* dest1/file2 symlink should be replaced */ + failure("Symlink to non-existing file should be replaced"); + assertIsReg("dest1/file2", -1); /* * Extract with both -x and -P */ - assertEqualInt(0, mkdir("dest2", 0755)); + assertMakeDir("dest2", 0755); /* "dir" is a symlink to existing "real_dir" */ - assertEqualInt(0, mkdir("dest2/real_dir", 0755)); -#if !defined(_WIN32) || defined(__CYGWIN__) - assertEqualInt(0, symlink("real_dir", "dest2/dir")); + assertMakeDir("dest2/real_dir", 0755); + if (canSymlink()) + assertMakeSymlink("dest2/dir", "real_dir"); /* "dir2" is a symlink to a non-existing "real_dir2" */ - assertEqualInt(0, symlink("real_dir2", "dest2/dir2")); -#else - skipping("symlink does not work on this platform"); -#endif + if (canSymlink()) + assertMakeSymlink("dest2/dir2", "real_dir2"); /* "dir3" is a symlink to an existing "non_dir3" */ assertEqualInt(0, mkfile("dest2/non_dir3", 0755, "abcdefgh", 8)); - assertEqualInt(0, symlink("non_dir3", "dest2/dir3")); + if (canSymlink()) + assertMakeSymlink("dest2/dir3", "non_dir3"); /* "file" is a symlink to existing "real_file" */ assertEqualInt(0, mkfile("dest2/real_file", 0755, "abcdefghi", 9)); - assertEqualInt(0, symlink("real_file", "dest2/file")); -#if !defined(_WIN32) || defined(__CYGWIN__) + if (canSymlink()) + assertMakeSymlink("dest2/file", "real_file"); /* "file2" is a symlink to non-existing "real_file2" */ - assertEqualInt(0, symlink("real_file2", "dest2/file2")); -#else - skipping("symlink does not work on this platform"); -#endif + if (canSymlink()) + assertMakeSymlink("dest2/file2", "real_file2"); assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog)); /* dest2/dir symlink should be followed */ - assertEqualInt(0, lstat("dest2/dir", &st)); - failure("tar -xP removed symlink instead of following it"); -#if !defined(_WIN32) || defined(__CYGWIN__) - if (assert(S_ISLNK(st.st_mode))) { - /* Only verify what the symlink points to if it - * really is a symlink. */ - failure("The symlink should point to a directory"); - assertEqualInt(0, stat("dest2/dir", &st)); - assert(S_ISDIR(st.st_mode)); - failure("The pre-existing directory should still be there"); - assertEqualInt(0, lstat("dest2/real_dir", &st2)); - assert(S_ISDIR(st2.st_mode)); - assertEqualInt(st.st_dev, st2.st_dev); - failure("symlink should still point to the existing directory"); - assertEqualInt(st.st_ino, st2.st_ino); + if (canSymlink()) { + assertIsSymlink("dest2/dir", "real_dir"); + assertIsDir("dest2/real_dir", -1); } -#else - skipping("symlink does not work on this platform"); -#endif + /* Contents of 'dir' should be restored */ - assertEqualInt(0, lstat("dest2/dir/d", &st)); - assert(S_ISDIR(st.st_mode)); - assertEqualInt(0, lstat("dest2/dir/f", &st)); - assert(S_ISREG(st.st_mode)); - assertEqualInt(3, st.st_size); + assertIsDir("dest2/dir/d", -1); + assertIsReg("dest2/dir/f", -1); + assertFileSize("dest2/dir/f", 3); /* dest2/dir2 symlink should be removed */ - assertEqualInt(0, lstat("dest2/dir2", &st)); failure("Broken symlink wasn't replaced with dir"); - assert(S_ISDIR(st.st_mode)); + assertIsDir("dest2/dir2", -1); /* dest2/dir3 symlink should be removed */ - assertEqualInt(0, lstat("dest2/dir3", &st)); failure("Symlink to non-dir wasn't replaced with dir"); - assert(S_ISDIR(st.st_mode)); + assertIsDir("dest2/dir3", -1); /* dest2/file symlink should be removed; * even -P shouldn't follow symlinks for files */ - assertEqualInt(0, lstat("dest2/file", &st)); failure("Symlink to existing file should be removed"); - assert(S_ISREG(st.st_mode)); + assertIsReg("dest2/file", -1); /* dest2/file2 symlink should be removed */ - assertEqualInt(0, lstat("dest2/file2", &st)); failure("Symlink to non-existing file should be removed"); - assert(S_ISREG(st.st_mode)); + assertIsReg("dest2/file2", -1); } diff --git a/usr.bin/tar/test/test_version.c b/usr.bin/tar/test/test_version.c index 4f249a5..e4aacc8 100644 --- a/usr.bin/tar/test/test_version.c +++ b/usr.bin/tar/test/test_version.c @@ -52,7 +52,8 @@ DEFINE_TEST(test_version) /* Version message should start with name of program, then space. */ assert(s > 6); failure("Version must start with 'bsdtar': ``%s''", p); - assertEqualMem(q, "bsdtar ", 7); + if (!assertEqualMem(q, "bsdtar ", 7)) + return; q += 7; s -= 7; /* Version number is a series of digits and periods. */ while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c index 4177e9c..8e8b4a5 100644 --- a/usr.bin/tar/util.c +++ b/usr.bin/tar/util.c @@ -293,7 +293,7 @@ set_chdir(struct bsdtar *bsdtar, const char *newdir) free(old_pending); } if (bsdtar->pending_chdir == NULL) - bsdtar_errc(1, errno, "No memory"); + lafe_errc(1, errno, "No memory"); } void @@ -303,7 +303,7 @@ do_chdir(struct bsdtar *bsdtar) return; if (chdir(bsdtar->pending_chdir) != 0) { - bsdtar_errc(1, 0, "could not chdir to '%s'\n", + lafe_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); @@ -367,7 +367,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) #if HAVE_REGEX_H r = apply_substitution(bsdtar, name, &subst_name, 0); if (r == -1) { - bsdtar_warnc(0, "Invalid substitution, skipping entry"); + lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -383,7 +383,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (archive_entry_hardlink(entry)) { r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 1); if (r == -1) { - bsdtar_warnc(0, "Invalid substitution, skipping entry"); + lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -394,7 +394,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1); if (r == -1) { - bsdtar_warnc(0, "Invalid substitution, skipping entry"); + lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -468,11 +468,11 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (p != name && !bsdtar->warned_lead_slash) { /* Generate a warning the first time this happens. */ if (slashonly) - bsdtar_warnc(0, + lafe_warnc(0, "Removing leading '%c' from member names", name[0]); else - bsdtar_warnc(0, + lafe_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c index 9d9025a..32e43c6 100644 --- a/usr.bin/tar/write.c +++ b/usr.bin/tar/write.c @@ -169,7 +169,7 @@ tar_mode_c(struct bsdtar *bsdtar) int r; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) - bsdtar_errc(1, 0, "no files or directories specified"); + lafe_errc(1, 0, "no files or directories specified"); a = archive_write_new(); @@ -223,21 +223,21 @@ tar_mode_c(struct bsdtar *bsdtar) r = archive_write_set_compression_compress(a); break; default: - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Unrecognized compression option -%c", bsdtar->create_compression); } if (r != ARCHIVE_OK) { - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Unsupported compression option -%c", bsdtar->create_compression); } } if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) - bsdtar_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_file(a, bsdtar->filename)) - bsdtar_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); } @@ -265,7 +265,7 @@ tar_mode_r(struct bsdtar *bsdtar) bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT | O_BINARY, 0666); #endif if (bsdtar->fd < 0) - bsdtar_errc(1, errno, + lafe_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); @@ -274,14 +274,14 @@ tar_mode_r(struct bsdtar *bsdtar) archive_read_support_format_gnutar(a); r = archive_read_open_fd(a, bsdtar->fd, 10240); if (r != ARCHIVE_OK) - bsdtar_errc(1, archive_errno(a), + lafe_errc(1, archive_errno(a), "Can't read archive %s: %s", bsdtar->filename, archive_error_string(a)); while (0 == archive_read_next_header(a, &entry)) { if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) { archive_read_finish(a); close(bsdtar->fd); - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Cannot append to compressed archive."); } /* Keep going until we hit end-of-archive */ @@ -310,7 +310,7 @@ tar_mode_r(struct bsdtar *bsdtar) format &= ARCHIVE_FORMAT_BASE_MASK; if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK) && format != ARCHIVE_FORMAT_EMPTY) { - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Format %s is incompatible with the archive %s.", bsdtar->create_format, bsdtar->filename); } @@ -327,11 +327,11 @@ tar_mode_r(struct bsdtar *bsdtar) archive_write_set_format(a, format); } if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) - bsdtar_errc(1, errno, "Could not seek to archive end"); + lafe_errc(1, errno, "Could not seek to archive end"); if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) - bsdtar_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) - bsdtar_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); /* XXX check return val XXX */ @@ -359,7 +359,7 @@ tar_mode_u(struct bsdtar *bsdtar) bsdtar->fd = open(bsdtar->filename, O_RDWR | O_BINARY); if (bsdtar->fd < 0) - bsdtar_errc(1, errno, + lafe_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); @@ -369,7 +369,7 @@ tar_mode_u(struct bsdtar *bsdtar) if (archive_read_open_fd(a, bsdtar->fd, bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Can't open %s: %s", bsdtar->filename, archive_error_string(a)); } @@ -379,7 +379,7 @@ tar_mode_u(struct bsdtar *bsdtar) if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) { archive_read_finish(a); close(bsdtar->fd); - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Cannot append to compressed archive."); } add_dir_list(bsdtar, archive_entry_pathname(entry), @@ -410,11 +410,11 @@ tar_mode_u(struct bsdtar *bsdtar) } else archive_write_set_bytes_per_block(a, DEFAULT_BYTES_PER_BLOCK); if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) - bsdtar_errc(1, errno, "Could not seek to archive end"); + lafe_errc(1, errno, "Could not seek to archive end"); if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) - bsdtar_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) - bsdtar_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); @@ -442,14 +442,14 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) /* Allocate a buffer for file data. */ if ((bsdtar->buff = malloc(FILEDATABUFLEN)) == NULL) - bsdtar_errc(1, 0, "cannot allocate memory"); + lafe_errc(1, 0, "cannot allocate memory"); if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) - bsdtar_errc(1, 0, "cannot create link resolver"); + lafe_errc(1, 0, "cannot create link resolver"); archive_entry_linkresolver_set_strategy(bsdtar->resolver, archive_format(a)); if ((bsdtar->diskreader = archive_read_disk_new()) == NULL) - bsdtar_errc(1, 0, "Cannot create read_disk object"); + lafe_errc(1, 0, "Cannot create read_disk object"); archive_read_disk_set_standard_lookup(bsdtar->diskreader); if (bsdtar->names_from_file != NULL) @@ -463,7 +463,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) bsdtar->argv++; arg = *bsdtar->argv; if (arg == NULL) { - bsdtar_warnc(0, "%s", + lafe_warnc(0, "%s", "Missing argument for -C"); bsdtar->return_value = 1; goto cleanup; @@ -493,7 +493,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) } if (archive_write_close(a)) { - bsdtar_warnc(0, "%s", archive_error_string(a)); + lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; } @@ -543,7 +543,7 @@ archive_names_from_file(struct bsdtar *bsdtar, struct archive *a) } lafe_line_reader_free(lr); if (bsdtar->next_line_is_dir) - bsdtar_errc(1, errno, + lafe_errc(1, errno, "Unexpected end of filename list; " "directory expected after -C"); } @@ -569,7 +569,7 @@ append_archive_filename(struct bsdtar *bsdtar, struct archive *a, archive_read_support_format_all(ina); archive_read_support_compression_all(ina); if (archive_read_open_file(ina, filename, 10240)) { - bsdtar_warnc(0, "%s", archive_error_string(ina)); + lafe_warnc(0, "%s", archive_error_string(ina)); bsdtar->return_value = 1; return (0); } @@ -577,7 +577,7 @@ append_archive_filename(struct bsdtar *bsdtar, struct archive *a, rc = append_archive(bsdtar, a, ina); if (rc != ARCHIVE_OK) { - bsdtar_warnc(0, "Error reading archive %s: %s", + lafe_warnc(0, "Error reading archive %s: %s", filename, archive_error_string(ina)); bsdtar->return_value = 1; } @@ -610,7 +610,7 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) - bsdtar_warnc(0, "%s: %s", + lafe_warnc(0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else @@ -651,7 +651,7 @@ copy_file_data(struct bsdtar *bsdtar, struct archive *a, bytes_written = archive_write_data(a, bsdtar->buff, bytes_read); if (bytes_written < bytes_read) { - bsdtar_warnc(0, "%s", archive_error_string(a)); + lafe_warnc(0, "%s", archive_error_string(a)); return (-1); } progress += bytes_written; @@ -678,7 +678,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) tree = tree_open(path); if (!tree) { - bsdtar_warnc(errno, "%s: Cannot open", path); + lafe_warnc(errno, "%s: Cannot open", path); bsdtar->return_value = 1; return; } @@ -691,11 +691,11 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) int descend; if (tree_ret == TREE_ERROR_FATAL) - bsdtar_errc(1, tree_errno(tree), + lafe_errc(1, tree_errno(tree), "%s: Unable to continue traversing directory tree", name); if (tree_ret == TREE_ERROR_DIR) { - bsdtar_warnc(errno, + lafe_warnc(errno, "%s: Couldn't visit directory", name); bsdtar->return_value = 1; } @@ -715,7 +715,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) lst = tree_current_lstat(tree); if (lst == NULL) { /* Couldn't lstat(); must not exist. */ - bsdtar_warnc(errno, "%s: Cannot stat", name); + lafe_warnc(errno, "%s: Cannot stat", name); /* Return error if files disappear during traverse. */ bsdtar->return_value = 1; continue; @@ -834,7 +834,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) r = archive_read_disk_entry_from_file(bsdtar->diskreader, entry, -1, st); if (r != ARCHIVE_OK) - bsdtar_warnc(archive_errno(bsdtar->diskreader), + lafe_warnc(archive_errno(bsdtar->diskreader), "%s", archive_error_string(bsdtar->diskreader)); if (r < ARCHIVE_WARN) continue; @@ -929,7 +929,7 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a, if (fd == -1) { bsdtar->return_value = 1; if (!bsdtar->verbose) - bsdtar_warnc(errno, + lafe_warnc(errno, "%s: could not open file", pathname); else fprintf(stderr, ": %s", strerror(errno)); @@ -940,7 +940,7 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a, e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) - bsdtar_warnc(0, "%s: %s", + lafe_warnc(0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); else @@ -1016,12 +1016,12 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, bytes_read); if (bytes_written < 0) { /* Write failed; this is bad */ - bsdtar_warnc(0, "%s", archive_error_string(a)); + lafe_warnc(0, "%s", archive_error_string(a)); return (-1); } if (bytes_written < bytes_read) { /* Write was truncated; warn but continue. */ - bsdtar_warnc(0, + lafe_warnc(0, "%s: Truncated write; file may have grown while being archived.", archive_entry_pathname(entry)); return (0); @@ -1030,7 +1030,7 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN); } if (bytes_read < 0) { - bsdtar_warnc(errno, + lafe_warnc(errno, "%s: Read error", archive_entry_pathname(entry)); bsdtar->return_value = 1; @@ -1112,11 +1112,11 @@ add_dir_list(struct bsdtar *bsdtar, const char *path, p = malloc(sizeof(*p)); if (p == NULL) - bsdtar_errc(1, ENOMEM, "Can't read archive directory"); + lafe_errc(1, ENOMEM, "Can't read archive directory"); p->name = strdup(path); if (p->name == NULL) - bsdtar_errc(1, ENOMEM, "Can't read archive directory"); + lafe_errc(1, ENOMEM, "Can't read archive directory"); p->mtime_sec = mtime_sec; p->mtime_nsec = mtime_nsec; p->next = NULL; @@ -1134,26 +1134,26 @@ test_for_append(struct bsdtar *bsdtar) struct stat s; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) - bsdtar_errc(1, 0, "no files or directories specified"); + lafe_errc(1, 0, "no files or directories specified"); if (bsdtar->filename == NULL) - bsdtar_errc(1, 0, "Cannot append to stdout."); + lafe_errc(1, 0, "Cannot append to stdout."); if (bsdtar->create_compression != 0) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Cannot append to %s with compression", bsdtar->filename); if (stat(bsdtar->filename, &s) != 0) return; if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode)) - bsdtar_errc(1, 0, + lafe_errc(1, 0, "Cannot append to %s: not a regular file.", bsdtar->filename); /* Is this an appropriate check here on Windows? */ /* if (GetFileType(handle) != FILE_TYPE_DISK) - bsdtar_errc(1, 0, "Cannot append"); + lafe_errc(1, 0, "Cannot append"); */ } |