From owner-svn-src-head@FreeBSD.ORG Sun Mar 8 05:47:23 2009 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 32541106564A; Sun, 8 Mar 2009 05:47:23 +0000 (UTC) (envelope-from kientzle@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 1EEC18FC1A; Sun, 8 Mar 2009 05:47:23 +0000 (UTC) (envelope-from kientzle@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n285lMfr073756; Sun, 8 Mar 2009 05:47:22 GMT (envelope-from kientzle@svn.freebsd.org) Received: (from kientzle@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n285lMQw073738; Sun, 8 Mar 2009 05:47:22 GMT (envelope-from kientzle@svn.freebsd.org) Message-Id: <200903080547.n285lMQw073738@svn.freebsd.org> From: Tim Kientzle Date: Sun, 8 Mar 2009 05:47:22 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r189520 - in head/usr.bin/tar: . test X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 08 Mar 2009 05:47:23 -0000 Author: kientzle Date: Sun Mar 8 05:47:21 2009 New Revision: 189520 URL: http://svn.freebsd.org/changeset/base/189520 Log: Merger r629-631,633-646,648,654,678,681,682 from libarchive.googlecode.com: Many changes for Windows compatibility. bsdtar_test now runs successfully on both POSIX platforms and Windows. Added: head/usr.bin/tar/test/test_patterns_2.tar.uu (contents, props changed) head/usr.bin/tar/test/test_patterns_3.tar.uu (contents, props changed) Deleted: head/usr.bin/tar/test/test_patterns_2.tgz.uu head/usr.bin/tar/test/test_patterns_3.tgz.uu Modified: head/usr.bin/tar/bsdtar.c head/usr.bin/tar/bsdtar_platform.h head/usr.bin/tar/getdate.y head/usr.bin/tar/read.c head/usr.bin/tar/siginfo.c head/usr.bin/tar/test/main.c head/usr.bin/tar/test/test.h head/usr.bin/tar/test/test_0.c head/usr.bin/tar/test/test_basic.c head/usr.bin/tar/test/test_copy.c head/usr.bin/tar/test/test_option_T.c head/usr.bin/tar/test/test_option_s.c head/usr.bin/tar/test/test_patterns.c head/usr.bin/tar/test/test_strip_components.c head/usr.bin/tar/test/test_symlink_dir.c head/usr.bin/tar/test/test_version.c head/usr.bin/tar/tree.c head/usr.bin/tar/util.c head/usr.bin/tar/write.c Modified: head/usr.bin/tar/bsdtar.c ============================================================================== --- head/usr.bin/tar/bsdtar.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/bsdtar.c Sun Mar 8 05:47:21 2009 (r189520) @@ -73,6 +73,9 @@ __FBSDID("$FreeBSD$"); #ifdef __linux #define _PATH_DEFTAPE "/dev/st0" #endif +#ifdef _WIN32 +#define _PATH_DEFTAPE "\\\\.\\tape0" +#endif #ifndef _PATH_DEFTAPE #define _PATH_DEFTAPE "/dev/tape" @@ -109,12 +112,20 @@ main(int argc, char **argv) memset(bsdtar, 0, sizeof(*bsdtar)); bsdtar->fd = -1; /* Mark as "unused" */ option_o = 0; +#ifdef _WIN32 + /* open() function is always with a binary mode. */ + _set_fmode(_O_BINARY); +#endif /* Need bsdtar->progname before calling bsdtar_warnc. */ if (*argv == NULL) bsdtar->progname = "bsdtar"; else { +#if _WIN32 + bsdtar->progname = strrchr(*argv, '\\'); +#else bsdtar->progname = strrchr(*argv, '/'); +#endif if (bsdtar->progname != NULL) bsdtar->progname++; else @@ -143,7 +154,7 @@ main(int argc, char **argv) bsdtar->extract_flags |= SECURITY; /* Defaults for root user: */ - if (bsdtar->user_uid == 0) { + if (bsdtar_is_privileged(bsdtar)) { /* --same-owner */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; /* -p */ @@ -152,6 +163,10 @@ main(int argc, char **argv) bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; } +#ifdef _WIN32 + /* Windows cannot set UNIX like uid/gid. */ + bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; +#endif bsdtar->argv = argv; bsdtar->argc = argc; Modified: head/usr.bin/tar/bsdtar_platform.h ============================================================================== --- head/usr.bin/tar/bsdtar_platform.h Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/bsdtar_platform.h Sun Mar 8 05:47:21 2009 (r189520) @@ -164,4 +164,10 @@ #define __LA_DEAD #endif +#ifdef _WIN32 +#include "bsdtar_windows.h" +#else +#define bsdtar_is_privileged(bsdtar) (bsdtar->user_uid == 0) +#endif + #endif /* !BSDTAR_PLATFORM_H_INCLUDED */ Modified: head/usr.bin/tar/getdate.y ============================================================================== --- head/usr.bin/tar/getdate.y Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/getdate.y Sun Mar 8 05:47:21 2009 (r189520) @@ -35,6 +35,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef _MSC_VER +#define __STDC__ /* for a bug of bison 2.1 on Windows */ +#endif + #define yyparse getdate_yyparse #define yylex getdate_yylex #define yyerror getdate_yyerror Modified: head/usr.bin/tar/read.c ============================================================================== --- head/usr.bin/tar/read.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/read.c Sun Mar 8 05:47:21 2009 (r189520) @@ -384,10 +384,18 @@ list_item_verbose(struct bsdtar *bsdtar, /* Format the time using 'ls -l' conventions. */ tim = (time_t)st->st_mtime; +#ifdef _WIN32 + /* Windows' strftime function does not support %e format. */ + if (abs(tim - now) > (365/2)*86400) + fmt = bsdtar->day_first ? "%d %b %Y" : "%b %d %Y"; + else + fmt = bsdtar->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; +#else if (abs(tim - now) > (365/2)*86400) fmt = bsdtar->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = bsdtar->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; +#endif strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); Modified: head/usr.bin/tar/siginfo.c ============================================================================== --- head/usr.bin/tar/siginfo.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/siginfo.c Sun Mar 8 05:47:21 2009 (r189520) @@ -82,8 +82,10 @@ siginfo_init(struct bsdtar *bsdtar) /* We want to catch SIGINFO, if it exists. */ bsdtar->siginfo->siginfo_old = signal(SIGINFO, siginfo_handler); #endif +#ifdef SIGUSR1 /* ... and treat SIGUSR1 the same way as SIGINFO. */ bsdtar->siginfo->sigusr1_old = signal(SIGUSR1, siginfo_handler); +#endif } void @@ -135,8 +137,10 @@ siginfo_done(struct bsdtar *bsdtar) /* Restore old SIGINFO handler. */ signal(SIGINFO, bsdtar->siginfo->siginfo_old); #endif +#ifdef SIGUSR1 /* And the old SIGUSR1 handler, too. */ signal(SIGUSR1, bsdtar->siginfo->sigusr1_old); +#endif /* Free strings. */ free(bsdtar->siginfo->path); Modified: head/usr.bin/tar/test/main.c ============================================================================== --- head/usr.bin/tar/test/main.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/main.c Sun Mar 8 05:47:21 2009 (r189520) @@ -504,6 +504,7 @@ test_assert_empty_file(const char *f1fmt (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = read(fd, buff, s); hexdump(buff, NULL, s, 0); + close(fd); } report_failure(NULL); return (0); @@ -564,11 +565,16 @@ test_assert_equal_file(const char *f1, c n2 = read(fd2, buff2, sizeof(buff2)); if (n1 != n2) break; - if (n1 == 0 && n2 == 0) + if (n1 == 0 && n2 == 0) { + close(fd1); + close(fd2); return (1); + } if (memcmp(buff1, buff2, n1) != 0) break; } + close(fd1); + close(fd2); failures ++; if (!verbose && previous_failures(test_filename, test_line)) return (0); @@ -648,6 +654,7 @@ test_assert_file_contents(const void *bu } contents = malloc(s * 2); n = read(fd, contents, s * 2); + close(fd); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); @@ -802,7 +809,11 @@ static int test_run(int i, const char *t /* If there were no failures, we can remove the work dir. */ if (failures == failures_before) { if (!keep_temp_files && chdir(tmpdir) == 0) { +#ifdef _WIN32 + systemf("rmdir /S /Q %s", tests[i].name); +#else systemf("rm -rf %s", tests[i].name); +#endif } } /* Return appropriate status. */ @@ -901,6 +912,9 @@ int main(int argc, char **argv) int i, tests_run = 0, tests_failed = 0, opt; time_t now; char *refdir_alloc = NULL; +#ifdef _WIN32 + char *testprg; +#endif const char *opt_arg, *progname, *p; char tmpdir[256]; char tmpdir_timestamp[256]; @@ -913,7 +927,8 @@ int main(int argc, char **argv) */ progname = p = argv[0]; while (*p != '\0') { - if (*p == '/') + /* Support \ or / dir separators for Windows compat. */ + if (*p == '/' || *p == '\\') progname = p + 1; ++p; } @@ -995,6 +1010,18 @@ int main(int argc, char **argv) if (testprog == NULL) usage(progname); #endif +#ifdef _WIN32 + /* + * 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] = '\\'; + } + testprog = testprg; +#endif /* * Create a temp directory for the following tests. Modified: head/usr.bin/tar/test/test.h ============================================================================== --- head/usr.bin/tar/test/test.h Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/test.h Sun Mar 8 05:47:21 2009 (r189520) @@ -45,7 +45,13 @@ #error Oops: No config.h and no pre-built configuration in test.h. #endif +#ifndef _WIN32 #include +#else +#define dirent direct +#include "../bsdtar_windows.h" +#include +#endif #include #include #include Modified: head/usr.bin/tar/test/test_0.c ============================================================================== --- head/usr.bin/tar/test/test_0.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/test_0.c Sun Mar 8 05:47:21 2009 (r189520) @@ -29,6 +29,11 @@ __FBSDID("$FreeBSD$"); * This first test does basic sanity checks on the environment. For * most of these, we just exit on failure. */ +#ifndef _WIN32 +#define DEV_NULL "/dev/null" +#else +#define DEV_NULL "NUL" +#endif DEFINE_TEST(test_0) { @@ -46,9 +51,9 @@ DEFINE_TEST(test_0) * Try to succesfully run the program; this requires that * we know some option that will succeed. */ - if (0 == systemf("%s --version >/dev/null", testprog)) { + if (0 == systemf("%s --version >" DEV_NULL, testprog)) { /* This worked. */ - } else if (0 == systemf("%s -W version >/dev/null", testprog)) { + } else if (0 == systemf("%s -W version >" DEV_NULL, testprog)) { /* This worked. */ } else { failure("Unable to successfully run any of the following:\n" Modified: head/usr.bin/tar/test/test_basic.c ============================================================================== --- head/usr.bin/tar/test/test_basic.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/test_basic.c Sun Mar 8 05:47:21 2009 (r189520) @@ -27,16 +27,23 @@ __FBSDID("$FreeBSD$"); static void -basic_tar(const char *target, const char *pack_options, const char *unpack_options) +basic_tar(const char *target, const char *pack_options, + const char *unpack_options, const char *flist) { struct stat st, st2; +#ifndef _WIN32 char buff[128]; +#endif int r; assertEqualInt(0, mkdir(target, 0775)); /* Use the tar program to create an archive. */ - r = systemf("%s cf - %s `cat filelist` >%s/archive 2>%s/pack.err", testprog, pack_options, target, target); +#ifndef _WIN32 + 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); @@ -65,7 +72,11 @@ basic_tar(const char *target, const char assertEqualInt(r, 0); if (r == 0) { assert(S_ISREG(st.st_mode)); +#ifndef _WIN32 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); @@ -77,7 +88,11 @@ basic_tar(const char *target, const char assertEqualInt(r, 0); if (r == 0) { assert(S_ISREG(st2.st_mode)); +#ifndef _WIN32 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); @@ -87,6 +102,7 @@ basic_tar(const char *target, const char assertEqualInt(st.st_ino, st2.st_ino); } +#ifndef _WIN32 /* Symlink */ r = lstat("symlink", &st); failure("Failed to stat file %s/symlink, errno=%d", target, errno); @@ -102,13 +118,18 @@ basic_tar(const char *target, const char assertEqualString(buff, "file"); } } +#endif /* dir */ r = lstat("dir", &st); if (r == 0) { assertEqualInt(r, 0); assert(S_ISDIR(st.st_mode)); +#ifndef _WIN32 assertEqualInt(0775, st.st_mode & 0777); +#else + assertEqualInt(0700, st.st_mode & 0700); +#endif } chdir(".."); @@ -119,6 +140,7 @@ DEFINE_TEST(test_basic) int fd; int filelist; int oldumask; + const char *flist; oldumask = umask(0); @@ -148,11 +170,16 @@ DEFINE_TEST(test_basic) /* All done. */ close(filelist); +#ifndef _WIN32 + flist = "filelist"; +#else + flist = "file linkfile symlink dir"; +#endif /* Archive/dearchive with a variety of options. */ - basic_tar("copy", "", ""); + basic_tar("copy", "", "", flist); /* tar doesn't handle cpio symlinks correctly */ /* basic_tar("copy_odc", "--format=odc", ""); */ - basic_tar("copy_ustar", "--format=ustar", ""); + basic_tar("copy_ustar", "--format=ustar", "", flist); umask(oldumask); } Modified: head/usr.bin/tar/test/test_copy.c ============================================================================== --- head/usr.bin/tar/test/test_copy.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/test_copy.c Sun Mar 8 05:47:21 2009 (r189520) @@ -64,6 +64,7 @@ create_tree(void) buff2[0] = 'm'; assertEqualInt(0, link(buff, buff2)); +#ifndef _WIN32 /* Create a symlink named "s/abcdef..." to the above. */ strcpy(buff2 + 3, buff); buff[0] = 's'; @@ -71,7 +72,9 @@ create_tree(void) buff2[1] = '.'; buff2[2] = '/'; assertEqualInt(0, symlink(buff2, buff)); - +#else + skipping("create a symlink to the above"); +#endif /* Create a dir named "d/abcdef...". */ buff[0] = 'd'; assertEqualInt(0, mkdir(buff, 0775)); @@ -153,6 +156,7 @@ verify_tree(int limit) } } +#ifndef _WIN32 /* * Symlink text doesn't include the 'original/' prefix, * so the limit here is 100 characters. @@ -174,7 +178,9 @@ verify_tree(int limit) } } } - +#else + skipping("verify symlink"); +#endif /* Verify dir "d/abcdef...". */ strcpy(name1, "d/"); strcat(name1, filename); Modified: head/usr.bin/tar/test/test_option_T.c ============================================================================== --- head/usr.bin/tar/test/test_option_T.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/test_option_T.c Sun Mar 8 05:47:21 2009 (r189520) @@ -41,6 +41,7 @@ DEFINE_TEST(test_option_T) { FILE *f; int r; + struct stat st; /* Create a simple dir heirarchy; bail if anything fails. */ if (!assertEqualInt(0, mkdir("d1", 0755))) return; @@ -127,19 +128,26 @@ DEFINE_TEST(test_option_T) assertEqualInt(0, mkdir("test4/d1", 0755)); assertEqualInt(1, touch("test4/d1/foo")); - systemf("%s -cf - -s /foo/bar/ test4/d1/foo | %s -xf - -C test4_out", - testprog, testprog); - assertEmptyFile("test4_out/test4/d1/bar"); - systemf("%s -cf - -s /d1/d2/ test4/d1/foo | %s -xf - -C test4_out", - testprog, testprog); - assertEmptyFile("test4_out/test4/d2/foo"); - systemf("%s -cf - -s ,test4/d1/foo,, test4/d1/foo | %s -tvf - > test4.lst", - testprog, testprog); - assertEmptyFile("test4.lst"); - systemf("%s -cf - test4/d1/foo | %s -xf - -s /foo/bar/ -C test4_out2", - testprog, testprog); - assertEmptyFile("test4_out2/test4/d1/bar"); - + /* Does bsdtar support -s option ? */ + systemf("%s -cf - -s /foo/bar/ test4/d1/foo > NUL 2> check.err", + testprog); + assertEqualInt(0, stat("check.err", &st)); + if (st.st_size == 0) { + systemf("%s -cf - -s /foo/bar/ test4/d1/foo | %s -xf - -C test4_out", + testprog, testprog); + assertEmptyFile("test4_out/test4/d1/bar"); + systemf("%s -cf - -s /d1/d2/ test4/d1/foo | %s -xf - -C test4_out", + testprog, testprog); + assertEmptyFile("test4_out/test4/d2/foo"); + systemf("%s -cf - -s ,test4/d1/foo,, test4/d1/foo | %s -tvf - > test4.lst", + testprog, testprog); + assertEmptyFile("test4.lst"); + systemf("%s -cf - test4/d1/foo | %s -xf - -s /foo/bar/ -C test4_out2", + testprog, testprog); + assertEmptyFile("test4_out2/test4/d1/bar"); + } 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. */ } Modified: head/usr.bin/tar/test/test_option_s.c ============================================================================== --- head/usr.bin/tar/test/test_option_s.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/test_option_s.c Sun Mar 8 05:47:21 2009 (r189520) @@ -42,12 +42,23 @@ mkfile(const char *fn, const char *conte DEFINE_TEST(test_option_s) { + struct stat st; + /* Create a sample file heirarchy. */ assertEqualInt(0, mkdir("in", 0755)); assertEqualInt(0, mkdir("in/d1", 0755)); assertEqualInt(0, mkfile("in/d1/foo", "foo")); assertEqualInt(0, mkfile("in/d1/bar", "bar")); + /* Does bsdtar support -s option ? */ + systemf("%s -cf - -s /foo/bar/ in/d1/foo > NUL 2> check.err", + testprog); + assertEqualInt(0, stat("check.err", &st)); + if (st.st_size != 0) { + skipping("bsdtar does not support -s option on this platform"); + return; + } + /* * Test 1: Filename substitution when creating archives. */ Modified: head/usr.bin/tar/test/test_patterns.c ============================================================================== --- head/usr.bin/tar/test/test_patterns.c Sun Mar 8 05:38:45 2009 (r189519) +++ head/usr.bin/tar/test/test_patterns.c Sun Mar 8 05:47:21 2009 (r189520) @@ -28,8 +28,8 @@ __FBSDID("$FreeBSD$"); DEFINE_TEST(test_patterns) { int fd, r; - const char *reffile2 = "test_patterns_2.tgz"; - const char *reffile3 = "test_patterns_3.tgz"; + const char *reffile2 = "test_patterns_2.tar"; + const char *reffile3 = "test_patterns_3.tar"; const char *p; /* @@ -45,9 +45,9 @@ DEFINE_TEST(test_patterns) fd = open("foo", O_CREAT | O_WRONLY, 0644); assert(fd >= 0); close(fd); - r = systemf("%s zcfv tar1.tgz foo > tar1a.out 2> tar1a.err", testprog); + r = systemf("%s cfv tar1.tgz foo > tar1a.out 2> tar1a.err", testprog); assertEqualInt(r, 0); - r = systemf("%s zxfv tar1.tgz foo bar > tar1b.out 2> tar1b.err", testprog); + r = systemf("%s xfv 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); @@ -59,7 +59,11 @@ DEFINE_TEST(test_patterns) r = systemf("%s tf %s /tmp/foo/bar > tar2a.out 2> tar2a.err", testprog, reffile2); assertEqualInt(r, 0); +#ifndef _WIN32 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"); assertEmptyFile("tar2a.err"); Added: head/usr.bin/tar/test/test_patterns_2.tar.uu ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.bin/tar/test/test_patterns_2.tar.uu Sun Mar 8 05:47:21 2009 (r189520) @@ -0,0 +1,232 @@ +$FreeBSD$ +begin 644 test_patterns_2.tar +M+W1M<"]F;V\O```````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#@`````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````P +M,#`V-#0@`#`P,3@`````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````#`P,#8T-"``,#`Q-S4P(``P,#`P,#`@`#`P,#`P +M,#`P,#`P(#$Q,#4Q,C$R-C4S(#`Q,S8V-P`@,``````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````!U