Compare commits
165 Commits
alpha_1_15
...
release_1_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5ef01710a | ||
|
|
e70e63612a | ||
|
|
28b26242c7 | ||
|
|
6b279a6f8c | ||
|
|
079f2d6807 | ||
|
|
b09417ca8d | ||
|
|
b15e3f1bbd | ||
|
|
1616b1d1b4 | ||
|
|
f23bc997fd | ||
|
|
8ec119a27f | ||
|
|
e35d05b1d2 | ||
|
|
cdb77dcd7b | ||
|
|
9bf87b195e | ||
|
|
643e3f2441 | ||
|
|
6e75833cb7 | ||
|
|
908d78d208 | ||
|
|
192f55e2a0 | ||
|
|
9d99fd13cd | ||
|
|
07902e9f9a | ||
|
|
0c94a109b9 | ||
|
|
27094c4fc3 | ||
|
|
5aca761e1b | ||
|
|
cc82db7f2d | ||
|
|
137ebf41fd | ||
|
|
49ea4c5057 | ||
|
|
409bddf38c | ||
|
|
c930802f31 | ||
|
|
ca14885884 | ||
|
|
01d6188297 | ||
|
|
759c5208c5 | ||
|
|
8457e06b99 | ||
|
|
8719c4f680 | ||
|
|
574022ab78 | ||
|
|
304d8b9f0c | ||
|
|
6712656eb2 | ||
|
|
415d9c9e15 | ||
|
|
1a8141ab8a | ||
|
|
023c766600 | ||
|
|
fdb46aa2e2 | ||
|
|
2504e7d3ae | ||
|
|
b0765e257c | ||
|
|
73d4c40a87 | ||
|
|
7aed52718f | ||
|
|
f572ca0cfb | ||
|
|
29236a68fa | ||
|
|
a4b1564d4a | ||
|
|
94d0385a57 | ||
|
|
603c1d60a6 | ||
|
|
29e9004b69 | ||
|
|
8da152d2b3 | ||
|
|
00746182f3 | ||
|
|
6c8815909d | ||
|
|
fde336b21a | ||
|
|
394b12d950 | ||
|
|
372638ae7a | ||
|
|
c455373e5b | ||
|
|
f2541c66cd | ||
|
|
7e6d54d0c0 | ||
|
|
876e554157 | ||
|
|
1118d24fd3 | ||
|
|
dc6f7ebf20 | ||
|
|
1102eeef1f | ||
|
|
e8a4c35a73 | ||
|
|
e359fad641 | ||
|
|
f5504a3bae | ||
|
|
341be8f405 | ||
|
|
4f2a22f306 | ||
|
|
a76ab340dd | ||
|
|
3ad3ca8734 | ||
|
|
82b2829e10 | ||
|
|
f852428d5f | ||
|
|
86e91a5bfc | ||
|
|
ec5743a628 | ||
|
|
bf7f4fdc1e | ||
|
|
98b9522499 | ||
|
|
6398dbe1a5 | ||
|
|
37724f5e20 | ||
|
|
c8c351684c | ||
|
|
01b6fb98bf | ||
|
|
f1309bffbf | ||
|
|
c732eb584e | ||
|
|
0680ad4d42 | ||
|
|
1942370acd | ||
|
|
94de7c4c77 | ||
|
|
05805868f2 | ||
|
|
57aa98047e | ||
|
|
7573967406 | ||
|
|
ab8711413c | ||
|
|
9fd9a0913d | ||
|
|
f4e4adea80 | ||
|
|
7110641821 | ||
|
|
fce454b5ca | ||
|
|
8ad985ea6c | ||
|
|
37edfd9e8f | ||
|
|
016f7c87a7 | ||
|
|
52b8bea074 | ||
|
|
5addfdcb03 | ||
|
|
e707b47aba | ||
|
|
76dc519f9f | ||
|
|
2a00376816 | ||
|
|
9766528b07 | ||
|
|
f26a90783b | ||
|
|
36fe16aaf5 | ||
|
|
89f1667fdb | ||
|
|
f2d65dda39 | ||
|
|
8370746251 | ||
|
|
2614af6d3c | ||
|
|
614def3113 | ||
|
|
b6365e9b03 | ||
|
|
bf789e3642 | ||
|
|
1421ee630d | ||
|
|
9869d0ae17 | ||
|
|
edc0b12c5e | ||
|
|
808cafa454 | ||
|
|
d1dedae402 | ||
|
|
de754902da | ||
|
|
bc7f4ad027 | ||
|
|
55abc110f5 | ||
|
|
4d753fced1 | ||
|
|
85a4e0fdeb | ||
|
|
583415c2c1 | ||
|
|
6488588c5f | ||
|
|
ac40b1e6f6 | ||
|
|
4bf5f6dca4 | ||
|
|
ecaff7cbba | ||
|
|
ea9e2d8d8e | ||
|
|
39e5d9182c | ||
|
|
c7aa519f09 | ||
|
|
bf5ba3dbef | ||
|
|
ea368b6d1b | ||
|
|
d0201294c0 | ||
|
|
c5b15c4ac1 | ||
|
|
4b3dd17c00 | ||
|
|
d339cc38af | ||
|
|
796a69787e | ||
|
|
4c54e234c1 | ||
|
|
c027e03924 | ||
|
|
6d615f12d4 | ||
|
|
8f970d2c1e | ||
|
|
d04af8f714 | ||
|
|
e2ecb82711 | ||
|
|
8e2e731733 | ||
|
|
19a63e523d | ||
|
|
5679d3020e | ||
|
|
1001c1b326 | ||
|
|
090d1d36ae | ||
|
|
2e1f904f2c | ||
|
|
6a0b5421b9 | ||
|
|
9c764b14a9 | ||
|
|
ae67839879 | ||
|
|
9588a106a7 | ||
|
|
b3627f3f07 | ||
|
|
ca7df3fe6b | ||
|
|
ab19642053 | ||
|
|
a71b154289 | ||
|
|
ceaef10443 | ||
|
|
5da6733724 | ||
|
|
8d6c177ecb | ||
|
|
1ecd6672e1 | ||
|
|
ca2f855c90 | ||
|
|
693134a4e7 | ||
|
|
90cec95580 | ||
|
|
b6fcb4ba8f | ||
|
|
6d1e9ab67e | ||
|
|
300210aa15 |
495
ChangeLog
495
ChangeLog
@@ -1,3 +1,456 @@
|
||||
2006-12-08 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* bootstrap: Add paxutils files to dot_ignore.
|
||||
|
||||
* configure.ac: Raise version number to 1.16.1
|
||||
* bootstrap (slurp): Create .(cvs|git)ignore if not present
|
||||
* po/.cvsignore, m4/.cvsignore: Remove automatically generated
|
||||
files.
|
||||
|
||||
2006-12-07 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* NEWS: Update
|
||||
* Makefile.am (distclean-local): Fixed
|
||||
* doc/tar.texi: Update documentation of --exclude-tag
|
||||
* src/create.c (dump_dir0): Move checks for exclude tags to
|
||||
dump_file0.
|
||||
(dump_dir): Move calls to ensure_slash to dump_file0
|
||||
* src/extract.c (extract_file): Call skip_member if open fails.
|
||||
Patch proposed by Jan-Benedict Glaw <jbglaw@lug-owl.de>
|
||||
* tests/truncate.at: Use genfile instead of dd, because on some
|
||||
systems /dev/zero is not available.
|
||||
|
||||
2006-12-04 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
* NEWS: Fix some race conditions with tar -x --same-owner.
|
||||
* src/extract.c (ARCHIVED_PERMSTATS): Add a comment saying that
|
||||
S_IRWXG | S_IRWXO might be masked out.
|
||||
(set_mode): Set the mode if some bits were masked out originally.
|
||||
(set_stat): Don't chmod before chown, as that might temporarily
|
||||
grant permissions that we don't want to grant. The chmod was
|
||||
there only to work around broken hosts, so add a comment advising
|
||||
users not to use those broken hosts instead.
|
||||
(repair_delayed_set_stat, extract_dir):
|
||||
Remember to mask out current umask before inverting permissions.
|
||||
(extract_dir): If the owner might change, or if the mode has
|
||||
special bits, create the directory 700 at first, but restore it later.
|
||||
(open_output_file): New arg mode; all uses changed.
|
||||
(extract_file, extract_node, extract_fifo): If the owner might
|
||||
change, omit group and other bits at first, but restore them after
|
||||
changing the owner.
|
||||
|
||||
2006-12-04 Jim Meyering <jim@meyering.net>
|
||||
|
||||
* doc/tar.texi (Long Options): Remove doubled word.
|
||||
|
||||
2006-11-30 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/xheader.c (xheader_read): Remove unused variable
|
||||
|
||||
* po/POTFILES.in: Remove src/mangle.c
|
||||
|
||||
* bootstrap: Implement --update-po and .bootstrap
|
||||
|
||||
* src/create.c (dump_dir0): Implement --exclude-tag option
|
||||
* src/tar.c: Likewise
|
||||
* doc/tar.texi (exclude): Document --exclude-tag
|
||||
|
||||
2006-11-29 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
* NEWS: Remove support for mangled names.
|
||||
* doc/tar.texi (verbose tutorial): Likewise.
|
||||
* src/Makefile.am (tar_SOURCES): Remove mangle.c.
|
||||
* src/common.h (extract_mangle): Remove decl.
|
||||
* src/extract.c (extract_mangle_wrapper): Remove.
|
||||
(prepare_to_extract): Remove support for mangled names.
|
||||
* src/list.c (read_and, print_header): Likewise.
|
||||
* src/mangle.c: Remove.
|
||||
* src/tar.h (GNUTYPE_NAMES): Remove.
|
||||
|
||||
Port to latest gnulib. There were a lot of changes, so the
|
||||
simplest way to get this up and running was to switch to coreutils
|
||||
bootstrap procedure. I noticed one feature missing after this
|
||||
merge: the ability to update a single .po file. I can add that
|
||||
later if need be.
|
||||
* README-cvs, bootstrap.conf: New files.
|
||||
* lib/.cvsignore: Remove Makefile.am, printf-parse.c, vasnprintf.c.
|
||||
Add fstatat.c, gnulib.mk, openat-proc.c, same-inode.h, stat_.h,
|
||||
tempname.c, tempname.h, uinttostr.c.
|
||||
* lib/printf-parse.c, lib/vasnprintf.c: New files, from coreutils,
|
||||
to override gnulib, so that we don't need xsize.h.
|
||||
* bootstrap: Replace with coreutils bootstrap, except add support
|
||||
for paxutils.
|
||||
* configure.ac (gl_USE_SYSTEM_EXTENSIONS): Remove, as gl_EARLY now
|
||||
does this.
|
||||
(gl_EARLY, gl_INIT): Add.
|
||||
(tar_GNULIB): Remove.
|
||||
* gnulib.modules: Add configmake.
|
||||
* lib/Makefile.tmpl: Remove, replacing with....
|
||||
* lib/Makefile.am: New file.
|
||||
* src/Makefile.am (tar.o): Remove dependency: Automake does this
|
||||
for us.
|
||||
* src/tar.c: Include <configmake.h> and <rmt-command.h>, not
|
||||
<localedir.h>.
|
||||
|
||||
2006-11-13 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/xheader.c (mtime_coder): Treat non-null data as a pointer to
|
||||
struct timespec, overriding st->mtime
|
||||
* src/create.c (start_header): Pass mtime as a call-specific data
|
||||
to xheader_store.
|
||||
|
||||
* tests/truncate.at: Do not use 'k' modifier in dd options.
|
||||
* tests/append02.at: Do not depend on command timing.
|
||||
|
||||
2006-11-01 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/tar.c (enum read_file_list_state.file_list_skip): New value
|
||||
(read_name_from_file): Skip zero-length entries
|
||||
|
||||
* tests/T-empty.at: New test case
|
||||
* tests/T-null.at: New test case
|
||||
* tests/extrac07.at: New test case
|
||||
* tests/Makefile.am: Add new test cases.
|
||||
* tests/testsuite.at: Add new test cases.
|
||||
* tests/extrac02.at: Add more keywords
|
||||
* tests/extrac04.at: Likewise
|
||||
* tests/extrac06.at: Likewise
|
||||
* tests/shortrec.at: Do not assume tar's default archive is stdout
|
||||
|
||||
2006-10-31 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/extract.c, src/xheader.c: Call last_component instead of
|
||||
base_name. The latter returns a malloced string since 2006-03-11.
|
||||
|
||||
2006-10-21 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* NEWS, configure.ac: Version 1.16
|
||||
* Makefile.am (distclean-local): New rule
|
||||
|
||||
2006-10-17 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/tar.c: Fix help output formatting. Thanks Benno
|
||||
Schulenberg.
|
||||
|
||||
2006-10-16 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* Makefile.am (dist-hook): Create a cpio archive.
|
||||
* NEWS: Update
|
||||
|
||||
2006-10-14 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* doc/tar.texi (Synopsis): Document tar exit codes.
|
||||
* src/create.c (dump_regular_file,dump_file0): Set exit_status to
|
||||
TAREXIT_DIFFERS if the file being dumped was truncated or
|
||||
otherwise changed.
|
||||
* src/tar.c: Do not attempt to close stderr after call to
|
||||
close_stdout.
|
||||
* tests/grow.at: New test case
|
||||
* tests/Makefile.am: New test grow.at
|
||||
* tests/testsuite.at: Likewise
|
||||
* tests/truncate.at (AT_KEYWORDS): Keyword `filechange'
|
||||
Test tar exit status.
|
||||
|
||||
* src/buffer.c (_open_archive): Make sure stdlis is set to stderr
|
||||
when we are writing archive to stdout (unless --index-file is
|
||||
used). Bug introduced on 2006-07-06.
|
||||
* tests/Makefile.am: New test verbose.at
|
||||
* tests/testsuite.at: Likewise
|
||||
* tests/indexfile.at (AT_KEYWORDS): Add stdout keyword
|
||||
* tests/verbose.at: New test case
|
||||
|
||||
2006-10-02 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* THANKS: Add Joerg Weilbier
|
||||
* src/buffer.c (new_volume): Initialize current_block
|
||||
* src/xheader.c (xheader_string_end): Fix diagnostic message.
|
||||
* tests/multiv05.at: New testcase.
|
||||
* tests/Makefile.am, tests/testsuite.at: Add multiv05.at
|
||||
|
||||
2006-09-27 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* bootstrap: Use ${package} instead of hardcoding package name
|
||||
* doc/tar.texi: Minor fix
|
||||
* src/tar.c (doc0, doc1, initialize_argp_doc): Removed. Rely on
|
||||
the new argp method instead.
|
||||
|
||||
2006-09-12 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* README: Fix a typo
|
||||
|
||||
2006-09-08 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
Adjust to latest gnulib.
|
||||
* lib/.cvsignore: Add at-func.c, configmake.h, fchmodat.c,
|
||||
fcntl.h, fcntl_.h, inttypes_.h, lstat.c, lstat.h.
|
||||
* tests/testsuite.at (AT_TAR_MKHIER): Use install-sh -d
|
||||
rather than the no-longer-included mkinstalldirs.
|
||||
|
||||
2006-09-08 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/incremen.c (try_purge_directory): Initialize struct st. Fix
|
||||
condition for selecting candidates for removal.
|
||||
* README-alpha: List texinfo among build prerequisites for the CVS
|
||||
version.
|
||||
|
||||
* bootstrap (intl_files_to_remove): Do not remove m4/inttypes-h.m4
|
||||
and m4/inttypes-pri.m4
|
||||
|
||||
2006-08-11 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
* bootstrap: Set XGETTEXT_OPTIONS to flag our printf-format functions,
|
||||
so that translators are warned about strings that are formats but
|
||||
don't look like formats. This might help prevent core dumps.
|
||||
* configure.ac (AM_GNU_GETTEXT): Upgrade to need-formatstring-macros.
|
||||
Suggested by Eric Blake to avoid problems like
|
||||
<http://lists.gnu.org/archive/html/bug-coreutils/2006-07/msg00087.html>.
|
||||
(AM_GNU_GETTEXT_VERSION): Upgrade from 0.12.1 to 0.15. The gettext
|
||||
manual says we should use the version number normally expected of
|
||||
maintainers, neither more nor less.
|
||||
|
||||
* bootstrap (get_translations):
|
||||
Don't exclude ky.po; it is working again.
|
||||
|
||||
2006-08-09 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
* bootstrap (get_translations):
|
||||
Don't exclude ja.po; it is working again.
|
||||
Don't remove all old .po files if we're merely updating one.
|
||||
* po/.cvsignore: Add Makevars.
|
||||
|
||||
2006-08-07 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
* src/tar.c: Work around limitation imposed by gettext 0.15
|
||||
by concatenating strings with "\v" after translation.
|
||||
(doc): Remove, splitting into:
|
||||
(doc0, doc1): New constants.
|
||||
(argp): Don't use doc; just initialize to NULL.
|
||||
(initialize_argp_doc): New function.
|
||||
(decode_options): Use it.
|
||||
|
||||
* bootstrap: Redo po file retrieval to match Bison's method,
|
||||
since the translation project changed its index format.
|
||||
Don't use --assume-autoconf; it's now replaced
|
||||
by looking in configure.ac.
|
||||
(m4/codeset.m4, m4/intdiv.m4, m4/inttypes-pri.m4, m4/isc-posix.m4):
|
||||
(m4/lcmessage.m4, m4/onceonly_2_57.m4, m4/gettext.m4, m4/glibc21.m4):
|
||||
(m4/inttypes_h.m4, m4/lib-ld.m4, m4/lib-prefix.m4, m4/po.m4):
|
||||
(m4/stdint_h.m4, m4/uintmax_t.m4, m4/ulonglong.m4):
|
||||
Don't rename to ..._gl.m4, as this is no longer needed.
|
||||
(m4/geteext.m4): Patch to remove the need for intl/*.
|
||||
Use autopoint manually, then remove the intl files,
|
||||
then undo changes to gnulib files that autoreconf made,
|
||||
and then run aclocal/autoconf/autoheader/automake.
|
||||
This makes our bootstrap procedure closer to Bison's.
|
||||
(po/Makevars): Generate automatically.
|
||||
* configure.ac (AC_PREREQ): Update from 2.59 to 2.60.
|
||||
(gl_AC_TYPE_INTMAX_T, jm_AC_TYPE_UINTMAX_T): Remove.
|
||||
* gnulib.modules: Add inttypes, stdint.
|
||||
* lib/.cvsignore: Add close-stream.c, close-stream.h,
|
||||
stdint.h, stdint_.h.
|
||||
* po/Makefile: Remove; now automatically generated.
|
||||
|
||||
* src/incremen.c (read_incr_db_01): Check input strings more
|
||||
carefully. Do not pass arbitrary char values to isspace, as
|
||||
this has undefined behavior. Likewise for assigning arbitrary
|
||||
uintmax_t values to other types.
|
||||
(read_negative_num, read_unsigned_num, read_timespec):
|
||||
New functions, to check input values a bit more carefuly.
|
||||
(read_num): Use read_unsigned_num. New arg MAX_VAL;
|
||||
all callers changed.
|
||||
(read_incr_db_2): Use these new functions.
|
||||
Use a consistent diagnostic for unexpected EOF.
|
||||
(read_directory_file): Do not assign arbitrary uintmax_t value
|
||||
to int.
|
||||
(read_timespec, write_directory_file_entry, write_directory_file):
|
||||
Handle negative time_t values correctly. We don't bother to do
|
||||
this with pre-2 formats, since presumably the time stamps were
|
||||
output incorrectly.
|
||||
|
||||
* src/tar.c (doc): Don't use \v in an argument to gettext;
|
||||
gettext 0.15 rejects this.
|
||||
|
||||
2006-07-24 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/tar.c (decode_options): Do not require -L with -M -Hpax.
|
||||
It could be needed in future, but currently it is not.
|
||||
* src/create.c (to_chars_subst): Update comment regarding OLDGNU
|
||||
vs. GNU format differences.
|
||||
(mode_to_chars): Treat OLDGNU_FORMAT same as GNU_FORMAT. Fixes bug
|
||||
reported by TAMUKI Shoichi <tamuki@linet.gr.jp>, which caused
|
||||
equivalent `tar cf ...' and `tar rf ...' commands to produce
|
||||
different archives.
|
||||
|
||||
* tests/append02.at: New test case
|
||||
* tests/Makefile.am, tests/testsuite.at: Add append02.at
|
||||
|
||||
2006-07-20 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
* bootstrap: Adjust to today's change to gnulib-tool by invoking
|
||||
it with --assume-autoconf='latest-stable'.
|
||||
|
||||
2006-07-16 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
Adjust to recent gnulib changes.
|
||||
* lib/.cvsignore: Remove atexit.c, exit.c, getndelim2.c, getndelim2.h,
|
||||
pathmax.h, paxconvert.c, paxerror.h, xstrdup.c.
|
||||
Add inttypes.h, mktime.c, wcwidth.h, xstrndup.c, xstrndup.h.
|
||||
* src/create.c (start_header): Adjust to API change to mode_adjust.
|
||||
|
||||
2006-07-09 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* THANKS: Add Ralf Wildenhues
|
||||
* NEWS: Update
|
||||
|
||||
2006-07-09 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
|
||||
|
||||
* doc/dumpdir.texi, doc/snapshot.texi, doc/sparse.texi,
|
||||
doc/tar.texi: Fix some typos.
|
||||
|
||||
2006-07-06 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
tar --index-file=FILE --file=- sent the archive to FILE, and
|
||||
the listing to stderr. Bug reported by Marcin Gryszkalis
|
||||
<mg@fork.pl>
|
||||
|
||||
* src/buffer.c (_open_archive): Remove stdlis initialization
|
||||
* src/tar.c (decode_options): Initialize stdlis
|
||||
* tests/indexfile.at: New test case
|
||||
* tests/testsuite.at: Include indexfile.at
|
||||
* tests/Makefile.am (TESTSUITE_AT): Add indexfile.at
|
||||
|
||||
2006-07-05 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* doc/Makefile.am (manual): Fix TEXINPUTS
|
||||
* doc/rendition.texi (FIXME): Do not use deprecated @quote-args.
|
||||
Do not use @allow-recursion. In its current form the macro is not
|
||||
recursive.
|
||||
|
||||
2006-07-03 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* bootstrap (update_po): Fix single translation update
|
||||
|
||||
2006-07-03 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* configure.ac (AC_CHECK_FUNCS): Check for getdtablesize
|
||||
* src/common.h (closeopen): New function
|
||||
* src/misc.c (chdir_arg): Use x2nrealloc to reallocate wd.
|
||||
(get_max_open_files,closeopen): New functions
|
||||
(chdir_do): Do not use save_cwd if it was already used more than
|
||||
max_open_files-4 times to avoid running off the file
|
||||
descriptors.
|
||||
* src/tar.c (main): Call closeopen
|
||||
* doc/tar.texi: Update --directory description
|
||||
* NEWS: Update
|
||||
|
||||
2006-06-26 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* doc/tar.texi: Update. Add cross-references.
|
||||
* doc/sparse.texi: Add cross-references.
|
||||
|
||||
2006-06-25 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* scripts/xsparse.c: A sample utility to expand sparse files
|
||||
extracted by third-party tars. It is not meant to be installed nor
|
||||
to be included in the distribution. It is here, so that it can be
|
||||
obtained either from CVS or from the tar web site.
|
||||
* doc/Makefile.am (tar_TEXINFOS): Sorted
|
||||
* doc/tar.texi (Other Tars): New node describing how to extract
|
||||
GNU-specific member formats using third-party tars.
|
||||
* src/common.h (sparse_file_p): Remove
|
||||
* src/sparse.c: Likewise
|
||||
* src/create.c: Use ST_IS_SPARSE instead of sparse_file_p (update
|
||||
paxutils first)
|
||||
* src/tar.c: --sparse-version turns on --sparse
|
||||
|
||||
2006-06-24 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/buffer.c (print_total_stats): Add default case.
|
||||
* src/common.h (name_init): New prototype.
|
||||
(xheader_string_end): Return bool.
|
||||
* src/extract.c (extract_volhdr): Add missing return
|
||||
* src/incremen.c: Remove not used variables
|
||||
* src/sparse.c (pax_dump_header_0): Return false if
|
||||
xheader_string_end fails (for 0.1 formats).
|
||||
(pax_dump_header): Return meaningful value
|
||||
* src/transform.c (segm_count): Change type to size_t
|
||||
* src/xheader.c (xhdr_tab.decoder): pass keyword as a second
|
||||
argument. All callers changed.
|
||||
(decode_record): Check for numeric overflow
|
||||
(xheader_string_end): Return boolean value. Check for possible
|
||||
numeric overflow
|
||||
|
||||
2006-06-23 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* doc/sparse.texi: New file
|
||||
* doc/Makefile.am (tar_TEXINFOS): Add sparse.texi
|
||||
* doc/intern.texi (Sparse Formats): New node
|
||||
* doc/tar.texi: Update master menu
|
||||
|
||||
* src/common.h (tar_sparse_major,tar_sparse_minor): New globals.
|
||||
* src/sparse.c: Implement sparse format versioning. Implement new
|
||||
version (1.0) of PAX sparse format.
|
||||
(pax_sparse_member_p): Fix condition
|
||||
(pax_dump_header): A dispatcher function
|
||||
(pax_dump_header_0,pax_dump_header_1): New functions.
|
||||
(pax_optab): Update
|
||||
(oldgnu_dump_header): Minor fix: make sure
|
||||
sparse_header.isextended is set before calling
|
||||
set_next_block_after
|
||||
* src/tar.c: New option --sparse-version
|
||||
* src/tar.h (struct tar_stat_info.sparse_major,sparse_minor): New
|
||||
members.
|
||||
* src/xheader.c: Implement new keywords: GNU.sparse.name,
|
||||
GNU.sparse.major, GNU.sparse.minor, GNU.sparse.realsize
|
||||
|
||||
* tests/spmpv00.at, tests/spmvp01.at, tests/spmvp10.at: New
|
||||
testcases.
|
||||
|
||||
* tests/Makefile.am: Add spmvp00.at, spmpv01.at, spmpv10.at
|
||||
* tests/shortrec.at: Explicitely give `-f -' to the tar invocation
|
||||
* tests/sparsemvp.at: Rewritten as an include file.
|
||||
(TAR_MVP_TEST): New macro
|
||||
* tests/testsuite.at: Include sparsemvp.at, spmvp00.at,
|
||||
spmvp01.at, spmvp10.at
|
||||
|
||||
2006-06-21 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* doc/tar.texi (Overriding File Metadata): New node
|
||||
Document --mtime option.
|
||||
* src/common.h (set_mtime_option,mtime_option): New globals
|
||||
* src/create.c (start_header): Override mtime if requested
|
||||
* src/tar.c: Implement new option, --mtime, allowing to set
|
||||
modification times for all archive members during creation.
|
||||
(struct tar_args): textual_date_option replaced with a linked list
|
||||
textual_date. All references updated.
|
||||
(get_date_or_file,report_textual_dates): New functions.
|
||||
* configure.ac: Raise version number to 1.15.92
|
||||
* NEWS: Update
|
||||
|
||||
2006-06-20 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/common.h (struct name.explicit): Remove
|
||||
Use variable names in all declarations
|
||||
(name_scan): Remove second argument
|
||||
* src/delete.c: Remove second argument from calls to name_scan
|
||||
* src/update.c: Likewise
|
||||
* src/incremen.c (procdir): Use is_individual_file to check for
|
||||
files explicitely specified in the command line. Fixes bug
|
||||
reported by Dat Head on 19 Jun 2006 (descending into mountpoints
|
||||
with --one-file-system in use)
|
||||
* src/misc.c (maybe_backup_file): Second argument is bool
|
||||
* src/names.c (name_next_elt): Call register_individual_file
|
||||
(namelist_match): Remove third argument
|
||||
(name_match): Change return type to bool
|
||||
(name_scan): Remove second argument
|
||||
* src/sparse.c (pax_dump_header): Store original sparse file name
|
||||
in GNU.sparse.name variable. The name field in the ustar header
|
||||
now contains generated name (%d/GNUSparseFile.%p/%f), so that
|
||||
non-pax-aware tars won't extract the file under the original
|
||||
filename.
|
||||
* src/xheader.c (GNU.sparse.name): New variable for storing sparse
|
||||
file name.
|
||||
|
||||
2006-06-13 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* doc/Makefile.am (check-options): Expand macros before grepping
|
||||
@@ -18,7 +471,7 @@
|
||||
* doc/Makefile.am (check-options): New goal
|
||||
* doc/tar.texi: Update
|
||||
* src/tar.c: Implement --overwrite-dir option (long ago
|
||||
documented).
|
||||
documented).
|
||||
|
||||
2006-06-12 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -85,7 +538,7 @@
|
||||
argument means "print dots instead of textual checkpoints".
|
||||
(tar_help): New function
|
||||
* src/transform.c: Minor fixes.
|
||||
|
||||
|
||||
2006-06-08 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* gnulib.modules: Add mkdtemp
|
||||
@@ -96,12 +549,12 @@
|
||||
(purge_directory): Re-implement renaming. Introduce
|
||||
'X' control code.
|
||||
(make_tmp_dir_name): Remove
|
||||
|
||||
|
||||
* src/transform.c (set_transform_expr,_transform_name_to_obstack):
|
||||
Implement NUMBER flag.
|
||||
(add_char_segment): Fix length assignement
|
||||
|
||||
* doc/tar.texi: Update
|
||||
* doc/tar.texi: Update
|
||||
|
||||
2006-06-07 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -123,7 +576,7 @@
|
||||
* tests/multiv04.at: Use genfile --files-from
|
||||
|
||||
(Above changes need new genfile.c from paxutils)
|
||||
|
||||
|
||||
* TODO: Update
|
||||
* NEWS: Update
|
||||
* doc/tar.texi: Update
|
||||
@@ -143,7 +596,7 @@
|
||||
2006-05-31 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/incremen.c (make_directory): Fix initialization of struct
|
||||
directory.
|
||||
directory.
|
||||
|
||||
2006-05-25 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -193,7 +646,7 @@
|
||||
* tests/append.at, tests/append01.at, tests/extrac01.at,
|
||||
tests/options.at, tests/options02.at, tests/same-order01.at,
|
||||
tests/same-order02.at: Make AT_SETUP more readable.
|
||||
|
||||
|
||||
2006-05-23 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* src/buffer.c (change_tape_menu): Fix typo (uninitialized
|
||||
@@ -241,12 +694,12 @@
|
||||
(AT_STAR_PREREQ): Provide md5 sum.
|
||||
|
||||
* lib/.cvsignore: Update
|
||||
|
||||
|
||||
2006-05-08 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
* tests/testsuite.at (AT_SORT_PREREQ, AT_UNPRIVILEGED_PREREQ): New
|
||||
|
||||
* tests/testsuite.at (AT_SORT_PREREQ, AT_UNPRIVILEGED_PREREQ): New
|
||||
defines
|
||||
|
||||
|
||||
* tests/extrac04.at, tests/incr03.at, tests/listed02.at,
|
||||
tests/pipe.at, tests/rename01.at, tests/rename02.at,
|
||||
tests/rename03.at, tests/same-order01.at: Call
|
||||
@@ -259,8 +712,8 @@
|
||||
directories. Handle renamed directories more effectively
|
||||
(initial implementation, more updates to follow).
|
||||
Source tree before this point is tagged
|
||||
alpha_1_15_90_incremental_1.
|
||||
|
||||
alpha_1_15_90_incremental_1.
|
||||
|
||||
* NEWS: Update
|
||||
* configure.ac: Raise version number to 1.15.91
|
||||
* src/common.h (rename_directory,append_incremental_renames): New
|
||||
@@ -284,12 +737,12 @@
|
||||
(procdir): Change return type to struct directory. Return
|
||||
immediately if the directory was already initialized. Discover
|
||||
directory renames using directory_meta_table.
|
||||
(append_incremental_renames): New function.
|
||||
(append_incremental_renames): New function.
|
||||
(read_directory_file, write_directory_file): Use new snapshot file
|
||||
format.
|
||||
* src/names.c (collect_and_sort_names): Update dir_contents of the
|
||||
first non-fake name entry when in listed incremental mode.
|
||||
|
||||
|
||||
* tests/incr03.at: New testcase
|
||||
* tests/rename01.at: New testcase
|
||||
* tests/rename02.at: New testcase
|
||||
@@ -311,12 +764,12 @@
|
||||
tests/pipe.at, tests/same-order01.at, tests/same-order02.at,
|
||||
tests/sparse01.at, tests/sparse03.at: Always use genfile --file,
|
||||
this enables extra error checking.
|
||||
|
||||
|
||||
* tests/multiv03.at: Attempt to extract a member with truncated
|
||||
file name from the archive.
|
||||
|
||||
|
||||
* src/buffer.c (_open_archive): Remove unnecessary argument to
|
||||
check_compressed_archive.
|
||||
check_compressed_archive.
|
||||
|
||||
2006-04-25 Sergey Poznyakoff <gray@gnu.org.ua>
|
||||
|
||||
@@ -330,7 +783,7 @@
|
||||
|
||||
* src/extract.c (extract_dir): Fix toggling existing directory
|
||||
permissions (Debian bug #361077). Use parts of patch provided by
|
||||
Ian Jackson <iwj@ubuntu.com>.
|
||||
Ian Jackson <iwj@ubuntu.com>.
|
||||
* src/compare.c: Minor changes
|
||||
* src/incremen.c (directory.new): New member
|
||||
(note_directory,find_directory: Use make_directory to create
|
||||
@@ -338,8 +791,8 @@
|
||||
(procdir): Avoid duplicating directories in the incremental
|
||||
backup map.
|
||||
* tests/Makefile.am (TESTSUITE_AT): Add extrac06.at
|
||||
* tests/testsuite.at: Include extrac06.at
|
||||
|
||||
* tests/testsuite.at: Include extrac06.at
|
||||
|
||||
2006-03-18 Ralf Wildenhues <Ralf.Wildenhues@gmx.de> (trivial change)
|
||||
|
||||
* tests/atlocal.in (PATH): Add build-aux from the source tree,
|
||||
|
||||
@@ -22,3 +22,10 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
EXTRA_DIST = ChangeLog.1 PORTS
|
||||
SUBDIRS = doc lib rmt src scripts po tests
|
||||
|
||||
dist-hook:
|
||||
-rm -f $(distdir).cpio
|
||||
find $(distdir) | cpio -Hcrc -o | \
|
||||
GZIP=$(GZIP_ENV) gzip -c > $(distdir).cpio.gz
|
||||
|
||||
distclean-local:
|
||||
-rm -f $(distdir).cpio.gz
|
||||
|
||||
57
NEWS
57
NEWS
@@ -1,7 +1,46 @@
|
||||
GNU tar NEWS - User visible changes.
|
||||
Please send GNU tar bug reports to <bug-tar@gnu.org>
|
||||
|
||||
version 1.15.91 - Sergey Poznyakoff, (CVS version)
|
||||
version 1.16.1
|
||||
|
||||
* New option --exclude-tag allows to specify "exclusion tag files", i.e.
|
||||
files whose presence in a directory means that the directory should not
|
||||
be archived.
|
||||
|
||||
* The --exclude-cache option excludes directories that contain the
|
||||
CACHEDIR.TAG file from being archived. Previous versions excluded
|
||||
directory contents only, while the directories themselves were
|
||||
still added to the archive.
|
||||
|
||||
* Support for reading ustar type 'N' header logical records has been removed.
|
||||
This GNU extension was generated only by very old versions of GNU 'tar'.
|
||||
Unfortunately its implementation had security holes; see
|
||||
<http://archives.neohapsis.com/archives/fulldisclosure/2006-11/0344.html>.
|
||||
We don't expect that any tar archives in practical use have type 'N'
|
||||
records, but if you have one and you trust its contents, you can
|
||||
decode it with GNU tar 1.16 or earlier.
|
||||
|
||||
* Race conditions have been fixed that in some cases briefly allowed
|
||||
files extracted by 'tar -x --same-owner' (or plain 'tar -x', when
|
||||
running as root) to be accessed by users that they shouldn't have been.
|
||||
|
||||
|
||||
version 1.16 - Sergey Poznyakoff, 2006-10-21
|
||||
|
||||
* After creating an archive, tar exits with code 1 if some files were
|
||||
changed while being read. Previous versions exited with code 2 (fatal
|
||||
error), and only if some files were truncated while being archived.
|
||||
|
||||
* New option --mtime allows to set modification times for all archive
|
||||
members during creation.
|
||||
|
||||
* Bug fixes
|
||||
** Avoid running off file descriptors when using multiple -C options.
|
||||
** tar --index-file=FILE --file=- sent the archive to FILE, and
|
||||
the listing to stderr.
|
||||
|
||||
|
||||
version 1.15.91 - Sergey Poznyakoff, 2006-06-16
|
||||
|
||||
* Incompatible changes
|
||||
|
||||
@@ -19,14 +58,14 @@ no longer uses globbing by default. For example, the above invocation
|
||||
is now interpreted as a request to extract from the archive the file
|
||||
named '*.c'.
|
||||
|
||||
To treat member names as globbing patterns, use --wildcards option.
|
||||
To treat member names as globbing patterns, use --wildcards option.
|
||||
If you wish tar to mimic the behavior of versions up to 1.15.90,
|
||||
add --wildcards to the value of the environment variable TAR_OPTIONS.
|
||||
|
||||
The exact way in which tar interprets member names is controlled by the
|
||||
following command line options:
|
||||
|
||||
--wildcards use wildcards
|
||||
--wildcards use wildcards
|
||||
--anchored patterns match file name start
|
||||
--ignore-case ignore case
|
||||
--wildcards-match-slash wildcards match `/'
|
||||
@@ -34,7 +73,7 @@ following command line options:
|
||||
Each of these options has a '--no-' counterpart that disables its
|
||||
effect (e.g. --no-wildcards).
|
||||
|
||||
These options affect both the interpretation of member names from
|
||||
These options affect both the interpretation of member names from
|
||||
command line and that of the exclusion patterns (given with --exclude
|
||||
and --exclude-from options). The defaults are:
|
||||
|
||||
@@ -50,7 +89,7 @@ case-sensitive matching for the rest of command line, one could write:
|
||||
|
||||
** Short option -l is now an alias of --check-links option, which complies
|
||||
with UNIX98. This ends the transition period started with version 1.14.
|
||||
|
||||
|
||||
* New features
|
||||
|
||||
** New option --transform allows to transform file names before storing them
|
||||
@@ -65,19 +104,19 @@ will add 'prefix/' to all file names stored in foo.tar.
|
||||
versions it worked only with --extract.
|
||||
|
||||
** New option --show-transformed-names enables display of transformed file
|
||||
or archive. It generalizes --show-stored-names option, introduced in
|
||||
1.15.90. In particular, when creating an archive in verbose mode, it lists
|
||||
or archive. It generalizes --show-stored-names option, introduced in
|
||||
1.15.90. In particular, when creating an archive in verbose mode, it lists
|
||||
member names as stored in the archive, i.e., with any eventual prefixes
|
||||
removed and file name transformations applied. The option is useful,
|
||||
for example, while comparing `tar cv' and `tar tv' outputs.
|
||||
|
||||
** New incremental snapshot file format keeps information about file names
|
||||
** New incremental snapshot file format keeps information about file names
|
||||
as well as that about directories.
|
||||
|
||||
** The --checkpoint option takes an optional argument specifying the number
|
||||
of records between the two successive checkpoints. Optional dot
|
||||
starting the argument intructs tar to print dots instead of textual
|
||||
checkpoints.
|
||||
checkpoints.
|
||||
|
||||
** The --totals option can be used with any tar operation (previous versions
|
||||
understood it only with --create). If an argument to this option is
|
||||
|
||||
2
README
2
README
@@ -78,7 +78,7 @@ scripts.
|
||||
|
||||
** `--disable-largefile' omits support for large files, even if the
|
||||
operating system supports large files. Typically, large files are
|
||||
those larger on 2 GB on a 32-bit host.
|
||||
those larger than 2 GB on a 32-bit host.
|
||||
|
||||
* Installation hints
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ that you have the latest stable version.
|
||||
- Gettext <http://www.gnu.org/software/gettext/>
|
||||
- Gzip <http://www.gnu.org/software/gzip/>
|
||||
- M4 <http://www.gnu.org/software/m4/>
|
||||
- Texinfo <http://www.gnu.org/software/texinfo>
|
||||
- Wget <http://www.gnu.org/software/wget/>
|
||||
|
||||
As of this writing, the latest stable version of Gzip is 1.2.4 but we
|
||||
|
||||
70
README-cvs
Normal file
70
README-cvs
Normal file
@@ -0,0 +1,70 @@
|
||||
-*- outline -*-
|
||||
|
||||
These notes intend to help people working on the CVS version of
|
||||
this package.
|
||||
|
||||
* Requirements
|
||||
|
||||
Only the sources are installed in the CVS repository (to ease the
|
||||
maintenance, merges etc.), therefore you will have to get the latest
|
||||
stable versions of the maintainer tools we depend upon, including:
|
||||
|
||||
- Automake <http://www.gnu.org/software/automake/>
|
||||
- Autoconf <http://www.gnu.org/software/autoconf/>
|
||||
- Bison <http://www.gnu.org/software/bison/>
|
||||
- Gettext <http://www.gnu.org/software/gettext/>
|
||||
- Gzip <http://www.gnu.org/software/gzip/>
|
||||
- Tar <http://www.gnu.org/software/tar/>
|
||||
- Wget <http://www.gnu.org/software/wget/>
|
||||
|
||||
As of this writing, the latest stable version of Gzip is 1.2.4 but we
|
||||
suggest using test version 1.3.5 (or later, if one becomes available).
|
||||
|
||||
Valgrind <http://valgrind.org/> is also highly recommended, if
|
||||
Valgrind supports your architecture.
|
||||
|
||||
Only building the initial full source tree will be a bit painful,
|
||||
later, a plain `cvs update -P && make' should be sufficient.
|
||||
|
||||
* First CVS checkout
|
||||
|
||||
Obviously, if you are reading these notes, you did manage to check out
|
||||
this package from CVS. The next step is to get other files needed to
|
||||
build, which are extracted from other source packages:
|
||||
|
||||
$ ./bootstrap
|
||||
|
||||
And there you are! Just
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make check
|
||||
|
||||
At this point, there should be no difference between your local copy,
|
||||
and the CVS master copy:
|
||||
|
||||
$ cvs diff
|
||||
|
||||
should output no difference.
|
||||
|
||||
Enjoy!
|
||||
|
||||
-----
|
||||
|
||||
Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301, USA.
|
||||
2
THANKS
2
THANKS
@@ -269,6 +269,7 @@ Jurgen Botz jbotz@orixa.mtholyoke.edu
|
||||
Jyh-Shyang Wang erik@vsp.ee.nctu.edu.tw
|
||||
Jörg Schilling schilling@fokus.fraunhofer.de
|
||||
Jörg Weule weule@cs.uni-duesseldorf.de
|
||||
Jörg Weilbier gnu@weilbier.net
|
||||
Jörgen Hågg Jorgen.Hagg@axis.se
|
||||
Jörgen Weigert jw@suse.de
|
||||
Jürgen Lüters jlueters@t-online.de
|
||||
@@ -391,6 +392,7 @@ Pierce Cantrell cantrell@ee.tamu.edu
|
||||
R. Kent Dybvig dyb@cadence.bloomington.in.us
|
||||
R. Scott Butler butler@prism.es.dupont.com
|
||||
Rainer Orth ro@TechFak.Uni-Bielefeld.DE
|
||||
Ralf Wildenhues Ralf.Wildenhues@gmx.de
|
||||
Ralf S. Engelschall rse@engelschall.com
|
||||
Ralf Suckow suckow@contrib.de
|
||||
Ralph Corderoy ralph@inputplus.co.uk
|
||||
|
||||
697
bootstrap
697
bootstrap
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Bootstrap 'tar' from CVS.
|
||||
# Bootstrap this package from CVS.
|
||||
|
||||
# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||
|
||||
@@ -21,174 +21,301 @@
|
||||
|
||||
# Written by Paul Eggert and Sergey Poznyakoff.
|
||||
|
||||
# URL of our text domain page in Translation Project
|
||||
TP_URL="http://www.iro.umontreal.ca/translation/maint/tar/"
|
||||
nl='
|
||||
'
|
||||
|
||||
# Ensure file names are sorted consistently across platforms;
|
||||
# e.g., m4/ulonglong_gl.m4 should follow m4/ulonglong.m4.
|
||||
# Ensure file names are sorted consistently across platforms.
|
||||
# Also, ensure diagnostics are in English, e.g., "wget --help" below.
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: $0 [--gnulib-srcdir=DIR][--paxutils-srcdir=DIR][--cvs-auth=AUTH-METHOD][--cvs-user=USERNAME][--no-po]
|
||||
Options are:
|
||||
--paxutils-srcdir=DIRNAME Specify the local directory where paxutils
|
||||
sources reside. Use this if you already
|
||||
have paxutils sources on your machine, and
|
||||
do not want to waste your bandwidth dowloading
|
||||
them again.
|
||||
--gnulib-srcdir=DIRNAME Specify the local directory where gnulib
|
||||
sources reside. Use this if you already
|
||||
have gnulib sources on your machine, and
|
||||
do not want to waste your bandwidth dowloading
|
||||
them again.
|
||||
--cvs-auth=METHOD Set the CVS access method used for downloading
|
||||
gnulib files. METHOD is one of the keywords
|
||||
accepted by cvs -d option (see info cvs
|
||||
repository).
|
||||
--cvs-user=USERNAME Set the CVS username to be used when accessing
|
||||
the gnulib repository.
|
||||
--no-po Do not download po files.
|
||||
--update-po[=LANG] Update po file(s) and exit.
|
||||
echo >&2 "\
|
||||
Usage: $0 [OPTION]...
|
||||
Bootstrap this package from the checked-out sources.
|
||||
|
||||
If the file \`.bootstrap' exists in the current working directory, its
|
||||
contents is read, comments and empty lines removed, shell variables expanded
|
||||
and the result is prepended to the command line options.
|
||||
Options:
|
||||
--paxutils-srcdir=DIRNAME Specify the local directory where paxutils
|
||||
sources reside. Use this if you already
|
||||
have paxutils sources on your machine, and
|
||||
do not want to waste your bandwidth dowloading
|
||||
them again.
|
||||
--gnulib-srcdir=DIRNAME Specify the local directory where gnulib
|
||||
sources reside. Use this if you already
|
||||
have gnulib sources on your machine, and
|
||||
do not want to waste your bandwidth dowloading
|
||||
them again.
|
||||
--copy Copy files instead of creating symbolic links.
|
||||
--force Attempt to bootstrap even if the sources seem
|
||||
not to have been checked out.
|
||||
--skip-po Do not download po files.
|
||||
--update-po[=LANG] Update po file(s) and exit.
|
||||
--cvs-user=USERNAME Set the CVS username to be used when accessing
|
||||
the gnulib repository.
|
||||
|
||||
Running without arguments will suffice in most cases. It is equivalent
|
||||
to
|
||||
If the file bootstrap.conf exists in the current working directory, its
|
||||
contents are read as shell variables to configure the bootstrap.
|
||||
|
||||
./bootstrap --cvs-auth=pserver
|
||||
Local defaults can be provided by placing the file \`.bootstrap' in the
|
||||
current working directory. The file is read after bootstrap.conf, comments
|
||||
and empty lines are removed, shell variables expanded and the result is
|
||||
prepended to the command line options.
|
||||
|
||||
EOF
|
||||
Running without arguments will suffice in most cases.
|
||||
"
|
||||
}
|
||||
|
||||
update_po() {
|
||||
if [ $# = 1 ]; then
|
||||
case $1 in
|
||||
*.po) POFILE=$1;;
|
||||
*) POFILE=${1}.po;;
|
||||
checkout() {
|
||||
if [ ! -d $1 ]; then
|
||||
echo "$0: getting $1 files..."
|
||||
|
||||
case ${CVS_AUTH-pserver} in
|
||||
pserver)
|
||||
CVS_PREFIX=':pserver:anonymous@';;
|
||||
ssh)
|
||||
CVS_PREFIX="$CVS_USER${CVS_USER+@}";;
|
||||
*)
|
||||
echo "$0: $CVS_AUTH: Unknown CVS access method" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
echo "$0: getting translation for $1..."
|
||||
wget -r --cache=off $TP_URL/$POFILE
|
||||
else
|
||||
echo "$0: getting translations into po..."
|
||||
(cd po &&
|
||||
rm -f dummy `ls | sed -n '/\.gmo$/p; /\.po/p'` &&
|
||||
wget -nv -nd -r -l 1 -A .po --cache off $TP_URL &&
|
||||
rm -f index.html index.html.[0-9]*
|
||||
ls *.po | sed 's/\.po$//' >LINGUAS
|
||||
) || exit
|
||||
|
||||
case $CVS_RSH in
|
||||
'') CVS_RSH=ssh; export CVS_RSH;;
|
||||
esac
|
||||
|
||||
trap "cleanup $1" 1 2 13 15
|
||||
|
||||
cvs -z3 -q -d ${CVS_PREFIX}cvs.savannah.gnu.org:/cvsroot/"$1" co $1 ||
|
||||
cleanup $1
|
||||
|
||||
trap - 1 2 13 15
|
||||
fi
|
||||
}
|
||||
|
||||
# Read configuration file
|
||||
cleanup() {
|
||||
status=$?
|
||||
rm -fr $1
|
||||
exit $status
|
||||
}
|
||||
|
||||
# Configuration.
|
||||
|
||||
# List of gnulib modules needed.
|
||||
gnulib_modules=
|
||||
|
||||
# Any gnulib files needed that are not in modules.
|
||||
gnulib_files=
|
||||
|
||||
# Translation Project URL, for the registry of all projects
|
||||
# and for the translation-team master directory.
|
||||
TP_URL='http://www.iro.umontreal.ca/translation/registry.cgi?domain='
|
||||
TP_PO_URL='http://www.iro.umontreal.ca/translation/teams/PO/'
|
||||
|
||||
extract_package_name='
|
||||
/^AC_INIT(/{
|
||||
/.*,.*,.*,/{
|
||||
s///
|
||||
s/[][]//g
|
||||
p
|
||||
q
|
||||
}
|
||||
s/AC_INIT(\[*//
|
||||
s/]*,.*//
|
||||
s/^GNU //
|
||||
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
|
||||
s/[^A-Za-z0-9_]/-/g
|
||||
p
|
||||
}
|
||||
'
|
||||
package=`sed -n "$extract_package_name" configure.ac` || exit
|
||||
|
||||
# Extra files from gnulib, which override files from other sources.
|
||||
gnulib_extra_files='
|
||||
build-aux/announce-gen
|
||||
build-aux/install-sh
|
||||
build-aux/missing
|
||||
build-aux/mdate-sh
|
||||
build-aux/texinfo.tex
|
||||
build-aux/depcomp
|
||||
build-aux/config.guess
|
||||
build-aux/config.sub
|
||||
doc/INSTALL
|
||||
'
|
||||
|
||||
# Other locale categories that need message catalogs.
|
||||
EXTRA_LOCALE_CATEGORIES=
|
||||
|
||||
# Additional xgettext options to use. Use "\\\newline" to break lines.
|
||||
XGETTEXT_OPTIONS='\\\
|
||||
--flag=_:1:pass-c-format\\\
|
||||
--flag=N_:1:pass-c-format\\\
|
||||
--flag=error:3:c-format --flag=error_at_line:5:c-format\\\
|
||||
'
|
||||
|
||||
# Files we don't want to import.
|
||||
excluded_files=
|
||||
|
||||
# File that should exist in the top directory of a checked out hierarchy,
|
||||
# but not in a distribution tarball.
|
||||
CVS_only_file=README-cvs
|
||||
|
||||
# Whether to use copies instead of symlinks.
|
||||
copy=false
|
||||
|
||||
# Override the default configuration, if necessary.
|
||||
test -r bootstrap.conf && . ./bootstrap.conf
|
||||
|
||||
# Read local configuration file
|
||||
if [ -r .bootstrap ]; then
|
||||
echo "$0: Reading configuration file .bootstrap"
|
||||
eval set -- "`sed 's/#.*$//;/^$/d' .bootstrap | tr '\n' ' '` $*"
|
||||
fi
|
||||
|
||||
# Translate configuration into internal form.
|
||||
|
||||
# Parse options.
|
||||
|
||||
DOWNLOAD_PO=yes
|
||||
for option
|
||||
do
|
||||
case $option in
|
||||
--help)
|
||||
usage
|
||||
exit;;
|
||||
--gnulib-srcdir=*)
|
||||
GNULIB_SRCDIR=`expr "$option" : '--gnulib-srcdir=\(.*\)'`;;
|
||||
--paxutils-srcdir=*)
|
||||
PAXUTILS_SRCDIR=`expr "$option" : '--paxutils-srcdir=\(.*\)'`;;
|
||||
--cvs-auth=*)
|
||||
CVS_AUTH=`expr "$option" : '--cvs-auth=\(.*\)'`;;
|
||||
--gnulib-srcdir=*)
|
||||
GNULIB_SRCDIR=`expr "$option" : '--gnulib-srcdir=\(.*\)'`;;
|
||||
--cvs-user=*)
|
||||
CVS_USER=`expr "$option" : '--cvs-user=\(.*\)'`;;
|
||||
--no-po)
|
||||
DOWNLOAD_PO=no;;
|
||||
--skip-po | --no-po) # --no-po is for compatibility with 'tar' tradition.
|
||||
DOWNLOAD_PO=skip;;
|
||||
--update-po=*)
|
||||
DOWNLOAD_PO=`expr "$option" : '--update-po=\(.*\)'`;;
|
||||
--update-po)
|
||||
DOWNLOAD_PO=only;;
|
||||
--force)
|
||||
CVS_only_file=;;
|
||||
--copy)
|
||||
copy=true;;
|
||||
*)
|
||||
echo >&2 "$0: $option: unknown option"
|
||||
exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
case $DOWNLOAD_PO in
|
||||
only) update_po
|
||||
exit 0
|
||||
;;
|
||||
no|yes) ;;
|
||||
*) update_po $DOWNLOAD_PO
|
||||
exit 0
|
||||
if test -n "$CVS_only_file" && test ! -r "$CVS_only_file"; then
|
||||
echo "$0: Bootstrapping from a non-checked-out distribution is risky." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$0: Bootstrapping CVS $package..."
|
||||
|
||||
# Get translations.
|
||||
|
||||
get_translations() {
|
||||
subdir=$1
|
||||
domain=$2
|
||||
po_file=$3
|
||||
|
||||
case $WGET_COMMAND in
|
||||
'')
|
||||
echo "$0: wget not available; skipping translations";;
|
||||
?*)
|
||||
echo "$0: getting ${po_file:-translations} into $subdir for $domain..." &&
|
||||
case $po_file in
|
||||
'') (cd $subdir && rm -f dummy `ls | sed -n '/\.gmo$/p; /\.po/p'`);;
|
||||
esac &&
|
||||
|
||||
$WGET_COMMAND -O "$subdir/$domain.html" "$TP_URL$domain" &&
|
||||
|
||||
sed -n 's|.*"http://[^"]*/translation/teams/PO/\([^/"]*\)/'"$domain"'-\([^/"]*\)\.[^."]*\.po".*|\1.\2|p' <"$subdir/$domain.html" |
|
||||
sort -k 1,1 -k 2,2n -k2,2 -k3,3n -k3,3 -k4,4n -k4,4 -k5,5n -k5.5 |
|
||||
awk -F. '
|
||||
{ if (lang && $1 != lang) print lang, ver }
|
||||
{ lang = $1; ver = substr($0, index($0, ".") + 1) }
|
||||
END { if (lang) print lang, ver }
|
||||
' | awk -v domain="$domain" -v po_file="$po_file" -v subdir="$subdir" '
|
||||
{
|
||||
lang = $1
|
||||
if (po_file && po_file != (lang ".po")) next
|
||||
|
||||
ver = $2
|
||||
urlfmt = ""
|
||||
printf "{ $WGET_COMMAND -O %s/%s.po '\'"$TP_PO_URL"'/%s/%s-%s.%s.po'\'' &&\n", subdir, lang, lang, domain, ver, lang
|
||||
printf " msgfmt -c -o /dev/null %s/%s.po || {\n", subdir, lang
|
||||
printf " echo >&2 '\'"$0"': omitting translation for %s'\''\n", lang
|
||||
printf " rm -f %s/%s.po; }; } &&\n", subdir, lang
|
||||
}
|
||||
END { print ":" }
|
||||
' | WGET_COMMAND="$WGET_COMMAND" sh;;
|
||||
esac &&
|
||||
ls "$subdir"/*.po 2>/dev/null |
|
||||
sed 's|.*/||; s|\.po$||' >"$subdir/LINGUAS" &&
|
||||
rm -f "$subdir/$domain.html"
|
||||
}
|
||||
|
||||
case `wget --help` in
|
||||
*'--no-cache'*)
|
||||
WGET_COMMAND='wget -nv --no-cache';;
|
||||
*'--cache=on/off'*)
|
||||
WGET_COMMAND='wget -nv --cache=off';;
|
||||
*'--non-verbose'*)
|
||||
WGET_COMMAND='wget -nv';;
|
||||
*)
|
||||
WGET_COMMAND='';;
|
||||
esac
|
||||
|
||||
echo "$0: Bootstrapping CVS tar..."
|
||||
case $DOWNLOAD_PO in
|
||||
'skip')
|
||||
;;
|
||||
'')
|
||||
get_translations po $package || exit
|
||||
;;
|
||||
'only')
|
||||
get_translations po $package
|
||||
exit
|
||||
;;
|
||||
*.po)
|
||||
get_translations po $package "$DOWNLOAD_PO"
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
get_translations po $package "${DOWNLOAD_PO}.po"
|
||||
exit
|
||||
esac
|
||||
|
||||
build_cvs_prefix() {
|
||||
CVS_PREFIX=:${1}:
|
||||
if [ "${2}" != - ]; then
|
||||
CVS_PREFIX=${CVS_PREFIX}${2}@
|
||||
fi
|
||||
if [ "$1" = "ext" ]; then
|
||||
if [ -z "${CVS_RSH}" ]; then
|
||||
CVS_RSH=ssh
|
||||
export CVS_RSH
|
||||
fi
|
||||
fi
|
||||
}
|
||||
# Get paxutils files.
|
||||
|
||||
# checkout package
|
||||
checkout() {
|
||||
if [ ! -d $1 ]; then
|
||||
echo "$0: getting $1 files..."
|
||||
|
||||
trap exit 1 2 13 15
|
||||
trap 'rm -fr $1; exit 1' 0
|
||||
|
||||
case "${CVS_AUTH-pserver}" in
|
||||
pserver) build_cvs_prefix pserver ${CVS_USER:-anonymous}
|
||||
;;
|
||||
gserver|server)
|
||||
build_cvs_prefix $CVS_AUTH ${CVS_USER--}
|
||||
;;
|
||||
ext) build_cvs_prefix $CVS_AUTH ${CVS_USER--}
|
||||
;;
|
||||
*) echo "$0: Unknown CVS access method" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
cvs -q -d ${CVS_PREFIX}cvs.sv.gnu.org:/cvsroot/$1 co $1 || exit
|
||||
|
||||
trap - 0
|
||||
fi
|
||||
}
|
||||
|
||||
gnulib_modules=
|
||||
newline='
|
||||
'
|
||||
|
||||
get_modules() {
|
||||
new_gnulib_modules=`sed '/^[ ]*#/d; /^[ ]*$/d' $*`
|
||||
case $gnulib_modules,$new_gnulib_modules in
|
||||
?*,?*) new_gnulib_modules=$newline$new_gnulib_modules;;
|
||||
esac
|
||||
gnulib_modules=$gnulib_modules$new_gnulib_modules
|
||||
}
|
||||
|
||||
# Get paxutils files
|
||||
case ${PAXUTILS_SRCDIR--} in
|
||||
-) checkout paxutils
|
||||
PAXUTILS_SRCDIR=paxutils
|
||||
esac
|
||||
|
||||
if [ -r $PAXUTILS_SRCDIR/gnulib.modules ]; then
|
||||
get_modules $PAXUTILS_SRCDIR/gnulib.modules
|
||||
gnulib_modules=`
|
||||
(echo "$gnulib_modules"; grep '^[^#]' $PAXUTILS_SRCDIR/gnulib.modules) |
|
||||
sort -u
|
||||
`
|
||||
fi
|
||||
|
||||
ignore_file_list=
|
||||
cleanup_ifl() {
|
||||
test -n "$ignore_file_list" && rm -f $ignore_file_list
|
||||
}
|
||||
|
||||
trap 'cleanup_ifl' 1 2 3 15
|
||||
|
||||
# ignorefile DIR FILE
|
||||
# add FILE to the temporary ignorelist in the directory DIR
|
||||
ignorefile() {
|
||||
file=$1/.ignore.$$
|
||||
echo "$2" >> $file
|
||||
if `echo $ignore_list | grep -qv $file`; then
|
||||
ignore_file_list="$ignore_file_list
|
||||
$file"
|
||||
fi
|
||||
}
|
||||
|
||||
# copy_files srcdir dstdir
|
||||
copy_files() {
|
||||
for file in `cat $1/DISTFILES`
|
||||
@@ -205,6 +332,7 @@ copy_files() {
|
||||
fi
|
||||
echo "$0: Copying file $1/$file to $2/$dst"
|
||||
cp -p $1/$file $2/$dst
|
||||
ignorefile $2 $dst
|
||||
done
|
||||
}
|
||||
|
||||
@@ -212,9 +340,10 @@ copy_files ${PAXUTILS_SRCDIR}/m4 m4
|
||||
echo "$0: Creating m4/paxutils.m4"
|
||||
(echo "# This file is generated automatically. Please, do not edit."
|
||||
echo "#"
|
||||
echo "AC_DEFUN([tar_PAXUTILS],["
|
||||
echo "AC_DEFUN([${package}_PAXUTILS],["
|
||||
cat ${PAXUTILS_SRCDIR}/m4/DISTFILES | sed '/^#/d;s/\(.*\)\.m4/pu_\1/' | tr a-z A-Z
|
||||
echo "])") > ./m4/paxutils.m4
|
||||
ignorefile m4 paxutils.m4
|
||||
|
||||
if [ -d rmt ]; then
|
||||
:
|
||||
@@ -232,86 +361,288 @@ copy_files ${PAXUTILS_SRCDIR}/paxlib lib pax
|
||||
# Get gnulib files.
|
||||
|
||||
case ${GNULIB_SRCDIR--} in
|
||||
-) checkout gnulib
|
||||
GNULIB_SRCDIR=gnulib
|
||||
-)
|
||||
checkout gnulib
|
||||
GNULIB_SRCDIR=gnulib
|
||||
esac
|
||||
|
||||
<$GNULIB_SRCDIR/gnulib-tool || exit
|
||||
gnulib_tool=$GNULIB_SRCDIR/gnulib-tool
|
||||
<$gnulib_tool || exit
|
||||
|
||||
get_modules gnulib.modules
|
||||
symlink_to_gnulib()
|
||||
{
|
||||
src=$GNULIB_SRCDIR/$1
|
||||
dst=${2-$1}
|
||||
|
||||
gnulib_modules=`echo "$gnulib_modules" | sort -u`
|
||||
previous_gnulib_modules=
|
||||
while [ "$gnulib_modules" != "$previous_gnulib_modules" ]; do
|
||||
previous_gnulib_modules=$gnulib_modules
|
||||
gnulib_modules=`
|
||||
(echo "$gnulib_modules"
|
||||
for gnulib_module in $gnulib_modules; do
|
||||
$GNULIB_SRCDIR/gnulib-tool --extract-dependencies $gnulib_module
|
||||
done) | sort -u
|
||||
`
|
||||
test -f "$src" && {
|
||||
if $copy; then
|
||||
{
|
||||
test ! -h "$dst" || {
|
||||
echo "$0: rm -f $dst" &&
|
||||
rm -f "$dst"
|
||||
}
|
||||
} &&
|
||||
test -f "$dst" &&
|
||||
cmp -s "$src" "$dst" || {
|
||||
echo "$0: cp -fp $src $dst" &&
|
||||
cp -fp "$src" "$dst"
|
||||
}
|
||||
else
|
||||
test -h "$dst" &&
|
||||
src_ls=`ls -diL "$src" 2>/dev/null` && set $src_ls && src_i=$1 &&
|
||||
dst_ls=`ls -diL "$dst" 2>/dev/null` && set $dst_ls && dst_i=$1 &&
|
||||
test "$src_i" = "$dst_i" || {
|
||||
dot_dots=
|
||||
case $src in
|
||||
/*) ;;
|
||||
*)
|
||||
case /$dst/ in
|
||||
*//* | */../* | */./* | /*/*/*/*/*/)
|
||||
echo >&2 "$0: invalid symlink calculation: $src -> $dst"
|
||||
exit 1;;
|
||||
/*/*/*/*/) dot_dots=../../../;;
|
||||
/*/*/*/) dot_dots=../../;;
|
||||
/*/*/) dot_dots=../;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
echo "$0: ln -fs $dot_dots$src $dst" &&
|
||||
ln -fs "$dot_dots$src" "$dst"
|
||||
}
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
cp_mark_as_generated()
|
||||
{
|
||||
cp_src=$1
|
||||
cp_dst=$2
|
||||
|
||||
if cmp -s "$cp_src" "$GNULIB_SRCDIR/$cp_dst"; then
|
||||
symlink_to_gnulib "$cp_dst"
|
||||
else
|
||||
case $cp_dst in
|
||||
*.[ch]) c1='/* '; c2=' */';;
|
||||
*.texi) c1='@c '; c2= ;;
|
||||
*.m4|*/Make*|Make*) c1='# ' ; c2= ;;
|
||||
*) c1= ; c2= ;;
|
||||
esac
|
||||
|
||||
if test -z "$c1"; then
|
||||
cmp -s "$cp_src" "$cp_dst" || {
|
||||
echo "$0: cp -f $cp_src $cp_dst" &&
|
||||
cp -f "$cp_src" "$cp_dst"
|
||||
}
|
||||
else
|
||||
# Copy the file first to get proper permissions if it
|
||||
# doesn't already exist. Then overwrite the copy.
|
||||
cp "$cp_src" "$cp_dst-t" &&
|
||||
(
|
||||
echo "$c1-*- buffer-read-only: t -*- vi: set ro:$c2" &&
|
||||
echo "${c1}DO NOT EDIT! GENERATED AUTOMATICALLY!$c2" &&
|
||||
cat "$cp_src"
|
||||
) > $cp_dst-t &&
|
||||
if cmp -s "$cp_dst-t" "$cp_dst"; then
|
||||
rm -f "$cp_dst-t"
|
||||
else
|
||||
echo "$0: cp $cp_src $cp_dst # with edits" &&
|
||||
mv -f "$cp_dst-t" "$cp_dst"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
version_controlled_file() {
|
||||
dir=$1
|
||||
file=$2
|
||||
found=no
|
||||
if test -d CVS; then
|
||||
grep -F "/$file/" $dir/CVS/Entries 2>/dev/null |
|
||||
grep '^/[^/]*/[0-9]' > /dev/null && found=yes
|
||||
elif test -d .git; then
|
||||
git-rm -n "$dir/$file" > /dev/null 2>&1 && found=yes
|
||||
else
|
||||
echo "$0: no version control for $dir/$file?" >&2
|
||||
fi
|
||||
test $found = yes
|
||||
}
|
||||
|
||||
slurp() {
|
||||
for dir in . `(cd $1 && find * -type d -print)`; do
|
||||
copied=
|
||||
sep=
|
||||
for file in `ls $1/$dir`; do
|
||||
test -d $1/$dir/$file && continue
|
||||
for excluded_file in $excluded_files; do
|
||||
test "$dir/$file" = "$excluded_file" && continue 2
|
||||
done
|
||||
if test $file = Makefile.am; then
|
||||
copied=$copied${sep}gnulib.mk; sep=$nl
|
||||
remove_intl='/^[^#].*\/intl/s/^/#/'
|
||||
sed "$remove_intl" $1/$dir/$file | cmp -s - $dir/gnulib.mk || {
|
||||
echo "$0: Copying $1/$dir/$file to $dir/gnulib.mk ..." &&
|
||||
rm -f $dir/gnulib.mk &&
|
||||
sed "$remove_intl" $1/$dir/$file >$dir/gnulib.mk
|
||||
}
|
||||
elif { test "${2+set}" = set && test -r $2/$dir/$file; } ||
|
||||
version_controlled_file $dir $file; then
|
||||
echo "$0: $dir/$file overrides $1/$dir/$file"
|
||||
else
|
||||
copied=$copied$sep$file; sep=$nl
|
||||
if test $file = gettext.m4; then
|
||||
echo "$0: patching m4/gettext.m4 to remove need for intl/* ..."
|
||||
rm -f $dir/$file
|
||||
sed '
|
||||
/^AC_DEFUN(\[AM_INTL_SUBDIR],/,/^]/c\
|
||||
AC_DEFUN([AM_INTL_SUBDIR], [
|
||||
/^AC_DEFUN(\[gt_INTL_SUBDIR_CORE],/,/^]/c\
|
||||
AC_DEFUN([gt_INTL_SUBDIR_CORE], [])
|
||||
$a\
|
||||
AC_DEFUN([gl_LOCK_EARLY], [])
|
||||
' $1/$dir/$file >$dir/$file
|
||||
else
|
||||
cp_mark_as_generated $1/$dir/$file $dir/$file
|
||||
fi
|
||||
fi || exit
|
||||
done
|
||||
|
||||
if test -n "$copied"; then
|
||||
copied="Makefile
|
||||
Makefile.in
|
||||
$copied"
|
||||
if test -d CVS; then
|
||||
dot_ig=.cvsignore
|
||||
else
|
||||
dor_ig=.gitignore
|
||||
fi
|
||||
|
||||
ig=$dir/$dot_ig
|
||||
if [ -f $dir/.ignore.$$ ]; then
|
||||
tfile=$dir/.ignore.$$
|
||||
else
|
||||
tfile=
|
||||
fi
|
||||
if test -f $ig; then
|
||||
echo "$copied" | sort -u - $ig | cmp -s - $ig ||
|
||||
echo "$copied" | sort -u - $ig $tfile -o $ig
|
||||
else
|
||||
copied="$dot_ig
|
||||
$copied"
|
||||
if [ "$dir" = "po" ]; then
|
||||
copied="LINGUAS
|
||||
Makevars
|
||||
POTFILES
|
||||
*.mo
|
||||
*.gmo
|
||||
*.po
|
||||
remove-potcdate.sed
|
||||
stamp-po
|
||||
$package.pot
|
||||
$copied"
|
||||
fi
|
||||
echo "$copied" | sort -u - $tfile -o $ig
|
||||
fi || exit
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Create boot temporary directories to import from gnulib and gettext.
|
||||
|
||||
bt='.#bootmp'
|
||||
bt2=${bt}2
|
||||
rm -fr $bt $bt2 &&
|
||||
mkdir $bt $bt2 || exit
|
||||
|
||||
# Import from gnulib.
|
||||
|
||||
test -d build-aux || {
|
||||
echo "$0: mkdir build-aux ..." &&
|
||||
mkdir build-aux
|
||||
} || exit
|
||||
gnulib_tool_options="\
|
||||
--import\
|
||||
--no-changelog\
|
||||
--aux-dir $bt/build-aux\
|
||||
--doc-base $bt/doc\
|
||||
--lib lib$package\
|
||||
--m4-base $bt/m4/\
|
||||
--source-base $bt/lib/\
|
||||
--tests-base $bt/tests\
|
||||
--local-dir gl\
|
||||
"
|
||||
echo "$0: $gnulib_tool $gnulib_tool_options --import ..."
|
||||
$gnulib_tool $gnulib_tool_options --import $gnulib_modules &&
|
||||
slurp $bt || exit
|
||||
|
||||
for file in $gnulib_files; do
|
||||
symlink_to_gnulib $file || exit
|
||||
done
|
||||
|
||||
gnulib_files=`
|
||||
(for gnulib_module in $gnulib_modules; do
|
||||
$GNULIB_SRCDIR/gnulib-tool --extract-filelist $gnulib_module
|
||||
done) | sort -u
|
||||
`
|
||||
|
||||
gnulib_dirs=`echo "$gnulib_files" | sed 's,/[^/]*$,,' | sort -u`
|
||||
mkdir -p $gnulib_dirs || exit
|
||||
# Import from gettext.
|
||||
|
||||
for gnulib_file in $gnulib_files; do
|
||||
dest=$gnulib_file
|
||||
echo "$0: (cd $bt2; autopoint) ..."
|
||||
cp configure.ac $bt2 &&
|
||||
(cd $bt2 && autopoint && rm configure.ac) &&
|
||||
slurp $bt2 $bt || exit
|
||||
|
||||
case $gnulib_file in
|
||||
m4/codeset.m4) continue;;
|
||||
m4/intdiv0.m4) continue;;
|
||||
m4/inttypes-pri.m4) continue;;
|
||||
m4/isc-posix.m4) continue;;
|
||||
m4/lcmessage.m4) continue;;
|
||||
m4/onceonly_2_57.m4) dest=m4/onceonly.m4;;
|
||||
# These will be overwritten by autopoint, which still uses
|
||||
# old jm_.* macro names, so we have to keep both copies.
|
||||
m4/gettext.m4 | m4/glibc21.m4 | m4/inttypes_h.m4 | m4/lib-ld.m4 | \
|
||||
m4/lib-prefix.m4 | m4/po.m4 | m4/stdint_h.m4 | m4/uintmax_t.m4 | \
|
||||
m4/ulonglong.m4)
|
||||
dest=`expr $gnulib_file : '\(.*\).m4'`_gl.m4;;
|
||||
esac
|
||||
rm -fr $bt $bt2 || exit
|
||||
|
||||
rm -f $dest &&
|
||||
echo "$0: Copying file $GNULIB_SRCDIR/$gnulib_file" &&
|
||||
cp -p $GNULIB_SRCDIR/$gnulib_file $dest || exit
|
||||
done
|
||||
|
||||
echo "$0: Creating m4/gnulib.m4"
|
||||
(echo "# This file is generated automatically. Please, do not edit."
|
||||
echo "#"
|
||||
echo "AC_DEFUN([tar_GNULIB],["
|
||||
for gnulib_module in $gnulib_modules; do
|
||||
echo "# $gnulib_module"
|
||||
$GNULIB_SRCDIR/gnulib-tool --extract-autoconf-snippet $gnulib_module
|
||||
done | sed '/AM_GNU_GETTEXT/d'
|
||||
echo "])") > ./m4/gnulib.m4
|
||||
|
||||
echo "$0: Creating lib/Makefile.am"
|
||||
(echo "# This file is generated automatically. Do not edit!"
|
||||
cat lib/Makefile.tmpl
|
||||
|
||||
for gnulib_module in $gnulib_modules; do
|
||||
echo "# $gnulib_module"
|
||||
$GNULIB_SRCDIR/gnulib-tool --extract-automake-snippet $gnulib_module
|
||||
done | sed 's/lib_SOURCES/libtar_a_SOURCES/g' ) > lib/Makefile.am
|
||||
|
||||
# Get translations.
|
||||
if test "$DOWNLOAD_PO" = "yes"; then
|
||||
update_po
|
||||
fi
|
||||
|
||||
# Reconfigure, getting other files.
|
||||
|
||||
echo "$0: autoreconf --verbose --install --force ..."
|
||||
autoreconf --verbose --install --force || exit 1
|
||||
for command in \
|
||||
'aclocal --force -I m4' \
|
||||
'autoconf --force' \
|
||||
'autoheader --force' \
|
||||
'automake --add-missing --copy --force-missing';
|
||||
do
|
||||
echo "$0: $command ..."
|
||||
$command || exit
|
||||
done
|
||||
|
||||
|
||||
# Get some extra files from gnulib, overriding existing files.
|
||||
|
||||
for file in $gnulib_extra_files; do
|
||||
case $file in
|
||||
*/INSTALL) dst=INSTALL;;
|
||||
*) dst=$file;;
|
||||
esac
|
||||
symlink_to_gnulib $file $dst || exit
|
||||
done
|
||||
|
||||
|
||||
# Create gettext configuration.
|
||||
echo "$0: Creating po/Makevars from po/Makevars.template ..."
|
||||
rm -f po/Makevars
|
||||
sed '
|
||||
/^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/
|
||||
/^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/
|
||||
/^XGETTEXT_OPTIONS *=/{
|
||||
s/$/ \\/
|
||||
a\
|
||||
'"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+}
|
||||
}
|
||||
' po/Makevars.template >po/Makevars
|
||||
|
||||
if test -d runtime-po; then
|
||||
# Similarly for runtime-po/Makevars, but not quite the same.
|
||||
rm -f runtime-po/Makevars
|
||||
sed '
|
||||
/^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/
|
||||
/^subdir *=.*/s/=.*/= runtime-po/
|
||||
/^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/
|
||||
/^XGETTEXT_OPTIONS *=/{
|
||||
s/$/ \\/
|
||||
a\
|
||||
'"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+}
|
||||
}
|
||||
' <po/Makevars.template >runtime-po/Makevars
|
||||
|
||||
# Copy identical files from po to runtime-po.
|
||||
(cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po)
|
||||
fi
|
||||
cleanup_ifl
|
||||
echo "$0: done. Now you can run './configure'."
|
||||
|
||||
61
bootstrap.conf
Normal file
61
bootstrap.conf
Normal file
@@ -0,0 +1,61 @@
|
||||
# Bootstrap configuration.
|
||||
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
|
||||
# We don't need these modules, even though gnulib-tool mistakenly
|
||||
# includes them because of gettext dependencies.
|
||||
avoided_gnulib_modules='
|
||||
--avoid=lock
|
||||
--avoid=size_max
|
||||
--avoid=xsize
|
||||
'
|
||||
|
||||
# gnulib modules used by this package.
|
||||
gnulib_modules="$avoided_gnulib_modules
|
||||
`grep '^[^#]' gnulib.modules`
|
||||
"
|
||||
|
||||
# Additional xgettext options to use. Use "\\\newline" to break lines.
|
||||
XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\
|
||||
--flag=_:1:pass-c-format\\\
|
||||
--flag=N_:1:pass-c-format\\\
|
||||
--flag=error:3:c-format --flag=error_at_line:5:c-format\\\
|
||||
--flag=asnprintf:3:c-format --flag=vasnprintf:3:c-format\\\
|
||||
--flag=argp_error:2:c-format\\\
|
||||
--flag=__argp_error:2:c-format\\\
|
||||
--flag=argp_failure:4:c-format\\\
|
||||
--flag=__argp_failure:4:c-format\\\
|
||||
--flag=argp_fmtstream_printf:2:c-format\\\
|
||||
--flag=__argp_fmtstream_printf:2:c-format\\\
|
||||
'
|
||||
|
||||
# Gettext supplies these files, but we don't need them since
|
||||
# we don't have an intl subdirectory.
|
||||
excluded_files='
|
||||
m4/glibc2.m4
|
||||
m4/intdiv0.m4
|
||||
m4/lcmessage.m4
|
||||
m4/lock.m4
|
||||
m4/printf-posix.m4
|
||||
m4/size_max.m4
|
||||
m4/uintmax_t.m4
|
||||
m4/ulonglong.m4
|
||||
m4/visibility.m4
|
||||
m4/xsize.m4
|
||||
'
|
||||
18
configure.ac
18
configure.ac
@@ -18,18 +18,19 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
AC_INIT([GNU tar], [1.15.91], [bug-tar@gnu.org])
|
||||
AC_INIT([GNU tar], [1.16.1], [bug-tar@gnu.org])
|
||||
AC_CONFIG_SRCDIR([src/tar.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_HEADERS([config.h:config.hin])
|
||||
AC_PREREQ([2.59])
|
||||
AC_PREREQ([2.60])
|
||||
AM_INIT_AUTOMAKE([1.9 gnits tar-ustar dist-bzip2 dist-shar std-options])
|
||||
|
||||
gl_USE_SYSTEM_EXTENSIONS
|
||||
AC_PROG_CC
|
||||
AC_EXEEXT
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_YACC
|
||||
gl_EARLY
|
||||
|
||||
AC_SYS_LARGEFILE
|
||||
AC_ISC_POSIX
|
||||
AC_C_INLINE
|
||||
@@ -80,15 +81,13 @@ AC_CHECK_TYPE(dev_t, unsigned)
|
||||
AC_CHECK_TYPE(ino_t, unsigned)
|
||||
|
||||
gt_TYPE_SSIZE_T
|
||||
gl_AC_TYPE_INTMAX_T
|
||||
jm_AC_TYPE_UINTMAX_T
|
||||
|
||||
# gnulib modules
|
||||
tar_GNULIB
|
||||
gl_INIT
|
||||
# paxutils modules
|
||||
tar_PAXUTILS
|
||||
|
||||
AC_CHECK_FUNCS(fsync lstat mkfifo readlink strerror symlink setlocale utimes)
|
||||
AC_CHECK_FUNCS(fsync getdtablesize lstat mkfifo readlink strerror symlink setlocale utimes)
|
||||
AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
|
||||
AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
|
||||
AC_CHECK_DECLS([time],,, [#include <time.h>])
|
||||
@@ -214,8 +213,8 @@ AC_CHECK_TYPE(iconv_t,:,
|
||||
])
|
||||
|
||||
# Gettext.
|
||||
AM_GNU_GETTEXT([external], [need-ngettext])
|
||||
AM_GNU_GETTEXT_VERSION(0.12.1)
|
||||
AM_GNU_GETTEXT([external], [need-formatstring-macros])
|
||||
AM_GNU_GETTEXT_VERSION([0.16])
|
||||
|
||||
# Initialize the test suite.
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
@@ -240,7 +239,6 @@ else
|
||||
BACKUP_SED_COND='/^\#IF_DATE_FORMAT_OK/,/^\#ELSE_DATE_FORMAT_OK/d;/^\#ENDIF_DATE_FORMAT_OK/d'
|
||||
fi
|
||||
|
||||
|
||||
AC_OUTPUT([Makefile\
|
||||
doc/Makefile\
|
||||
lib/Makefile\
|
||||
|
||||
@@ -24,10 +24,11 @@ tar_TEXINFOS = \
|
||||
freemanuals.texi\
|
||||
genfile.texi\
|
||||
getdate.texi\
|
||||
intern.texi\
|
||||
header.texi\
|
||||
intern.texi\
|
||||
rendition.texi\
|
||||
snapshot.texi\
|
||||
sparse.texi\
|
||||
value.texi
|
||||
EXTRA_DIST = gendocs_template mastermenu.el texify.sed
|
||||
DISTCLEANFILES=*.info*
|
||||
@@ -78,7 +79,7 @@ TEXI2DVI=texi2dvi -t '@set $(RENDITION)' -E
|
||||
# Usual value is:
|
||||
# /usr/share/texmf/pdftex/plain/misc:/usr/share/texmf/pdftex/config
|
||||
manual:
|
||||
TEXINPUTS=$(srcdir):$(top_srcdir)/config:$(TEXINPUTS) \
|
||||
TEXINPUTS=$(srcdir):$(top_srcdir)/build-tex:$(TEXINPUTS) \
|
||||
MAKEINFO="$(MAKEINFO) $(MAKEINFOFLAGS)" \
|
||||
TEXI2DVI="$(TEXI2DVI) -t @finalout" \
|
||||
$(GENDOCS) tar 'GNU tar manual'
|
||||
|
||||
@@ -125,7 +125,7 @@ corresponding directory, in variable @code{GNU.dumpdir}.
|
||||
@item GNU and old GNU archives
|
||||
|
||||
These formats implement special header type @samp{D}, which is similar
|
||||
to ustar header @samp{5} (directory), except that it preceeds a data
|
||||
to ustar header @samp{5} (directory), except that it precedes a data
|
||||
block containing the dumpdir.
|
||||
@end itemize
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
@menu
|
||||
* Standard:: Basic Tar Format
|
||||
* Extensions:: @acronym{GNU} Extensions to the Archive Format
|
||||
* Sparse Formats:: Storing Sparse Files
|
||||
* Snapshot Files::
|
||||
* Dumpdir::
|
||||
@end menu
|
||||
@@ -320,6 +321,10 @@ use the @acronym{GNU}-added fields of the header, other versions of
|
||||
@command{tar} program will give an error, the most likely one being a
|
||||
checksum error.
|
||||
|
||||
@node Sparse Formats
|
||||
@unnumberedsec Storing Sparse Files
|
||||
@include sparse.texi
|
||||
|
||||
@node Snapshot Files
|
||||
@unnumberedsec Format of the Incremental Snapshot Files
|
||||
@include snapshot.texi
|
||||
@@ -327,3 +332,4 @@ checksum error.
|
||||
@node Dumpdir
|
||||
@unnumberedsec Dumpdir
|
||||
@include dumpdir.texi
|
||||
|
||||
|
||||
@@ -55,8 +55,6 @@
|
||||
@c Output various FIXME information only in PROOF rendition.
|
||||
|
||||
@macro FIXME{string}
|
||||
@allow-recursion
|
||||
@quote-arg
|
||||
@ifset PROOF
|
||||
@ifset PROOF_FOOTNOTED
|
||||
@footnote{@strong{FIXME:} \string\}
|
||||
@@ -71,14 +69,12 @@
|
||||
@end macro
|
||||
|
||||
@macro FIXME-ref{string}
|
||||
@quote-arg
|
||||
@ifset PROOF
|
||||
@strong{<REF>} \string\ @strong{</>}
|
||||
@end ifset
|
||||
@end macro
|
||||
|
||||
@macro FIXME-pxref{string}
|
||||
@quote-arg
|
||||
@ifset PROOF
|
||||
@strong{<PXREF>} \string\ @strong{</>}
|
||||
@end ifset
|
||||
@@ -86,7 +82,6 @@
|
||||
@end macro
|
||||
|
||||
@macro FIXME-xref{string}
|
||||
@quote-arg
|
||||
@ifset PROOF
|
||||
@strong{<XREF>} \string\ @strong{</>}
|
||||
@end ifset
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
A @dfn{snapshot file} (or @dfn{directory file}) is created during
|
||||
incremental backups (@pxref{Incremental Dumps}). It
|
||||
contains the status of the filesystem at the time of the dump and is
|
||||
contains the status of the file system at the time of the dump and is
|
||||
used to determine which files were modified since the last backup.
|
||||
|
||||
@GNUTAR{} version @value{VERSION} supports two snapshot file
|
||||
@@ -47,7 +47,7 @@ time of the last backup. First number is the number of seconds, the
|
||||
second one is the number of nanoseconds, since the beginning of the
|
||||
epoch.
|
||||
|
||||
Following lines contain directory metadate, one line per
|
||||
Following lines contain directory metadata, one line per
|
||||
directory. The line format is:
|
||||
|
||||
@smallexample
|
||||
|
||||
235
doc/sparse.texi
Normal file
235
doc/sparse.texi
Normal file
@@ -0,0 +1,235 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
@c This file is distributed under GFDL 1.1 or any later version
|
||||
@c published by the Free Software Foundation.
|
||||
|
||||
@cindex sparse formats
|
||||
@cindex sparse versions
|
||||
The notion of sparse file, and the ways of handling it from the point
|
||||
of view of @GNUTAR{} user have been described in detail in
|
||||
@ref{sparse}. This chapter describes the internal format @GNUTAR{}
|
||||
uses to store such files.
|
||||
|
||||
The support for sparse files in @GNUTAR{} has a long history. The
|
||||
earliest version featuring this support that I was able to find was 1.09,
|
||||
released in November, 1990. The format introduced back then is called
|
||||
@dfn{old GNU} sparse format and in spite of the fact that its design
|
||||
contained many flaws, it was the only format @GNUTAR{} supported
|
||||
until version 1.14 (May, 2004), which introduced initial support for
|
||||
sparse archives in @acronym{PAX} archives (@pxref{posix}). This
|
||||
format was not free from design flows, either and it was subsequently
|
||||
improved in versions 1.15.2 (November, 2005) and 1.15.92 (June,
|
||||
2006).
|
||||
|
||||
In addition to GNU sparse format, @GNUTAR{} is able to read and
|
||||
extract sparse files archived by @command{star}.
|
||||
|
||||
The following subsections describe each format in detail.
|
||||
|
||||
@menu
|
||||
* Old GNU Format::
|
||||
* PAX 0:: PAX Format, Versions 0.0 and 0.1
|
||||
* PAX 1:: PAX Format, Version 1.0
|
||||
@end menu
|
||||
|
||||
@node Old GNU Format
|
||||
@appendixsubsec Old GNU Format
|
||||
|
||||
@cindex sparse formats, Old GNU
|
||||
@cindex Old GNU sparse format
|
||||
The format introduced some time around 1990 (v. 1.09). It was
|
||||
designed on top of standard @code{ustar} headers in such an
|
||||
unfortunate way that some of its fields overwrote fields required by
|
||||
POSIX.
|
||||
|
||||
An old GNU sparse header is designated by type @samp{S}
|
||||
(@code{GNUTYPE_SPARSE}) and has the following layout:
|
||||
|
||||
@multitable @columnfractions 0.10 0.10 0.20 0.20 0.40
|
||||
@headitem Offset @tab Size @tab Name @tab Data type @tab Contents
|
||||
@item 0 @tab 345 @tab @tab N/A @tab Not used.
|
||||
@item 345 @tab 12 @tab atime @tab Number @tab @code{atime} of the file.
|
||||
@item 357 @tab 12 @tab ctime @tab Number @tab @code{ctime} of the file .
|
||||
@item 369 @tab 12 @tab offset @tab Number @tab For
|
||||
multivolume archives: the offset of the start of this volume.
|
||||
@item 381 @tab 4 @tab @tab N/A @tab Not used.
|
||||
@item 385 @tab 1 @tab @tab N/A @tab Not used.
|
||||
@item 386 @tab 96 @tab sp @tab @code{sparse_header} @tab (4 entries) File map.
|
||||
@item 482 @tab 1 @tab isextended @tab Bool @tab @code{1} if an
|
||||
extension sparse header follows, @code{0} otherwise.
|
||||
@item 483 @tab 12 @tab realsize @tab Number @tab Real size of the file.
|
||||
@end multitable
|
||||
|
||||
Each of @code{sparse_header} object at offset 386 describes a single
|
||||
data chunk. It has the following structure:
|
||||
|
||||
@multitable @columnfractions 0.10 0.10 0.20 0.60
|
||||
@headitem Offset @tab Size @tab Data type @tab Contents
|
||||
@item 0 @tab 12 @tab Number @tab Offset of the
|
||||
beginning of the chunk.
|
||||
@item 12 @tab 12 @tab Number @tab Size of the chunk.
|
||||
@end multitable
|
||||
|
||||
If the member contains more than four chunks, the @code{isextended}
|
||||
field of the header has the value @code{1} and the main header is
|
||||
followed by one or more @dfn{extension headers}. Each such header has
|
||||
the following structure:
|
||||
|
||||
@multitable @columnfractions 0.10 0.10 0.20 0.20 0.40
|
||||
@headitem Offset @tab Size @tab Name @tab Data type @tab Contents
|
||||
@item 0 @tab 21 @tab sp @tab @code{sparse_header} @tab
|
||||
(21 entires) File map.
|
||||
@item 504 @tab 1 @tab isextended @tab Bool @tab @code{1} if an
|
||||
extension sparse header follows, or @code{0} otherwise.
|
||||
@end multitable
|
||||
|
||||
A header with @code{isextended=0} ends the map.
|
||||
|
||||
@node PAX 0
|
||||
@appendixsubsec PAX Format, Versions 0.0 and 0.1
|
||||
|
||||
@cindex sparse formats, v.0.0
|
||||
There are two formats available in this branch. The version @code{0.0}
|
||||
is the initial version of sparse format used by @command{tar}
|
||||
versions 1.14--1.15.1. The sparse file map is kept in extended
|
||||
(@code{x}) PAX header variables:
|
||||
|
||||
@table @code
|
||||
@vrindex GNU.sparse.size, extended header variable
|
||||
@item GNU.sparse.size
|
||||
Real size of the stored file
|
||||
|
||||
@item GNU.sparse.numblocks
|
||||
@vrindex GNU.sparse.numblocks, extended header variable
|
||||
Number of blocks in the sparse map
|
||||
|
||||
@item GNU.sparse.offset
|
||||
@vrindex GNU.sparse.offset, extended header variable
|
||||
Offset of the data block
|
||||
|
||||
@item GNU.sparse.numbytes
|
||||
@vrindex GNU.sparse.numbytes, extended header variable
|
||||
Size of the data block
|
||||
@end table
|
||||
|
||||
The latter two variables repeat for each data block, so the overall
|
||||
structure is like this:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
GNU.sparse.size=@var{size}
|
||||
GNU.sparse.numblocks=@var{numblocks}
|
||||
repeat @var{numblocks} times
|
||||
GNU.sparse.offset=@var{offset}
|
||||
GNU.sparse.numbytes=@var{numbytes}
|
||||
end repeat
|
||||
@end group
|
||||
@end smallexample
|
||||
|
||||
This format presented the following two problems:
|
||||
|
||||
@enumerate 1
|
||||
@item
|
||||
Whereas the POSIX specification allows a variable to appear multiple
|
||||
times in a header, it requires that only the last occurrence be
|
||||
meaningful. Thus, multiple occurrences of @code{GNU.sparse.offset} and
|
||||
@code{GNU.sparse.numbytes} are conflicting with the POSIX specs.
|
||||
|
||||
@item
|
||||
Attempting to extract such archives using a third-party @command{tar}s
|
||||
results in extraction of sparse files in @emph{compressed form}. If
|
||||
the @command{tar} implementation in question does not support POSIX
|
||||
format, it will also extract a file containing extension header
|
||||
attributes. This file can be used to expand the file to its original
|
||||
state. However, posix-aware @command{tar}s will usually ignore the
|
||||
unknown variables, which makes restoring the file more
|
||||
difficult. @xref{extracting sparse v.0.x, Extraction of sparse
|
||||
members in v.0.0 format}, for the detailed description of how to
|
||||
restore such members using non-GNU @command{tar}s.
|
||||
@end enumerate
|
||||
|
||||
@cindex sparse formats, v.0.1
|
||||
@GNUTAR{} 1.15.2 introduced sparse format version @code{0.1}, which
|
||||
attempted to solve these problems. As its predecessor, this format
|
||||
stores sparse map in the extended POSIX header. It retains
|
||||
@code{GNU.sparse.size} and @code{GNU.sparse.numblocks} variables, but
|
||||
instead of @code{GNU.sparse.offset}/@code{GNU.sparse.numbytes} pairs
|
||||
it uses a single variable:
|
||||
|
||||
@table @code
|
||||
@item GNU.sparse.map
|
||||
@vrindex GNU.sparse.map, extended header variable
|
||||
Map of non-null data chunks. It is a string consisting of
|
||||
comma-separated values "@var{offset},@var{size}[,@var{offset-1},@var{size-1}...]"
|
||||
@end table
|
||||
|
||||
To address the 2nd problem, the @code{name} field in @code{ustar}
|
||||
is replaced with a special name, constructed using the following pattern:
|
||||
|
||||
@smallexample
|
||||
%d/GNUSparseFile.%p/%f
|
||||
@end smallexample
|
||||
|
||||
@vrindex GNU.sparse.name, extended header variable
|
||||
The real name of the sparse file is stored in the variable
|
||||
@code{GNU.sparse.name}. Thus, those @command{tar} implementations
|
||||
that are not aware of GNU extensions will at least extract the files
|
||||
into separate directories, giving the user a possibility to expand it
|
||||
afterwards. @xref{extracting sparse v.0.x, Extraction of sparse
|
||||
members in v.0.1 format}, for the detailed description of how to
|
||||
restore such members using non-GNU @command{tar}s.
|
||||
|
||||
The resulting @code{GNU.sparse.map} string can be @emph{very} long.
|
||||
Although POSIX does not impose any limit on the length of a @code{x}
|
||||
header variable, this possibly can confuse some tars.
|
||||
|
||||
@node PAX 1
|
||||
@appendixsubsec PAX Format, Version 1.0
|
||||
|
||||
@cindex sparse formats, v.1.0
|
||||
The version @code{1.0} of sparse format was introduced with @GNUTAR{}
|
||||
1.15.92. Its main objective was to make the resulting file
|
||||
extractable with little effort even by non-posix aware @command{tar}
|
||||
implementations. Starting from this version, the extended header
|
||||
preceding a sparse member always contains the following variables that
|
||||
identify the format being used:
|
||||
|
||||
@table @code
|
||||
@item GNU.sparse.major
|
||||
@vrindex GNU.sparse.major, extended header variable
|
||||
Major version
|
||||
|
||||
@item GNU.sparse.minor
|
||||
@vrindex GNU.sparse.minor, extended header variable
|
||||
Minor version
|
||||
@end table
|
||||
|
||||
The @code{name} field in @code{ustar} header contains a special name,
|
||||
constructed using the following pattern:
|
||||
|
||||
@smallexample
|
||||
%d/GNUSparseFile.%p/%f
|
||||
@end smallexample
|
||||
|
||||
@vrindex GNU.sparse.name, extended header variable, in v.1.0
|
||||
@vrindex GNU.sparse.realsize, extended header variable
|
||||
The real name of the sparse file is stored in the variable
|
||||
@code{GNU.sparse.name}. The real size of the file is stored in the
|
||||
variable @code{GNU.sparse.realsize}.
|
||||
|
||||
The sparse map itself is stored in the file data block, preceding the actual
|
||||
file data. It consists of a series of octal numbers of arbitrary length, delimited
|
||||
by newlines. The map is padded with nulls to the nearest block boundary.
|
||||
|
||||
The first number gives the number of entries in the map. Following are map entries,
|
||||
each one consisting of two numbers giving the offset and size of the
|
||||
data block it describes.
|
||||
|
||||
The format is designed in such a way that non-posix aware tars and tars not
|
||||
supporting @code{GNU.sparse.*} keywords will extract each sparse file
|
||||
in its condensed form with the file map prepended and will place it
|
||||
into a separate directory. Then, using a simple program it would be
|
||||
possible to expand the file to its original form even without @GNUTAR{}.
|
||||
@xref{Sparse Recovery}, for the detailed information on how to extract
|
||||
sparse members without @GNUTAR{}.
|
||||
|
||||
1619
doc/tar.texi
1619
doc/tar.texi
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ argmatch
|
||||
argp
|
||||
backupfile
|
||||
closeout
|
||||
configmake
|
||||
dirname
|
||||
error
|
||||
exclude
|
||||
@@ -22,6 +23,7 @@ gettext
|
||||
gettime
|
||||
hash
|
||||
human
|
||||
inttypes
|
||||
lchown
|
||||
localcharset
|
||||
memset
|
||||
@@ -38,6 +40,7 @@ savedir
|
||||
setenv
|
||||
stat-time
|
||||
stdbool
|
||||
stdint
|
||||
stpcpy
|
||||
strdup
|
||||
strtol
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.deps
|
||||
Makefile
|
||||
Makefile.am
|
||||
Makefile.in
|
||||
__fpending.c
|
||||
__fpending.h
|
||||
@@ -26,7 +25,7 @@ argp-pvh.c
|
||||
argp-xinl.c
|
||||
argp.h
|
||||
asnprintf.c
|
||||
atexit.c
|
||||
at-func.c
|
||||
backupfile.c
|
||||
backupfile.h
|
||||
basename.c
|
||||
@@ -34,9 +33,12 @@ charset.alias
|
||||
chdir-long.c
|
||||
chdir-long.h
|
||||
chown.c
|
||||
close-stream.c
|
||||
close-stream.h
|
||||
closeout.c
|
||||
closeout.h
|
||||
config.charset
|
||||
configmake.h
|
||||
creat-safer.c
|
||||
dirname.c
|
||||
dirname.h
|
||||
@@ -45,19 +47,22 @@ error.c
|
||||
error.h
|
||||
exclude.c
|
||||
exclude.h
|
||||
exit.c
|
||||
exit.h
|
||||
exitfail.c
|
||||
exitfail.h
|
||||
fchmodat.c
|
||||
fchown-stub.c
|
||||
fcntl--.h
|
||||
fcntl-safer.h
|
||||
fcntl.h
|
||||
fcntl_.h
|
||||
fd-safer.c
|
||||
fileblocks.c
|
||||
fnmatch.c
|
||||
fnmatch.h
|
||||
fnmatch_.h
|
||||
fnmatch_loop.c
|
||||
fstatat.c
|
||||
ftruncate.c
|
||||
full-write.c
|
||||
full-write.h
|
||||
@@ -70,8 +75,6 @@ getdelim.c
|
||||
getdelim.h
|
||||
getline.c
|
||||
getline.h
|
||||
getndelim2.c
|
||||
getndelim2.h
|
||||
getopt.c
|
||||
getopt.h
|
||||
getopt1.c
|
||||
@@ -81,6 +84,7 @@ getpagesize.h
|
||||
gettext.h
|
||||
gettime.c
|
||||
gettimeofday.c
|
||||
gnulib.mk
|
||||
hash.c
|
||||
hash.h
|
||||
human.c
|
||||
@@ -89,11 +93,15 @@ imaxtostr.c
|
||||
intprops.h
|
||||
inttostr.c
|
||||
inttostr.h
|
||||
inttypes.h
|
||||
inttypes_.h
|
||||
lchown.c
|
||||
lchown.h
|
||||
localcharset.c
|
||||
localcharset.h
|
||||
localedir.h
|
||||
lstat.c
|
||||
lstat.h
|
||||
malloc.c
|
||||
mbchar.c
|
||||
mbchar.h
|
||||
@@ -108,6 +116,7 @@ minmax.h
|
||||
mkdirat.c
|
||||
mkdtemp.c
|
||||
mkdtemp.h
|
||||
mktime.c
|
||||
modechange.c
|
||||
modechange.h
|
||||
obstack.c
|
||||
@@ -116,19 +125,16 @@ offtostr.c
|
||||
open-safer.c
|
||||
openat-die.c
|
||||
openat-priv.h
|
||||
openat-proc.c
|
||||
openat.c
|
||||
openat.h
|
||||
pathmax.h
|
||||
paxconvert.c
|
||||
paxerror.c
|
||||
paxerror.h
|
||||
paxexit.c
|
||||
paxlib.h
|
||||
paxnames.c
|
||||
pipe-safer.c
|
||||
printf-args.c
|
||||
printf-args.h
|
||||
printf-parse.c
|
||||
printf-parse.h
|
||||
quote.c
|
||||
quote.h
|
||||
@@ -152,6 +158,7 @@ safe-read.c
|
||||
safe-read.h
|
||||
safe-write.c
|
||||
safe-write.h
|
||||
same-inode.h
|
||||
save-cwd.c
|
||||
save-cwd.h
|
||||
savedir.c
|
||||
@@ -161,8 +168,11 @@ setenv.h
|
||||
size_max.h
|
||||
stat-macros.h
|
||||
stat-time.h
|
||||
stat_.h
|
||||
stdbool.h
|
||||
stdbool_.h
|
||||
stdint.h
|
||||
stdint_.h
|
||||
stpcpy.c
|
||||
stpcpy.h
|
||||
strcase.h
|
||||
@@ -189,9 +199,12 @@ sysexit_.h
|
||||
sysexits.h
|
||||
system-ioctl.h
|
||||
system.h
|
||||
tempname.c
|
||||
tempname.h
|
||||
time_r.c
|
||||
time_r.h
|
||||
timespec.h
|
||||
uinttostr.c
|
||||
umaxtostr.c
|
||||
unistd--.h
|
||||
unistd-safer.h
|
||||
@@ -202,7 +215,6 @@ unsetenv.c
|
||||
utime.c
|
||||
utimens.c
|
||||
utimens.h
|
||||
vasnprintf.c
|
||||
vasnprintf.h
|
||||
verify.h
|
||||
version-etc-fsf.c
|
||||
@@ -210,13 +222,15 @@ version-etc.c
|
||||
version-etc.h
|
||||
vsnprintf.c
|
||||
vsnprintf.h
|
||||
wcwidth.h
|
||||
xalloc-die.c
|
||||
xalloc.h
|
||||
xgetcwd.c
|
||||
xgetcwd.h
|
||||
xmalloc.c
|
||||
xsize.h
|
||||
xstrdup.c
|
||||
xstrndup.c
|
||||
xstrndup.h
|
||||
xstrtol.c
|
||||
xstrtol.h
|
||||
xstrtoul.c
|
||||
|
||||
41
lib/Makefile.am
Normal file
41
lib/Makefile.am
Normal file
@@ -0,0 +1,41 @@
|
||||
# Makefile for GNU tar library. -*- Makefile -*-
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004,
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
include gnulib.mk
|
||||
|
||||
rmt-command.h : Makefile
|
||||
rm -f $@-t $@
|
||||
echo "#ifndef DEFAULT_RMT_COMMAND" >> $@-t
|
||||
echo "# define DEFAULT_RMT_COMMAND \"$(DEFAULT_RMT_DIR)/`echo rmt | sed '$(transform)'`$(EXEEXT)\"" >> $@-t
|
||||
echo "#endif" >> $@-t
|
||||
mv $@-t $@
|
||||
BUILT_SOURCES += rmt-command.h
|
||||
CLEANFILES += rmt-command.h rmt-command.h-t
|
||||
|
||||
noinst_HEADERS += system.h system-ioctl.h rmt.h paxlib.h stdopen.h
|
||||
libtar_a_SOURCES += \
|
||||
paxerror.c paxexit.c paxlib.h paxnames.c \
|
||||
prepargs.c prepargs.h \
|
||||
rtapelib.c \
|
||||
rmt.h \
|
||||
stdopen.c stdopen.h \
|
||||
system.h system-ioctl.h
|
||||
|
||||
libtar_a_LIBADD += $(LIBOBJS)
|
||||
libtar_a_DEPENDENCIES += $(LIBOBJS)
|
||||
@@ -1,56 +0,0 @@
|
||||
# Makefile for GNU tar library. -*- Makefile -*-
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2004,
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2, or (at your option)
|
||||
## any later version.
|
||||
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program; if not, write to the Free Software
|
||||
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
## 02110-1301, USA.
|
||||
|
||||
noinst_LIBRARIES = libtar.a
|
||||
noinst_HEADERS = system.h system-ioctl.h localedir.h rmt.h paxlib.h stdopen.h
|
||||
libtar_a_SOURCES = prepargs.c prepargs.h rtapelib.c paxerror.c paxexit.c paxnames.c stdopen.c
|
||||
|
||||
localedir = $(datadir)/locale
|
||||
|
||||
DISTCLEANFILES = localedir.h
|
||||
localedir.h : Makefile
|
||||
echo '#define LOCALEDIR "$(localedir)"' >$@
|
||||
echo "#ifndef DEFAULT_RMT_COMMAND" >> $@
|
||||
echo "# define DEFAULT_RMT_COMMAND \"$(DEFAULT_RMT_DIR)/`echo rmt | sed '$(transform)'`$(EXEEXT)\"" >> $@
|
||||
echo "#endif" >> $@
|
||||
|
||||
rtapelib.o: localedir.h
|
||||
|
||||
libtar_a_LIBADD = $(LIBOBJS) $(ALLOCA)
|
||||
libtar_a_DEPENDENCIES = $(libtar_a_LIBADD)
|
||||
|
||||
BUILT_SOURCES =
|
||||
AM_CPPFLAGS =
|
||||
EXTRA_DIST = Makefile.tmpl
|
||||
MAINTAINERCLEANFILES =
|
||||
MOSTLYCLEANFILES =
|
||||
lib_OBJECTS = $(libtar_a_OBJECTS)
|
||||
|
||||
# Special rule for getdate
|
||||
#
|
||||
# Say $(srcdir), so GNU make does not report an ambiguity with the .y.c rule.
|
||||
$(srcdir)/getdate.c: getdate.y
|
||||
cd $(srcdir) && \
|
||||
$(YACC) $(YFLAGS) getdate.y && \
|
||||
mv -f y.tab.c getdate.c
|
||||
|
||||
SUFFIXES = .o .c .h
|
||||
CLEANFILES =
|
||||
# gnulib modules
|
||||
535
lib/printf-parse.c
Normal file
535
lib/printf-parse.c
Normal file
@@ -0,0 +1,535 @@
|
||||
/* Formatted output to strings.
|
||||
Copyright (C) 1999-2000, 2002-2004, 2006 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#if WIDE_CHAR_VERSION
|
||||
# include "wprintf-parse.h"
|
||||
#else
|
||||
# include "printf-parse.h"
|
||||
#endif
|
||||
|
||||
/* Get size_t, NULL. */
|
||||
#include <stddef.h>
|
||||
|
||||
/* Get intmax_t. */
|
||||
#if HAVE_STDINT_H_WITH_UINTMAX
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
#if HAVE_INTTYPES_H_WITH_UINTMAX
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
|
||||
/* malloc(), realloc(), free(). */
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX ((size_t) -1)
|
||||
#endif
|
||||
|
||||
#if WIDE_CHAR_VERSION
|
||||
# define PRINTF_PARSE wprintf_parse
|
||||
# define CHAR_T wchar_t
|
||||
# define DIRECTIVE wchar_t_directive
|
||||
# define DIRECTIVES wchar_t_directives
|
||||
#else
|
||||
# define PRINTF_PARSE printf_parse
|
||||
# define CHAR_T char
|
||||
# define DIRECTIVE char_directive
|
||||
# define DIRECTIVES char_directives
|
||||
#endif
|
||||
|
||||
#ifdef STATIC
|
||||
STATIC
|
||||
#endif
|
||||
int
|
||||
PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
|
||||
{
|
||||
const CHAR_T *cp = format; /* pointer into format */
|
||||
size_t arg_posn = 0; /* number of regular arguments consumed */
|
||||
size_t d_allocated; /* allocated elements of d->dir */
|
||||
size_t a_allocated; /* allocated elements of a->arg */
|
||||
size_t max_width_length = 0;
|
||||
size_t max_precision_length = 0;
|
||||
|
||||
d->count = 0;
|
||||
d_allocated = 1;
|
||||
d->dir = malloc (d_allocated * sizeof (DIRECTIVE));
|
||||
if (d->dir == NULL)
|
||||
/* Out of memory. */
|
||||
return -1;
|
||||
|
||||
a->count = 0;
|
||||
a_allocated = 0;
|
||||
a->arg = NULL;
|
||||
|
||||
#define REGISTER_ARG(_index_,_type_) \
|
||||
{ \
|
||||
size_t n = (_index_); \
|
||||
if (n >= a_allocated) \
|
||||
{ \
|
||||
size_t memory_size; \
|
||||
argument *memory; \
|
||||
\
|
||||
a_allocated *= 2; \
|
||||
if (a_allocated <= n) \
|
||||
a_allocated = n + 1; \
|
||||
if (SIZE_MAX / sizeof (argument) < a_allocated) \
|
||||
/* Overflow, would lead to out of memory. */ \
|
||||
goto error; \
|
||||
memory_size = a_allocated * sizeof (argument); \
|
||||
memory = (a->arg \
|
||||
? realloc (a->arg, memory_size) \
|
||||
: malloc (memory_size)); \
|
||||
if (memory == NULL) \
|
||||
/* Out of memory. */ \
|
||||
goto error; \
|
||||
a->arg = memory; \
|
||||
} \
|
||||
while (a->count <= n) \
|
||||
a->arg[a->count++].type = TYPE_NONE; \
|
||||
if (a->arg[n].type == TYPE_NONE) \
|
||||
a->arg[n].type = (_type_); \
|
||||
else if (a->arg[n].type != (_type_)) \
|
||||
/* Ambiguous type for positional argument. */ \
|
||||
goto error; \
|
||||
}
|
||||
|
||||
while (*cp != '\0')
|
||||
{
|
||||
CHAR_T c = *cp++;
|
||||
if (c == '%')
|
||||
{
|
||||
size_t arg_index = ARG_NONE;
|
||||
DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */
|
||||
|
||||
/* Initialize the next directive. */
|
||||
dp->dir_start = cp - 1;
|
||||
dp->flags = 0;
|
||||
dp->width_start = NULL;
|
||||
dp->width_end = NULL;
|
||||
dp->width_arg_index = ARG_NONE;
|
||||
dp->precision_start = NULL;
|
||||
dp->precision_end = NULL;
|
||||
dp->precision_arg_index = ARG_NONE;
|
||||
dp->arg_index = ARG_NONE;
|
||||
|
||||
/* Test for positional argument. */
|
||||
if (*cp >= '0' && *cp <= '9')
|
||||
{
|
||||
const CHAR_T *np;
|
||||
|
||||
for (np = cp; *np >= '0' && *np <= '9'; np++)
|
||||
;
|
||||
if (*np == '$')
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
for (np = cp; *np >= '0' && *np <= '9'; np++)
|
||||
if (n < SIZE_MAX / 10)
|
||||
n = 10 * n + (*np - '0');
|
||||
else
|
||||
/* n too large for memory. */
|
||||
goto error;
|
||||
if (n == 0)
|
||||
/* Positional argument 0. */
|
||||
goto error;
|
||||
arg_index = n - 1;
|
||||
cp = np + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the flags. */
|
||||
for (;;)
|
||||
{
|
||||
if (*cp == '\'')
|
||||
{
|
||||
dp->flags |= FLAG_GROUP;
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == '-')
|
||||
{
|
||||
dp->flags |= FLAG_LEFT;
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == '+')
|
||||
{
|
||||
dp->flags |= FLAG_SHOWSIGN;
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == ' ')
|
||||
{
|
||||
dp->flags |= FLAG_SPACE;
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == '#')
|
||||
{
|
||||
dp->flags |= FLAG_ALT;
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == '0')
|
||||
{
|
||||
dp->flags |= FLAG_ZERO;
|
||||
cp++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse the field width. */
|
||||
if (*cp == '*')
|
||||
{
|
||||
dp->width_start = cp;
|
||||
cp++;
|
||||
dp->width_end = cp;
|
||||
if (max_width_length < 1)
|
||||
max_width_length = 1;
|
||||
|
||||
/* Test for positional argument. */
|
||||
if (*cp >= '0' && *cp <= '9')
|
||||
{
|
||||
const CHAR_T *np;
|
||||
|
||||
for (np = cp; *np >= '0' && *np <= '9'; np++)
|
||||
;
|
||||
if (*np == '$')
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
for (np = cp; *np >= '0' && *np <= '9'; np++)
|
||||
if (n < SIZE_MAX / 10)
|
||||
n = 10 * n + (*np - '0');
|
||||
else
|
||||
/* n too large for memory. */
|
||||
goto error;
|
||||
if (n == 0)
|
||||
/* Positional argument 0. */
|
||||
goto error;
|
||||
dp->width_arg_index = n - 1;
|
||||
cp = np + 1;
|
||||
}
|
||||
}
|
||||
if (dp->width_arg_index == ARG_NONE)
|
||||
{
|
||||
dp->width_arg_index = arg_posn++;
|
||||
if (dp->width_arg_index == ARG_NONE)
|
||||
/* arg_posn wrapped around. */
|
||||
goto error;
|
||||
}
|
||||
REGISTER_ARG (dp->width_arg_index, TYPE_INT);
|
||||
}
|
||||
else if (*cp >= '0' && *cp <= '9')
|
||||
{
|
||||
size_t width_length;
|
||||
|
||||
dp->width_start = cp;
|
||||
for (; *cp >= '0' && *cp <= '9'; cp++)
|
||||
;
|
||||
dp->width_end = cp;
|
||||
width_length = dp->width_end - dp->width_start;
|
||||
if (max_width_length < width_length)
|
||||
max_width_length = width_length;
|
||||
}
|
||||
|
||||
/* Parse the precision. */
|
||||
if (*cp == '.')
|
||||
{
|
||||
cp++;
|
||||
if (*cp == '*')
|
||||
{
|
||||
dp->precision_start = cp - 1;
|
||||
cp++;
|
||||
dp->precision_end = cp;
|
||||
if (max_precision_length < 2)
|
||||
max_precision_length = 2;
|
||||
|
||||
/* Test for positional argument. */
|
||||
if (*cp >= '0' && *cp <= '9')
|
||||
{
|
||||
const CHAR_T *np;
|
||||
|
||||
for (np = cp; *np >= '0' && *np <= '9'; np++)
|
||||
;
|
||||
if (*np == '$')
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
for (np = cp; *np >= '0' && *np <= '9'; np++)
|
||||
if (n < SIZE_MAX / 10)
|
||||
n = 10 * n + (*np - '0');
|
||||
else
|
||||
/* n too large for memory. */
|
||||
goto error;
|
||||
if (n == 0)
|
||||
/* Positional argument 0. */
|
||||
goto error;
|
||||
dp->precision_arg_index = n - 1;
|
||||
cp = np + 1;
|
||||
}
|
||||
}
|
||||
if (dp->precision_arg_index == ARG_NONE)
|
||||
{
|
||||
dp->precision_arg_index = arg_posn++;
|
||||
if (dp->precision_arg_index == ARG_NONE)
|
||||
/* arg_posn wrapped around. */
|
||||
goto error;
|
||||
}
|
||||
REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t precision_length;
|
||||
|
||||
dp->precision_start = cp - 1;
|
||||
for (; *cp >= '0' && *cp <= '9'; cp++)
|
||||
;
|
||||
dp->precision_end = cp;
|
||||
precision_length = dp->precision_end - dp->precision_start;
|
||||
if (max_precision_length < precision_length)
|
||||
max_precision_length = precision_length;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
arg_type type;
|
||||
|
||||
/* Parse argument type/size specifiers. */
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (*cp == 'h')
|
||||
{
|
||||
flags |= (1 << (flags & 1));
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == 'L')
|
||||
{
|
||||
flags |= 4;
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == 'l')
|
||||
{
|
||||
flags += 8;
|
||||
cp++;
|
||||
}
|
||||
#ifdef HAVE_INTMAX_T
|
||||
else if (*cp == 'j')
|
||||
{
|
||||
if (sizeof (intmax_t) > sizeof (long))
|
||||
{
|
||||
/* intmax_t = long long */
|
||||
flags += 16;
|
||||
}
|
||||
else if (sizeof (intmax_t) > sizeof (int))
|
||||
{
|
||||
/* intmax_t = long */
|
||||
flags += 8;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
#endif
|
||||
else if (*cp == 'z' || *cp == 'Z')
|
||||
{
|
||||
/* 'z' is standardized in ISO C 99, but glibc uses 'Z'
|
||||
because the warning facility in gcc-2.95.2 understands
|
||||
only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */
|
||||
if (sizeof (size_t) > sizeof (long))
|
||||
{
|
||||
/* size_t = long long */
|
||||
flags += 16;
|
||||
}
|
||||
else if (sizeof (size_t) > sizeof (int))
|
||||
{
|
||||
/* size_t = long */
|
||||
flags += 8;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
else if (*cp == 't')
|
||||
{
|
||||
if (sizeof (ptrdiff_t) > sizeof (long))
|
||||
{
|
||||
/* ptrdiff_t = long long */
|
||||
flags += 16;
|
||||
}
|
||||
else if (sizeof (ptrdiff_t) > sizeof (int))
|
||||
{
|
||||
/* ptrdiff_t = long */
|
||||
flags += 8;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the conversion character. */
|
||||
c = *cp++;
|
||||
switch (c)
|
||||
{
|
||||
case 'd': case 'i':
|
||||
#ifdef HAVE_LONG_LONG
|
||||
if (flags >= 16 || (flags & 4))
|
||||
type = TYPE_LONGLONGINT;
|
||||
else
|
||||
#endif
|
||||
if (flags >= 8)
|
||||
type = TYPE_LONGINT;
|
||||
else if (flags & 2)
|
||||
type = TYPE_SCHAR;
|
||||
else if (flags & 1)
|
||||
type = TYPE_SHORT;
|
||||
else
|
||||
type = TYPE_INT;
|
||||
break;
|
||||
case 'o': case 'u': case 'x': case 'X':
|
||||
#ifdef HAVE_LONG_LONG
|
||||
if (flags >= 16 || (flags & 4))
|
||||
type = TYPE_ULONGLONGINT;
|
||||
else
|
||||
#endif
|
||||
if (flags >= 8)
|
||||
type = TYPE_ULONGINT;
|
||||
else if (flags & 2)
|
||||
type = TYPE_UCHAR;
|
||||
else if (flags & 1)
|
||||
type = TYPE_USHORT;
|
||||
else
|
||||
type = TYPE_UINT;
|
||||
break;
|
||||
case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
|
||||
case 'a': case 'A':
|
||||
#ifdef HAVE_LONG_DOUBLE
|
||||
if (flags >= 16 || (flags & 4))
|
||||
type = TYPE_LONGDOUBLE;
|
||||
else
|
||||
#endif
|
||||
type = TYPE_DOUBLE;
|
||||
break;
|
||||
case 'c':
|
||||
if (flags >= 8)
|
||||
#ifdef HAVE_WINT_T
|
||||
type = TYPE_WIDE_CHAR;
|
||||
#else
|
||||
goto error;
|
||||
#endif
|
||||
else
|
||||
type = TYPE_CHAR;
|
||||
break;
|
||||
#ifdef HAVE_WINT_T
|
||||
case 'C':
|
||||
type = TYPE_WIDE_CHAR;
|
||||
c = 'c';
|
||||
break;
|
||||
#endif
|
||||
case 's':
|
||||
if (flags >= 8)
|
||||
#ifdef HAVE_WCHAR_T
|
||||
type = TYPE_WIDE_STRING;
|
||||
#else
|
||||
goto error;
|
||||
#endif
|
||||
else
|
||||
type = TYPE_STRING;
|
||||
break;
|
||||
#ifdef HAVE_WCHAR_T
|
||||
case 'S':
|
||||
type = TYPE_WIDE_STRING;
|
||||
c = 's';
|
||||
break;
|
||||
#endif
|
||||
case 'p':
|
||||
type = TYPE_POINTER;
|
||||
break;
|
||||
case 'n':
|
||||
#ifdef HAVE_LONG_LONG
|
||||
if (flags >= 16 || (flags & 4))
|
||||
type = TYPE_COUNT_LONGLONGINT_POINTER;
|
||||
else
|
||||
#endif
|
||||
if (flags >= 8)
|
||||
type = TYPE_COUNT_LONGINT_POINTER;
|
||||
else if (flags & 2)
|
||||
type = TYPE_COUNT_SCHAR_POINTER;
|
||||
else if (flags & 1)
|
||||
type = TYPE_COUNT_SHORT_POINTER;
|
||||
else
|
||||
type = TYPE_COUNT_INT_POINTER;
|
||||
break;
|
||||
case '%':
|
||||
type = TYPE_NONE;
|
||||
break;
|
||||
default:
|
||||
/* Unknown conversion character. */
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (type != TYPE_NONE)
|
||||
{
|
||||
dp->arg_index = arg_index;
|
||||
if (dp->arg_index == ARG_NONE)
|
||||
{
|
||||
dp->arg_index = arg_posn++;
|
||||
if (dp->arg_index == ARG_NONE)
|
||||
/* arg_posn wrapped around. */
|
||||
goto error;
|
||||
}
|
||||
REGISTER_ARG (dp->arg_index, type);
|
||||
}
|
||||
dp->conversion = c;
|
||||
dp->dir_end = cp;
|
||||
}
|
||||
|
||||
d->count++;
|
||||
if (d->count >= d_allocated)
|
||||
{
|
||||
DIRECTIVE *memory;
|
||||
|
||||
if (SIZE_MAX / (2 * sizeof (DIRECTIVE)) < d_allocated)
|
||||
/* Overflow, would lead to out of memory. */
|
||||
goto error;
|
||||
d_allocated *= 2;
|
||||
memory = realloc (d->dir, d_allocated * sizeof (DIRECTIVE));
|
||||
if (memory == NULL)
|
||||
/* Out of memory. */
|
||||
goto error;
|
||||
d->dir = memory;
|
||||
}
|
||||
}
|
||||
}
|
||||
d->dir[d->count].dir_start = cp;
|
||||
|
||||
d->max_width_length = max_width_length;
|
||||
d->max_precision_length = max_precision_length;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (a->arg)
|
||||
free (a->arg);
|
||||
if (d->dir)
|
||||
free (d->dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef DIRECTIVES
|
||||
#undef DIRECTIVE
|
||||
#undef CHAR_T
|
||||
#undef PRINTF_PARSE
|
||||
918
lib/vasnprintf.c
Normal file
918
lib/vasnprintf.c
Normal file
@@ -0,0 +1,918 @@
|
||||
/* vsprintf with automatic memory allocation.
|
||||
Copyright (C) 1999, 2002-2006 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
/* Tell glibc's <stdio.h> to provide a prototype for snprintf().
|
||||
This must come before <config.h> because <config.h> may include
|
||||
<features.h>, and once <features.h> has been included, it's too late. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifndef IN_LIBINTL
|
||||
# include <alloca.h>
|
||||
#endif
|
||||
|
||||
/* Specification. */
|
||||
#if WIDE_CHAR_VERSION
|
||||
# include "vasnwprintf.h"
|
||||
#else
|
||||
# include "vasnprintf.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h> /* snprintf(), sprintf() */
|
||||
#include <stdlib.h> /* abort(), malloc(), realloc(), free() */
|
||||
#include <string.h> /* memcpy(), strlen() */
|
||||
#include <errno.h> /* errno */
|
||||
#include <limits.h> /* CHAR_BIT, INT_MAX */
|
||||
#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */
|
||||
#if WIDE_CHAR_VERSION
|
||||
# include "wprintf-parse.h"
|
||||
#else
|
||||
# include "printf-parse.h"
|
||||
#endif
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX ((size_t) -1)
|
||||
#endif
|
||||
|
||||
/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */
|
||||
#ifndef EOVERFLOW
|
||||
# define EOVERFLOW E2BIG
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WCHAR_T
|
||||
# ifdef HAVE_WCSLEN
|
||||
# define local_wcslen wcslen
|
||||
# else
|
||||
/* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid
|
||||
a dependency towards this library, here is a local substitute.
|
||||
Define this substitute only once, even if this file is included
|
||||
twice in the same compilation unit. */
|
||||
# ifndef local_wcslen_defined
|
||||
# define local_wcslen_defined 1
|
||||
static size_t
|
||||
local_wcslen (const wchar_t *s)
|
||||
{
|
||||
const wchar_t *ptr;
|
||||
|
||||
for (ptr = s; *ptr != (wchar_t) 0; ptr++)
|
||||
;
|
||||
return ptr - s;
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if WIDE_CHAR_VERSION
|
||||
# define VASNPRINTF vasnwprintf
|
||||
# define CHAR_T wchar_t
|
||||
# define DIRECTIVE wchar_t_directive
|
||||
# define DIRECTIVES wchar_t_directives
|
||||
# define PRINTF_PARSE wprintf_parse
|
||||
# define USE_SNPRINTF 1
|
||||
# if HAVE_DECL__SNWPRINTF
|
||||
/* On Windows, the function swprintf() has a different signature than
|
||||
on Unix; we use the _snwprintf() function instead. */
|
||||
# define SNPRINTF _snwprintf
|
||||
# else
|
||||
/* Unix. */
|
||||
# define SNPRINTF swprintf
|
||||
# endif
|
||||
#else
|
||||
# define VASNPRINTF vasnprintf
|
||||
# define CHAR_T char
|
||||
# define DIRECTIVE char_directive
|
||||
# define DIRECTIVES char_directives
|
||||
# define PRINTF_PARSE printf_parse
|
||||
# define USE_SNPRINTF (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF)
|
||||
# if HAVE_DECL__SNPRINTF
|
||||
/* Windows. */
|
||||
# define SNPRINTF _snprintf
|
||||
# else
|
||||
/* Unix. */
|
||||
# define SNPRINTF snprintf
|
||||
# endif
|
||||
#endif
|
||||
|
||||
CHAR_T *
|
||||
VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args)
|
||||
{
|
||||
DIRECTIVES d;
|
||||
arguments a;
|
||||
|
||||
if (PRINTF_PARSE (format, &d, &a) < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define CLEANUP() \
|
||||
free (d.dir); \
|
||||
if (a.arg) \
|
||||
free (a.arg);
|
||||
|
||||
if (printf_fetchargs (args, &a) < 0)
|
||||
{
|
||||
CLEANUP ();
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
size_t buf_neededlength;
|
||||
CHAR_T *buf;
|
||||
CHAR_T *buf_malloced;
|
||||
const CHAR_T *cp;
|
||||
size_t i;
|
||||
DIRECTIVE *dp;
|
||||
/* Output string accumulator. */
|
||||
CHAR_T *result;
|
||||
size_t allocated;
|
||||
size_t length;
|
||||
|
||||
/* Allocate a small buffer that will hold a directive passed to
|
||||
sprintf or snprintf. */
|
||||
buf_neededlength = 7 + d.max_width_length + d.max_precision_length + 6;
|
||||
#if HAVE_ALLOCA
|
||||
if (buf_neededlength < 4000 / sizeof (CHAR_T))
|
||||
{
|
||||
buf = (CHAR_T *) alloca (buf_neededlength * sizeof (CHAR_T));
|
||||
buf_malloced = NULL;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (SIZE_MAX / sizeof (CHAR_T) < buf_neededlength)
|
||||
goto out_of_memory_1;
|
||||
buf = (CHAR_T *) malloc (buf_neededlength * sizeof (CHAR_T));
|
||||
if (buf == NULL)
|
||||
goto out_of_memory_1;
|
||||
buf_malloced = buf;
|
||||
}
|
||||
|
||||
if (resultbuf != NULL)
|
||||
{
|
||||
result = resultbuf;
|
||||
allocated = *lengthp;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = NULL;
|
||||
allocated = 0;
|
||||
}
|
||||
length = 0;
|
||||
/* Invariants:
|
||||
result is either == resultbuf or == NULL or malloc-allocated.
|
||||
If length > 0, then result != NULL. */
|
||||
|
||||
/* Ensures that allocated >= length + extra. Aborts through a jump to
|
||||
out_of_memory if size is too big. */
|
||||
#define ENSURE_ALLOCATION(extra) \
|
||||
{ \
|
||||
size_t needed = length + (extra); \
|
||||
if (needed < length) \
|
||||
goto out_of_memory; \
|
||||
if (needed > allocated) \
|
||||
{ \
|
||||
size_t memory_size; \
|
||||
CHAR_T *memory; \
|
||||
\
|
||||
allocated = (allocated > 0 ? 2 * allocated : 12); \
|
||||
if (needed > allocated) \
|
||||
allocated = needed; \
|
||||
if (SIZE_MAX / sizeof (CHAR_T) < allocated) \
|
||||
goto out_of_memory; \
|
||||
memory_size = allocated * sizeof (CHAR_T); \
|
||||
if (result == resultbuf || result == NULL) \
|
||||
memory = (CHAR_T *) malloc (memory_size); \
|
||||
else \
|
||||
memory = (CHAR_T *) realloc (result, memory_size); \
|
||||
if (memory == NULL) \
|
||||
goto out_of_memory; \
|
||||
if (result == resultbuf && length > 0) \
|
||||
memcpy (memory, result, length * sizeof (CHAR_T)); \
|
||||
result = memory; \
|
||||
} \
|
||||
}
|
||||
|
||||
for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++)
|
||||
{
|
||||
if (cp != dp->dir_start)
|
||||
{
|
||||
size_t n = dp->dir_start - cp;
|
||||
|
||||
ENSURE_ALLOCATION (n);
|
||||
memcpy (result + length, cp, n * sizeof (CHAR_T));
|
||||
length += n;
|
||||
}
|
||||
if (i == d.count)
|
||||
break;
|
||||
|
||||
/* Execute a single directive. */
|
||||
if (dp->conversion == '%')
|
||||
{
|
||||
if (!(dp->arg_index == ARG_NONE))
|
||||
abort ();
|
||||
ENSURE_ALLOCATION (1);
|
||||
result[length] = '%';
|
||||
length += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(dp->arg_index != ARG_NONE))
|
||||
abort ();
|
||||
|
||||
if (dp->conversion == 'n')
|
||||
{
|
||||
switch (a.arg[dp->arg_index].type)
|
||||
{
|
||||
case TYPE_COUNT_SCHAR_POINTER:
|
||||
*a.arg[dp->arg_index].a.a_count_schar_pointer = length;
|
||||
break;
|
||||
case TYPE_COUNT_SHORT_POINTER:
|
||||
*a.arg[dp->arg_index].a.a_count_short_pointer = length;
|
||||
break;
|
||||
case TYPE_COUNT_INT_POINTER:
|
||||
*a.arg[dp->arg_index].a.a_count_int_pointer = length;
|
||||
break;
|
||||
case TYPE_COUNT_LONGINT_POINTER:
|
||||
*a.arg[dp->arg_index].a.a_count_longint_pointer = length;
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case TYPE_COUNT_LONGLONGINT_POINTER:
|
||||
*a.arg[dp->arg_index].a.a_count_longlongint_pointer = length;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arg_type type = a.arg[dp->arg_index].type;
|
||||
CHAR_T *p;
|
||||
unsigned int prefix_count;
|
||||
int prefixes[2];
|
||||
#if !USE_SNPRINTF
|
||||
size_t tmp_length;
|
||||
CHAR_T tmpbuf[700];
|
||||
CHAR_T *tmp;
|
||||
|
||||
/* Allocate a temporary buffer of sufficient size for calling
|
||||
sprintf. */
|
||||
{
|
||||
size_t width;
|
||||
size_t precision;
|
||||
|
||||
width = 0;
|
||||
if (dp->width_start != dp->width_end)
|
||||
{
|
||||
if (dp->width_arg_index != ARG_NONE)
|
||||
{
|
||||
int arg;
|
||||
|
||||
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
|
||||
abort ();
|
||||
arg = a.arg[dp->width_arg_index].a.a_int;
|
||||
width = (arg < 0 ? (unsigned int) (-arg) : arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
const CHAR_T *digitp = dp->width_start;
|
||||
|
||||
do
|
||||
{
|
||||
size_t w_tmp = width * 10 + (*digitp++ - '0');
|
||||
if (SIZE_MAX / 10 < width || w_tmp < width)
|
||||
goto out_of_memory;
|
||||
width = w_tmp;
|
||||
}
|
||||
while (digitp != dp->width_end);
|
||||
}
|
||||
}
|
||||
|
||||
precision = 6;
|
||||
if (dp->precision_start != dp->precision_end)
|
||||
{
|
||||
if (dp->precision_arg_index != ARG_NONE)
|
||||
{
|
||||
int arg;
|
||||
|
||||
if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
|
||||
abort ();
|
||||
arg = a.arg[dp->precision_arg_index].a.a_int;
|
||||
precision = (arg < 0 ? 0 : arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
const CHAR_T *digitp = dp->precision_start + 1;
|
||||
|
||||
precision = 0;
|
||||
while (digitp != dp->precision_end)
|
||||
{
|
||||
size_t p1 = 10 * precision + (*digitp++ - '0');
|
||||
precision = ((SIZE_MAX / 10 < precision
|
||||
|| p1 < precision)
|
||||
? SIZE_MAX : p1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (dp->conversion)
|
||||
{
|
||||
|
||||
case 'd': case 'i': case 'u':
|
||||
# ifdef HAVE_LONG_LONG
|
||||
if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned long long) * CHAR_BIT
|
||||
* 0.30103 /* binary -> decimal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
else
|
||||
# endif
|
||||
if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned long) * CHAR_BIT
|
||||
* 0.30103 /* binary -> decimal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
else
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned int) * CHAR_BIT
|
||||
* 0.30103 /* binary -> decimal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
if (tmp_length < precision)
|
||||
tmp_length = precision;
|
||||
/* Multiply by 2, as an estimate for FLAG_GROUP. */
|
||||
/* Add 1, to account for a leading sign. */
|
||||
tmp_length = (tmp_length < SIZE_MAX / 2
|
||||
? 2 * tmp_length + 1
|
||||
: SIZE_MAX);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
# ifdef HAVE_LONG_LONG
|
||||
if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned long long) * CHAR_BIT
|
||||
* 0.333334 /* binary -> octal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
else
|
||||
# endif
|
||||
if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned long) * CHAR_BIT
|
||||
* 0.333334 /* binary -> octal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
else
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned int) * CHAR_BIT
|
||||
* 0.333334 /* binary -> octal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
if (tmp_length < precision)
|
||||
tmp_length = precision;
|
||||
/* Add 1, to account for a leading sign. */
|
||||
tmp_length += (tmp_length < SIZE_MAX);
|
||||
break;
|
||||
|
||||
case 'x': case 'X':
|
||||
# ifdef HAVE_LONG_LONG
|
||||
if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned long long) * CHAR_BIT
|
||||
* 0.25 /* binary -> hexadecimal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
else
|
||||
# endif
|
||||
if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned long) * CHAR_BIT
|
||||
* 0.25 /* binary -> hexadecimal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
else
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (unsigned int) * CHAR_BIT
|
||||
* 0.25 /* binary -> hexadecimal */
|
||||
)
|
||||
+ 1; /* turn floor into ceil */
|
||||
if (tmp_length < precision)
|
||||
tmp_length = precision;
|
||||
/* Add 2, to account for a leading sign or alternate form. */
|
||||
if (tmp_length <= SIZE_MAX / 2)
|
||||
tmp_length *= 2;
|
||||
break;
|
||||
|
||||
case 'f': case 'F':
|
||||
# ifdef HAVE_LONG_DOUBLE
|
||||
if (type == TYPE_LONGDOUBLE)
|
||||
tmp_length =
|
||||
(unsigned int) (LDBL_MAX_EXP
|
||||
* 0.30103 /* binary -> decimal */
|
||||
* 2 /* estimate for FLAG_GROUP */
|
||||
)
|
||||
+ 1 /* turn floor into ceil */
|
||||
+ 10; /* sign, decimal point etc. */
|
||||
else
|
||||
# endif
|
||||
tmp_length =
|
||||
(unsigned int) (DBL_MAX_EXP
|
||||
* 0.30103 /* binary -> decimal */
|
||||
* 2 /* estimate for FLAG_GROUP */
|
||||
)
|
||||
+ 1 /* turn floor into ceil */
|
||||
+ 10; /* sign, decimal point etc. */
|
||||
tmp_length += precision;
|
||||
if (tmp_length < precision)
|
||||
goto out_of_memory;
|
||||
break;
|
||||
|
||||
case 'e': case 'E': case 'g': case 'G':
|
||||
case 'a': case 'A':
|
||||
tmp_length =
|
||||
12; /* sign, decimal point, exponent etc. */
|
||||
tmp_length += precision;
|
||||
if (tmp_length < precision)
|
||||
goto out_of_memory;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
# if defined HAVE_WINT_T && !WIDE_CHAR_VERSION
|
||||
if (type == TYPE_WIDE_CHAR)
|
||||
tmp_length = MB_CUR_MAX;
|
||||
else
|
||||
# endif
|
||||
tmp_length = 1;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
# ifdef HAVE_WCHAR_T
|
||||
if (type == TYPE_WIDE_STRING)
|
||||
{
|
||||
tmp_length =
|
||||
local_wcslen (a.arg[dp->arg_index].a.a_wide_string);
|
||||
|
||||
# if !WIDE_CHAR_VERSION
|
||||
if (SIZE_MAX / MB_CUR_MAX < tmp_length)
|
||||
goto out_of_memory;
|
||||
tmp_length *= MB_CUR_MAX;
|
||||
# endif
|
||||
}
|
||||
else
|
||||
# endif
|
||||
tmp_length = strlen (a.arg[dp->arg_index].a.a_string);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
tmp_length =
|
||||
(unsigned int) (sizeof (void *) * CHAR_BIT
|
||||
* 0.25 /* binary -> hexadecimal */
|
||||
)
|
||||
+ 1 /* turn floor into ceil */
|
||||
+ 2; /* account for leading 0x */
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (tmp_length < width)
|
||||
tmp_length = width;
|
||||
|
||||
tmp_length++; /* account for trailing NUL */
|
||||
if (!tmp_length)
|
||||
goto out_of_memory;
|
||||
}
|
||||
|
||||
if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T))
|
||||
tmp = tmpbuf;
|
||||
else
|
||||
{
|
||||
if (SIZE_MAX / sizeof (CHAR_T) < tmp_length)
|
||||
/* Overflow, would lead to out of memory. */
|
||||
goto out_of_memory;
|
||||
tmp = (CHAR_T *) malloc (tmp_length * sizeof (CHAR_T));
|
||||
if (tmp == NULL)
|
||||
/* Out of memory. */
|
||||
goto out_of_memory;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Construct the format string for calling snprintf or
|
||||
sprintf. */
|
||||
p = buf;
|
||||
*p++ = '%';
|
||||
if (dp->flags & FLAG_GROUP)
|
||||
*p++ = '\'';
|
||||
if (dp->flags & FLAG_LEFT)
|
||||
*p++ = '-';
|
||||
if (dp->flags & FLAG_SHOWSIGN)
|
||||
*p++ = '+';
|
||||
if (dp->flags & FLAG_SPACE)
|
||||
*p++ = ' ';
|
||||
if (dp->flags & FLAG_ALT)
|
||||
*p++ = '#';
|
||||
if (dp->flags & FLAG_ZERO)
|
||||
*p++ = '0';
|
||||
if (dp->width_start != dp->width_end)
|
||||
{
|
||||
size_t n = dp->width_end - dp->width_start;
|
||||
memcpy (p, dp->width_start, n * sizeof (CHAR_T));
|
||||
p += n;
|
||||
}
|
||||
if (dp->precision_start != dp->precision_end)
|
||||
{
|
||||
size_t n = dp->precision_end - dp->precision_start;
|
||||
memcpy (p, dp->precision_start, n * sizeof (CHAR_T));
|
||||
p += n;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case TYPE_LONGLONGINT:
|
||||
case TYPE_ULONGLONGINT:
|
||||
*p++ = 'l';
|
||||
/*FALLTHROUGH*/
|
||||
#endif
|
||||
case TYPE_LONGINT:
|
||||
case TYPE_ULONGINT:
|
||||
#ifdef HAVE_WINT_T
|
||||
case TYPE_WIDE_CHAR:
|
||||
#endif
|
||||
#ifdef HAVE_WCHAR_T
|
||||
case TYPE_WIDE_STRING:
|
||||
#endif
|
||||
*p++ = 'l';
|
||||
break;
|
||||
#ifdef HAVE_LONG_DOUBLE
|
||||
case TYPE_LONGDOUBLE:
|
||||
*p++ = 'L';
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*p = dp->conversion;
|
||||
#if USE_SNPRINTF
|
||||
p[1] = '%';
|
||||
p[2] = 'n';
|
||||
p[3] = '\0';
|
||||
#else
|
||||
p[1] = '\0';
|
||||
#endif
|
||||
|
||||
/* Construct the arguments for calling snprintf or sprintf. */
|
||||
prefix_count = 0;
|
||||
if (dp->width_arg_index != ARG_NONE)
|
||||
{
|
||||
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
|
||||
abort ();
|
||||
prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int;
|
||||
}
|
||||
if (dp->precision_arg_index != ARG_NONE)
|
||||
{
|
||||
if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
|
||||
abort ();
|
||||
prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int;
|
||||
}
|
||||
|
||||
#if USE_SNPRINTF
|
||||
/* Prepare checking whether snprintf returns the count
|
||||
via %n. */
|
||||
ENSURE_ALLOCATION (1);
|
||||
result[length] = '\0';
|
||||
#endif
|
||||
|
||||
for (;;)
|
||||
{
|
||||
size_t maxlen;
|
||||
int count;
|
||||
int retcount;
|
||||
|
||||
maxlen = allocated - length;
|
||||
count = -1;
|
||||
retcount = 0;
|
||||
|
||||
#if USE_SNPRINTF
|
||||
# define SNPRINTF_BUF(arg) \
|
||||
switch (prefix_count) \
|
||||
{ \
|
||||
case 0: \
|
||||
retcount = SNPRINTF (result + length, maxlen, buf, \
|
||||
arg, &count); \
|
||||
break; \
|
||||
case 1: \
|
||||
retcount = SNPRINTF (result + length, maxlen, buf, \
|
||||
prefixes[0], arg, &count); \
|
||||
break; \
|
||||
case 2: \
|
||||
retcount = SNPRINTF (result + length, maxlen, buf, \
|
||||
prefixes[0], prefixes[1], arg, \
|
||||
&count); \
|
||||
break; \
|
||||
default: \
|
||||
abort (); \
|
||||
}
|
||||
#else
|
||||
# define SNPRINTF_BUF(arg) \
|
||||
switch (prefix_count) \
|
||||
{ \
|
||||
case 0: \
|
||||
count = sprintf (tmp, buf, arg); \
|
||||
break; \
|
||||
case 1: \
|
||||
count = sprintf (tmp, buf, prefixes[0], arg); \
|
||||
break; \
|
||||
case 2: \
|
||||
count = sprintf (tmp, buf, prefixes[0], prefixes[1],\
|
||||
arg); \
|
||||
break; \
|
||||
default: \
|
||||
abort (); \
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_SCHAR:
|
||||
{
|
||||
int arg = a.arg[dp->arg_index].a.a_schar;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_UCHAR:
|
||||
{
|
||||
unsigned int arg = a.arg[dp->arg_index].a.a_uchar;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_SHORT:
|
||||
{
|
||||
int arg = a.arg[dp->arg_index].a.a_short;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_USHORT:
|
||||
{
|
||||
unsigned int arg = a.arg[dp->arg_index].a.a_ushort;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_INT:
|
||||
{
|
||||
int arg = a.arg[dp->arg_index].a.a_int;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_UINT:
|
||||
{
|
||||
unsigned int arg = a.arg[dp->arg_index].a.a_uint;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_LONGINT:
|
||||
{
|
||||
long int arg = a.arg[dp->arg_index].a.a_longint;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_ULONGINT:
|
||||
{
|
||||
unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case TYPE_LONGLONGINT:
|
||||
{
|
||||
long long int arg = a.arg[dp->arg_index].a.a_longlongint;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
case TYPE_ULONGLONGINT:
|
||||
{
|
||||
unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case TYPE_DOUBLE:
|
||||
{
|
||||
double arg = a.arg[dp->arg_index].a.a_double;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_LONG_DOUBLE
|
||||
case TYPE_LONGDOUBLE:
|
||||
{
|
||||
long double arg = a.arg[dp->arg_index].a.a_longdouble;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case TYPE_CHAR:
|
||||
{
|
||||
int arg = a.arg[dp->arg_index].a.a_char;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_WINT_T
|
||||
case TYPE_WIDE_CHAR:
|
||||
{
|
||||
wint_t arg = a.arg[dp->arg_index].a.a_wide_char;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case TYPE_STRING:
|
||||
{
|
||||
const char *arg = a.arg[dp->arg_index].a.a_string;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_WCHAR_T
|
||||
case TYPE_WIDE_STRING:
|
||||
{
|
||||
const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case TYPE_POINTER:
|
||||
{
|
||||
void *arg = a.arg[dp->arg_index].a.a_pointer;
|
||||
SNPRINTF_BUF (arg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
#if USE_SNPRINTF
|
||||
/* Portability: Not all implementations of snprintf()
|
||||
are ISO C 99 compliant. Determine the number of
|
||||
bytes that snprintf() has produced or would have
|
||||
produced. */
|
||||
if (count >= 0)
|
||||
{
|
||||
/* Verify that snprintf() has NUL-terminated its
|
||||
result. */
|
||||
if (count < maxlen && result[length + count] != '\0')
|
||||
abort ();
|
||||
/* Portability hack. */
|
||||
if (retcount > count)
|
||||
count = retcount;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* snprintf() doesn't understand the '%n'
|
||||
directive. */
|
||||
if (p[1] != '\0')
|
||||
{
|
||||
/* Don't use the '%n' directive; instead, look
|
||||
at the snprintf() return value. */
|
||||
p[1] = '\0';
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Look at the snprintf() return value. */
|
||||
if (retcount < 0)
|
||||
{
|
||||
/* HP-UX 10.20 snprintf() is doubly deficient:
|
||||
It doesn't understand the '%n' directive,
|
||||
*and* it returns -1 (rather than the length
|
||||
that would have been required) when the
|
||||
buffer is too small. */
|
||||
size_t bigger_need =
|
||||
(allocated > 12 ? allocated : 12);
|
||||
ENSURE_ALLOCATION (bigger_need);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
count = retcount;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Attempt to handle failure. */
|
||||
if (count < 0)
|
||||
{
|
||||
if (!(result == resultbuf || result == NULL))
|
||||
free (result);
|
||||
if (buf_malloced != NULL)
|
||||
free (buf_malloced);
|
||||
CLEANUP ();
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if !USE_SNPRINTF
|
||||
if (count >= tmp_length)
|
||||
/* tmp_length was incorrectly calculated - fix the
|
||||
code above! */
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
/* Make room for the result. */
|
||||
if (count >= maxlen)
|
||||
{
|
||||
/* Need at least count bytes. But allocate
|
||||
proportionally, to avoid looping eternally if
|
||||
snprintf() reports a too small count. */
|
||||
ENSURE_ALLOCATION (count < allocated
|
||||
? allocated : count);
|
||||
#if USE_SNPRINTF
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_SNPRINTF
|
||||
/* The snprintf() result did fit. */
|
||||
#else
|
||||
/* Append the sprintf() result. */
|
||||
memcpy (result + length, tmp, count * sizeof (CHAR_T));
|
||||
if (tmp != tmpbuf)
|
||||
free (tmp);
|
||||
#endif
|
||||
|
||||
length += count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the final NUL. */
|
||||
ENSURE_ALLOCATION (1);
|
||||
result[length] = '\0';
|
||||
|
||||
if (result != resultbuf && length + 1 < allocated)
|
||||
{
|
||||
/* Shrink the allocated memory if possible. */
|
||||
CHAR_T *memory;
|
||||
|
||||
memory = (CHAR_T *) realloc (result, (length + 1) * sizeof (CHAR_T));
|
||||
if (memory != NULL)
|
||||
result = memory;
|
||||
}
|
||||
|
||||
if (buf_malloced != NULL)
|
||||
free (buf_malloced);
|
||||
CLEANUP ();
|
||||
*lengthp = length;
|
||||
if (length > INT_MAX)
|
||||
goto length_overflow;
|
||||
return result;
|
||||
|
||||
length_overflow:
|
||||
/* We could produce such a big string, but its length doesn't fit into
|
||||
an 'int'. POSIX says that snprintf() fails with errno = EOVERFLOW in
|
||||
this case. */
|
||||
if (result != resultbuf)
|
||||
free (result);
|
||||
errno = EOVERFLOW;
|
||||
return NULL;
|
||||
|
||||
out_of_memory:
|
||||
if (!(result == resultbuf || result == NULL))
|
||||
free (result);
|
||||
if (buf_malloced != NULL)
|
||||
free (buf_malloced);
|
||||
out_of_memory_1:
|
||||
CLEANUP ();
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#undef SNPRINTF
|
||||
#undef USE_SNPRINTF
|
||||
#undef PRINTF_PARSE
|
||||
#undef DIRECTIVES
|
||||
#undef DIRECTIVE
|
||||
#undef CHAR_T
|
||||
#undef VASNPRINTF
|
||||
@@ -1,3 +0,0 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
*.m4
|
||||
@@ -1,20 +0,0 @@
|
||||
index.html
|
||||
*.po
|
||||
LINGUAS
|
||||
Makefile.in.in
|
||||
Makevars.template
|
||||
Rules-quot
|
||||
boldquot.sed
|
||||
en@boldquot.header
|
||||
en@quot.header
|
||||
insert-header.sin
|
||||
quot.sed
|
||||
remove-potcdate.sin
|
||||
Makefile.in
|
||||
POTFILES
|
||||
Makefile
|
||||
tar.pot
|
||||
remove-potcdate.sed
|
||||
*.gmo
|
||||
*.mo
|
||||
stamp-po
|
||||
41
po/Makevars
41
po/Makevars
@@ -1,41 +0,0 @@
|
||||
# Makefile variables for PO directory in any package using GNU gettext.
|
||||
|
||||
# Usually the message domain is the same as the package name.
|
||||
DOMAIN = $(PACKAGE)
|
||||
|
||||
# These two variables depend on the location of this directory.
|
||||
subdir = po
|
||||
top_builddir = ..
|
||||
|
||||
# These options get passed to xgettext.
|
||||
XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
|
||||
|
||||
# This is the copyright holder that gets inserted into the header of the
|
||||
# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
|
||||
# package. (Note that the msgstr strings, extracted from the package's
|
||||
# sources, belong to the copyright holder of the package.) Translators are
|
||||
# expected to transfer the copyright for their translations to this person
|
||||
# or entity, or to disclaim their copyright. The empty string stands for
|
||||
# the public domain; in this case the translators are expected to disclaim
|
||||
# their copyright.
|
||||
COPYRIGHT_HOLDER = Free Software Foundation, Inc.
|
||||
|
||||
# This is the email address or URL to which the translators shall report
|
||||
# bugs in the untranslated strings:
|
||||
# - Strings which are not entire sentences, see the maintainer guidelines
|
||||
# in the GNU gettext documentation, section 'Preparing Strings'.
|
||||
# - Strings which use unclear terms or require additional context to be
|
||||
# understood.
|
||||
# - Strings which make invalid assumptions about notation of date, time or
|
||||
# money.
|
||||
# - Pluralisation problems.
|
||||
# - Incorrect English spelling.
|
||||
# - Incorrect formatting.
|
||||
# It can be your email address, or a mailing list address where translators
|
||||
# can write to without being subscribed, or the URL of a web page through
|
||||
# which the translators can contact you.
|
||||
MSGID_BUGS_ADDRESS = bug-tar@gnu.org
|
||||
|
||||
# This is the list of locale categories, beyond LC_MESSAGES, for which the
|
||||
# message catalogs shall be used. It is usually empty.
|
||||
EXTRA_LOCALE_CATEGORIES =
|
||||
@@ -46,7 +46,6 @@ src/delete.c
|
||||
src/extract.c
|
||||
src/incremen.c
|
||||
src/list.c
|
||||
src/mangle.c
|
||||
src/misc.c
|
||||
src/names.c
|
||||
src/tar.c
|
||||
|
||||
468
scripts/xsparse.c
Normal file
468
scripts/xsparse.c
Normal file
@@ -0,0 +1,468 @@
|
||||
/* xsparse - expands compressed sparse file images extracted from GNU tar
|
||||
archives.
|
||||
|
||||
Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
Written by Sergey Poznyakoff
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Bound on length of the string representing an off_t.
|
||||
See INT_STRLEN_BOUND in intprops.h for explanation */
|
||||
#define OFF_T_STRLEN_BOUND ((sizeof (off_t) * CHAR_BIT) * 146 / 485 + 1)
|
||||
#define OFF_T_STRSIZE_BOUND (OFF_T_STRLEN_BOUND+1)
|
||||
|
||||
#define BLOCKSIZE 512
|
||||
|
||||
struct sp_array
|
||||
{
|
||||
off_t offset;
|
||||
size_t numbytes;
|
||||
};
|
||||
|
||||
char *progname;
|
||||
int verbose;
|
||||
|
||||
void
|
||||
die (int code, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf (stderr, "%s: ", progname);
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
fprintf (stderr, "\n");
|
||||
exit (code);
|
||||
}
|
||||
|
||||
void *
|
||||
emalloc (size_t size)
|
||||
{
|
||||
char *p = malloc (size);
|
||||
if (!p)
|
||||
die (1, "not enough memory");
|
||||
return p;
|
||||
}
|
||||
|
||||
off_t
|
||||
string_to_off (char *p, char **endp)
|
||||
{
|
||||
off_t v = 0;
|
||||
|
||||
for (; *p; p++)
|
||||
{
|
||||
int digit = *p - '0';
|
||||
off_t x = v * 10;
|
||||
if (9 < (unsigned) digit)
|
||||
{
|
||||
if (endp)
|
||||
{
|
||||
*endp = p;
|
||||
break;
|
||||
}
|
||||
die (1, "number parse error near %s", p);
|
||||
}
|
||||
else if (x / 10 != v)
|
||||
die (1, "number out of allowed range, near %s", p);
|
||||
v = x + digit;
|
||||
if (v < 0)
|
||||
die (1, "negative number");
|
||||
}
|
||||
if (endp)
|
||||
*endp = p;
|
||||
return v;
|
||||
}
|
||||
|
||||
size_t
|
||||
string_to_size (char *p, char **endp)
|
||||
{
|
||||
off_t v = string_to_off (p, endp);
|
||||
size_t ret = v;
|
||||
if (ret != v)
|
||||
die (1, "number too big");
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t sparse_map_size;
|
||||
struct sp_array *sparse_map;
|
||||
|
||||
void
|
||||
get_line (char *s, int size, FILE *stream)
|
||||
{
|
||||
char *p = fgets (s, size, stream);
|
||||
size_t len;
|
||||
|
||||
if (!p)
|
||||
die (1, "unexpected end of file");
|
||||
len = strlen (p);
|
||||
if (s[len - 1] != '\n')
|
||||
die (1, "buffer overflow");
|
||||
s[len - 1] = 0;
|
||||
}
|
||||
|
||||
int
|
||||
get_var (FILE *fp, char **name, char **value)
|
||||
{
|
||||
static char *buffer;
|
||||
static size_t bufsize = OFF_T_STRSIZE_BOUND;
|
||||
char *p, *q;
|
||||
|
||||
buffer = emalloc (bufsize);
|
||||
do
|
||||
{
|
||||
size_t len, s;
|
||||
|
||||
if (!fgets (buffer, bufsize, fp))
|
||||
return 0;
|
||||
len = strlen (buffer);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
s = string_to_size (buffer, &p);
|
||||
if (*p != ' ')
|
||||
die (1, "malformed header: expected space but found %s", p);
|
||||
if (buffer[len-1] != '\n')
|
||||
{
|
||||
if (bufsize < s + 1)
|
||||
{
|
||||
bufsize = s + 1;
|
||||
buffer = realloc (buffer, bufsize);
|
||||
if (!buffer)
|
||||
die (1, "not enough memory");
|
||||
}
|
||||
if (!fgets (buffer + len, s - len + 1, fp))
|
||||
die (1, "unexpected end of file or read error");
|
||||
}
|
||||
p++;
|
||||
}
|
||||
while (memcmp (p, "GNU.sparse.", 11));
|
||||
|
||||
p += 11;
|
||||
q = strchr (p, '=');
|
||||
if (!q)
|
||||
die (1, "malformed header: expected `=' not found");
|
||||
*q++ = 0;
|
||||
q[strlen (q) - 1] = 0;
|
||||
*name = p;
|
||||
*value = q;
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *outname;
|
||||
off_t outsize;
|
||||
unsigned version_major;
|
||||
unsigned version_minor;
|
||||
|
||||
void
|
||||
read_xheader (char *name)
|
||||
{
|
||||
char *kw, *val;
|
||||
FILE *fp = fopen (name, "r");
|
||||
char *expect = NULL;
|
||||
size_t i = 0;
|
||||
|
||||
if (verbose)
|
||||
printf ("Reading extended header file\n");
|
||||
|
||||
while (get_var (fp, &kw, &val))
|
||||
{
|
||||
if (verbose)
|
||||
printf ("Found variable GNU.sparse.%s = %s\n", kw, val);
|
||||
|
||||
if (expect && strcmp (kw, expect))
|
||||
die (1, "bad keyword sequence: expected `%s' but found `%s'",
|
||||
expect, kw);
|
||||
expect = NULL;
|
||||
if (strcmp (kw, "name") == 0)
|
||||
{
|
||||
outname = emalloc (strlen (val) + 1);
|
||||
strcpy (outname, val);
|
||||
}
|
||||
else if (strcmp (kw, "major") == 0)
|
||||
{
|
||||
version_major = string_to_size (val, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "minor") == 0)
|
||||
{
|
||||
version_minor = string_to_size (val, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "realsize") == 0
|
||||
|| strcmp (kw, "size") == 0)
|
||||
{
|
||||
outsize = string_to_off (val, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "numblocks") == 0)
|
||||
{
|
||||
sparse_map_size = string_to_size (val, NULL);
|
||||
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
||||
}
|
||||
else if (strcmp (kw, "offset") == 0)
|
||||
{
|
||||
sparse_map[i].offset = string_to_off (val, NULL);
|
||||
expect = "numbytes";
|
||||
}
|
||||
else if (strcmp (kw, "numbytes") == 0)
|
||||
{
|
||||
sparse_map[i++].numbytes = string_to_size (val, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "map") == 0)
|
||||
{
|
||||
for (i = 0; i < sparse_map_size; i++)
|
||||
{
|
||||
sparse_map[i].offset = string_to_off (val, &val);
|
||||
if (*val != ',')
|
||||
die (1, "bad GNU.sparse.map: expected `,' but found `%c'",
|
||||
*val);
|
||||
sparse_map[i].numbytes = string_to_size (val+1, &val);
|
||||
if (*val != ',')
|
||||
{
|
||||
if (!(*val == 0 && i == sparse_map_size-1))
|
||||
die (1, "bad GNU.sparse.map: expected `,' but found `%c'",
|
||||
*val);
|
||||
}
|
||||
else
|
||||
val++;
|
||||
}
|
||||
if (*val)
|
||||
die (1, "bad GNU.sparse.map: garbage at the end");
|
||||
}
|
||||
}
|
||||
if (expect)
|
||||
die (1, "bad keyword sequence: expected `%s' not found", expect);
|
||||
if (version_major == 0 && sparse_map_size == 0)
|
||||
die (1, "size of the sparse map unknown");
|
||||
if (i != sparse_map_size)
|
||||
die (1, "not all sparse entries supplied");
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
void
|
||||
read_map (FILE *ifp)
|
||||
{
|
||||
size_t i;
|
||||
char nbuf[OFF_T_STRSIZE_BOUND];
|
||||
|
||||
if (verbose)
|
||||
printf ("Reading v.1.0 sparse map\n");
|
||||
|
||||
get_line (nbuf, sizeof nbuf, ifp);
|
||||
sparse_map_size = string_to_size (nbuf, NULL);
|
||||
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
||||
|
||||
for (i = 0; i < sparse_map_size; i++)
|
||||
{
|
||||
get_line (nbuf, sizeof nbuf, ifp);
|
||||
sparse_map[i].offset = string_to_off (nbuf, NULL);
|
||||
get_line (nbuf, sizeof nbuf, ifp);
|
||||
sparse_map[i].numbytes = string_to_size (nbuf, NULL);
|
||||
}
|
||||
|
||||
fseek (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
|
||||
SEEK_SET);
|
||||
}
|
||||
|
||||
void
|
||||
expand_sparse (FILE *sfp, int ofd)
|
||||
{
|
||||
size_t i;
|
||||
size_t maxbytes = 0;
|
||||
char *buffer;
|
||||
|
||||
for (i = 0; i < sparse_map_size; i++)
|
||||
if (maxbytes < sparse_map[i].numbytes)
|
||||
maxbytes = sparse_map[i].numbytes;
|
||||
|
||||
for (buffer = malloc (maxbytes); !buffer; maxbytes /= 2)
|
||||
if (maxbytes == 0)
|
||||
die (1, "not enough memory");
|
||||
|
||||
for (i = 0; i < sparse_map_size; i++)
|
||||
{
|
||||
size_t size = sparse_map[i].numbytes;
|
||||
|
||||
lseek (ofd, sparse_map[i].offset, SEEK_SET);
|
||||
while (size)
|
||||
{
|
||||
size_t rdsize = (size < maxbytes) ? size : maxbytes;
|
||||
if (rdsize != fread (buffer, 1, rdsize, sfp))
|
||||
die (1, "read error (%d)", errno);
|
||||
if (rdsize != write (ofd, buffer, rdsize))
|
||||
die (1, "write error (%d)", errno);
|
||||
size -= rdsize;
|
||||
}
|
||||
}
|
||||
free (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
usage (int code)
|
||||
{
|
||||
printf ("Usage: %s [OPTIONS] infile [outfile]\n", progname);
|
||||
printf ("%s: expand sparse files extracted from GNU archives\n",
|
||||
progname);
|
||||
printf ("\nOPTIONS are:\n\n");
|
||||
printf (" -h Display this help list\n");
|
||||
printf (" -n Dry run: do nothing, print what would have been done\n");
|
||||
printf (" -v Increase verbosity level\n");
|
||||
printf (" -x FILE Parse extended header FILE\n\n");
|
||||
|
||||
exit (code);
|
||||
}
|
||||
|
||||
void
|
||||
guess_outname (char *name)
|
||||
{
|
||||
char *p;
|
||||
char *s;
|
||||
|
||||
if (name[0] == '.' && name[1] == '/')
|
||||
name += 2;
|
||||
|
||||
p = name + strlen (name) - 1;
|
||||
s = NULL;
|
||||
|
||||
for (; p > name && *p != '/'; p--)
|
||||
;
|
||||
if (*p == '/')
|
||||
s = p + 1;
|
||||
if (p != name)
|
||||
{
|
||||
for (p--; p > name && *p != '/'; p--)
|
||||
;
|
||||
}
|
||||
|
||||
if (*p != '/')
|
||||
{
|
||||
if (s)
|
||||
outname = s;
|
||||
else
|
||||
{
|
||||
outname = emalloc (4 + strlen (name));
|
||||
strcpy (outname, "../");
|
||||
strcpy (outname + 3, name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t len = p - name + 1;
|
||||
outname = emalloc (len + strlen (s) + 1);
|
||||
memcpy (outname, name, len);
|
||||
strcpy (outname + len, s);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
int dry_run = 0;
|
||||
char *xheader_file = NULL;
|
||||
char *inname;
|
||||
FILE *ifp;
|
||||
struct stat st;
|
||||
int ofd;
|
||||
|
||||
progname = argv[0];
|
||||
while ((c = getopt (argc, argv, "hnvx:")) != EOF)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
usage (0);
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
xheader_file = optarg;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
dry_run = 1;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
|
||||
default:
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc == 0 || argc > 2)
|
||||
usage (1);
|
||||
|
||||
if (xheader_file)
|
||||
read_xheader (xheader_file);
|
||||
|
||||
inname = argv[0];
|
||||
if (argv[1])
|
||||
outname = argv[1];
|
||||
|
||||
if (stat (inname, &st))
|
||||
die (1, "cannot stat %s (%d)", inname, errno);
|
||||
|
||||
ifp = fopen (inname, "r");
|
||||
if (ifp == NULL)
|
||||
die (1, "cannot open file %s (%d)", inname, errno);
|
||||
|
||||
if (!xheader_file || version_major == 1)
|
||||
read_map (ifp);
|
||||
|
||||
if (!outname)
|
||||
guess_outname (inname);
|
||||
|
||||
ofd = open (outname, O_RDWR|O_CREAT|O_TRUNC, st.st_mode);
|
||||
if (ofd == -1)
|
||||
die (1, "cannot open file %s (%d)", outname, errno);
|
||||
|
||||
if (verbose)
|
||||
printf ("Expanding file `%s' to `%s'\n", inname, outname);
|
||||
|
||||
if (dry_run)
|
||||
{
|
||||
printf ("Finished dry run\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
expand_sparse (ifp, ofd);
|
||||
|
||||
fclose (ifp);
|
||||
close (ofd);
|
||||
|
||||
if (verbose)
|
||||
printf ("Done\n");
|
||||
|
||||
if (outsize)
|
||||
{
|
||||
if (stat (outname, &st))
|
||||
die (1, "cannot stat output file %s (%d)", outname, errno);
|
||||
if (st.st_size != outsize)
|
||||
die (1, "expanded file has wrong size");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Makefile for GNU tar sources.
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003 Free
|
||||
# Software Foundation, Inc.
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2003, 2006
|
||||
# Free Software Foundation, Inc.
|
||||
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
@@ -30,7 +30,6 @@ tar_SOURCES = \
|
||||
xheader.c\
|
||||
incremen.c\
|
||||
list.c\
|
||||
mangle.c\
|
||||
misc.c\
|
||||
names.c\
|
||||
sparse.c\
|
||||
@@ -40,9 +39,7 @@ tar_SOURCES = \
|
||||
update.c\
|
||||
utf8.c
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/lib -I../ -I../lib
|
||||
|
||||
tar.o: ../lib/localedir.h
|
||||
INCLUDES = -I$(top_srcdir)/lib -I../ -I../lib
|
||||
|
||||
LDADD = ../lib/libtar.a $(LIBINTL) $(LIBICONV)
|
||||
|
||||
|
||||
37
src/buffer.c
37
src/buffer.c
@@ -338,6 +338,9 @@ print_total_stats ()
|
||||
print_stats (stderr, _("Total bytes read"),
|
||||
records_read * record_size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,16 +436,6 @@ _open_archive (enum access_mode wanted_access)
|
||||
{
|
||||
int backed_up_flag = 0;
|
||||
|
||||
if (index_file_name)
|
||||
{
|
||||
stdlis = freopen (index_file_name, "w", stdout);
|
||||
if (! stdlis)
|
||||
open_error (index_file_name);
|
||||
close_stdout_set_file_name (index_file_name);
|
||||
}
|
||||
else
|
||||
stdlis = to_stdout_option ? stderr : stdout;
|
||||
|
||||
if (record_size == 0)
|
||||
FATAL_ERROR ((0, 0, _("Invalid value for record_size")));
|
||||
|
||||
@@ -482,7 +475,8 @@ _open_archive (enum access_mode wanted_access)
|
||||
break;
|
||||
}
|
||||
|
||||
if (wanted_access == ACCESS_WRITE
|
||||
if (!index_file_name
|
||||
&& wanted_access == ACCESS_WRITE
|
||||
&& strcmp (archive_name_array[0], "-") == 0)
|
||||
stdlis = stderr;
|
||||
}
|
||||
@@ -510,14 +504,16 @@ _open_archive (enum access_mode wanted_access)
|
||||
|
||||
case ACCESS_WRITE:
|
||||
archive = STDOUT_FILENO;
|
||||
stdlis = stderr;
|
||||
if (!index_file_name)
|
||||
stdlis = stderr;
|
||||
break;
|
||||
|
||||
case ACCESS_UPDATE:
|
||||
archive = STDIN_FILENO;
|
||||
stdlis = stderr;
|
||||
write_archive_to_stdout = true;
|
||||
record_end = record_start; /* set up for 1st record = # 0 */
|
||||
if (!index_file_name)
|
||||
stdlis = stderr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1033,7 +1029,8 @@ new_volume (enum access_mode mode)
|
||||
assign_string (&volume_label, NULL);
|
||||
assign_string (&continued_file_name, NULL);
|
||||
continued_file_size = continued_file_offset = 0;
|
||||
|
||||
current_block = record_start;
|
||||
|
||||
if (rmtclose (archive) != 0)
|
||||
close_warn (*archive_name_cursor);
|
||||
|
||||
@@ -1127,22 +1124,24 @@ try_new_volume ()
|
||||
size_t status;
|
||||
union block *header;
|
||||
struct tar_stat_info dummy;
|
||||
|
||||
int access;
|
||||
|
||||
switch (subcommand_option)
|
||||
{
|
||||
case APPEND_SUBCOMMAND:
|
||||
case CAT_SUBCOMMAND:
|
||||
case UPDATE_SUBCOMMAND:
|
||||
if (!new_volume (ACCESS_UPDATE))
|
||||
return true;
|
||||
access = ACCESS_UPDATE;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!new_volume (ACCESS_READ))
|
||||
return true;
|
||||
access = ACCESS_READ;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!new_volume (access))
|
||||
return true;
|
||||
|
||||
while ((status = rmtread (archive, record_start->buffer, record_size))
|
||||
== SAFE_READ_ERROR)
|
||||
archive_read_error ();
|
||||
|
||||
193
src/common.h
193
src/common.h
@@ -197,6 +197,11 @@ GLOBAL bool multi_volume_option;
|
||||
do not get archived (also see after_date_option above). */
|
||||
GLOBAL struct timespec newer_mtime_option;
|
||||
|
||||
/* If true, override actual mtime (see below) */
|
||||
GLOBAL bool set_mtime_option;
|
||||
/* Value to be put in mtime header field instead of the actual mtime */
|
||||
GLOBAL struct timespec mtime_option;
|
||||
|
||||
/* Return true if newer_mtime_option is initialized. */
|
||||
#define NEWER_OPTION_INITIALIZED(opt) (0 <= (opt).tv_nsec)
|
||||
|
||||
@@ -247,6 +252,8 @@ GLOBAL size_t strip_name_components;
|
||||
GLOBAL bool show_omitted_dirs_option;
|
||||
|
||||
GLOBAL bool sparse_option;
|
||||
GLOBAL unsigned tar_sparse_major;
|
||||
GLOBAL unsigned tar_sparse_minor;
|
||||
|
||||
GLOBAL bool starting_file_option;
|
||||
|
||||
@@ -289,7 +296,7 @@ GLOBAL int archive;
|
||||
/* Nonzero when outputting to /dev/null. */
|
||||
GLOBAL bool dev_null_output;
|
||||
|
||||
/* Timestamps: */
|
||||
/* Timestamps: */
|
||||
GLOBAL struct timespec start_time; /* when we started execution */
|
||||
GLOBAL struct timespec volume_start_time; /* when the current volume was
|
||||
opened*/
|
||||
@@ -316,8 +323,6 @@ struct name
|
||||
Set with the -C option. */
|
||||
uintmax_t found_count; /* number of times a matching file has
|
||||
been found */
|
||||
int explicit; /* this name was explicitely given in the
|
||||
command line */
|
||||
int matching_flags; /* this name is a regexp, not literal */
|
||||
char const *dir_contents; /* for incremental_option */
|
||||
|
||||
@@ -375,22 +380,23 @@ extern char *continued_file_name;
|
||||
extern uintmax_t continued_file_size;
|
||||
extern uintmax_t continued_file_offset;
|
||||
|
||||
size_t available_space_after (union block *);
|
||||
size_t available_space_after (union block *pointer);
|
||||
off_t current_block_ordinal (void);
|
||||
void close_archive (void);
|
||||
void closeout_volume_number (void);
|
||||
void compute_duration (void);
|
||||
union block *find_next_block (void);
|
||||
void flush_read (void);
|
||||
void flush_write (void);
|
||||
void flush_archive (void);
|
||||
void init_volume_number (void);
|
||||
void open_archive (enum access_mode);
|
||||
void open_archive (enum access_mode mode);
|
||||
void print_total_stats (void);
|
||||
void reset_eof (void);
|
||||
void set_next_block_after (union block *);
|
||||
void set_next_block_after (union block *block);
|
||||
void clear_read_error_count (void);
|
||||
void xclose (int fd);
|
||||
void archive_write_error (ssize_t) __attribute__ ((noreturn));
|
||||
void archive_write_error (ssize_t status) __attribute__ ((noreturn));
|
||||
void archive_read_error (void);
|
||||
off_t seek_archive (off_t size);
|
||||
void set_start_time (void);
|
||||
@@ -411,12 +417,13 @@ enum dump_status
|
||||
dump_status_not_implemented
|
||||
};
|
||||
|
||||
bool file_dumpable_p (struct tar_stat_info *);
|
||||
bool file_dumpable_p (struct tar_stat_info *st);
|
||||
void create_archive (void);
|
||||
void pad_archive (off_t size_left);
|
||||
void dump_file (const char *, int, dev_t);
|
||||
void dump_file (const char *st, int top_level, dev_t parent_device);
|
||||
union block *start_header (struct tar_stat_info *st);
|
||||
void finish_header (struct tar_stat_info *, union block *, off_t);
|
||||
void finish_header (struct tar_stat_info *st, union block *header,
|
||||
off_t block_ordinal);
|
||||
void simple_finish_header (union block *header);
|
||||
union block * write_extended (bool global, struct tar_stat_info *st,
|
||||
union block *old_header);
|
||||
@@ -436,16 +443,16 @@ void check_links (void);
|
||||
#define UNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf))
|
||||
#define GNAME_TO_CHARS(name,buf) string_to_chars (name, buf, sizeof(buf))
|
||||
|
||||
bool gid_to_chars (gid_t, char *, size_t);
|
||||
bool major_to_chars (major_t, char *, size_t);
|
||||
bool minor_to_chars (minor_t, char *, size_t);
|
||||
bool mode_to_chars (mode_t, char *, size_t);
|
||||
bool off_to_chars (off_t, char *, size_t);
|
||||
bool size_to_chars (size_t, char *, size_t);
|
||||
bool time_to_chars (time_t, char *, size_t);
|
||||
bool uid_to_chars (uid_t, char *, size_t);
|
||||
bool uintmax_to_chars (uintmax_t, char *, size_t);
|
||||
void string_to_chars (char const *, char *, size_t);
|
||||
bool gid_to_chars (gid_t gid, char *buf, size_t size);
|
||||
bool major_to_chars (major_t m, char *buf, size_t size);
|
||||
bool minor_to_chars (minor_t m, char *buf, size_t size);
|
||||
bool mode_to_chars (mode_t m, char *buf, size_t size);
|
||||
bool off_to_chars (off_t off, char *buf, size_t size);
|
||||
bool size_to_chars (size_t v, char *buf, size_t size);
|
||||
bool time_to_chars (time_t t, char *buf, size_t size);
|
||||
bool uid_to_chars (uid_t uid, char *buf, size_t size);
|
||||
bool uintmax_to_chars (uintmax_t v, char *buf, size_t size);
|
||||
void string_to_chars (char const *s, char *buf, size_t size);
|
||||
|
||||
/* Module diffarch.c. */
|
||||
|
||||
@@ -468,11 +475,11 @@ void delete_archive_members (void);
|
||||
|
||||
/* Module incremen.c. */
|
||||
|
||||
char *get_directory_contents (char *, dev_t);
|
||||
char *get_directory_contents (char *dir_name, dev_t device);
|
||||
const char *append_incremental_renames (const char *dump);
|
||||
void read_directory_file (void);
|
||||
void write_directory_file (void);
|
||||
void purge_directory (char const *);
|
||||
void purge_directory (char const *directory_name);
|
||||
void list_dumpdir (char *buffer, size_t size);
|
||||
void update_parent_directory (const char *name);
|
||||
|
||||
@@ -504,9 +511,9 @@ extern enum archive_format current_format;
|
||||
extern size_t recent_long_name_blocks;
|
||||
extern size_t recent_long_link_blocks;
|
||||
|
||||
void decode_header (union block *, struct tar_stat_info *,
|
||||
enum archive_format *, int);
|
||||
char const *tartime (struct timespec, bool);
|
||||
void decode_header (union block *header, struct tar_stat_info *stat_info,
|
||||
enum archive_format *format_pointer, int do_user_group);
|
||||
char const *tartime (struct timespec t, bool full_time);
|
||||
|
||||
#define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where))
|
||||
#define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where))
|
||||
@@ -518,45 +525,39 @@ char const *tartime (struct timespec, bool);
|
||||
#define UID_FROM_HEADER(where) uid_from_header (where, sizeof (where))
|
||||
#define UINTMAX_FROM_HEADER(where) uintmax_from_header (where, sizeof (where))
|
||||
|
||||
gid_t gid_from_header (const char *, size_t);
|
||||
major_t major_from_header (const char *, size_t);
|
||||
minor_t minor_from_header (const char *, size_t);
|
||||
mode_t mode_from_header (const char *, size_t);
|
||||
off_t off_from_header (const char *, size_t);
|
||||
size_t size_from_header (const char *, size_t);
|
||||
time_t time_from_header (const char *, size_t);
|
||||
uid_t uid_from_header (const char *, size_t);
|
||||
uintmax_t uintmax_from_header (const char *, size_t);
|
||||
gid_t gid_from_header (const char *buf, size_t size);
|
||||
major_t major_from_header (const char *buf, size_t size);
|
||||
minor_t minor_from_header (const char *buf, size_t size);
|
||||
mode_t mode_from_header (const char *buf, size_t size);
|
||||
off_t off_from_header (const char *buf, size_t size);
|
||||
size_t size_from_header (const char *buf, size_t size);
|
||||
time_t time_from_header (const char *buf, size_t size);
|
||||
uid_t uid_from_header (const char *buf, size_t size);
|
||||
uintmax_t uintmax_from_header (const char * buf, size_t size);
|
||||
|
||||
void list_archive (void);
|
||||
void print_for_mkdir (char *, int, mode_t);
|
||||
void print_header (struct tar_stat_info *, off_t);
|
||||
void read_and (void (*) (void));
|
||||
void print_for_mkdir (char *dirname, int length, mode_t mode);
|
||||
void print_header (struct tar_stat_info *st, off_t block_ordinal);
|
||||
void read_and (void (*do_something) (void));
|
||||
enum read_header read_header_primitive (bool raw_extended_headers,
|
||||
struct tar_stat_info *info);
|
||||
enum read_header read_header (bool);
|
||||
enum read_header read_header (bool raw_extended_headers);
|
||||
enum read_header tar_checksum (union block *header, bool silent);
|
||||
void skip_file (off_t);
|
||||
void skip_file (off_t size);
|
||||
void skip_member (void);
|
||||
|
||||
/* Module mangle.c. */
|
||||
|
||||
void extract_mangle (void);
|
||||
|
||||
/* Module misc.c. */
|
||||
|
||||
void assign_string (char **, const char *);
|
||||
char *quote_copy_string (const char *);
|
||||
int unquote_string (char *);
|
||||
void assign_string (char **dest, const char *src);
|
||||
char *quote_copy_string (const char *str);
|
||||
int unquote_string (char *str);
|
||||
|
||||
void code_ns_fraction (int, char *);
|
||||
char const *code_timespec (struct timespec, char *);
|
||||
void code_ns_fraction (int ns, char *p);
|
||||
char const *code_timespec (struct timespec ts, char *sbuf);
|
||||
enum { BILLION = 1000000000, LOG10_BILLION = 9 };
|
||||
enum { TIMESPEC_STRSIZE_BOUND =
|
||||
UINTMAX_STRSIZE_BOUND + LOG10_BILLION + sizeof "-." - 1 };
|
||||
|
||||
size_t dot_dot_prefix_len (char const *);
|
||||
|
||||
enum remove_option
|
||||
{
|
||||
ORDINARY_REMOVE_OPTION,
|
||||
@@ -571,31 +572,32 @@ enum remove_option
|
||||
meta-info to the incremental dumps, this should become unnecessary */
|
||||
WANT_DIRECTORY_REMOVE_OPTION
|
||||
};
|
||||
int remove_any_file (const char *, enum remove_option);
|
||||
bool maybe_backup_file (const char *, int);
|
||||
int remove_any_file (const char *file_name, enum remove_option option);
|
||||
bool maybe_backup_file (const char *file_name, bool this_is_the_archive);
|
||||
void undo_last_backup (void);
|
||||
|
||||
int deref_stat (bool, char const *, struct stat *);
|
||||
int deref_stat (bool deref, char const *name, struct stat *buf);
|
||||
|
||||
int chdir_arg (char const *);
|
||||
void chdir_do (int);
|
||||
void closeopen (void);
|
||||
int chdir_arg (char const *dir);
|
||||
void chdir_do (int dir);
|
||||
|
||||
void close_diag (char const *name);
|
||||
void open_diag (char const *name);
|
||||
void read_diag_details (char const *name, off_t offset, size_t size);
|
||||
void readlink_diag (char const *name);
|
||||
void savedir_diag (char const *name);
|
||||
void seek_diag_details (char const *, off_t);
|
||||
void seek_diag_details (char const *name, off_t offset);
|
||||
void stat_diag (char const *name);
|
||||
void write_error_details (char const *, size_t, size_t);
|
||||
void write_fatal (char const *) __attribute__ ((noreturn));
|
||||
void write_fatal_details (char const *, ssize_t, size_t)
|
||||
void write_error_details (char const *name, size_t status, size_t size);
|
||||
void write_fatal (char const *name) __attribute__ ((noreturn));
|
||||
void write_fatal_details (char const *name, ssize_t status, size_t size)
|
||||
__attribute__ ((noreturn));
|
||||
|
||||
pid_t xfork (void);
|
||||
void xpipe (int[2]);
|
||||
void xpipe (int fd[2]);
|
||||
|
||||
void *page_aligned_alloc (void **, size_t);
|
||||
void *page_aligned_alloc (void **ptr, size_t size);
|
||||
int set_file_atime (int fd, char const *file,
|
||||
struct timespec const timespec[2]);
|
||||
|
||||
@@ -603,35 +605,35 @@ int set_file_atime (int fd, char const *file,
|
||||
|
||||
extern struct name *gnu_list_name;
|
||||
|
||||
void gid_to_gname (gid_t, char **gname);
|
||||
int gname_to_gid (char const *, gid_t *);
|
||||
void uid_to_uname (uid_t, char **uname);
|
||||
int uname_to_uid (char const *, uid_t *);
|
||||
void gid_to_gname (gid_t gid, char **gname);
|
||||
int gname_to_gid (char const *gname, gid_t *pgid);
|
||||
void uid_to_uname (uid_t uid, char **uname);
|
||||
int uname_to_uid (char const *uname, uid_t *puid);
|
||||
|
||||
void init_names (void);
|
||||
void name_add_name (const char *, int);
|
||||
void name_init (void);
|
||||
void name_add_name (const char *name, int matching_flags);
|
||||
void name_add_dir (const char *name);
|
||||
void name_term (void);
|
||||
const char *name_next (int);
|
||||
const char *name_next (int change_dirs);
|
||||
void name_gather (void);
|
||||
struct name *addname (char const *, int);
|
||||
int name_match (const char *);
|
||||
struct name *addname (char const *string, int change_dir);
|
||||
bool name_match (const char *name);
|
||||
void names_notfound (void);
|
||||
void collect_and_sort_names (void);
|
||||
struct name *name_scan (const char *, bool);
|
||||
struct name *name_scan (const char *name);
|
||||
char *name_from_list (void);
|
||||
void blank_name_list (void);
|
||||
char *new_name (const char *, const char *);
|
||||
char *new_name (const char *dir_name, const char *name);
|
||||
size_t stripped_prefix_len (char const *file_name, size_t num);
|
||||
bool all_names_found (struct tar_stat_info *);
|
||||
bool all_names_found (struct tar_stat_info *st);
|
||||
|
||||
bool excluded_name (char const *);
|
||||
bool excluded_name (char const *name);
|
||||
|
||||
void add_avoided_name (char const *);
|
||||
bool is_avoided_name (char const *);
|
||||
bool is_individual_file (char const *);
|
||||
void add_avoided_name (char const *name);
|
||||
bool is_avoided_name (char const *name);
|
||||
bool is_individual_file (char const *name);
|
||||
|
||||
bool contains_dot_dot (char const *);
|
||||
bool contains_dot_dot (char const *name);
|
||||
|
||||
#define ISFOUND(c) ((occurrence_option == 0) ? (c)->found_count : \
|
||||
(c)->found_count == occurrence_option)
|
||||
@@ -642,8 +644,8 @@ bool contains_dot_dot (char const *);
|
||||
|
||||
void usage (int);
|
||||
|
||||
int confirm (const char *, const char *);
|
||||
void request_stdin (const char *);
|
||||
int confirm (const char *message_action, const char *name);
|
||||
void request_stdin (const char *option);
|
||||
|
||||
void tar_stat_init (struct tar_stat_info *st);
|
||||
void tar_stat_destroy (struct tar_stat_info *st);
|
||||
@@ -660,22 +662,23 @@ void update_archive (void);
|
||||
|
||||
/* Module xheader.c. */
|
||||
|
||||
void xheader_decode (struct tar_stat_info *);
|
||||
void xheader_decode (struct tar_stat_info *stat);
|
||||
void xheader_decode_global (void);
|
||||
void xheader_store (char const *, struct tar_stat_info const *, void const *);
|
||||
void xheader_read (union block *, size_t);
|
||||
void xheader_store (char const *keyword, struct tar_stat_info const *st,
|
||||
void const *data);
|
||||
void xheader_read (union block *header, size_t size);
|
||||
void xheader_write (char type, char *name, struct xheader *xhdr);
|
||||
void xheader_write_global (void);
|
||||
void xheader_finish (struct xheader *);
|
||||
void xheader_destroy (struct xheader *);
|
||||
void xheader_finish (struct xheader *hdr);
|
||||
void xheader_destroy (struct xheader *hdr);
|
||||
char *xheader_xhdr_name (struct tar_stat_info *st);
|
||||
char *xheader_ghdr_name (void);
|
||||
void xheader_write (char, char *, struct xheader *);
|
||||
void xheader_write (char type, char *name, struct xheader *xhdr);
|
||||
void xheader_write_global (void);
|
||||
void xheader_set_option (char *string);
|
||||
void xheader_string_begin (void);
|
||||
void xheader_string_add (char const *s);
|
||||
void xheader_string_end (char const *keyword);
|
||||
bool xheader_string_end (char const *keyword);
|
||||
bool xheader_keyword_deleted_p (const char *kw);
|
||||
char *xheader_format_name (struct tar_stat_info *st, const char *fmt,
|
||||
size_t n);
|
||||
@@ -704,13 +707,13 @@ int sys_exec_info_script (const char **archive_name, int volume_number);
|
||||
void report_difference (struct tar_stat_info *st, const char *message, ...);
|
||||
|
||||
/* Module sparse.c */
|
||||
bool sparse_file_p (struct tar_stat_info *);
|
||||
bool sparse_member_p (struct tar_stat_info *);
|
||||
bool sparse_fixup_header (struct tar_stat_info *);
|
||||
enum dump_status sparse_dump_file (int, struct tar_stat_info *);
|
||||
enum dump_status sparse_extract_file (int, struct tar_stat_info *, off_t *);
|
||||
enum dump_status sparse_skip_file (struct tar_stat_info *);
|
||||
bool sparse_diff_file (int, struct tar_stat_info *);
|
||||
bool sparse_member_p (struct tar_stat_info *st);
|
||||
bool sparse_fixup_header (struct tar_stat_info *st);
|
||||
enum dump_status sparse_dump_file (int, struct tar_stat_info *st);
|
||||
enum dump_status sparse_extract_file (int fd, struct tar_stat_info *st,
|
||||
off_t *size);
|
||||
enum dump_status sparse_skip_file (struct tar_stat_info *st);
|
||||
bool sparse_diff_file (int, struct tar_stat_info *st);
|
||||
|
||||
/* Module utf8.c */
|
||||
bool string_ascii_p (const char *str);
|
||||
@@ -720,5 +723,3 @@ bool utf8_convert (bool to_utf, char const *input, char **output);
|
||||
void set_transform_expr (const char *expr);
|
||||
bool transform_name (char **pinput);
|
||||
bool transform_name_fp (char **pinput, char *(*fun)(char *));
|
||||
|
||||
|
||||
|
||||
144
src/create.c
144
src/create.c
@@ -33,6 +33,65 @@ struct link
|
||||
size_t nlink;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct exclude_tag
|
||||
{
|
||||
const char *name;
|
||||
size_t length;
|
||||
struct exclude_tag *next;
|
||||
};
|
||||
|
||||
static struct exclude_tag *exclude_tags;
|
||||
|
||||
void
|
||||
add_exclude_tag (const char *name)
|
||||
{
|
||||
struct exclude_tag *tag = xmalloc (sizeof tag[0]);
|
||||
tag->next = exclude_tags;
|
||||
tag->name = name;
|
||||
tag->length = strlen (name);
|
||||
exclude_tags = tag;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_exclude_tags (char *dirname)
|
||||
{
|
||||
static char *tagname;
|
||||
static size_t tagsize;
|
||||
struct exclude_tag *tag;
|
||||
size_t dlen = strlen (dirname);
|
||||
char *nptr = NULL;
|
||||
char *ret = NULL;
|
||||
|
||||
for (tag = exclude_tags; tag; tag = tag->next)
|
||||
{
|
||||
size_t size = dlen + tag->length + 1;
|
||||
if (size > tagsize)
|
||||
{
|
||||
tagsize = size;
|
||||
tagname = xrealloc (tagname, tagsize);
|
||||
}
|
||||
|
||||
if (!nptr)
|
||||
{
|
||||
strcpy (tagname, dirname);
|
||||
nptr = tagname + dlen;
|
||||
}
|
||||
strcpy (nptr, tag->name);
|
||||
if (access (tagname, F_OK) == 0)
|
||||
{
|
||||
if (verbose_option)
|
||||
WARN ((0, 0,
|
||||
_("%s: contains a cache directory tag %s; not dumped"),
|
||||
quotearg_colon (dirname),
|
||||
quotearg_n (1, tag->name)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* The maximum uintmax_t value that can be represented with DIGITS digits,
|
||||
assuming that each digit is BITS_PER_DIGIT wide. */
|
||||
@@ -128,7 +187,7 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
|
||||
char const *minval_string;
|
||||
char const *maxval_string = STRINGIFY_BIGINT (maxval, maxbuf);
|
||||
char const *value_string;
|
||||
|
||||
|
||||
if (gnu_format)
|
||||
{
|
||||
uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1;
|
||||
@@ -138,7 +197,7 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
|
||||
}
|
||||
else
|
||||
minval_string = "0";
|
||||
|
||||
|
||||
if (negative)
|
||||
{
|
||||
char *p = STRINGIFY_BIGINT (- value, valbuf + 1);
|
||||
@@ -147,13 +206,18 @@ to_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize,
|
||||
}
|
||||
else
|
||||
value_string = STRINGIFY_BIGINT (value, valbuf);
|
||||
|
||||
|
||||
if (substitute)
|
||||
{
|
||||
int negsub;
|
||||
uintmax_t sub = substitute (&negsub) & maxval;
|
||||
/* FIXME: This is the only place where GNU_FORMAT differs from
|
||||
OLDGNU_FORMAT. Apart from this they are completely identical. */
|
||||
/* NOTE: This is one of the few places where GNU_FORMAT differs from
|
||||
OLDGNU_FORMAT. The actual differences are:
|
||||
|
||||
1. In OLDGNU_FORMAT all strings in a tar header end in \0
|
||||
2. Incremental archives use oldgnu_header.
|
||||
|
||||
Apart from this they are completely identical. */
|
||||
uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub;
|
||||
char subbuf[UINTMAX_STRSIZE_BOUND + 1];
|
||||
char *sub_string = STRINGIFY_BIGINT (s, subbuf + 1);
|
||||
@@ -236,7 +300,7 @@ to_chars (int negative, uintmax_t value, size_t valsize,
|
||||
}
|
||||
else
|
||||
substitute = NULL; /* No substitution for formats, other than GNU */
|
||||
|
||||
|
||||
return to_chars_subst (negative, gnu_format, value, valsize, substitute,
|
||||
where, size, type);
|
||||
}
|
||||
@@ -291,7 +355,8 @@ mode_to_chars (mode_t v, char *p, size_t s)
|
||||
&& S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC
|
||||
&& archive_format != POSIX_FORMAT
|
||||
&& archive_format != USTAR_FORMAT
|
||||
&& archive_format != GNU_FORMAT)
|
||||
&& archive_format != GNU_FORMAT
|
||||
&& archive_format != OLDGNU_FORMAT)
|
||||
{
|
||||
negative = v < 0;
|
||||
u = v;
|
||||
@@ -380,7 +445,7 @@ bool
|
||||
file_dumpable_p (struct tar_stat_info *st)
|
||||
{
|
||||
if (dev_null_output)
|
||||
return totals_option && sparse_option && sparse_file_p (st);
|
||||
return totals_option && sparse_option && ST_IS_SPARSE (st->stat);
|
||||
return !(st->archive_file_size == 0
|
||||
&& (st->stat.st_mode & MODE_R) == MODE_R);
|
||||
}
|
||||
@@ -663,7 +728,8 @@ start_header (struct tar_stat_info *st)
|
||||
if (mode_option)
|
||||
st->stat.st_mode =
|
||||
((st->stat.st_mode & ~MODE_ALL)
|
||||
| mode_adjust (st->stat.st_mode, mode_option, initial_umask));
|
||||
| mode_adjust (st->stat.st_mode, S_ISDIR (st->stat.st_mode) != 0,
|
||||
initial_umask, mode_option, NULL));
|
||||
|
||||
/* Paul Eggert tried the trivial test ($WRITER cf a b; $READER tvf a)
|
||||
for a few tars and came up with the following interoperability
|
||||
@@ -730,12 +796,12 @@ start_header (struct tar_stat_info *st)
|
||||
}
|
||||
|
||||
{
|
||||
struct timespec mtime = st->mtime;
|
||||
struct timespec mtime = set_mtime_option ? mtime_option : st->mtime;
|
||||
if (archive_format == POSIX_FORMAT)
|
||||
{
|
||||
if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
|
||||
|| mtime.tv_nsec != 0)
|
||||
xheader_store ("mtime", st, NULL);
|
||||
xheader_store ("mtime", st, &mtime);
|
||||
if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec)
|
||||
mtime.tv_sec = 0;
|
||||
}
|
||||
@@ -967,8 +1033,8 @@ dump_regular_file (int fd, struct tar_stat_info *st)
|
||||
size_left),
|
||||
quotearg_colon (st->orig_file_name),
|
||||
STRINGIFY_BIGINT (size_left, buf)));
|
||||
if (! ignore_failed_read_option)
|
||||
exit_status = TAREXIT_FAILURE;
|
||||
if (! ignore_failed_read_option)
|
||||
exit_status = TAREXIT_DIFFERS;
|
||||
pad_archive (size_left - (bufsize-count));
|
||||
return dump_status_short;
|
||||
}
|
||||
@@ -976,6 +1042,7 @@ dump_regular_file (int fd, struct tar_stat_info *st)
|
||||
return dump_status_ok;
|
||||
}
|
||||
|
||||
|
||||
/* Look in directory DIRNAME for a cache directory tag file
|
||||
with the magic name "CACHEDIR.TAG" and a standard header,
|
||||
as described at:
|
||||
@@ -993,7 +1060,7 @@ check_cache_directory (char *dirname)
|
||||
static char tagname[] = "CACHEDIR.TAG";
|
||||
char *tagpath;
|
||||
int fd;
|
||||
int tag_present = false;
|
||||
bool tag_present = false;
|
||||
|
||||
tagpath = xmalloc (strlen (dirname) + strlen (tagname) + 1);
|
||||
strcpy (tagpath, dirname);
|
||||
@@ -1106,17 +1173,7 @@ dump_dir0 (char *directory,
|
||||
quotearg_colon (st->orig_file_name)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (exclude_caches_option
|
||||
&& check_cache_directory(st->orig_file_name))
|
||||
{
|
||||
if (verbose_option)
|
||||
WARN ((0, 0,
|
||||
_("%s: contains a cache directory tag; not dumped"),
|
||||
quotearg_colon (st->orig_file_name)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
char const *entry;
|
||||
size_t entry_len;
|
||||
@@ -1167,9 +1224,6 @@ dump_dir (int fd, struct tar_stat_info *st, int top_level, dev_t parent_device)
|
||||
return false;
|
||||
}
|
||||
|
||||
ensure_slash (&st->orig_file_name);
|
||||
ensure_slash (&st->file_name);
|
||||
|
||||
dump_dir0 (directory, st, top_level, parent_device);
|
||||
|
||||
free (directory);
|
||||
@@ -1392,6 +1446,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
{
|
||||
union block *header;
|
||||
char type;
|
||||
off_t original_size;
|
||||
struct timespec original_ctime;
|
||||
struct timespec restore_times[2];
|
||||
off_t block_ordinal = -1;
|
||||
@@ -1411,7 +1466,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
stat_diag (p);
|
||||
return;
|
||||
}
|
||||
st->archive_file_size = st->stat.st_size;
|
||||
st->archive_file_size = original_size = st->stat.st_size;
|
||||
st->atime = restore_times[0] = get_stat_atime (&st->stat);
|
||||
st->mtime = restore_times[1] = get_stat_mtime (&st->stat);
|
||||
st->ctime = original_ctime = get_stat_ctime (&st->stat);
|
||||
@@ -1489,6 +1544,22 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
|
||||
if (is_dir)
|
||||
{
|
||||
ensure_slash (&st->orig_file_name);
|
||||
ensure_slash (&st->file_name);
|
||||
|
||||
if (exclude_caches_option
|
||||
&& check_cache_directory (st->orig_file_name))
|
||||
{
|
||||
if (verbose_option)
|
||||
WARN ((0, 0,
|
||||
_("%s: contains a cache directory tag; not dumped"),
|
||||
quotearg_colon (st->orig_file_name)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (check_exclude_tags (st->orig_file_name))
|
||||
return;
|
||||
|
||||
ok = dump_dir (fd, st, top_level, parent_device);
|
||||
|
||||
/* dump_dir consumes FD if successful. */
|
||||
@@ -1499,7 +1570,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
{
|
||||
enum dump_status status;
|
||||
|
||||
if (fd != -1 && sparse_option && sparse_file_p (st))
|
||||
if (fd != -1 && sparse_option && ST_IS_SPARSE (st->stat))
|
||||
{
|
||||
status = sparse_dump_file (fd, st);
|
||||
if (status == dump_status_not_implemented)
|
||||
@@ -1523,7 +1594,7 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
}
|
||||
|
||||
file_count_links (st);
|
||||
|
||||
|
||||
ok = status == dump_status_ok;
|
||||
}
|
||||
|
||||
@@ -1548,9 +1619,14 @@ dump_file0 (struct tar_stat_info *st, const char *p,
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0)
|
||||
WARN ((0, 0, _("%s: file changed as we read it"),
|
||||
quotearg_colon (p)));
|
||||
if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0
|
||||
|| original_size < final_stat.st_size)
|
||||
{
|
||||
WARN ((0, 0, _("%s: file changed as we read it"),
|
||||
quotearg_colon (p)));
|
||||
if (exit_status == TAREXIT_SUCCESS)
|
||||
exit_status = TAREXIT_DIFFERS;
|
||||
}
|
||||
else if (atime_preserve_option == replace_atime_preserve
|
||||
&& set_file_atime (fd, p, restore_times) != 0)
|
||||
utime_error (p);
|
||||
|
||||
@@ -174,7 +174,7 @@ delete_archive_members (void)
|
||||
abort ();
|
||||
|
||||
case HEADER_SUCCESS:
|
||||
if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
|
||||
if ((name = name_scan (current_stat_info.file_name)) == NULL)
|
||||
{
|
||||
skip_member ();
|
||||
break;
|
||||
@@ -285,7 +285,7 @@ delete_archive_members (void)
|
||||
|
||||
/* Found another header. */
|
||||
|
||||
if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
|
||||
if ((name = name_scan (current_stat_info.file_name)) != NULL)
|
||||
{
|
||||
name->found_count++;
|
||||
if (ISFOUND(name))
|
||||
|
||||
115
src/extract.c
115
src/extract.c
@@ -37,7 +37,8 @@ enum permstatus
|
||||
/* This file may have existed already; its permissions are unknown. */
|
||||
UNKNOWN_PERMSTATUS,
|
||||
|
||||
/* This file was created using the permissions from the archive. */
|
||||
/* This file was created using the permissions from the archive,
|
||||
except with S_IRWXG | S_IRWXO masked out if 0 < same_owner_option. */
|
||||
ARCHIVED_PERMSTATUS,
|
||||
|
||||
/* This is an intermediate directory; the archive did not specify
|
||||
@@ -149,12 +150,15 @@ set_mode (char const *file_name,
|
||||
{
|
||||
mode = stat_info->st_mode;
|
||||
|
||||
/* If we created the file and it has a usual mode, then its mode
|
||||
is normally set correctly already. But on many hosts, some
|
||||
/* If we created the file and it has a mode that we set already
|
||||
with O_CREAT, then its mode is often set correctly already.
|
||||
But if we are changing ownership, the mode's group and and
|
||||
other permission bits were omitted originally, so it's less
|
||||
likely that the mode is OK now. Also, on many hosts, some
|
||||
directories inherit the setgid bits from their parents, so we
|
||||
we must set directories' modes explicitly. */
|
||||
if (permstatus == ARCHIVED_PERMSTATUS
|
||||
&& ! (mode & ~ MODE_RWX)
|
||||
if ((permstatus == ARCHIVED_PERMSTATUS
|
||||
&& ! (mode & ~ (0 < same_owner_option ? S_IRWXU : MODE_RWX)))
|
||||
&& typeflag != DIRTYPE
|
||||
&& typeflag != GNUTYPE_DUMPDIR)
|
||||
return;
|
||||
@@ -217,7 +221,7 @@ check_time (char const *file_name, struct timespec t)
|
||||
/* Restore stat attributes (owner, group, mode and times) for
|
||||
FILE_NAME, using information given in *ST.
|
||||
If CUR_INFO is nonzero, *CUR_INFO is the
|
||||
file's currernt status.
|
||||
file's current status.
|
||||
If not restoring permissions, invert the
|
||||
INVERT_PERMISSIONS bits from the file's current permissions.
|
||||
PERMSTATUS specifies the status of the file's permissions.
|
||||
@@ -265,11 +269,11 @@ set_stat (char const *file_name,
|
||||
}
|
||||
|
||||
/* Some systems allow non-root users to give files away. Once this
|
||||
done, it is not possible anymore to change file permissions, so we
|
||||
have to set permissions prior to possibly giving files away. */
|
||||
|
||||
set_mode (file_name, &st->stat, cur_info,
|
||||
invert_permissions, permstatus, typeflag);
|
||||
done, it is not possible anymore to change file permissions.
|
||||
However, setting file permissions now would be incorrect, since
|
||||
they would apply to the wrong user, and there would be a race
|
||||
condition. So, don't use systems that allow non-root users to
|
||||
give files away. */
|
||||
}
|
||||
|
||||
if (0 < same_owner_option && permstatus != INTERDIR_PERMSTATUS)
|
||||
@@ -278,29 +282,36 @@ set_stat (char const *file_name,
|
||||
the symbolic link itself. In this case, a mere chown would change
|
||||
the attributes of the file the symbolic link is pointing to, and
|
||||
should be avoided. */
|
||||
int chown_result = 1;
|
||||
|
||||
if (typeflag == SYMTYPE)
|
||||
{
|
||||
#if HAVE_LCHOWN
|
||||
if (lchown (file_name, st->stat.st_uid, st->stat.st_gid) < 0)
|
||||
chown_error_details (file_name,
|
||||
st->stat.st_uid, st->stat.st_gid);
|
||||
chown_result = lchown (file_name, st->stat.st_uid, st->stat.st_gid);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (chown (file_name, st->stat.st_uid, st->stat.st_gid) < 0)
|
||||
chown_error_details (file_name,
|
||||
st->stat.st_uid, st->stat.st_gid);
|
||||
|
||||
/* On a few systems, and in particular, those allowing to give files
|
||||
away, changing the owner or group destroys the suid or sgid bits.
|
||||
So let's attempt setting these bits once more. */
|
||||
if (st->stat.st_mode & (S_ISUID | S_ISGID | S_ISVTX))
|
||||
set_mode (file_name, &st->stat, 0,
|
||||
invert_permissions, permstatus, typeflag);
|
||||
chown_result = chown (file_name, st->stat.st_uid, st->stat.st_gid);
|
||||
}
|
||||
|
||||
if (chown_result == 0)
|
||||
{
|
||||
/* Changing the owner can flip st_mode bits in some cases, so
|
||||
ignore cur_info if it might be obsolete now. */
|
||||
if (cur_info
|
||||
&& cur_info->st_mode & S_IXUGO
|
||||
&& cur_info->st_mode & (S_ISUID | S_ISGID))
|
||||
cur_info = NULL;
|
||||
}
|
||||
else if (chown_result < 0)
|
||||
chown_error_details (file_name,
|
||||
st->stat.st_uid, st->stat.st_gid);
|
||||
}
|
||||
|
||||
if (typeflag != SYMTYPE)
|
||||
set_mode (file_name, &st->stat, cur_info,
|
||||
invert_permissions, permstatus, typeflag);
|
||||
}
|
||||
|
||||
/* Remember to restore stat attributes (owner, group, mode and times)
|
||||
@@ -374,7 +385,8 @@ repair_delayed_set_stat (char const *dir,
|
||||
data->atime = current_stat_info.atime;
|
||||
data->mtime = current_stat_info.mtime;
|
||||
data->invert_permissions =
|
||||
(MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode));
|
||||
((current_stat_info.stat.st_mode ^ st.st_mode)
|
||||
& MODE_RWX & ~ current_umask);
|
||||
data->permstatus = ARCHIVED_PERMSTATUS;
|
||||
return;
|
||||
}
|
||||
@@ -626,8 +638,9 @@ extract_dir (char *file_name, int typeflag)
|
||||
else if (typeflag == GNUTYPE_DUMPDIR)
|
||||
skip_member ();
|
||||
|
||||
mode = (current_stat_info.stat.st_mode |
|
||||
(we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX;
|
||||
mode = current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR);
|
||||
if (0 < same_owner_option || current_stat_info.stat.st_mode & ~ MODE_RWX)
|
||||
mode &= S_IRWXU;
|
||||
|
||||
while ((status = mkdir (file_name, mode)))
|
||||
{
|
||||
@@ -670,7 +683,8 @@ extract_dir (char *file_name, int typeflag)
|
||||
{
|
||||
if (status == 0)
|
||||
delay_set_stat (file_name, ¤t_stat_info,
|
||||
MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
|
||||
((mode ^ current_stat_info.stat.st_mode)
|
||||
& MODE_RWX & ~ current_umask),
|
||||
ARCHIVED_PERMSTATUS);
|
||||
else /* For an already existing directory, invert_perms must be 0 */
|
||||
delay_set_stat (file_name, ¤t_stat_info,
|
||||
@@ -682,14 +696,13 @@ extract_dir (char *file_name, int typeflag)
|
||||
|
||||
|
||||
static int
|
||||
open_output_file (char *file_name, int typeflag)
|
||||
open_output_file (char *file_name, int typeflag, mode_t mode)
|
||||
{
|
||||
int fd;
|
||||
int openflag = (O_WRONLY | O_BINARY | O_CREAT
|
||||
| (old_files_option == OVERWRITE_OLD_FILES
|
||||
? O_TRUNC
|
||||
: O_EXCL));
|
||||
mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
|
||||
|
||||
#if O_CTG
|
||||
/* Contiguous files (on the Masscomp) have to specify the size in
|
||||
@@ -728,6 +741,9 @@ extract_file (char *file_name, int typeflag)
|
||||
size_t count;
|
||||
size_t written;
|
||||
int interdir_made = 0;
|
||||
mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
|
||||
mode_t invert_permissions =
|
||||
0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
|
||||
|
||||
/* FIXME: deal with protection issues. */
|
||||
|
||||
@@ -745,11 +761,12 @@ extract_file (char *file_name, int typeflag)
|
||||
else
|
||||
{
|
||||
do
|
||||
fd = open_output_file (file_name, typeflag);
|
||||
fd = open_output_file (file_name, typeflag, mode ^ invert_permissions);
|
||||
while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
skip_member ();
|
||||
open_error (file_name);
|
||||
return 1;
|
||||
}
|
||||
@@ -810,7 +827,7 @@ extract_file (char *file_name, int typeflag)
|
||||
if (to_command_option)
|
||||
sys_wait_command ();
|
||||
else
|
||||
set_stat (file_name, ¤t_stat_info, NULL, 0,
|
||||
set_stat (file_name, ¤t_stat_info, NULL, invert_permissions,
|
||||
(old_files_option == OVERWRITE_OLD_FILES ?
|
||||
UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS),
|
||||
typeflag);
|
||||
@@ -871,7 +888,7 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made)
|
||||
if (h && ! h->after_links
|
||||
&& strncmp (file_name, h->file_name, h->file_name_len) == 0
|
||||
&& ISSLASH (file_name[h->file_name_len])
|
||||
&& (base_name (file_name) == file_name + h->file_name_len + 1))
|
||||
&& (last_component (file_name) == file_name + h->file_name_len + 1))
|
||||
{
|
||||
do
|
||||
{
|
||||
@@ -988,16 +1005,19 @@ extract_node (char *file_name, int typeflag)
|
||||
{
|
||||
int status;
|
||||
int interdir_made = 0;
|
||||
mode_t mode = current_stat_info.stat.st_mode & ~ current_umask;
|
||||
mode_t invert_permissions =
|
||||
0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
|
||||
|
||||
do
|
||||
status = mknod (file_name, current_stat_info.stat.st_mode,
|
||||
status = mknod (file_name, mode ^ invert_permissions,
|
||||
current_stat_info.stat.st_rdev);
|
||||
while (status && maybe_recoverable (file_name, &interdir_made));
|
||||
|
||||
if (status != 0)
|
||||
mknod_error (file_name);
|
||||
else
|
||||
set_stat (file_name, ¤t_stat_info, NULL, 0,
|
||||
set_stat (file_name, ¤t_stat_info, NULL, invert_permissions,
|
||||
ARCHIVED_PERMSTATUS, typeflag);
|
||||
return status;
|
||||
}
|
||||
@@ -1009,13 +1029,16 @@ extract_fifo (char *file_name, int typeflag)
|
||||
{
|
||||
int status;
|
||||
int interdir_made = 0;
|
||||
mode_t mode = current_stat_info.stat.st_mode & ~ current_umask;
|
||||
mode_t invert_permissions =
|
||||
0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
|
||||
|
||||
while ((status = mkfifo (file_name, current_stat_info.stat.st_mode)))
|
||||
while ((status = mkfifo (file_name, mode)) != 0)
|
||||
if (!maybe_recoverable (file_name, &interdir_made))
|
||||
break;
|
||||
|
||||
if (status == 0)
|
||||
set_stat (file_name, ¤t_stat_info, NULL, 0,
|
||||
set_stat (file_name, ¤t_stat_info, NULL, invert_permissions,
|
||||
ARCHIVED_PERMSTATUS, typeflag);
|
||||
else
|
||||
mkfifo_error (file_name);
|
||||
@@ -1023,19 +1046,13 @@ extract_fifo (char *file_name, int typeflag)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
extract_mangle_wrapper (char *file_name, int typeflag)
|
||||
{
|
||||
extract_mangle ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
extract_volhdr (char *file_name, int typeflag)
|
||||
{
|
||||
if (verbose_option)
|
||||
fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
|
||||
skip_member ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1120,10 +1137,6 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
|
||||
*fun = extract_volhdr;
|
||||
break;
|
||||
|
||||
case GNUTYPE_NAMES:
|
||||
*fun = extract_mangle_wrapper;
|
||||
break;
|
||||
|
||||
case GNUTYPE_MULTIVOL:
|
||||
ERROR ((0, 0,
|
||||
_("%s: Cannot extract -- file is continued from another volume"),
|
||||
@@ -1330,10 +1343,10 @@ rename_directory (char *src, char *dst)
|
||||
e = errno;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case EXDEV:
|
||||
/* FIXME: Fall back to recursive copying */
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1345,7 +1358,7 @@ rename_directory (char *src, char *dst)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fatal_exit (void)
|
||||
{
|
||||
|
||||
471
src/incremen.c
471
src/incremen.c
@@ -46,7 +46,7 @@ enum children
|
||||
#define DIR_IS_FOUND(d) ((d)->flags & DIRF_FOUND)
|
||||
#define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW)
|
||||
#define DIR_IS_RENAMED(d) ((d)->flags & DIRF_RENAMED)
|
||||
|
||||
|
||||
#define DIR_SET_FLAG(d,f) (d)->flags |= (f)
|
||||
#define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
|
||||
|
||||
@@ -127,7 +127,7 @@ make_directory (const char *name)
|
||||
directory->name[namelen-1] = 0;
|
||||
return directory;
|
||||
}
|
||||
|
||||
|
||||
/* Create and link a new directory entry for directory NAME, having a
|
||||
device number DEV and an inode number INO, with NFS indicating
|
||||
whether it is an NFS device and FOUND indicating whether we have
|
||||
@@ -154,7 +154,7 @@ note_directory (char const *name, struct timespec mtime,
|
||||
}
|
||||
else
|
||||
directory->contents = NULL;
|
||||
|
||||
|
||||
if (! ((directory_table
|
||||
|| (directory_table = hash_initialize (0, 0,
|
||||
hash_directory_name,
|
||||
@@ -211,7 +211,7 @@ void
|
||||
update_parent_directory (const char *name)
|
||||
{
|
||||
struct directory *directory;
|
||||
char *p, *name_buffer;
|
||||
char *p;
|
||||
|
||||
p = dir_name (name);
|
||||
directory = find_directory (p);
|
||||
@@ -234,13 +234,12 @@ procdir (char *name_buffer, struct stat *stat_data,
|
||||
{
|
||||
struct directory *directory;
|
||||
bool nfs = NFS_FILE_STAT (*stat_data);
|
||||
struct name *np;
|
||||
|
||||
if ((directory = find_directory (name_buffer)) != NULL)
|
||||
{
|
||||
if (DIR_IS_INITED (directory))
|
||||
return directory;
|
||||
|
||||
|
||||
/* With NFS, the same file can have two different devices
|
||||
if an NFS directory is mounted in multiple locations,
|
||||
which is relatively common when automounting.
|
||||
@@ -279,7 +278,7 @@ procdir (char *name_buffer, struct stat *stat_data,
|
||||
}
|
||||
else
|
||||
directory->children = CHANGED_CHILDREN;
|
||||
|
||||
|
||||
DIR_SET_FLAG (directory, DIRF_FOUND);
|
||||
}
|
||||
else
|
||||
@@ -311,7 +310,7 @@ procdir (char *name_buffer, struct stat *stat_data,
|
||||
if (verbose)
|
||||
WARN ((0, 0, _("%s: Directory is new"),
|
||||
quotearg_colon (name_buffer)));
|
||||
directory->children =
|
||||
directory->children =
|
||||
(listed_incremental_option
|
||||
|| (OLDER_STAT_TIME (*stat_data, m)
|
||||
|| (after_date_option
|
||||
@@ -325,13 +324,13 @@ procdir (char *name_buffer, struct stat *stat_data,
|
||||
omit it... */
|
||||
if (one_file_system_option && device != stat_data->st_dev
|
||||
/* ... except if it was explicitely given in the command line */
|
||||
&& !((np = name_scan (name_buffer, true)) && np->explicit))
|
||||
&& !is_individual_file (name_buffer))
|
||||
directory->children = NO_CHILDREN;
|
||||
else if (children == ALL_CHILDREN)
|
||||
else if (children == ALL_CHILDREN)
|
||||
directory->children = ALL_CHILDREN;
|
||||
|
||||
DIR_SET_FLAG (directory, DIRF_INIT);
|
||||
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
@@ -344,7 +343,7 @@ dumpdir_locate (const char *dump, const char *name)
|
||||
while (*dump)
|
||||
{
|
||||
/* Ignore 'R' (rename) and 'X' (tempname) entries, since they break
|
||||
alphabetical ordering.
|
||||
alphabetical ordering.
|
||||
They normally do not occur in dumpdirs from the snapshot files,
|
||||
but this function is also used by purge_directory, which operates
|
||||
on a dumpdir from the archive, hence the need for this test. */
|
||||
@@ -388,7 +387,7 @@ compare_dirnames (const void *first, const void *second)
|
||||
DIR must be returned by a previous call to savedir().
|
||||
|
||||
File names in DIRECTORY->contents must be sorted
|
||||
alphabetically.
|
||||
alphabetically.
|
||||
|
||||
DIRECTORY->contents is replaced with the created template. Each entry is
|
||||
prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
|
||||
@@ -462,7 +461,7 @@ scan_directory (char *dir_name, dev_t device)
|
||||
size_t name_length; /* used length in name_buffer */
|
||||
struct stat stat_data;
|
||||
struct directory *directory;
|
||||
|
||||
|
||||
if (! dirp)
|
||||
savedir_error (dir_name);
|
||||
|
||||
@@ -476,7 +475,7 @@ scan_directory (char *dir_name, dev_t device)
|
||||
if (deref_stat (dereference_option, name_buffer, &stat_data))
|
||||
{
|
||||
stat_diag (name_buffer);
|
||||
/* FIXME: used to be
|
||||
/* FIXME: used to be
|
||||
children = CHANGED_CHILDREN;
|
||||
but changed to: */
|
||||
free (name_buffer);
|
||||
@@ -485,7 +484,7 @@ scan_directory (char *dir_name, dev_t device)
|
||||
}
|
||||
|
||||
directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
|
||||
|
||||
|
||||
if (dirp && directory->children != NO_CHILDREN)
|
||||
{
|
||||
char *entry; /* directory entry being scanned */
|
||||
@@ -505,7 +504,7 @@ scan_directory (char *dir_name, dev_t device)
|
||||
name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
|
||||
}
|
||||
strcpy (name_buffer + name_length, entry + 1);
|
||||
|
||||
|
||||
if (excluded_name (name_buffer))
|
||||
*entry = 'N';
|
||||
else
|
||||
@@ -516,7 +515,7 @@ scan_directory (char *dir_name, dev_t device)
|
||||
*entry = 'N';
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (S_ISDIR (stat_data.st_mode))
|
||||
{
|
||||
procdir (name_buffer, &stat_data, device,
|
||||
@@ -524,15 +523,15 @@ scan_directory (char *dir_name, dev_t device)
|
||||
verbose_option);
|
||||
*entry = 'D';
|
||||
}
|
||||
|
||||
|
||||
else if (one_file_system_option && device != stat_data.st_dev)
|
||||
*entry = 'N';
|
||||
|
||||
else if (*entry == 'Y')
|
||||
/* New entry, skip further checks */;
|
||||
|
||||
|
||||
/* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */
|
||||
|
||||
|
||||
else if (OLDER_STAT_TIME (stat_data, m)
|
||||
&& (!after_date_option
|
||||
|| OLDER_STAT_TIME (stat_data, c)))
|
||||
@@ -542,7 +541,7 @@ scan_directory (char *dir_name, dev_t device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
free (name_buffer);
|
||||
if (dirp)
|
||||
free (dirp);
|
||||
@@ -571,7 +570,7 @@ rename_handler (void *data, void *proc_data)
|
||||
{
|
||||
struct directory *dir = data;
|
||||
struct obstack *stk = proc_data;
|
||||
|
||||
|
||||
if (DIR_IS_RENAMED (dir))
|
||||
{
|
||||
struct directory *prev, *p;
|
||||
@@ -621,7 +620,7 @@ append_incremental_renames (const char *dump)
|
||||
|
||||
if (directory_table == NULL)
|
||||
return dump;
|
||||
|
||||
|
||||
obstack_init (&stk);
|
||||
if (dump)
|
||||
{
|
||||
@@ -630,7 +629,7 @@ append_incremental_renames (const char *dump)
|
||||
}
|
||||
else
|
||||
size = 0;
|
||||
|
||||
|
||||
hash_do_for_each (directory_table, rename_handler, &stk);
|
||||
if (obstack_object_size (&stk) != size)
|
||||
{
|
||||
@@ -662,7 +661,8 @@ read_incr_db_01 (int version, const char *initbuf)
|
||||
{
|
||||
int n;
|
||||
uintmax_t u;
|
||||
time_t t = u;
|
||||
time_t sec;
|
||||
long int nsec;
|
||||
char *buf = 0;
|
||||
size_t bufsize;
|
||||
char *ebuf;
|
||||
@@ -683,41 +683,49 @@ read_incr_db_01 (int version, const char *initbuf)
|
||||
buf = strdup (initbuf);
|
||||
bufsize = strlen (buf) + 1;
|
||||
}
|
||||
|
||||
t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
|
||||
if (buf == ebuf || (u == 0 && errno == EINVAL))
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
|
||||
sec = TYPE_MINIMUM (time_t);
|
||||
nsec = -1;
|
||||
errno = 0;
|
||||
u = strtoumax (buf, &ebuf, 10);
|
||||
if (!errno && TYPE_MAXIMUM (time_t) < u)
|
||||
errno = ERANGE;
|
||||
if (errno || buf == ebuf)
|
||||
ERROR ((0, errno, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
lineno,
|
||||
_("Invalid time stamp")));
|
||||
else if (t != u)
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
lineno,
|
||||
_("Time stamp out of range")));
|
||||
else if (version == 1)
|
||||
{
|
||||
newer_mtime_option.tv_sec = t;
|
||||
|
||||
t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
|
||||
if (buf == ebuf || (u == 0 && errno == EINVAL))
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
lineno,
|
||||
_("Invalid time stamp")));
|
||||
else if (t != u)
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
lineno,
|
||||
_("Time stamp out of range")));
|
||||
newer_mtime_option.tv_nsec = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pre-1 incremental format does not contain nanoseconds */
|
||||
newer_mtime_option.tv_sec = t;
|
||||
newer_mtime_option.tv_nsec = 0;
|
||||
sec = u;
|
||||
|
||||
if (version == 1 && *ebuf)
|
||||
{
|
||||
char const *buf_ns = ebuf + 1;
|
||||
errno = 0;
|
||||
u = strtoumax (buf_ns, &ebuf, 10);
|
||||
if (!errno && BILLION <= u)
|
||||
errno = ERANGE;
|
||||
if (errno || buf_ns == ebuf)
|
||||
{
|
||||
ERROR ((0, errno, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
lineno,
|
||||
_("Invalid time stamp")));
|
||||
sec = TYPE_MINIMUM (time_t);
|
||||
}
|
||||
else
|
||||
nsec = u;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pre-1 incremental format does not contain nanoseconds */
|
||||
nsec = 0;
|
||||
}
|
||||
}
|
||||
newer_mtime_option.tv_sec = sec;
|
||||
newer_mtime_option.tv_nsec = nsec;
|
||||
|
||||
|
||||
while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
|
||||
{
|
||||
@@ -726,65 +734,79 @@ read_incr_db_01 (int version, const char *initbuf)
|
||||
bool nfs = buf[0] == '+';
|
||||
char *strp = buf + nfs;
|
||||
struct timespec mtime;
|
||||
|
||||
|
||||
lineno++;
|
||||
|
||||
|
||||
if (buf[n - 1] == '\n')
|
||||
buf[n - 1] = '\0';
|
||||
|
||||
|
||||
if (version == 1)
|
||||
{
|
||||
errno = 0;
|
||||
mtime.tv_sec = u = strtoumax (strp, &ebuf, 10);
|
||||
if (!isspace (*ebuf))
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid modification time (seconds)")));
|
||||
else if (mtime.tv_sec != u)
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Modification time (seconds) out of range")));
|
||||
u = strtoumax (strp, &ebuf, 10);
|
||||
if (!errno && TYPE_MAXIMUM (time_t) < u)
|
||||
errno = ERANGE;
|
||||
if (errno || strp == ebuf || *ebuf != ' ')
|
||||
{
|
||||
ERROR ((0, errno, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid modification time (seconds)")));
|
||||
sec = (time_t) -1;
|
||||
}
|
||||
else
|
||||
sec = u;
|
||||
strp = ebuf;
|
||||
|
||||
|
||||
errno = 0;
|
||||
mtime.tv_nsec = u = strtoumax (strp, &ebuf, 10);
|
||||
if (!isspace (*ebuf))
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid modification time (nanoseconds)")));
|
||||
else if (mtime.tv_nsec != u)
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Modification time (nanoseconds) out of range")));
|
||||
u = strtoumax (strp, &ebuf, 10);
|
||||
if (!errno && BILLION <= u)
|
||||
errno = ERANGE;
|
||||
if (errno || strp == ebuf || *ebuf != ' ')
|
||||
{
|
||||
ERROR ((0, errno, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid modification time (nanoseconds)")));
|
||||
nsec = -1;
|
||||
}
|
||||
else
|
||||
nsec = u;
|
||||
mtime.tv_sec = sec;
|
||||
mtime.tv_nsec = nsec;
|
||||
strp = ebuf;
|
||||
}
|
||||
else
|
||||
memset (&mtime, 0, sizeof mtime);
|
||||
|
||||
|
||||
errno = 0;
|
||||
dev = u = strtoumax (strp, &ebuf, 10);
|
||||
if (!isspace (*ebuf))
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid device number")));
|
||||
else if (dev != u)
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Device number out of range")));
|
||||
u = strtoumax (strp, &ebuf, 10);
|
||||
if (!errno && TYPE_MAXIMUM (dev_t) < u)
|
||||
errno = ERANGE;
|
||||
if (errno || strp == ebuf || *ebuf != ' ')
|
||||
{
|
||||
ERROR ((0, errno, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid device number")));
|
||||
dev = (dev_t) -1;
|
||||
}
|
||||
else
|
||||
dev = u;
|
||||
strp = ebuf;
|
||||
|
||||
errno = 0;
|
||||
ino = u = strtoumax (strp, &ebuf, 10);
|
||||
if (!isspace (*ebuf))
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid inode number")));
|
||||
else if (ino != u)
|
||||
ERROR ((0, 0, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Inode number out of range")));
|
||||
u = strtoumax (strp, &ebuf, 10);
|
||||
if (!errno && TYPE_MAXIMUM (ino_t) < u)
|
||||
errno = ERANGE;
|
||||
if (errno || strp == ebuf || *ebuf != ' ')
|
||||
{
|
||||
ERROR ((0, errno, "%s:%ld: %s",
|
||||
quotearg_colon (listed_incremental_option), lineno,
|
||||
_("Invalid inode number")));
|
||||
ino = (ino_t) -1;
|
||||
}
|
||||
else
|
||||
ino = u;
|
||||
strp = ebuf;
|
||||
|
||||
|
||||
strp++;
|
||||
unquote_string (strp);
|
||||
note_directory (strp, mtime, dev, ino, nfs, false, NULL);
|
||||
@@ -811,31 +833,128 @@ read_obstack (FILE *fp, struct obstack *stk, size_t *pcount)
|
||||
}
|
||||
|
||||
/* Read from file FP a nul-terminated string and convert it to
|
||||
uintmax_t. Return the resulting value in PVAL.
|
||||
intmax_t. Return the resulting value in PVAL. Assume '-' has
|
||||
already been read.
|
||||
|
||||
Throw fatal error if the string cannot be converted.
|
||||
|
||||
Return the last character read or EOF on end of file. */
|
||||
Throw a fatal error if the string cannot be converted or if the
|
||||
converted value is less than MIN_VAL. */
|
||||
|
||||
static int
|
||||
read_num (FILE *fp, uintmax_t *pval)
|
||||
static void
|
||||
read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
|
||||
{
|
||||
int c;
|
||||
size_t i;
|
||||
char buf[UINTMAX_STRSIZE_BOUND], *ep;
|
||||
char buf[INT_BUFSIZE_BOUND (intmax_t)];
|
||||
char *ep;
|
||||
buf[0] = '-';
|
||||
|
||||
for (i = 0, c = getc (fp); c != EOF && c != 0; c = getc (fp), i++)
|
||||
for (i = 1; ISDIGIT (c = getc (fp)); i++)
|
||||
{
|
||||
if (i == sizeof buf - 1)
|
||||
FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
|
||||
buf[i] = c;
|
||||
}
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
if (ferror (fp))
|
||||
FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
|
||||
else
|
||||
FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
|
||||
}
|
||||
|
||||
buf[i] = 0;
|
||||
errno = 0;
|
||||
*pval = strtoimax (buf, &ep, 10);
|
||||
if (c || errno || *pval < min_val)
|
||||
FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
|
||||
}
|
||||
|
||||
/* Read from file FP a nul-terminated string and convert it to
|
||||
uintmax_t. Return the resulting value in PVAL. Assume C has
|
||||
already been read.
|
||||
|
||||
Throw a fatal error if the string cannot be converted or if the
|
||||
converted value exceeds MAX_VAL.
|
||||
|
||||
Return the last character read or EOF on end of file. */
|
||||
|
||||
static int
|
||||
read_unsigned_num (int c, FILE *fp, uintmax_t max_val, uintmax_t *pval)
|
||||
{
|
||||
size_t i;
|
||||
char buf[UINTMAX_STRSIZE_BOUND], *ep;
|
||||
|
||||
for (i = 0; ISDIGIT (c); i++)
|
||||
{
|
||||
if (i == sizeof buf - 1)
|
||||
FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
|
||||
buf[i] = c;
|
||||
c = getc (fp);
|
||||
}
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
if (ferror (fp))
|
||||
FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
|
||||
else if (i == 0)
|
||||
return c;
|
||||
else
|
||||
FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
|
||||
}
|
||||
|
||||
buf[i] = 0;
|
||||
errno = 0;
|
||||
*pval = strtoumax (buf, &ep, 10);
|
||||
if (*ep)
|
||||
FATAL_ERROR ((0, 0, _("Unexpected field value in snapshot file")));
|
||||
if (c || errno || max_val < *pval)
|
||||
FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read from file FP a nul-terminated string and convert it to
|
||||
uintmax_t. Return the resulting value in PVAL.
|
||||
|
||||
Throw a fatal error if the string cannot be converted or if the
|
||||
converted value exceeds MAX_VAL.
|
||||
|
||||
Return the last character read or EOF on end of file. */
|
||||
|
||||
static int
|
||||
read_num (FILE *fp, uintmax_t max_val, uintmax_t *pval)
|
||||
{
|
||||
return read_unsigned_num (getc (fp), fp, max_val, pval);
|
||||
}
|
||||
|
||||
/* Read from FP two NUL-terminated strings representing a struct
|
||||
timespec. Return the resulting value in PVAL.
|
||||
|
||||
Throw a fatal error if the string cannot be converted. */
|
||||
|
||||
static void
|
||||
read_timespec (FILE *fp, struct timespec *pval)
|
||||
{
|
||||
int c = getc (fp);
|
||||
intmax_t i;
|
||||
uintmax_t u;
|
||||
|
||||
if (c == '-')
|
||||
{
|
||||
read_negative_num (fp, TYPE_MINIMUM (time_t), &i);
|
||||
c = 0;
|
||||
pval->tv_sec = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = read_unsigned_num (c, fp, TYPE_MAXIMUM (time_t), &u);
|
||||
pval->tv_sec = u;
|
||||
}
|
||||
|
||||
if (c || read_num (fp, BILLION - 1, &u))
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Unexpected EOF in snapshot file")));
|
||||
pval->tv_nsec = u;
|
||||
}
|
||||
|
||||
/* Read incremental snapshot format 2 */
|
||||
static void
|
||||
@@ -843,28 +962,10 @@ read_incr_db_2 ()
|
||||
{
|
||||
uintmax_t u;
|
||||
struct obstack stk;
|
||||
|
||||
|
||||
obstack_init (&stk);
|
||||
|
||||
if (read_num (listed_incremental_stream, &u))
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Error reading time stamp")));
|
||||
newer_mtime_option.tv_sec = u;
|
||||
if (newer_mtime_option.tv_sec != u)
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Time stamp out of range")));
|
||||
|
||||
if (read_num (listed_incremental_stream, &u))
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Error reading time stamp")));
|
||||
newer_mtime_option.tv_nsec = u;
|
||||
if (newer_mtime_option.tv_nsec != u)
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Time stamp out of range")));
|
||||
read_timespec (listed_incremental_stream, &newer_mtime_option);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@@ -875,43 +976,21 @@ read_incr_db_2 ()
|
||||
char *name;
|
||||
char *content;
|
||||
size_t s;
|
||||
|
||||
if (read_num (listed_incremental_stream, &u))
|
||||
|
||||
if (read_num (listed_incremental_stream, 1, &u))
|
||||
return; /* Normal return */
|
||||
|
||||
nfs = u;
|
||||
|
||||
if (read_num (listed_incremental_stream, &u))
|
||||
break;
|
||||
mtime.tv_sec = u;
|
||||
if (mtime.tv_sec != u)
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Modification time (seconds) out of range")));
|
||||
|
||||
if (read_num (listed_incremental_stream, &u))
|
||||
break;
|
||||
mtime.tv_nsec = u;
|
||||
if (mtime.tv_nsec != u)
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Modification time (nanoseconds) out of range")));
|
||||
|
||||
if (read_num (listed_incremental_stream, &u))
|
||||
read_timespec (listed_incremental_stream, &mtime);
|
||||
|
||||
if (read_num (listed_incremental_stream, TYPE_MAXIMUM (dev_t), &u))
|
||||
break;
|
||||
dev = u;
|
||||
if (dev != u)
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Device number out of range")));
|
||||
|
||||
if (read_num (listed_incremental_stream, &u))
|
||||
if (read_num (listed_incremental_stream, TYPE_MAXIMUM (ino_t), &u))
|
||||
break;
|
||||
ino = u;
|
||||
if (ino != u)
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Inode number out of range")));
|
||||
|
||||
if (read_obstack (listed_incremental_stream, &stk, &s))
|
||||
break;
|
||||
@@ -922,16 +1001,16 @@ read_incr_db_2 ()
|
||||
;
|
||||
if (getc (listed_incremental_stream) != 0)
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Missing record terminator")));
|
||||
|
||||
|
||||
content = obstack_finish (&stk);
|
||||
note_directory (name, mtime, dev, ino, nfs, false, content);
|
||||
obstack_free (&stk, content);
|
||||
}
|
||||
FATAL_ERROR ((0, 0, "%s: %s",
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Unexpected EOF")));
|
||||
quotearg_colon (listed_incremental_option),
|
||||
_("Unexpected EOF in snapshot file")));
|
||||
}
|
||||
|
||||
/* Read incremental snapshot file (directory file).
|
||||
@@ -946,7 +1025,6 @@ read_directory_file (void)
|
||||
int fd;
|
||||
char *buf = 0;
|
||||
size_t bufsize;
|
||||
long lineno = 1;
|
||||
|
||||
/* Open the file for both read and write. That way, we can write
|
||||
it later without having to reopen it, and don't have to worry if
|
||||
@@ -969,7 +1047,7 @@ read_directory_file (void)
|
||||
if (0 < getline (&buf, &bufsize, listed_incremental_stream))
|
||||
{
|
||||
char *ebuf;
|
||||
int incremental_version;
|
||||
uintmax_t incremental_version;
|
||||
|
||||
if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
|
||||
{
|
||||
@@ -980,7 +1058,7 @@ read_directory_file (void)
|
||||
if (!*ebuf)
|
||||
ERROR((1, 0, _("Bad incremental file format")));
|
||||
|
||||
incremental_version = (errno = 0, strtoumax (ebuf+1, &ebuf, 10));
|
||||
incremental_version = strtoumax (ebuf + 1, NULL, 10);
|
||||
}
|
||||
else
|
||||
incremental_version = 0;
|
||||
@@ -995,12 +1073,12 @@ read_directory_file (void)
|
||||
case TAR_INCREMENTAL_VERSION:
|
||||
read_incr_db_2 ();
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
ERROR ((1, 0, _("Unsupported incremental format version: %d"),
|
||||
ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
|
||||
incremental_version));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (ferror (listed_incremental_stream))
|
||||
@@ -1024,7 +1102,9 @@ write_directory_file_entry (void *entry, void *data)
|
||||
|
||||
s = DIR_IS_NFS (directory) ? "1" : "0";
|
||||
fwrite (s, 2, 1, fp);
|
||||
s = umaxtostr (directory->mtime.tv_sec, buf);
|
||||
s = (TYPE_SIGNED (time_t)
|
||||
? imaxtostr (directory->mtime.tv_sec, buf)
|
||||
: umaxtostr (directory->mtime.tv_sec, buf));
|
||||
fwrite (s, strlen (s) + 1, 1, fp);
|
||||
s = umaxtostr (directory->mtime.tv_nsec, buf);
|
||||
fwrite (s, strlen (s) + 1, 1, fp);
|
||||
@@ -1067,7 +1147,9 @@ write_directory_file (void)
|
||||
fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
|
||||
TAR_INCREMENTAL_VERSION);
|
||||
|
||||
s = umaxtostr (start_time.tv_sec, buf);
|
||||
s = (TYPE_SIGNED (time_t)
|
||||
? imaxtostr (start_time.tv_sec, buf)
|
||||
: umaxtostr (start_time.tv_sec, buf));
|
||||
fwrite (s, strlen (s) + 1, 1, fp);
|
||||
s = umaxtostr (start_time.tv_nsec, buf);
|
||||
fwrite (s, strlen (s) + 1, 1, fp);
|
||||
@@ -1140,7 +1222,7 @@ dumpdir_ok (char *dumpdir)
|
||||
char *p;
|
||||
int has_tempdir = 0;
|
||||
int expect = 0;
|
||||
|
||||
|
||||
for (p = dumpdir; *p; p += strlen (p) + 1)
|
||||
{
|
||||
if (expect && *p != expect)
|
||||
@@ -1162,7 +1244,7 @@ dumpdir_ok (char *dumpdir)
|
||||
else
|
||||
has_tempdir = 1;
|
||||
break;
|
||||
|
||||
|
||||
case 'R':
|
||||
if (p[1] == 0)
|
||||
{
|
||||
@@ -1177,14 +1259,14 @@ dumpdir_ok (char *dumpdir)
|
||||
}
|
||||
expect = 'T';
|
||||
break;
|
||||
|
||||
|
||||
case 'T':
|
||||
if (expect != 'T')
|
||||
{
|
||||
ERROR ((0, 0,
|
||||
_("Malformed dumpdir: 'T' not preceeded by 'R'")));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (p[1] == 0 && !has_tempdir)
|
||||
{
|
||||
ERROR ((0, 0,
|
||||
@@ -1193,7 +1275,7 @@ dumpdir_ok (char *dumpdir)
|
||||
}
|
||||
expect = 0;
|
||||
break;
|
||||
|
||||
|
||||
case 'N':
|
||||
case 'Y':
|
||||
case 'D':
|
||||
@@ -1218,7 +1300,7 @@ dumpdir_ok (char *dumpdir)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Examine the directories under directory_name and delete any
|
||||
files that were not there at the time of the back-up. */
|
||||
static bool
|
||||
@@ -1227,7 +1309,7 @@ try_purge_directory (char const *directory_name)
|
||||
char *current_dir;
|
||||
char *cur, *arc, *p;
|
||||
char *temp_stub = NULL;
|
||||
|
||||
|
||||
if (!is_dumpdir (¤t_stat_info))
|
||||
return false;
|
||||
|
||||
@@ -1241,13 +1323,13 @@ try_purge_directory (char const *directory_name)
|
||||
/* Verify if dump directory is sane */
|
||||
if (!dumpdir_ok (current_stat_info.dumpdir))
|
||||
return false;
|
||||
|
||||
|
||||
/* Process renames */
|
||||
for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
|
||||
{
|
||||
if (*arc == 'X')
|
||||
{
|
||||
#define TEMP_DIR_TEMPLATE "tar.XXXXXX"
|
||||
#define TEMP_DIR_TEMPLATE "tar.XXXXXX"
|
||||
size_t len = strlen (arc + 1);
|
||||
temp_stub = xrealloc (temp_stub, len + 1 + sizeof TEMP_DIR_TEMPLATE);
|
||||
memcpy (temp_stub, arc + 1, len);
|
||||
@@ -1275,7 +1357,7 @@ try_purge_directory (char const *directory_name)
|
||||
src = temp_stub;
|
||||
else if (*dst == 0)
|
||||
dst = temp_stub;
|
||||
|
||||
|
||||
if (!rename_directory (src, dst))
|
||||
{
|
||||
free (temp_stub);
|
||||
@@ -1288,7 +1370,7 @@ try_purge_directory (char const *directory_name)
|
||||
}
|
||||
|
||||
free (temp_stub);
|
||||
|
||||
|
||||
/* Process deletes */
|
||||
p = NULL;
|
||||
for (cur = current_dir; *cur; cur += strlen (cur) + 1)
|
||||
@@ -1299,22 +1381,23 @@ try_purge_directory (char const *directory_name)
|
||||
free (p);
|
||||
p = new_name (directory_name, cur);
|
||||
|
||||
if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
|
||||
|| (*entry == 'D' && S_ISDIR (st.st_mode))
|
||||
|| (*entry == 'Y' && !S_ISDIR (st.st_mode)))
|
||||
if (deref_stat (false, p, &st))
|
||||
{
|
||||
if (deref_stat (false, p, &st))
|
||||
if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
|
||||
dirs and check it here? */
|
||||
{
|
||||
if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
|
||||
dirs and check it here? */
|
||||
{
|
||||
stat_diag (p);
|
||||
WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
|
||||
quotearg_colon (p)));
|
||||
}
|
||||
continue;
|
||||
stat_diag (p);
|
||||
WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
|
||||
quotearg_colon (p)));
|
||||
}
|
||||
else if (one_file_system_option && st.st_dev != root_device)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
|
||||
|| (*entry == 'D' && !S_ISDIR (st.st_mode))
|
||||
|| (*entry == 'Y' && S_ISDIR (st.st_mode)))
|
||||
{
|
||||
if (one_file_system_option && st.st_dev != root_device)
|
||||
{
|
||||
WARN ((0, 0,
|
||||
_("%s: directory is on a different device: not purging"),
|
||||
@@ -1336,7 +1419,7 @@ try_purge_directory (char const *directory_name)
|
||||
}
|
||||
}
|
||||
free (p);
|
||||
|
||||
|
||||
free (current_dir);
|
||||
return true;
|
||||
}
|
||||
@@ -1347,7 +1430,7 @@ purge_directory (char const *directory_name)
|
||||
if (!try_purge_directory (directory_name))
|
||||
skip_member ();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
list_dumpdir (char *buffer, size_t size)
|
||||
{
|
||||
|
||||
@@ -107,7 +107,6 @@ read_and (void (*do_something) (void))
|
||||
{
|
||||
case GNUTYPE_VOLHDR:
|
||||
case GNUTYPE_MULTIVOL:
|
||||
case GNUTYPE_NAMES:
|
||||
break;
|
||||
|
||||
case DIRTYPE:
|
||||
@@ -1047,10 +1046,6 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
|
||||
modes[0] = 'M';
|
||||
break;
|
||||
|
||||
case GNUTYPE_NAMES:
|
||||
modes[0] = 'N';
|
||||
break;
|
||||
|
||||
case GNUTYPE_LONGNAME:
|
||||
case GNUTYPE_LONGLINK:
|
||||
modes[0] = 'L';
|
||||
@@ -1234,10 +1229,6 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
|
||||
uintbuf));
|
||||
fprintf (stdlis, _("--Continued at byte %s--\n"), size);
|
||||
break;
|
||||
|
||||
case GNUTYPE_NAMES:
|
||||
fprintf (stdlis, _("--Mangled file names--\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
fflush (stdlis);
|
||||
|
||||
121
src/mangle.c
121
src/mangle.c
@@ -1,121 +0,0 @@
|
||||
/* Encode long filenames for GNU tar.
|
||||
Copyright 1988, 92, 94, 96, 97, 99, 2000 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
#include <system.h>
|
||||
#include "common.h"
|
||||
#include <quotearg.h>
|
||||
|
||||
struct mangled
|
||||
{
|
||||
struct mangled *next;
|
||||
int type;
|
||||
char mangled[NAME_FIELD_SIZE];
|
||||
char *linked_to;
|
||||
char normal[1];
|
||||
};
|
||||
|
||||
/* Extract a GNUTYPE_NAMES record contents. It seems that such are
|
||||
not produced anymore by GNU tar, but we leave the reading code
|
||||
around nevertheless, for salvaging old tapes. */
|
||||
void
|
||||
extract_mangle (void)
|
||||
{
|
||||
off_t size = current_stat_info.stat.st_size;
|
||||
char *buffer = xmalloc ((size_t) (size + 1));
|
||||
char *copy = buffer;
|
||||
char *cursor = buffer;
|
||||
|
||||
if (size != (size_t) size || size == (size_t) -1)
|
||||
xalloc_die ();
|
||||
|
||||
buffer[size] = '\0';
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
union block *block = find_next_block ();
|
||||
size_t available;
|
||||
|
||||
if (!block)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in mangled names")));
|
||||
return;
|
||||
}
|
||||
available = available_space_after (block);
|
||||
if (available > size)
|
||||
available = size;
|
||||
memcpy (copy, block->buffer, available);
|
||||
copy += available;
|
||||
size -= available;
|
||||
set_next_block_after ((union block *) (block->buffer + available - 1));
|
||||
}
|
||||
|
||||
while (*cursor)
|
||||
{
|
||||
char *next_cursor;
|
||||
char *name;
|
||||
char *name_end;
|
||||
|
||||
next_cursor = strchr (cursor, '\n');
|
||||
*next_cursor++ = '\0';
|
||||
|
||||
if (!strncmp (cursor, "Rename ", 7))
|
||||
{
|
||||
|
||||
name = cursor + 7;
|
||||
name_end = strchr (name, ' ');
|
||||
while (strncmp (name_end, " to ", 4))
|
||||
{
|
||||
name_end++;
|
||||
name_end = strchr (name_end, ' ');
|
||||
}
|
||||
*name_end = '\0';
|
||||
if (next_cursor[-2] == '/')
|
||||
next_cursor[-2] = '\0';
|
||||
unquote_string (name_end + 4);
|
||||
if (rename (name, name_end + 4))
|
||||
ERROR ((0, errno, _("%s: Cannot rename to %s"),
|
||||
quotearg_colon (name), quote_n (1, name_end + 4)));
|
||||
else if (verbose_option)
|
||||
WARN ((0, 0, _("Renamed %s to %s"), name, name_end + 4));
|
||||
}
|
||||
#ifdef HAVE_SYMLINK
|
||||
else if (!strncmp (cursor, "Symlink ", 8))
|
||||
{
|
||||
name = cursor + 8;
|
||||
name_end = strchr (name, ' ');
|
||||
while (strncmp (name_end, " to ", 4))
|
||||
{
|
||||
name_end++;
|
||||
name_end = strchr (name_end, ' ');
|
||||
}
|
||||
*name_end = '\0';
|
||||
unquote_string (name);
|
||||
unquote_string (name_end + 4);
|
||||
if (symlink (name, name_end + 4)
|
||||
&& (unlink (name_end + 4) || symlink (name, name_end + 4)))
|
||||
ERROR ((0, errno, _("%s: Cannot symlink to %s"),
|
||||
quotearg_colon (name), quote_n (1, name_end + 4)));
|
||||
else if (verbose_option)
|
||||
WARN ((0, 0, _("Symlinked %s to %s"), name, name_end + 4));
|
||||
}
|
||||
#endif
|
||||
else
|
||||
ERROR ((0, 0, _("Unknown demangling command %s"), cursor));
|
||||
|
||||
cursor = next_cursor;
|
||||
}
|
||||
}
|
||||
55
src/misc.c
55
src/misc.c
@@ -18,10 +18,13 @@
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
#include <system.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <rmt.h>
|
||||
#include "common.h"
|
||||
#include <quotearg.h>
|
||||
#include <save-cwd.h>
|
||||
#include <xgetcwd.h>
|
||||
#include <unlinkdir.h>
|
||||
#include <utimens.h>
|
||||
|
||||
@@ -412,7 +415,7 @@ remove_any_file (const char *file_name, enum remove_option option)
|
||||
so, we do not have to backup block or character devices, nor remote
|
||||
entities. */
|
||||
bool
|
||||
maybe_backup_file (const char *file_name, int this_is_the_archive)
|
||||
maybe_backup_file (const char *file_name, bool this_is_the_archive)
|
||||
{
|
||||
struct stat file_stat;
|
||||
|
||||
@@ -542,8 +545,14 @@ chdir_arg (char const *dir)
|
||||
{
|
||||
if (wds == wd_alloc)
|
||||
{
|
||||
wd_alloc = 2 * (wd_alloc + 1);
|
||||
wd = xrealloc (wd, sizeof *wd * wd_alloc);
|
||||
if (wd_alloc == 0)
|
||||
{
|
||||
wd_alloc = 2;
|
||||
wd = xmalloc (sizeof *wd * wd_alloc);
|
||||
}
|
||||
else
|
||||
wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
|
||||
|
||||
if (! wds)
|
||||
{
|
||||
wd[wds].name = ".";
|
||||
@@ -568,13 +577,41 @@ chdir_arg (char const *dir)
|
||||
return wds++;
|
||||
}
|
||||
|
||||
/* Return maximum number of open files */
|
||||
int
|
||||
get_max_open_files ()
|
||||
{
|
||||
#if defined _SC_OPEN_MAX
|
||||
return sysconf (_SC_OPEN_MAX);
|
||||
#elif defined RLIMIT_NOFILE
|
||||
struct rlimit rlim;
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
|
||||
return rlim.rlim_max;
|
||||
#elif defined HAVE_GETDTABLESIZE
|
||||
return getdtablesize ();
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Close all descriptors, except the first three */
|
||||
void
|
||||
closeopen ()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = get_max_open_files () - 1; i > 2; i--)
|
||||
close (i);
|
||||
}
|
||||
|
||||
/* Change to directory I. If I is 0, change to the initial working
|
||||
directory; otherwise, I must be a value returned by chdir_arg. */
|
||||
void
|
||||
chdir_do (int i)
|
||||
{
|
||||
static int previous;
|
||||
|
||||
static int saved_count;
|
||||
|
||||
if (previous != i)
|
||||
{
|
||||
struct wd *prev = &wd[previous];
|
||||
@@ -583,7 +620,15 @@ chdir_do (int i)
|
||||
if (! prev->saved)
|
||||
{
|
||||
prev->saved = 1;
|
||||
if (save_cwd (&prev->saved_cwd) != 0)
|
||||
saved_count++;
|
||||
/* Make sure we still have at least one descriptor available */
|
||||
if (saved_count >= get_max_open_files () - 4)
|
||||
{
|
||||
/* Force restore_cwd to use chdir_long */
|
||||
prev->saved_cwd.desc = -1;
|
||||
prev->saved_cwd.name = xgetcwd ();
|
||||
}
|
||||
else if (save_cwd (&prev->saved_cwd) != 0)
|
||||
FATAL_ERROR ((0, 0, _("Cannot save working directory")));
|
||||
}
|
||||
|
||||
|
||||
27
src/names.c
27
src/names.c
@@ -471,7 +471,6 @@ addname (char const *string, int change_dir)
|
||||
name->matching_flags = matching_flags;
|
||||
name->change_dir = change_dir;
|
||||
name->dir_contents = NULL;
|
||||
name->explicit = 1;
|
||||
|
||||
*nametail = name;
|
||||
nametail = &name->next;
|
||||
@@ -481,7 +480,7 @@ addname (char const *string, int change_dir)
|
||||
/* Find a match for FILE_NAME (whose string length is LENGTH) in the name
|
||||
list. */
|
||||
static struct name *
|
||||
namelist_match (char const *file_name, size_t length, bool exact)
|
||||
namelist_match (char const *file_name, size_t length)
|
||||
{
|
||||
struct name *p;
|
||||
|
||||
@@ -497,7 +496,7 @@ namelist_match (char const *file_name, size_t length, bool exact)
|
||||
|
||||
/* Return true if and only if name FILE_NAME (from an archive) matches any
|
||||
name from the namelist. */
|
||||
int
|
||||
bool
|
||||
name_match (const char *file_name)
|
||||
{
|
||||
size_t length = strlen (file_name);
|
||||
@@ -507,17 +506,17 @@ name_match (const char *file_name)
|
||||
struct name *cursor = namelist;
|
||||
|
||||
if (!cursor)
|
||||
return 1;
|
||||
|
||||
return true;
|
||||
|
||||
if (cursor->name[0] == 0)
|
||||
{
|
||||
chdir_do (cursor->change_dir);
|
||||
namelist = 0;
|
||||
nametail = &namelist;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
cursor = namelist_match (file_name, length, false);
|
||||
cursor = namelist_match (file_name, length);
|
||||
if (cursor)
|
||||
{
|
||||
if (!(ISSLASH (file_name[cursor->length]) && recursion_option)
|
||||
@@ -544,10 +543,10 @@ name_match (const char *file_name)
|
||||
{
|
||||
name_gather (); /* read one more */
|
||||
if (namelist->found_count)
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,7 +780,6 @@ add_hierarchy_to_namelist (struct name *name, dev_t device)
|
||||
}
|
||||
strcpy (namebuf + name_length, string + 1);
|
||||
np = addname (namebuf, change_dir);
|
||||
np->explicit = 0;
|
||||
add_hierarchy_to_namelist (np, device);
|
||||
}
|
||||
}
|
||||
@@ -856,18 +854,15 @@ collect_and_sort_names (void)
|
||||
1. It returns a pointer to the name it matched, and doesn't set FOUND
|
||||
in structure. The caller will have to do that if it wants to.
|
||||
2. If the namelist is empty, it returns null, unlike name_match, which
|
||||
returns TRUE.
|
||||
3. The second argument (EXACT) controls matching algorithm. If it
|
||||
is TRUE, the exact matching is used. However, regular expressions are
|
||||
always matched as such, no matter what the value of EXACT is. */
|
||||
returns TRUE. */
|
||||
struct name *
|
||||
name_scan (const char *file_name, bool exact)
|
||||
name_scan (const char *file_name)
|
||||
{
|
||||
size_t length = strlen (file_name);
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct name *cursor = namelist_match (file_name, length, exact);
|
||||
struct name *cursor = namelist_match (file_name, length);
|
||||
if (cursor)
|
||||
return cursor;
|
||||
|
||||
|
||||
311
src/sparse.c
311
src/sparse.c
@@ -1,6 +1,6 @@
|
||||
/* Functions for dealing with sparse files
|
||||
|
||||
Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
@@ -409,15 +409,6 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
|
||||
return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
|
||||
}
|
||||
|
||||
/* Returns true if the file represented by stat is a sparse one */
|
||||
bool
|
||||
sparse_file_p (struct tar_stat_info *st)
|
||||
{
|
||||
return (ST_NBLOCKS (st->stat)
|
||||
< (st->stat.st_size / ST_NBLOCKSIZE
|
||||
+ (st->stat.st_size % ST_NBLOCKSIZE != 0)));
|
||||
}
|
||||
|
||||
bool
|
||||
sparse_member_p (struct tar_stat_info *st)
|
||||
{
|
||||
@@ -740,11 +731,9 @@ oldgnu_dump_header (struct tar_sparse_file *file)
|
||||
oldgnu_store_sparse_info (file, &i,
|
||||
blk->sparse_header.sp,
|
||||
SPARSES_IN_SPARSE_HEADER);
|
||||
set_next_block_after (blk);
|
||||
if (i < file->stat_info->sparse_map_avail)
|
||||
blk->sparse_header.isextended = 1;
|
||||
else
|
||||
break;
|
||||
set_next_block_after (blk);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -843,16 +832,15 @@ static struct tar_sparse_optab const star_optab = {
|
||||
};
|
||||
|
||||
|
||||
/* GNU PAX sparse file format. The sparse file map is stored in
|
||||
x header:
|
||||
/* GNU PAX sparse file format. There are several versions:
|
||||
|
||||
* 0.0
|
||||
|
||||
The initial version of sparse format used by tar 1.14-1.15.1.
|
||||
The sparse file map is stored in x header:
|
||||
|
||||
GNU.sparse.size Real size of the stored file
|
||||
GNU.sparse.numblocks Number of blocks in the sparse map
|
||||
GNU.sparse.map Map of non-null data chunks. A string consisting
|
||||
of comma-separated values "offset,size[,offset,size]..."
|
||||
|
||||
Tar versions 1.14-1.15.1 instead of the latter used:
|
||||
|
||||
repeat numblocks time
|
||||
GNU.sparse.offset Offset of the next data block
|
||||
GNU.sparse.numbytes Size of the next data block
|
||||
@@ -860,44 +848,89 @@ static struct tar_sparse_optab const star_optab = {
|
||||
|
||||
This has been reported as conflicting with the POSIX specs. The reason is
|
||||
that offsets and sizes of non-zero data blocks were stored in multiple
|
||||
instances of GNU.sparse.offset/GNU.sparse.numbytes variables. However,
|
||||
instances of GNU.sparse.offset/GNU.sparse.numbytes variables, whereas
|
||||
POSIX requires the latest occurrence of the variable to override all
|
||||
previous occurrences.
|
||||
|
||||
To avoid this incompatibility new keyword GNU.sparse.map was introduced
|
||||
in tar 1.15.2. Some people might still need the 1.14 way of handling
|
||||
sparse files for the compatibility reasons: it can be achieved by
|
||||
specifying `--pax-option delete=GNU.sparse.map' in the command line.
|
||||
|
||||
To avoid this incompatibility two following versions were introduced.
|
||||
|
||||
See FIXME-1.14-1.15.1-1.20, below.
|
||||
* 0.1
|
||||
|
||||
Used by tar 1.15.2 -- 1.15.91 (alpha releases).
|
||||
|
||||
The sparse file map is stored in
|
||||
x header:
|
||||
|
||||
GNU.sparse.size Real size of the stored file
|
||||
GNU.sparse.numblocks Number of blocks in the sparse map
|
||||
GNU.sparse.map Map of non-null data chunks. A string consisting
|
||||
of comma-separated values "offset,size[,offset,size]..."
|
||||
|
||||
The resulting GNU.sparse.map string can be *very* long. While POSIX does not
|
||||
impose any limit on the length of a x header variable, this can confuse some
|
||||
tars.
|
||||
|
||||
* 1.0
|
||||
|
||||
Starting from this version, the exact sparse format version is specified
|
||||
explicitely in the header using the following variables:
|
||||
|
||||
GNU.sparse.major Major version
|
||||
GNU.sparse.minor Minor version
|
||||
|
||||
X header keeps the following variables:
|
||||
|
||||
GNU.sparse.name Real file name of the sparse file
|
||||
GNU.sparse.realsize Real size of the stored file (corresponds to the old
|
||||
GNU.sparse.size variable)
|
||||
|
||||
The name field of the ustar header is constructed using the pattern
|
||||
"%d/GNUSparseFile.%p/%f".
|
||||
|
||||
The sparse map itself is stored in the file data block, preceding the actual
|
||||
file data. It consists of a series of octal numbers of arbitrary length,
|
||||
delimited by newlines. The map is padded with nulls to the nearest block
|
||||
boundary.
|
||||
|
||||
The first number gives the number of entries in the map. Following are map
|
||||
entries, each one consisting of two numbers giving the offset and size of
|
||||
the data block it describes.
|
||||
|
||||
The format is designed in such a way that non-posix aware tars and tars not
|
||||
supporting GNU.sparse.* keywords will extract each sparse file in its
|
||||
condensed form with the file map attached and will place it into a separate
|
||||
directory. Then, using a simple program it would be possible to expand the
|
||||
file to its original form even without GNU tar.
|
||||
|
||||
Bu default, v.1.0 archives are created. To use other formats,
|
||||
--sparse-version option is provided. Additionally, v.0.0 can be obtained
|
||||
by deleting GNU.sparse.map from 0.1 format: --sparse-version 0.1
|
||||
--pax-option delete=GNU.sparse.map
|
||||
*/
|
||||
|
||||
static bool
|
||||
pax_sparse_member_p (struct tar_sparse_file *file)
|
||||
{
|
||||
return file->stat_info->sparse_map_avail > 0;
|
||||
return file->stat_info->sparse_map_avail > 0
|
||||
|| file->stat_info->sparse_major > 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
pax_dump_header (struct tar_sparse_file *file)
|
||||
pax_dump_header_0 (struct tar_sparse_file *file)
|
||||
{
|
||||
off_t block_ordinal = current_block_ordinal ();
|
||||
union block *blk;
|
||||
size_t i;
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
struct sp_array *map = file->stat_info->sparse_map;
|
||||
|
||||
char *save_file_name = NULL;
|
||||
|
||||
/* Store the real file size */
|
||||
xheader_store ("GNU.sparse.size", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL);
|
||||
|
||||
/* FIXME-1.14-1.15.1-1.20: See the comment above.
|
||||
Starting with 1.17 this should display a warning about POSIX-incompatible
|
||||
keywords being generated. In 1.20, the true branch of the if block below
|
||||
will be removed and GNU.sparse.map will be marked in xhdr_tab as
|
||||
protected. */
|
||||
|
||||
if (xheader_keyword_deleted_p ("GNU.sparse.map"))
|
||||
if (xheader_keyword_deleted_p ("GNU.sparse.map")
|
||||
|| tar_sparse_minor == 0)
|
||||
{
|
||||
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
{
|
||||
@@ -907,6 +940,11 @@ pax_dump_header (struct tar_sparse_file *file)
|
||||
}
|
||||
else
|
||||
{
|
||||
xheader_store ("GNU.sparse.name", file->stat_info, NULL);
|
||||
save_file_name = file->stat_info->file_name;
|
||||
file->stat_info->file_name = xheader_format_name (file->stat_info,
|
||||
"%d/GNUSparseFile.%p/%f", 0);
|
||||
|
||||
xheader_string_begin ();
|
||||
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
{
|
||||
@@ -916,12 +954,205 @@ pax_dump_header (struct tar_sparse_file *file)
|
||||
xheader_string_add (",");
|
||||
xheader_string_add (umaxtostr (map[i].numbytes, nbuf));
|
||||
}
|
||||
xheader_string_end ("GNU.sparse.map");
|
||||
if (!xheader_string_end ("GNU.sparse.map"))
|
||||
{
|
||||
free (file->stat_info->file_name);
|
||||
file->stat_info->file_name = save_file_name;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
blk = start_header (file->stat_info);
|
||||
/* Store the effective (shrunken) file size */
|
||||
OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
|
||||
finish_header (file->stat_info, blk, block_ordinal);
|
||||
if (save_file_name)
|
||||
{
|
||||
free (file->stat_info->file_name);
|
||||
file->stat_info->file_name = save_file_name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
pax_dump_header_1 (struct tar_sparse_file *file)
|
||||
{
|
||||
off_t block_ordinal = current_block_ordinal ();
|
||||
union block *blk;
|
||||
char *p, *q;
|
||||
size_t i;
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
off_t size = 0;
|
||||
struct sp_array *map = file->stat_info->sparse_map;
|
||||
char *save_file_name = file->stat_info->file_name;
|
||||
|
||||
#define COPY_STRING(b,dst,src) do \
|
||||
{ \
|
||||
char *endp = b->buffer + BLOCKSIZE; \
|
||||
char *srcp = src; \
|
||||
while (*srcp) \
|
||||
{ \
|
||||
if (dst == endp) \
|
||||
{ \
|
||||
set_next_block_after (b); \
|
||||
b = find_next_block (); \
|
||||
dst = b->buffer; \
|
||||
endp = b->buffer + BLOCKSIZE; \
|
||||
} \
|
||||
*dst++ = *srcp++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Compute stored file size */
|
||||
p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
|
||||
size += strlen (p) + 1;
|
||||
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
{
|
||||
p = umaxtostr (map[i].offset, nbuf);
|
||||
size += strlen (p) + 1;
|
||||
p = umaxtostr (map[i].numbytes, nbuf);
|
||||
size += strlen (p) + 1;
|
||||
}
|
||||
size = (size + BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
file->stat_info->archive_file_size += size * BLOCKSIZE;
|
||||
|
||||
/* Store sparse file identification */
|
||||
xheader_store ("GNU.sparse.major", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.minor", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.name", file->stat_info, NULL);
|
||||
xheader_store ("GNU.sparse.realsize", file->stat_info, NULL);
|
||||
|
||||
file->stat_info->file_name = xheader_format_name (file->stat_info,
|
||||
"%d/GNUSparseFile.%p/%f", 0);
|
||||
|
||||
blk = start_header (file->stat_info);
|
||||
/* Store the effective (shrunken) file size */
|
||||
OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
|
||||
finish_header (file->stat_info, blk, block_ordinal);
|
||||
free (file->stat_info->file_name);
|
||||
file->stat_info->file_name = save_file_name;
|
||||
|
||||
blk = find_next_block ();
|
||||
q = blk->buffer;
|
||||
p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
|
||||
COPY_STRING (blk, q, p);
|
||||
COPY_STRING (blk, q, "\n");
|
||||
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
{
|
||||
p = umaxtostr (map[i].offset, nbuf);
|
||||
COPY_STRING (blk, q, p);
|
||||
COPY_STRING (blk, q, "\n");
|
||||
p = umaxtostr (map[i].numbytes, nbuf);
|
||||
COPY_STRING (blk, q, p);
|
||||
COPY_STRING (blk, q, "\n");
|
||||
}
|
||||
memset (q, 0, BLOCKSIZE - (q - blk->buffer));
|
||||
set_next_block_after (blk);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
pax_dump_header (struct tar_sparse_file *file)
|
||||
{
|
||||
file->stat_info->sparse_major = tar_sparse_major;
|
||||
file->stat_info->sparse_minor = tar_sparse_minor;
|
||||
|
||||
return (file->stat_info->sparse_major == 0) ?
|
||||
pax_dump_header_0 (file) : pax_dump_header_1 (file);
|
||||
}
|
||||
|
||||
static bool
|
||||
decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
|
||||
{
|
||||
uintmax_t u;
|
||||
char *arg_lim;
|
||||
|
||||
if (!ISDIGIT (*arg))
|
||||
return false;
|
||||
|
||||
u = strtoumax (arg, &arg_lim, 10);
|
||||
|
||||
if (! (u <= maxval && errno != ERANGE) || *arg_lim)
|
||||
return false;
|
||||
|
||||
*num = u;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
pax_decode_header (struct tar_sparse_file *file)
|
||||
{
|
||||
if (file->stat_info->sparse_major > 0)
|
||||
{
|
||||
uintmax_t u;
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
union block *blk;
|
||||
char *p;
|
||||
size_t i;
|
||||
|
||||
#define COPY_BUF(b,buf,src) do \
|
||||
{ \
|
||||
char *endp = b->buffer + BLOCKSIZE; \
|
||||
char *dst = buf; \
|
||||
do \
|
||||
{ \
|
||||
if (dst == buf + UINTMAX_STRSIZE_BOUND -1) \
|
||||
{ \
|
||||
ERROR ((0, 0, _("%s: numeric overflow in sparse archive member"), \
|
||||
file->stat_info->orig_file_name)); \
|
||||
return false; \
|
||||
} \
|
||||
if (src == endp) \
|
||||
{ \
|
||||
set_next_block_after (b); \
|
||||
b = find_next_block (); \
|
||||
src = b->buffer; \
|
||||
endp = b->buffer + BLOCKSIZE; \
|
||||
} \
|
||||
*dst = *src++; \
|
||||
} \
|
||||
while (*dst++ != '\n'); \
|
||||
dst[-1] = 0; \
|
||||
} while (0)
|
||||
|
||||
set_next_block_after (current_header);
|
||||
blk = find_next_block ();
|
||||
p = blk->buffer;
|
||||
COPY_BUF (blk,nbuf,p);
|
||||
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
|
||||
{
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
return false;
|
||||
}
|
||||
file->stat_info->sparse_map_size = u;
|
||||
file->stat_info->sparse_map = xcalloc (file->stat_info->sparse_map_size,
|
||||
sizeof (*file->stat_info->sparse_map));
|
||||
file->stat_info->sparse_map_avail = 0;
|
||||
for (i = 0; i < file->stat_info->sparse_map_size; i++)
|
||||
{
|
||||
struct sp_array sp;
|
||||
|
||||
COPY_BUF (blk,nbuf,p);
|
||||
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
|
||||
{
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
return false;
|
||||
}
|
||||
sp.offset = u;
|
||||
COPY_BUF (blk,nbuf,p);
|
||||
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
|
||||
{
|
||||
ERROR ((0, 0, _("%s: malformed sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
return false;
|
||||
}
|
||||
sp.numbytes = u;
|
||||
sparse_add_map (file->stat_info, &sp);
|
||||
}
|
||||
set_next_block_after (blk);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -930,8 +1161,8 @@ static struct tar_sparse_optab const pax_optab = {
|
||||
NULL, /* No done function */
|
||||
pax_sparse_member_p,
|
||||
pax_dump_header,
|
||||
NULL, /* No decode_header function */
|
||||
NULL, /* No fixup_header function */
|
||||
NULL,
|
||||
pax_decode_header,
|
||||
NULL, /* No scan_block function */
|
||||
sparse_dump_region,
|
||||
sparse_extract_region,
|
||||
|
||||
275
src/tar.c
275
src/tar.c
@@ -41,10 +41,11 @@
|
||||
|
||||
#include <argmatch.h>
|
||||
#include <closeout.h>
|
||||
#include <configmake.h>
|
||||
#include <exitfail.h>
|
||||
#include <getdate.h>
|
||||
#include <localedir.h>
|
||||
#include <rmt.h>
|
||||
#include <rmt-command.h>
|
||||
#include <prepargs.h>
|
||||
#include <quotearg.h>
|
||||
#include <version-etc.h>
|
||||
@@ -254,6 +255,7 @@ enum
|
||||
DELETE_OPTION,
|
||||
EXCLUDE_CACHES_OPTION,
|
||||
EXCLUDE_OPTION,
|
||||
EXCLUDE_TAG_OPTION,
|
||||
FORCE_LOCAL_OPTION,
|
||||
GROUP_OPTION,
|
||||
HANG_OPTION,
|
||||
@@ -263,6 +265,7 @@ enum
|
||||
INDEX_FILE_OPTION,
|
||||
KEEP_NEWER_FILES_OPTION,
|
||||
MODE_OPTION,
|
||||
MTIME_OPTION,
|
||||
NEWER_MTIME_OPTION,
|
||||
NO_ANCHORED_OPTION,
|
||||
NO_DELAY_DIRECTORY_RESTORE_OPTION,
|
||||
@@ -300,6 +303,7 @@ enum
|
||||
SHOW_DEFAULTS_OPTION,
|
||||
SHOW_OMITTED_DIRS_OPTION,
|
||||
SHOW_TRANSFORMED_NAMES_OPTION,
|
||||
SPARSE_VERSION_OPTION,
|
||||
STRIP_COMPONENTS_OPTION,
|
||||
SUFFIX_OPTION,
|
||||
TEST_LABEL_OPTION,
|
||||
@@ -318,13 +322,16 @@ enum
|
||||
|
||||
const char *argp_program_version = "tar (" PACKAGE_NAME ") " VERSION;
|
||||
const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
|
||||
static char doc[] = N_("GNU `tar' saves many files together into a single tape or disk archive, and can restore individual files from the archive.\n\
|
||||
static char const doc[] = N_("\
|
||||
GNU `tar' saves many files together into a single tape or disk archive, \
|
||||
and can restore individual files from the archive.\n\
|
||||
\n\
|
||||
Examples:\n\
|
||||
tar -cf archive.tar foo bar # Create archive.tar from files foo and bar.\n\
|
||||
tar -tvf archive.tar # List all files in archive.tar verbosely.\n\
|
||||
tar -xf archive.tar # Extract all files from archive.tar.\n\
|
||||
\vThe backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
|
||||
tar -xf archive.tar # Extract all files from archive.tar.\n")
|
||||
"\v"
|
||||
N_("The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
|
||||
The version control may be set with --backup or VERSION_CONTROL, values are:\n\n\
|
||||
none, off never make backups\n\
|
||||
t, numbered make numbered backups\n\
|
||||
@@ -381,6 +388,8 @@ static struct argp_option options[] = {
|
||||
|
||||
{"sparse", 'S', 0, 0,
|
||||
N_("handle sparse files efficiently"), GRID+1 },
|
||||
{"sparse-version", SPARSE_VERSION_OPTION, N_("MAJOR[.MINOR]"), 0,
|
||||
N_("set version of the sparse format to use (implies --sparse)"), GRID+1},
|
||||
{"incremental", 'G', 0, 0,
|
||||
N_("handle old GNU-format incremental backup"), GRID+1 },
|
||||
{"listed-incremental", 'g', N_("FILE"), 0,
|
||||
@@ -399,7 +408,7 @@ static struct argp_option options[] = {
|
||||
|
||||
#define GRID 30
|
||||
{NULL, 0, NULL, 0,
|
||||
N_("Overwrite control:\n"), GRID+1 },
|
||||
N_("Overwrite control:"), GRID },
|
||||
|
||||
{"verify", 'W', 0, 0,
|
||||
N_("attempt to verify the archive after writing it"), GRID+1 },
|
||||
@@ -444,6 +453,8 @@ static struct argp_option options[] = {
|
||||
N_("force NAME as owner for added files"), GRID+1 },
|
||||
{"group", GROUP_OPTION, N_("NAME"), 0,
|
||||
N_("force NAME as group for added files"), GRID+1 },
|
||||
{"mtime", MTIME_OPTION, N_("DATE-OR-FILE"), 0,
|
||||
N_("set mtime for added files from DATE-OR-FILE"), GRID+1 },
|
||||
{"mode", MODE_OPTION, N_("CHANGES"), 0,
|
||||
N_("force (symbolic) mode CHANGES for added files"), GRID+1 },
|
||||
{"atime-preserve", ATIME_PRESERVE_OPTION,
|
||||
@@ -479,7 +490,7 @@ static struct argp_option options[] = {
|
||||
|
||||
#define GRID 60
|
||||
{NULL, 0, NULL, 0,
|
||||
N_("Device selection and switching:\n"), GRID+1 },
|
||||
N_("Device selection and switching:"), GRID },
|
||||
|
||||
{"file", 'f', N_("ARCHIVE"), 0,
|
||||
N_("use archive file or device ARCHIVE"), GRID+1 },
|
||||
@@ -518,7 +529,7 @@ static struct argp_option options[] = {
|
||||
|
||||
#define GRID 70
|
||||
{NULL, 0, NULL, 0,
|
||||
N_("Device blocking:"), GRID+1 },
|
||||
N_("Device blocking:"), GRID },
|
||||
|
||||
{"blocking-factor", 'b', N_("BLOCKS"), 0,
|
||||
N_("BLOCKS x 512 bytes per record"), GRID+1 },
|
||||
@@ -594,6 +605,8 @@ static struct argp_option options[] = {
|
||||
N_("exclude patterns listed in FILE"), GRID+1 },
|
||||
{"exclude-caches", EXCLUDE_CACHES_OPTION, 0, 0,
|
||||
N_("exclude directories containing a cache tag"), GRID+1 },
|
||||
{"exclude-tag", EXCLUDE_TAG_OPTION, N_("FILE"), 0,
|
||||
N_("exclude directories containing FILE"), GRID+1 },
|
||||
{"no-recursion", NO_RECURSION_OPTION, 0, 0,
|
||||
N_("avoid descending automatically in directories"), GRID+1 },
|
||||
{"one-file-system", ONE_FILE_SYSTEM_OPTION, 0, 0,
|
||||
@@ -608,10 +621,9 @@ static struct argp_option options[] = {
|
||||
N_("begin at member MEMBER-NAME in the archive"), GRID+1 },
|
||||
{"newer", 'N', N_("DATE-OR-FILE"), 0,
|
||||
N_("only store files newer than DATE-OR-FILE"), GRID+1 },
|
||||
{"after-date", 0, 0, OPTION_ALIAS, NULL, GRID+1 },
|
||||
{"newer-mtime", NEWER_MTIME_OPTION, N_("DATE"), 0,
|
||||
N_("compare date and time when data changed only"), GRID+1 },
|
||||
{"after-date", 'N', N_("DATE"), 0,
|
||||
N_("same as -N"), GRID+1 },
|
||||
{"backup", BACKUP_OPTION, N_("CONTROL"), OPTION_ARG_OPTIONAL,
|
||||
N_("backup before removal, choose version CONTROL"), GRID+1 },
|
||||
{"suffix", SUFFIX_OPTION, N_("STRING"), 0,
|
||||
@@ -625,10 +637,10 @@ static struct argp_option options[] = {
|
||||
N_("strip NUMBER leading components from file names on extraction"),
|
||||
GRID+1 },
|
||||
{"transform", TRANSFORM_OPTION, N_("EXPRESSION"), 0,
|
||||
N_("Use sed replace EXPRESSION to transform file names"), GRID+1 },
|
||||
N_("use sed replace EXPRESSION to transform file names"), GRID+1 },
|
||||
#undef GRID
|
||||
|
||||
#define GRID 95
|
||||
#define GRID 95
|
||||
{NULL, 0, NULL, 0,
|
||||
N_("File name matching options (affect both exclude and include patterns):"),
|
||||
GRID },
|
||||
@@ -649,7 +661,7 @@ static struct argp_option options[] = {
|
||||
{"wildcards-match-slash", WILDCARDS_MATCH_SLASH_OPTION, 0, 0,
|
||||
N_("wildcards match `/' (default for exclusion)"), GRID+1 },
|
||||
#undef GRID
|
||||
|
||||
|
||||
#define GRID 100
|
||||
{NULL, 0, NULL, 0,
|
||||
N_("Informative output:"), GRID },
|
||||
@@ -743,8 +755,9 @@ enum wildcards
|
||||
|
||||
struct tar_args /* Variables used during option parsing */
|
||||
{
|
||||
char const *textual_date_option; /* Keeps the argument to --newer-mtime
|
||||
option if it represents a textual date */
|
||||
struct textual_date *textual_date; /* Keeps the arguments to --newer-mtime
|
||||
and/or --date option if they are
|
||||
textual dates */
|
||||
enum wildcards wildcards; /* Wildcard settings (--wildcards/
|
||||
--no-wildcards) */
|
||||
int matching_flags; /* exclude_fnmatch options */
|
||||
@@ -768,7 +781,7 @@ struct tar_args /* Variables used during option parsing */
|
||||
| (args)->matching_flags \
|
||||
| recursion_option)
|
||||
|
||||
#ifdef REMOTE_SHELL
|
||||
#ifdef REMOTE_SHELL
|
||||
# define DECL_SHOW_DEFAULT_SETTINGS(stream, printer) \
|
||||
{ \
|
||||
printer (stream, \
|
||||
@@ -796,7 +809,7 @@ struct tar_args /* Variables used during option parsing */
|
||||
static void
|
||||
show_default_settings (FILE *fp)
|
||||
DECL_SHOW_DEFAULT_SETTINGS(fp, fprintf)
|
||||
|
||||
|
||||
static void
|
||||
show_default_settings_fs (argp_fmtstream_t fs)
|
||||
DECL_SHOW_DEFAULT_SETTINGS(fs, argp_fmtstream_printf)
|
||||
@@ -866,7 +879,7 @@ set_stat_signal (const char *name)
|
||||
{ "QUIT", SIGQUIT }
|
||||
};
|
||||
struct sigtab *p;
|
||||
|
||||
|
||||
for (p = sigtab; p < sigtab + sizeof (sigtab) / sizeof (sigtab[0]); p++)
|
||||
if (strcmp (p->name, name) == 0)
|
||||
{
|
||||
@@ -876,6 +889,67 @@ set_stat_signal (const char *name)
|
||||
FATAL_ERROR ((0, 0, _("Unknown signal name: %s"), name));
|
||||
}
|
||||
|
||||
|
||||
struct textual_date
|
||||
{
|
||||
struct textual_date *next;
|
||||
struct timespec *ts;
|
||||
const char *option;
|
||||
const char *date;
|
||||
};
|
||||
|
||||
static void
|
||||
get_date_or_file (struct tar_args *args, const char *option,
|
||||
const char *str, struct timespec *ts)
|
||||
{
|
||||
if (FILE_SYSTEM_PREFIX_LEN (str) != 0
|
||||
|| ISSLASH (*str)
|
||||
|| *str == '.')
|
||||
{
|
||||
struct stat st;
|
||||
if (deref_stat (dereference_option, str, &st) != 0)
|
||||
{
|
||||
stat_error (str);
|
||||
USAGE_ERROR ((0, 0, _("Date sample file not found")));
|
||||
}
|
||||
*ts = get_stat_mtime (&st);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! get_date (ts, str, NULL))
|
||||
{
|
||||
WARN ((0, 0, _("Substituting %s for unknown date format %s"),
|
||||
tartime (*ts, false), quote (str)));
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct textual_date *p = xmalloc (sizeof (*p));
|
||||
p->ts = ts;
|
||||
p->option = option;
|
||||
p->date = str;
|
||||
p->next = args->textual_date;
|
||||
args->textual_date = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
report_textual_dates (struct tar_args *args)
|
||||
{
|
||||
struct textual_date *p;
|
||||
for (p = args->textual_date; p; )
|
||||
{
|
||||
struct textual_date *next = p->next;
|
||||
char const *treated_as = tartime (*p->ts, true);
|
||||
if (strcmp (p->date, treated_as) != 0)
|
||||
WARN ((0, 0, _("Option %s: Treating date `%s' as %s"),
|
||||
p->option, p->date, treated_as));
|
||||
free (p);
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static volatile int _argp_hang;
|
||||
|
||||
@@ -883,7 +957,8 @@ enum read_file_list_state /* Result of reading file name from the list file */
|
||||
{
|
||||
file_list_success, /* OK, name read successfully */
|
||||
file_list_end, /* End of list file */
|
||||
file_list_zero /* Zero separator encountered where it should not */
|
||||
file_list_zero, /* Zero separator encountered where it should not */
|
||||
file_list_skip /* Empty (zero-length) entry encountered, skip it */
|
||||
};
|
||||
|
||||
/* Read from FP a sequence of characters up to FILENAME_TERMINATOR and put them
|
||||
@@ -901,13 +976,15 @@ read_name_from_file (FILE *fp, struct obstack *stk)
|
||||
{
|
||||
/* We have read a zero separator. The file possibly is
|
||||
zero-separated */
|
||||
/* FATAL_ERROR((0, 0, N_("file name contains null character"))); */
|
||||
return file_list_zero;
|
||||
}
|
||||
obstack_1grow (stk, c);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (counter == 0 && c != EOF)
|
||||
return file_list_skip;
|
||||
|
||||
obstack_1grow (stk, 0);
|
||||
|
||||
return (counter == 0 && c == EOF) ? file_list_end : file_list_success;
|
||||
@@ -988,31 +1065,42 @@ update_argv (const char *filename, struct argp_state *state)
|
||||
open_fatal (filename);
|
||||
}
|
||||
|
||||
while ((read_state = read_name_from_file (fp, &argv_stk)) == file_list_success)
|
||||
count++;
|
||||
|
||||
if (read_state == file_list_zero)
|
||||
while ((read_state = read_name_from_file (fp, &argv_stk)) != file_list_end)
|
||||
{
|
||||
size_t size;
|
||||
switch (read_state)
|
||||
{
|
||||
case file_list_success:
|
||||
count++;
|
||||
break;
|
||||
|
||||
WARN ((0, 0, N_("%s: file name read contains nul character"),
|
||||
quotearg_colon (filename)));
|
||||
case file_list_end: /* won't happen, just to pacify gcc */
|
||||
break;
|
||||
|
||||
/* Prepare new stack contents */
|
||||
size = obstack_object_size (&argv_stk);
|
||||
p = obstack_finish (&argv_stk);
|
||||
for (; size > 0; size--, p++)
|
||||
if (*p)
|
||||
obstack_1grow (&argv_stk, *p);
|
||||
else
|
||||
obstack_1grow (&argv_stk, '\n');
|
||||
obstack_1grow (&argv_stk, 0);
|
||||
count = 1;
|
||||
case file_list_zero:
|
||||
{
|
||||
size_t size;
|
||||
|
||||
/* Read rest of files using new filename terminator */
|
||||
filename_terminator = 0;
|
||||
while (read_name_from_file (fp, &argv_stk) == file_list_success)
|
||||
count++;
|
||||
WARN ((0, 0, N_("%s: file name read contains nul character"),
|
||||
quotearg_colon (filename)));
|
||||
|
||||
/* Prepare new stack contents */
|
||||
size = obstack_object_size (&argv_stk);
|
||||
p = obstack_finish (&argv_stk);
|
||||
for (; size > 0; size--, p++)
|
||||
if (*p)
|
||||
obstack_1grow (&argv_stk, *p);
|
||||
else
|
||||
obstack_1grow (&argv_stk, '\n');
|
||||
obstack_1grow (&argv_stk, 0);
|
||||
count = 1;
|
||||
/* Read rest of files using new filename terminator */
|
||||
filename_terminator = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case file_list_skip:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_stdin)
|
||||
@@ -1055,11 +1143,11 @@ tar_help (struct argp_state *state)
|
||||
ARGP_HELP_STD_HELP & ~ARGP_HELP_BUG_ADDR);
|
||||
/* FIXME: use struct uparams.rmargin (from argp-help.c) instead of 79 */
|
||||
fs = argp_make_fmtstream (state->out_stream, 0, 79, 0);
|
||||
|
||||
|
||||
argp_fmtstream_printf (fs, "\n%s\n\n",
|
||||
_("Valid arguments for --quoting-style options are:"));
|
||||
tar_list_quoting_styles (fs, " ");
|
||||
|
||||
|
||||
argp_fmtstream_puts (fs, _("\n*This* tar defaults to:\n"));
|
||||
show_default_settings_fs (fs);
|
||||
argp_fmtstream_putc (fs, '\n');
|
||||
@@ -1215,6 +1303,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
multi_volume_option = true;
|
||||
break;
|
||||
|
||||
case MTIME_OPTION:
|
||||
get_date_or_file (args, "--mtime", arg, &mtime_option);
|
||||
set_mtime_option = true;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
seekable_archive = true;
|
||||
break;
|
||||
@@ -1226,31 +1319,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
case NEWER_MTIME_OPTION:
|
||||
if (NEWER_OPTION_INITIALIZED (newer_mtime_option))
|
||||
USAGE_ERROR ((0, 0, _("More than one threshold date")));
|
||||
|
||||
if (FILE_SYSTEM_PREFIX_LEN (arg) != 0
|
||||
|| ISSLASH (*arg)
|
||||
|| *arg == '.')
|
||||
{
|
||||
struct stat st;
|
||||
if (deref_stat (dereference_option, arg, &st) != 0)
|
||||
{
|
||||
stat_error (arg);
|
||||
USAGE_ERROR ((0, 0, _("Date sample file not found")));
|
||||
}
|
||||
newer_mtime_option = get_stat_mtime (&st);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! get_date (&newer_mtime_option, arg, NULL))
|
||||
{
|
||||
WARN ((0, 0, _("Substituting %s for unknown date format %s"),
|
||||
tartime (newer_mtime_option, false), quote (arg)));
|
||||
newer_mtime_option.tv_nsec = 0;
|
||||
}
|
||||
else
|
||||
args->textual_date_option = arg;
|
||||
}
|
||||
|
||||
get_date_or_file (args,
|
||||
key == NEWER_MTIME_OPTION ? "--newer-mtime"
|
||||
: "--after-date", arg, &newer_mtime_option);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
@@ -1277,7 +1348,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
/* Print block numbers for debugging bad tar archives. */
|
||||
|
||||
/* It would surely make sense to exchange -B and -R, but it seems
|
||||
that -B has been used for a long while in Sun tar ans most
|
||||
that -B has been used for a long while in Sun tar and most
|
||||
BSD-derived systems. This is a consequence of the block/record
|
||||
terminology confusion. */
|
||||
|
||||
@@ -1285,7 +1356,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
break;
|
||||
|
||||
case 's':
|
||||
/* Names to extr are sorted. */
|
||||
/* Names to extract are sorted. */
|
||||
|
||||
same_order_option = true;
|
||||
break;
|
||||
@@ -1294,6 +1365,22 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
sparse_option = true;
|
||||
break;
|
||||
|
||||
case SPARSE_VERSION_OPTION:
|
||||
sparse_option = true;
|
||||
{
|
||||
char *p;
|
||||
tar_sparse_major = strtoul (arg, &p, 10);
|
||||
if (*p)
|
||||
{
|
||||
if (*p != '.')
|
||||
USAGE_ERROR ((0, 0, _("Invalid sparse version value")));
|
||||
tar_sparse_minor = strtoul (p + 1, &p, 10);
|
||||
if (*p)
|
||||
USAGE_ERROR ((0, 0, _("Invalid sparse version value")));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
set_subcommand_option (LIST_SUBCOMMAND);
|
||||
verbose_option++;
|
||||
@@ -1423,6 +1510,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
exclude_caches_option = true;
|
||||
break;
|
||||
|
||||
case EXCLUDE_TAG_OPTION:
|
||||
add_exclude_tag (arg);
|
||||
break;
|
||||
|
||||
case FORCE_LOCAL_OPTION:
|
||||
force_local_option = true;
|
||||
break;
|
||||
@@ -1651,7 +1742,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
|
||||
case TRANSFORM_OPTION:
|
||||
set_transform_expr (arg);
|
||||
break;
|
||||
|
||||
|
||||
case USE_COMPRESS_PROGRAM_OPTION:
|
||||
set_use_compress_program_option (arg);
|
||||
break;
|
||||
@@ -1829,7 +1920,7 @@ decode_options (int argc, char **argv)
|
||||
struct tar_args args;
|
||||
|
||||
/* Set some default option values. */
|
||||
args.textual_date_option = NULL;
|
||||
args.textual_date = NULL;
|
||||
args.wildcards = default_wildcards;
|
||||
args.matching_flags = 0;
|
||||
args.include_anchored = EXCLUDE_ANCHORED;
|
||||
@@ -1848,6 +1939,8 @@ decode_options (int argc, char **argv)
|
||||
newer_mtime_option.tv_nsec = -1;
|
||||
recursion_option = FNM_LEADING_DIR;
|
||||
unquote_option = true;
|
||||
tar_sparse_major = 1;
|
||||
tar_sparse_minor = 0;
|
||||
|
||||
owner_option = -1;
|
||||
group_option = -1;
|
||||
@@ -1951,7 +2044,7 @@ decode_options (int argc, char **argv)
|
||||
/* Warn about implicit use of the wildcards in command line arguments.
|
||||
See TODO */
|
||||
warn_regex_usage = args.wildcards == default_wildcards;
|
||||
|
||||
|
||||
/* Derive option values and check option consistency. */
|
||||
|
||||
if (archive_format == DEFAULT_FORMAT)
|
||||
@@ -1970,13 +2063,6 @@ decode_options (int argc, char **argv)
|
||||
| FORMAT_MASK (GNU_FORMAT)
|
||||
| FORMAT_MASK (POSIX_FORMAT));
|
||||
|
||||
if (multi_volume_option
|
||||
&& archive_format == POSIX_FORMAT
|
||||
&& subcommand_option == CREATE_SUBCOMMAND
|
||||
&& !tape_length_option)
|
||||
USAGE_ERROR ((0, 0,
|
||||
_("creating multi-volume archives in posix format requires using --tape-length (-L) option")));
|
||||
|
||||
if (occurrence_option)
|
||||
{
|
||||
if (!args.input_files)
|
||||
@@ -2130,6 +2216,16 @@ decode_options (int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initialize stdlis */
|
||||
if (index_file_name)
|
||||
{
|
||||
stdlis = fopen (index_file_name, "w");
|
||||
if (! stdlis)
|
||||
open_error (index_file_name);
|
||||
}
|
||||
else
|
||||
stdlis = to_stdout_option ? stderr : stdout;
|
||||
|
||||
archive_name_cursor = archive_name_array;
|
||||
|
||||
/* Prepare for generating backup names. */
|
||||
@@ -2146,13 +2242,8 @@ decode_options (int argc, char **argv)
|
||||
backup_option = false;
|
||||
}
|
||||
|
||||
if (verbose_option && args.textual_date_option)
|
||||
{
|
||||
char const *treated_as = tartime (newer_mtime_option, true);
|
||||
if (strcmp (args.textual_date_option, treated_as) != 0)
|
||||
WARN ((0, 0, _("Treating date `%s' as %s"),
|
||||
args.textual_date_option, treated_as));
|
||||
}
|
||||
if (verbose_option)
|
||||
report_textual_dates (&args);
|
||||
}
|
||||
|
||||
|
||||
@@ -2177,6 +2268,9 @@ main (int argc, char **argv)
|
||||
/* Make sure we have first three descriptors available */
|
||||
stdopen ();
|
||||
|
||||
/* Close all inherited open descriptors, except for the first three */
|
||||
closeopen ();
|
||||
|
||||
/* Pre-allocate a few structures. */
|
||||
|
||||
allocated_archive_names = 10;
|
||||
@@ -2255,13 +2349,14 @@ main (int argc, char **argv)
|
||||
free (archive_name_array);
|
||||
name_term ();
|
||||
|
||||
if (stdlis == stdout)
|
||||
close_stdout ();
|
||||
|
||||
if (exit_status == TAREXIT_FAILURE)
|
||||
error (0, 0, _("Error exit delayed from previous errors"));
|
||||
if (ferror (stderr) || fclose (stderr) != 0)
|
||||
|
||||
if (stdlis == stdout)
|
||||
close_stdout ();
|
||||
else if (ferror (stderr) || fclose (stderr) != 0)
|
||||
exit_status = TAREXIT_FAILURE;
|
||||
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +165,7 @@ struct oldgnu_header
|
||||
'A' Solaris Access Control List
|
||||
'E' Solaris Extended Attribute File
|
||||
'I' Inode only, as in 'star'
|
||||
'N' Obsolete GNU tar, for file names that do not fit into the main header.
|
||||
'X' POSIX 1003.1-2001 eXtended (VU version) */
|
||||
|
||||
/* This is a dir entry that contains the names of files that were in the
|
||||
@@ -180,9 +181,6 @@ struct oldgnu_header
|
||||
/* This is the continuation of a file that began on another volume. */
|
||||
#define GNUTYPE_MULTIVOL 'M'
|
||||
|
||||
/* For storing filenames that do not fit into the main header. */
|
||||
#define GNUTYPE_NAMES 'N'
|
||||
|
||||
/* This is for sparse files. */
|
||||
#define GNUTYPE_SPARSE 'S'
|
||||
|
||||
@@ -295,6 +293,8 @@ struct tar_stat_info
|
||||
bool is_sparse; /* Is the file sparse */
|
||||
|
||||
/* For sparse files: */
|
||||
unsigned sparse_major;
|
||||
unsigned sparse_minor;
|
||||
size_t sparse_map_avail; /* Index to the first unused element in
|
||||
sparse_map array. Zero if the file is
|
||||
not sparse */
|
||||
@@ -318,5 +318,3 @@ union block
|
||||
struct star_in_header star_in_header;
|
||||
struct star_ext_header star_ext_header;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ struct replace_segm
|
||||
|
||||
/* Compiled replacement expression */
|
||||
static struct replace_segm *repl_head, *repl_tail;
|
||||
static segm_count; /* Number of elements in the above list */
|
||||
static size_t segm_count; /* Number of elements in the above list */
|
||||
|
||||
static struct replace_segm *
|
||||
add_segment (void)
|
||||
@@ -377,7 +377,6 @@ bool
|
||||
_transform_name_to_obstack (char *input)
|
||||
{
|
||||
regmatch_t *rmp;
|
||||
char *p;
|
||||
int rc;
|
||||
size_t nmatches = 0;
|
||||
enum case_ctl_type case_ctl = ctl_stop, /* Current case conversion op */
|
||||
@@ -501,7 +500,7 @@ _transform_name_to_obstack (char *input)
|
||||
bool
|
||||
transform_name_fp (char **pinput, char *(*fun)(char *))
|
||||
{
|
||||
char *str, *p;
|
||||
char *str;
|
||||
bool ret = _transform_name_to_obstack (*pinput);
|
||||
if (ret)
|
||||
{
|
||||
|
||||
@@ -131,7 +131,7 @@ update_archive (void)
|
||||
archive_format = current_format;
|
||||
|
||||
if (subcommand_option == UPDATE_SUBCOMMAND
|
||||
&& (name = name_scan (current_stat_info.file_name, false)) != NULL)
|
||||
&& (name = name_scan (current_stat_info.file_name)) != NULL)
|
||||
{
|
||||
struct stat s;
|
||||
|
||||
|
||||
212
src/xheader.c
212
src/xheader.c
@@ -264,7 +264,7 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, size_t n)
|
||||
case 'f':
|
||||
if (st)
|
||||
{
|
||||
base = base_name (st->orig_file_name);
|
||||
base = last_component (st->orig_file_name);
|
||||
len += strlen (base) - 2;
|
||||
}
|
||||
break;
|
||||
@@ -430,7 +430,7 @@ struct xhdr_tab
|
||||
char const *keyword;
|
||||
void (*coder) (struct tar_stat_info const *, char const *,
|
||||
struct xheader *, void const *data);
|
||||
void (*decoder) (struct tar_stat_info *, char const *, size_t);
|
||||
void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
|
||||
bool protect;
|
||||
};
|
||||
|
||||
@@ -484,7 +484,8 @@ decode_record (char **ptr,
|
||||
{
|
||||
char *start = *ptr;
|
||||
char *p = start;
|
||||
unsigned long int len;
|
||||
uintmax_t u;
|
||||
size_t len;
|
||||
char *len_lim;
|
||||
char const *keyword;
|
||||
char *nextp;
|
||||
@@ -501,8 +502,13 @@ decode_record (char **ptr,
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
len = strtoul (p, &len_lim, 10);
|
||||
|
||||
len = u = strtoumax (p, &len_lim, 10);
|
||||
if (len != u || errno == ERANGE)
|
||||
{
|
||||
ERROR ((0, 0, _("Extended header length is out of allowed range")));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (len_max < len)
|
||||
{
|
||||
int len_len = len_lim - p;
|
||||
@@ -551,7 +557,7 @@ run_override_list (struct keyword_list *kp, struct tar_stat_info *st)
|
||||
{
|
||||
struct xhdr_tab const *t = locate_handler (kp->pattern);
|
||||
if (t)
|
||||
t->decoder (st, kp->value, strlen (kp->value));
|
||||
t->decoder (st, t->keyword, kp->value, strlen (kp->value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +573,7 @@ decx (void *data, char const *keyword, char const *value, size_t size)
|
||||
|
||||
t = locate_handler (keyword);
|
||||
if (t)
|
||||
t->decoder (st, value, size);
|
||||
t->decoder (st, keyword, value, size);
|
||||
else
|
||||
ERROR((0, 0, _("Ignoring unknown extended header keyword `%s'"),
|
||||
keyword));
|
||||
@@ -641,12 +647,10 @@ void
|
||||
xheader_read (union block *p, size_t size)
|
||||
{
|
||||
size_t j = 0;
|
||||
size_t nblocks;
|
||||
|
||||
free (extended_header.buffer);
|
||||
size += BLOCKSIZE;
|
||||
extended_header.size = size;
|
||||
nblocks = (size + BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
extended_header.buffer = xmalloc (size + 1);
|
||||
extended_header.buffer[size] = '\0';
|
||||
|
||||
@@ -746,18 +750,19 @@ xheader_string_add (char const *s)
|
||||
x_obstack_grow (&extended_header, s, strlen (s));
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
xheader_string_end (char const *keyword)
|
||||
{
|
||||
size_t len;
|
||||
size_t p;
|
||||
size_t n = 0;
|
||||
uintmax_t len;
|
||||
uintmax_t p;
|
||||
uintmax_t n = 0;
|
||||
size_t size;
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
char const *np;
|
||||
char *cp;
|
||||
|
||||
if (extended_header.buffer)
|
||||
return;
|
||||
return false;
|
||||
extended_header_init ();
|
||||
|
||||
len = strlen (keyword) + string_length + 3; /* ' ' + '=' + '\n' */
|
||||
@@ -771,6 +776,15 @@ xheader_string_end (char const *keyword)
|
||||
while (n != p);
|
||||
|
||||
p = strlen (keyword) + n + 2;
|
||||
size = p;
|
||||
if (size != p)
|
||||
{
|
||||
ERROR ((0, 0,
|
||||
_("Generated keyword/value pair is too long (keyword=%s, length=%s)"),
|
||||
keyword, nbuf));
|
||||
obstack_free (extended_header.stk, obstack_finish (extended_header.stk));
|
||||
return false;
|
||||
}
|
||||
x_obstack_blank (&extended_header, p);
|
||||
x_obstack_1grow (&extended_header, '\n');
|
||||
cp = obstack_next_free (extended_header.stk) - string_length - p - 1;
|
||||
@@ -779,6 +793,7 @@ xheader_string_end (char const *keyword)
|
||||
*cp++ = ' ';
|
||||
cp = stpcpy (cp, keyword);
|
||||
*cp++ = '=';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -983,6 +998,7 @@ dummy_coder (struct tar_stat_info const *st __attribute__ ((unused)),
|
||||
|
||||
static void
|
||||
dummy_decoder (struct tar_stat_info *st __attribute__ ((unused)),
|
||||
char const *keyword __attribute__ ((unused)),
|
||||
char const *arg __attribute__ ((unused)),
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
@@ -996,11 +1012,13 @@ atime_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
atime_decoder (struct tar_stat_info *st, char const *arg,
|
||||
atime_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
struct timespec ts;
|
||||
if (decode_time (&ts, arg, "atime"))
|
||||
if (decode_time (&ts, arg, keyword))
|
||||
st->atime = ts;
|
||||
}
|
||||
|
||||
@@ -1012,11 +1030,13 @@ gid_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
gid_decoder (struct tar_stat_info *st, char const *arg,
|
||||
gid_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (gid_t), "gid"))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (gid_t), keyword))
|
||||
st->stat.st_gid = u;
|
||||
}
|
||||
|
||||
@@ -1028,7 +1048,9 @@ gname_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
gname_decoder (struct tar_stat_info *st, char const *arg,
|
||||
gname_decoder (struct tar_stat_info *st,
|
||||
char const *keyword __attribute__((unused)),
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
decode_string (&st->gname, arg);
|
||||
@@ -1042,7 +1064,9 @@ linkpath_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
linkpath_decoder (struct tar_stat_info *st, char const *arg,
|
||||
linkpath_decoder (struct tar_stat_info *st,
|
||||
char const *keyword __attribute__((unused)),
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
decode_string (&st->link_name, arg);
|
||||
@@ -1056,27 +1080,32 @@ ctime_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
ctime_decoder (struct tar_stat_info *st, char const *arg,
|
||||
ctime_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
struct timespec ts;
|
||||
if (decode_time (&ts, arg, "ctime"))
|
||||
if (decode_time (&ts, arg, keyword))
|
||||
st->ctime = ts;
|
||||
}
|
||||
|
||||
static void
|
||||
mtime_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data __attribute__ ((unused)))
|
||||
struct xheader *xhdr, void const *data)
|
||||
{
|
||||
code_time (st->mtime, keyword, xhdr);
|
||||
const struct timespec mtime = data ? *(struct timespec *) data : st->mtime;
|
||||
code_time (mtime, keyword, xhdr);
|
||||
}
|
||||
|
||||
static void
|
||||
mtime_decoder (struct tar_stat_info *st, char const *arg,
|
||||
mtime_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
struct timespec ts;
|
||||
if (decode_time (&ts, arg, "mtime"))
|
||||
if (decode_time (&ts, arg, keyword))
|
||||
st->mtime = ts;
|
||||
}
|
||||
|
||||
@@ -1088,7 +1117,9 @@ path_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
path_decoder (struct tar_stat_info *st, char const *arg,
|
||||
path_decoder (struct tar_stat_info *st,
|
||||
char const *keyword __attribute__((unused)),
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
decode_string (&st->orig_file_name, arg);
|
||||
@@ -1104,11 +1135,13 @@ size_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
size_decoder (struct tar_stat_info *st, char const *arg,
|
||||
size_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "size"))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
st->stat.st_size = u;
|
||||
}
|
||||
|
||||
@@ -1120,11 +1153,13 @@ uid_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
uid_decoder (struct tar_stat_info *st, char const *arg,
|
||||
uid_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uid_t), "uid"))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uid_t), keyword))
|
||||
st->stat.st_uid = u;
|
||||
}
|
||||
|
||||
@@ -1136,7 +1171,9 @@ uname_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
uname_decoder (struct tar_stat_info *st, char const *arg,
|
||||
uname_decoder (struct tar_stat_info *st,
|
||||
char const *keyword __attribute__((unused)),
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
decode_string (&st->uname, arg);
|
||||
@@ -1150,11 +1187,13 @@ sparse_size_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_size_decoder (struct tar_stat_info *st, char const *arg,
|
||||
sparse_size_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.size"))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
st->stat.st_size = u;
|
||||
}
|
||||
|
||||
@@ -1167,11 +1206,13 @@ sparse_numblocks_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_numblocks_decoder (struct tar_stat_info *st, char const *arg,
|
||||
sparse_numblocks_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numblocks"))
|
||||
if (decode_num (&u, arg, SIZE_MAX, keyword))
|
||||
{
|
||||
st->sparse_map_size = u;
|
||||
st->sparse_map = xcalloc (u, sizeof st->sparse_map[0]);
|
||||
@@ -1188,11 +1229,13 @@ sparse_offset_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_offset_decoder (struct tar_stat_info *st, char const *arg,
|
||||
sparse_offset_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.offset"))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
{
|
||||
if (st->sparse_map_avail < st->sparse_map_size)
|
||||
st->sparse_map[st->sparse_map_avail].offset = u;
|
||||
@@ -1211,26 +1254,29 @@ sparse_numbytes_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_numbytes_decoder (struct tar_stat_info *st, char const *arg,
|
||||
sparse_numbytes_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numbytes"))
|
||||
if (decode_num (&u, arg, SIZE_MAX, keyword))
|
||||
{
|
||||
if (st->sparse_map_avail < st->sparse_map_size)
|
||||
st->sparse_map[st->sparse_map_avail++].numbytes = u;
|
||||
else
|
||||
ERROR ((0, 0, _("Malformed extended header: excess %s=%s"),
|
||||
"GNU.sparse.numbytes", arg));
|
||||
keyword, arg));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_map_decoder (struct tar_stat_info *st, char const *arg,
|
||||
sparse_map_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
int offset = 1;
|
||||
static char *keyword = "GNU.sparse.map";
|
||||
|
||||
st->sparse_map_avail = 0;
|
||||
while (1)
|
||||
@@ -1304,7 +1350,9 @@ dumpdir_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
dumpdir_decoder (struct tar_stat_info *st, char const *arg,
|
||||
dumpdir_decoder (struct tar_stat_info *st,
|
||||
char const *keyword __attribute__((unused)),
|
||||
char const *arg,
|
||||
size_t size)
|
||||
{
|
||||
st->dumpdir = xmalloc (size);
|
||||
@@ -1319,7 +1367,10 @@ volume_label_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
volume_label_decoder (struct tar_stat_info *st, char const *arg, size_t size)
|
||||
volume_label_decoder (struct tar_stat_info *st,
|
||||
char const *keyword __attribute__((unused)),
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
decode_string (&volume_label, arg);
|
||||
}
|
||||
@@ -1333,10 +1384,12 @@ volume_size_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
volume_size_decoder (struct tar_stat_info *st, char const *arg, size_t size)
|
||||
volume_size_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg, size_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), "GNU.volume.size"))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword))
|
||||
continued_file_size = u;
|
||||
}
|
||||
|
||||
@@ -1350,21 +1403,60 @@ volume_offset_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
}
|
||||
|
||||
static void
|
||||
volume_offset_decoder (struct tar_stat_info *st, char const *arg, size_t size)
|
||||
volume_offset_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg, size_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), "GNU.volume.offset"))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword))
|
||||
continued_file_offset = u;
|
||||
}
|
||||
|
||||
static void
|
||||
volume_filename_decoder (struct tar_stat_info *st, char const *arg,
|
||||
size_t size)
|
||||
volume_filename_decoder (struct tar_stat_info *st,
|
||||
char const *keyword __attribute__((unused)),
|
||||
char const *arg,
|
||||
size_t size __attribute__((unused)))
|
||||
{
|
||||
decode_string (&continued_file_name, arg);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sparse_major_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data)
|
||||
{
|
||||
code_num (st->sparse_major, keyword, xhdr);
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_major_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword))
|
||||
st->sparse_major = u;
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_minor_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data)
|
||||
{
|
||||
code_num (st->sparse_minor, keyword, xhdr);
|
||||
}
|
||||
|
||||
static void
|
||||
sparse_minor_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
size_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword))
|
||||
st->sparse_minor = u;
|
||||
}
|
||||
|
||||
struct xhdr_tab const xhdr_tab[] = {
|
||||
{ "atime", atime_coder, atime_decoder, false },
|
||||
{ "comment", dummy_coder, dummy_decoder, false },
|
||||
@@ -1380,10 +1472,20 @@ struct xhdr_tab const xhdr_tab[] = {
|
||||
{ "uname", uname_coder, uname_decoder, false },
|
||||
|
||||
/* Sparse file handling */
|
||||
{ "GNU.sparse.size", sparse_size_coder, sparse_size_decoder, true },
|
||||
{ "GNU.sparse.name", path_coder, path_decoder,
|
||||
true },
|
||||
{ "GNU.sparse.major", sparse_major_coder, sparse_major_decoder,
|
||||
true },
|
||||
{ "GNU.sparse.minor", sparse_minor_coder, sparse_minor_decoder,
|
||||
true },
|
||||
{ "GNU.sparse.realsize", sparse_size_coder, sparse_size_decoder,
|
||||
true },
|
||||
{ "GNU.sparse.numblocks", sparse_numblocks_coder, sparse_numblocks_decoder,
|
||||
true },
|
||||
/* tar 1.14 - 1.15.1 keywords. Multiplse instances of these appeared in 'x'
|
||||
|
||||
/* tar 1.14 - 1.15.90 keywords. */
|
||||
{ "GNU.sparse.size", sparse_size_coder, sparse_size_decoder, true },
|
||||
/* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x'
|
||||
headers, and each of them was meaningful. It confilcted with POSIX specs,
|
||||
which requires that "when extended header records conflict, the last one
|
||||
given in the header shall take precedence." */
|
||||
@@ -1391,7 +1493,7 @@ struct xhdr_tab const xhdr_tab[] = {
|
||||
true },
|
||||
{ "GNU.sparse.numbytes", sparse_numbytes_coder, sparse_numbytes_decoder,
|
||||
true },
|
||||
/* tar >=1.16 keyword, introduced to remove the above-mentioned conflict. */
|
||||
/* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */
|
||||
{ "GNU.sparse.map", NULL /* Unused, see pax_dump_header() */,
|
||||
sparse_map_decoder, false },
|
||||
|
||||
|
||||
@@ -46,9 +46,12 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
|
||||
## ------------ ##
|
||||
|
||||
TESTSUITE_AT = \
|
||||
T-empty.at\
|
||||
T-null.at\
|
||||
testsuite.at\
|
||||
append.at\
|
||||
append01.at\
|
||||
append02.at\
|
||||
chtype.at\
|
||||
comprec.at\
|
||||
delete01.at\
|
||||
@@ -62,12 +65,15 @@ TESTSUITE_AT = \
|
||||
extrac04.at\
|
||||
extrac05.at\
|
||||
extrac06.at\
|
||||
extrac07.at\
|
||||
gzip.at\
|
||||
grow.at\
|
||||
incremental.at\
|
||||
incr01.at\
|
||||
incr02.at\
|
||||
incr03.at\
|
||||
incr04.at\
|
||||
indexfile.at\
|
||||
ignfail.at\
|
||||
link01.at\
|
||||
listed01.at\
|
||||
@@ -81,6 +87,7 @@ TESTSUITE_AT = \
|
||||
multiv02.at\
|
||||
multiv03.at\
|
||||
multiv04.at\
|
||||
multiv05.at\
|
||||
old.at\
|
||||
options.at\
|
||||
options02.at\
|
||||
@@ -97,10 +104,14 @@ TESTSUITE_AT = \
|
||||
sparse03.at\
|
||||
sparsemv.at\
|
||||
sparsemvp.at\
|
||||
spmvp00.at\
|
||||
spmvp01.at\
|
||||
spmvp10.at\
|
||||
truncate.at\
|
||||
update.at\
|
||||
volsize.at\
|
||||
volume.at\
|
||||
verbose.at\
|
||||
version.at\
|
||||
star/gtarfail.at\
|
||||
star/gtarfail2.at\
|
||||
|
||||
52
tests/T-empty.at
Normal file
52
tests/T-empty.at
Normal file
@@ -0,0 +1,52 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# Tar 1.16 coredumped if a filelist file contained empty (zero-length)
|
||||
# entries
|
||||
# Reported by: Karl Berry <karl@freefriends.org>
|
||||
# References: <200610301353.k9UDr1O30680@f7.net>
|
||||
|
||||
AT_SETUP([files-from: empty entries])
|
||||
AT_KEYWORDS([files-from empty])
|
||||
|
||||
AT_DATA([file-list],
|
||||
[jeden
|
||||
dwa
|
||||
|
||||
trzy
|
||||
])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
AT_SORT_PREREQ
|
||||
|
||||
genfile --file jeden
|
||||
genfile --file dwa
|
||||
genfile --file trzy
|
||||
|
||||
tar cfvT archive ../file-list | sort
|
||||
],
|
||||
[0],
|
||||
[dwa
|
||||
jeden
|
||||
trzy
|
||||
],
|
||||
[],[],[],[ustar]) # Testing one format is enough
|
||||
|
||||
AT_CLEANUP
|
||||
46
tests/T-null.at
Normal file
46
tests/T-null.at
Normal file
@@ -0,0 +1,46 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
AT_SETUP([files-from: 0-separated file without -0])
|
||||
AT_KEYWORDS([files-from null])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
AT_SORT_PREREQ
|
||||
|
||||
echo dwa > temp
|
||||
echo trzy >> temp
|
||||
cat temp | tr '\n' '\0' > temp1
|
||||
echo jeden > file-list
|
||||
cat temp1 >> file-list
|
||||
|
||||
genfile -f "jeden
|
||||
dwa"
|
||||
genfile -f trzy
|
||||
|
||||
tar cfTv archive file-list | sort
|
||||
],
|
||||
[0],
|
||||
[jeden\ndwa
|
||||
trzy
|
||||
],
|
||||
[tar: file-list: file name read contains nul character
|
||||
],[],[ustar]) # Testing one format is enough
|
||||
|
||||
AT_CLEANUP
|
||||
75
tests/append02.at
Normal file
75
tests/append02.at
Normal file
@@ -0,0 +1,75 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# Using tar 1.15.x the following equivalent command sets:
|
||||
#
|
||||
# 1. tar cf archive file1 file2
|
||||
# and
|
||||
# 2. tar cfT archive /dev/null
|
||||
# tar rf archive file1
|
||||
# tar rt archive file2
|
||||
#
|
||||
# produced different archives (GNU format is assumed). Namely, in the
|
||||
# second case the mode field of all members, except the first, was truncated
|
||||
# to lower 3 octets (& 0777).
|
||||
#
|
||||
# References:
|
||||
# <200607210526.AA03440@tamuki.linet.gr.jp>
|
||||
# http://lists.gnu.org/archive/html/bug-tar/2006-07/msg00029.html
|
||||
|
||||
# The test case below verifies that the equivalent create and append commands
|
||||
# produce binary equivalent archives for all formats.
|
||||
|
||||
AT_SETUP([append vs. create])
|
||||
AT_KEYWORDS([append append02 append-gnu])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
genfile --file file1
|
||||
genfile --file file2
|
||||
|
||||
# Make sure file timestamps in the archive will not differ
|
||||
MTIME="--mtime=@0"
|
||||
|
||||
# For PAX archives, we need to make sure extended header names are
|
||||
# reproducible and that their contents won't change with time
|
||||
if test $[]TEST_TAR_FORMAT = posix; then
|
||||
TAR_OPTIONS="$TAR_OPTIONS --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=mtime,delete=atime,delete=ctime"
|
||||
fi
|
||||
|
||||
echo Creating archive.1
|
||||
tar $MTIME -cf archive.1 file1 file2
|
||||
|
||||
echo Creating archive.2
|
||||
tar $MTIME -cf archive.2 -T /dev/null
|
||||
tar $MTIME -rf archive.2 file1
|
||||
tar $MTIME -rf archive.2 file2
|
||||
|
||||
echo Comparing archives
|
||||
cmp archive.1 archive.2
|
||||
],
|
||||
[0],
|
||||
[Creating archive.1
|
||||
Creating archive.2
|
||||
Comparing archives
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
# End of append02.at
|
||||
@@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2004, 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,7 @@
|
||||
# Could not extract symlinks over an existing file.
|
||||
|
||||
AT_SETUP([extracting symlinks over an existing file])
|
||||
AT_KEYWORDS([extract extract02])
|
||||
AT_KEYWORDS([extract extract02 symlink])
|
||||
|
||||
# FIXME: Skip if symlinks are not supported on the system
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# Check for fnmatch problems in glibc 2.1.95.
|
||||
|
||||
AT_SETUP([extract + fnmatch])
|
||||
AT_KEYWORDS([extract extract04])
|
||||
AT_KEYWORDS([extract extract04 fnmatch])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
AT_SORT_PREREQ
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#
|
||||
|
||||
AT_SETUP([mode of extracted directories])
|
||||
AT_KEYWORDS([extract extract06])
|
||||
AT_KEYWORDS([extract extract06 directory mode])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
|
||||
|
||||
56
tests/extrac07.at
Normal file
56
tests/extrac07.at
Normal file
@@ -0,0 +1,56 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# Tar 1.16 failed to extract archives that have symlinks
|
||||
# in read-only directories.
|
||||
#
|
||||
# Reported-by: Eelco Dolstra <eelco@cs.uu.nl>
|
||||
# References: <45475D78.8050708@cs.uu.nl>
|
||||
|
||||
AT_SETUP([extracting symlinks to a read-only dir])
|
||||
AT_KEYWORDS([extract extract07 read-only symlink])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
echo Prepare the directory
|
||||
mkdir dir
|
||||
genfile -f foo
|
||||
cd dir
|
||||
ln -s ../foo .
|
||||
cd ..
|
||||
chmod -w dir
|
||||
|
||||
echo Create the archive
|
||||
tar cf archive dir || exit 1
|
||||
|
||||
echo Extract
|
||||
mkdir out
|
||||
tar -C out -xvf archive
|
||||
],
|
||||
[0],
|
||||
[Prepare the directory
|
||||
Create the archive
|
||||
Extract
|
||||
dir/
|
||||
dir/foo
|
||||
],[],[],[ustar]) # Testing one format is enough
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
40
tests/grow.at
Normal file
40
tests/grow.at
Normal file
@@ -0,0 +1,40 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# Tar should exit with error code 1 (file differs) if any files have
|
||||
# changed during archiving.
|
||||
|
||||
AT_SETUP([grow])
|
||||
AT_KEYWORDS([grow filechange])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
genfile --file foo --length 50000k
|
||||
genfile --file baz
|
||||
genfile --run 'tar -vcf bar foo baz' --checkpoint 10 --length 1024 \
|
||||
--append foo
|
||||
],
|
||||
[1],
|
||||
[foo
|
||||
baz
|
||||
],
|
||||
[tar: foo: file changed as we read it
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
47
tests/indexfile.at
Normal file
47
tests/indexfile.at
Normal file
@@ -0,0 +1,47 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# tar --index-file=FILE --file=- sent the archive to FILE, and
|
||||
# the listing to stderr.
|
||||
# Reported by Marcin Gryszkalis <mg@fork.pl>
|
||||
# References: <200607061943.06645.mg@fork.pl>
|
||||
|
||||
AT_SETUP([tar --index-file=FILE --file=-])
|
||||
AT_KEYWORDS([stdout indexfile])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
|
||||
mkdir directory
|
||||
genfile --file=directory/a --length=1035
|
||||
|
||||
echo "Creating the archive"
|
||||
tar -c -v -f - --index-file=idx directory > archive
|
||||
|
||||
echo "Testing the archive"
|
||||
tar -tf archive
|
||||
],
|
||||
[0],
|
||||
[Creating the archive
|
||||
Testing the archive
|
||||
directory/
|
||||
directory/a
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
67
tests/multiv05.at
Normal file
67
tests/multiv05.at
Normal file
@@ -0,0 +1,67 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# Up to version 1.15.91 tar was unable to recognize all volumes
|
||||
# given after an aout-of-sync volume.
|
||||
# Reported by: Joerg Weilbier <gnu@weilbier.net>
|
||||
# References: <200610011952.29880.gnu@weilbier.net>
|
||||
|
||||
AT_SETUP([Restoring after an out of sync folume])
|
||||
AT_KEYWORDS([multivolume multiv multiv05 sync])
|
||||
m4_define([FILELIST],[jeden,dwa,trzy,cztery,piec,szesc])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
exec <&-
|
||||
|
||||
m4_foreach([f],
|
||||
[FILELIST],
|
||||
[genfile --length 250k --file f
|
||||
])
|
||||
|
||||
echo Creating archive
|
||||
tar -c -M -L 502 -f a.tar -f b.tar -f c.tar m4_foreach([f],[FILELIST],f )
|
||||
echo separator
|
||||
mkdir bak
|
||||
mv m4_foreach([f],[FILELIST],f )bak
|
||||
tar -vxM -f a.tar -f c.tar -f b.tar -f c.tar
|
||||
m4_foreach([f],
|
||||
[FILELIST],
|
||||
[echo Diffing f
|
||||
cmp bak/f f || exit 1
|
||||
])],
|
||||
[0],
|
||||
[Creating archive
|
||||
separator]
|
||||
m4_foreach([file],
|
||||
[FILELIST],
|
||||
[file
|
||||
])dnl
|
||||
m4_foreach([file],
|
||||
[FILELIST],
|
||||
[Diffing file
|
||||
])dnl
|
||||
,
|
||||
[tar: `trzy' is not continued on this volume
|
||||
],[],[], [gnu])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ AT_KEYWORDS([shortrec.at])
|
||||
AT_TAR_CHECK([
|
||||
mkdir directory
|
||||
(cd directory && touch a b c d e f g h i j k l m n o p q r)
|
||||
tar -c -b 1 directory | tar -t >/dev/null
|
||||
tar -c -b 1 -f - directory | tar -t -f - >/dev/null
|
||||
tar -c -b 1 -f archive directory
|
||||
tar -t -f archive >/dev/null
|
||||
tar -t -f - < archive >/dev/null
|
||||
|
||||
@@ -18,28 +18,27 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
AT_SETUP([sparse files in PAX MV archives])
|
||||
AT_KEYWORDS([sparse multiv sparsemvp])
|
||||
|
||||
# Check if sparse files are correctly split between PAX multi-volume
|
||||
# archives.
|
||||
# See comment in sparsemv.at for the description.
|
||||
|
||||
dnl TAR_MVP_TEST version map1 map2
|
||||
m4_define([TAR_MVP_TEST],[
|
||||
AT_TAR_CHECK([
|
||||
exec <&-
|
||||
genfile --sparse --file sparsefile 0 ABCDEFGHIJK 1M ABCDEFGHI || AT_SKIP_TEST
|
||||
genfile --sparse --file sparsefile $2 || AT_SKIP_TEST
|
||||
echo "Pass 1: Split between data blocks"
|
||||
echo "Create archive"
|
||||
tar --sparse -c --record-size=512 -M -L6 -f arc.1 -f arc.2 -f arc.3 sparsefile
|
||||
tar --sparse --sparse-version=$1 -c --record-size=512 -M -L6 -f arc.1 -f arc.2 -f arc.3 sparsefile
|
||||
echo "Test archive"
|
||||
tar -t -M -f arc.1 -f arc.2 -f arc.3
|
||||
echo "Compare archive"
|
||||
tar -d -M -f arc.1 -f arc.2 -f arc.3
|
||||
|
||||
echo "Pass 2: Split within a data block"
|
||||
genfile --sparse --file sparsefile 0 ABCDEFGHIJ 1M ABCDEFGHI || AT_SKIP_TEST
|
||||
genfile --sparse --file sparsefile $3 || AT_SKIP_TEST
|
||||
echo "Create archive"
|
||||
tar --sparse -c --record-size=512 -M -L6 -f arc.1 -f arc.2 -f arc.3 sparsefile
|
||||
tar --sparse --sparse-version=$1 -c --record-size=512 -M -L6 -f arc.1 -f arc.2 -f arc.3 sparsefile
|
||||
echo "Test archive"
|
||||
tar -t -M -f arc.1 -f arc.2 -f arc.3
|
||||
echo "Compare archive"
|
||||
@@ -57,6 +56,5 @@ Test archive
|
||||
sparsefile
|
||||
Compare archive
|
||||
],
|
||||
[],[],[],[pax])
|
||||
[],[],[],[pax])])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
26
tests/spmvp00.at
Normal file
26
tests/spmvp00.at
Normal file
@@ -0,0 +1,26 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
AT_SETUP([sparse files in PAX MV archives, v.0.0])
|
||||
AT_KEYWORDS([sparse multiv sparsemvp sparsemvp00])
|
||||
|
||||
TAR_MVP_TEST(0.0, [0 ABCDEFGHI 1M ABCDEFGHI], [0 ABCDEFGH 1M ABCDEFGHI])
|
||||
|
||||
AT_CLEANUP
|
||||
26
tests/spmvp01.at
Normal file
26
tests/spmvp01.at
Normal file
@@ -0,0 +1,26 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
AT_SETUP([sparse files in PAX MV archives, v.0.1])
|
||||
AT_KEYWORDS([sparse multiv sparsemvp sparsemvp01])
|
||||
|
||||
TAR_MVP_TEST(0.1, [0 ABCDEFGHIJK 1M ABCDEFGHI], [0 ABCDEFGHIJ 1M ABCDEFGHI])
|
||||
|
||||
AT_CLEANUP
|
||||
26
tests/spmvp10.at
Normal file
26
tests/spmvp10.at
Normal file
@@ -0,0 +1,26 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
AT_SETUP([sparse files in PAX MV archives, v.1.0])
|
||||
AT_KEYWORDS([sparse multiv sparsemvp sparsemvp10])
|
||||
|
||||
TAR_MVP_TEST(1.0, [0 ABCDEFGH 1M ABCDEFGHI], [0 ABCDEFG 1M ABCDEFGHI])
|
||||
|
||||
AT_CLEANUP
|
||||
@@ -55,7 +55,7 @@ dnl match second argument, skip the test.
|
||||
|
||||
m4_define([AT_TARBALL_PREREQ],[
|
||||
test -z "$[]TEST_DATA_DIR" && AT_SKIP_TEST
|
||||
tarball_prereq $1 $2 $[]TEST_DATA_DIR $[]TEST_DATA_URL || AT_SKIP_TEST])
|
||||
tarball_prereq $1 $2 $[]TEST_DATA_DIR $[]TEST_DATA_URL || AT_SKIP_TEST])
|
||||
|
||||
dnl AT_TARBALL_PREREQ(tarball, md5sum) - Same for star testfiles
|
||||
m4_define([AT_STAR_PREREQ],[
|
||||
@@ -84,9 +84,11 @@ test $result -eq 0 && AT_SKIP_TEST
|
||||
])
|
||||
|
||||
m4_define([AT_TAR_MKHIER],[
|
||||
mkinstalldirs $1 >/dev/null
|
||||
install-sh -d $1 >/dev/null
|
||||
m4_if([$2],,,genfile --file [$1]/[$2])])
|
||||
|
||||
m4_include([sparsemvp.at])
|
||||
|
||||
AT_INIT
|
||||
|
||||
AT_TESTED([tar])
|
||||
@@ -98,8 +100,15 @@ m4_include([pipe.at])
|
||||
m4_include([options.at])
|
||||
m4_include([options02.at])
|
||||
|
||||
m4_include([T-empty.at])
|
||||
m4_include([T-null.at])
|
||||
|
||||
m4_include([indexfile.at])
|
||||
m4_include([verbose.at])
|
||||
|
||||
m4_include([append.at])
|
||||
m4_include([append01.at])
|
||||
m4_include([append02.at])
|
||||
|
||||
m4_include([delete01.at])
|
||||
m4_include([delete02.at])
|
||||
@@ -113,6 +122,7 @@ m4_include([extrac03.at])
|
||||
m4_include([extrac04.at])
|
||||
m4_include([extrac05.at])
|
||||
m4_include([extrac06.at])
|
||||
m4_include([extrac07.at])
|
||||
|
||||
m4_include([gzip.at])
|
||||
|
||||
@@ -142,6 +152,7 @@ m4_include([multiv01.at])
|
||||
m4_include([multiv02.at])
|
||||
m4_include([multiv03.at])
|
||||
m4_include([multiv04.at])
|
||||
m4_include([multiv05.at])
|
||||
|
||||
m4_include([old.at])
|
||||
|
||||
@@ -156,7 +167,9 @@ m4_include([sparse01.at])
|
||||
m4_include([sparse02.at])
|
||||
m4_include([sparse03.at])
|
||||
m4_include([sparsemv.at])
|
||||
m4_include([sparsemvp.at])
|
||||
m4_include([spmvp00.at])
|
||||
m4_include([spmvp01.at])
|
||||
m4_include([spmvp10.at])
|
||||
|
||||
m4_include([update.at])
|
||||
|
||||
@@ -166,6 +179,7 @@ m4_include([volsize.at])
|
||||
m4_include([comprec.at])
|
||||
|
||||
m4_include([truncate.at])
|
||||
m4_include([grow.at])
|
||||
|
||||
m4_include([star/gtarfail.at])
|
||||
m4_include([star/gtarfail2.at])
|
||||
@@ -176,4 +190,3 @@ m4_include([star/ustar-big-2g.at])
|
||||
m4_include([star/ustar-big-8g.at])
|
||||
|
||||
m4_include([star/pax-big-10g.at])
|
||||
|
||||
|
||||
@@ -24,28 +24,30 @@
|
||||
# http://lists.gnu.org/archive/html/bug-tar/2005-05/msg00008.html
|
||||
#
|
||||
# The test case is based on the script by Frank Heckenbach <frank@g-n-u.de>
|
||||
# Additionally, the test verifies if tar exits with code 1 (file differs).
|
||||
|
||||
AT_SETUP([truncate])
|
||||
AT_KEYWORDS([truncated files])
|
||||
AT_KEYWORDS([truncate filechange])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
genfile --file foo --length 50000k
|
||||
genfile --file baz
|
||||
genfile --run 'tar -vcf bar foo baz' --checkpoint 10 --length 49995k --truncate foo
|
||||
echo Exit status: $?
|
||||
echo separator
|
||||
sleep 1
|
||||
dd if=/dev/zero of=foo bs=1k seek=49995 count=5 >/dev/null 2>&1
|
||||
genfile --file foo --seek 49995k --length 5k --pattern=zeros
|
||||
tar dvf bar],
|
||||
[1],
|
||||
[foo
|
||||
baz
|
||||
Exit status: 1
|
||||
separator
|
||||
foo
|
||||
foo: Mod time differs
|
||||
baz
|
||||
],
|
||||
[tar: foo: File shrank by 5120 bytes; padding with zeros
|
||||
tar: Error exit delayed from previous errors
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
44
tests/verbose.at
Normal file
44
tests/verbose.at
Normal file
@@ -0,0 +1,44 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# Version 1.15.92 sent verbose output to the archive when invoked
|
||||
# as tar cvf - foo.txt > foo.tar
|
||||
# Reported by Mike Frysinger <vapier@gentoo.org>
|
||||
# References: <200610131946.20530.vapier@gentoo.org>
|
||||
|
||||
AT_SETUP([tar cvf -])
|
||||
AT_KEYWORDS([stdout verbose])
|
||||
|
||||
AT_TAR_CHECK([
|
||||
genfile --file file --length 10240
|
||||
echo Creating the archive
|
||||
tar cvf - file > archive
|
||||
echo Testing the archive
|
||||
tar tf archive
|
||||
],
|
||||
[0],
|
||||
[Creating the archive
|
||||
Testing the archive
|
||||
file
|
||||
],
|
||||
[file
|
||||
])
|
||||
|
||||
AT_CLEANUP
|
||||
Reference in New Issue
Block a user