summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 16:14:31 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 16:14:31 +0000
commit2d5707c7479eacb3b1ad98e01b53f56a88f8fb78 (patch)
treed9c334e83692851c02e3e1b8e65570c97bc82481
parentInitial commit. (diff)
downloadrsync-2d5707c7479eacb3b1ad98e01b53f56a88f8fb78.tar.xz
rsync-2d5707c7479eacb3b1ad98e01b53f56a88f8fb78.zip
Adding upstream version 3.2.7.upstream/3.2.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.cirrus.yml23
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore60
-rw-r--r--COPYING683
-rw-r--r--Doxyfile187
-rw-r--r--INSTALL.md242
-rw-r--r--Makefile.in369
-rw-r--r--NEWS.md4770
-rw-r--r--README.md144
-rw-r--r--SECURITY.md12
-rw-r--r--TODO528
-rw-r--r--access.c292
-rw-r--r--aclocal.m417
-rw-r--r--acls.c1140
-rw-r--r--authenticate.c376
-rw-r--r--backup.c355
-rw-r--r--batch.c312
-rw-r--r--byteorder.h131
-rw-r--r--case_N.h92
-rw-r--r--checksum.c799
-rw-r--r--chmod.c249
-rw-r--r--cleanup.c298
-rw-r--r--clientname.c538
-rw-r--r--clientserver.c1538
-rwxr-xr-xcmd-or-msg11
-rw-r--r--compat.c887
-rw-r--r--config.guess1684
-rw-r--r--config.h.in847
-rw-r--r--config.sub1793
-rwxr-xr-xconfigure27
-rw-r--r--configure.ac1445
-rwxr-xr-xconfigure.sh13395
-rw-r--r--connection.c47
-rw-r--r--csprotocol.txt110
-rwxr-xr-xdaemon-parm.awk114
-rw-r--r--daemon-parm.txt68
-rwxr-xr-xdefine-from-md.awk41
-rw-r--r--delete.c240
-rw-r--r--doc/README-SGML20
-rw-r--r--doc/profile.txt42
-rw-r--r--doc/rsync.sgml351
-rw-r--r--errcode.h64
-rw-r--r--exclude.c1697
-rw-r--r--fileio.c328
-rw-r--r--flist.c3410
-rw-r--r--generator.c2434
-rw-r--r--getfsdev.c22
-rw-r--r--getgroups.c61
-rw-r--r--hashtable.c662
-rwxr-xr-xhelp-from-md.awk40
-rw-r--r--hlink.c566
-rw-r--r--ifuncs.h112
-rwxr-xr-xinstall-sh238
-rw-r--r--inums.h57
-rw-r--r--io.c2460
-rw-r--r--io.h52
-rw-r--r--itypes.h71
-rw-r--r--latest-year.h1
-rw-r--r--lib/addrinfo.h180
-rw-r--r--lib/compat.c272
-rw-r--r--lib/dummy.in2
-rw-r--r--lib/getaddrinfo.c504
-rw-r--r--lib/getpass.c72
-rw-r--r--lib/inet_ntop.c186
-rw-r--r--lib/inet_pton.c212
-rw-r--r--lib/md-defines.h37
-rw-r--r--lib/md5-asm-x86_64.S701
-rw-r--r--lib/md5.c321
-rw-r--r--lib/mdfour.c247
-rw-r--r--lib/mdigest.h22
-rw-r--r--lib/permstring.c65
-rw-r--r--lib/permstring.h3
-rw-r--r--lib/pool_alloc.3268
-rw-r--r--lib/pool_alloc.c375
-rw-r--r--lib/pool_alloc.h21
-rw-r--r--lib/snprintf.c1512
-rw-r--r--lib/sysacls.c2802
-rw-r--r--lib/sysacls.h307
-rw-r--r--lib/sysxattrs.c301
-rw-r--r--lib/sysxattrs.h26
-rw-r--r--lib/wildmatch.c368
-rw-r--r--lib/wildmatch.h6
-rw-r--r--loadparm.c592
-rw-r--r--log.c910
-rw-r--r--m4/have_type.m421
-rw-r--r--m4/header_major_fixed.m427
-rw-r--r--m4/socklen_t.m445
-rw-r--r--m4/validate_cache_system_type.m423
-rw-r--r--main.c1864
-rw-r--r--match.c445
-rwxr-xr-xmaybe-make-man35
-rwxr-xr-xmd-convert634
l---------md2man1
-rwxr-xr-xmkgitver22
-rw-r--r--mkproto.awk40
-rw-r--r--options.c3116
-rw-r--r--packaging/auto-Makefile12
-rwxr-xr-xpackaging/branch-from-patch174
-rwxr-xr-xpackaging/cull-options148
-rw-r--r--packaging/lsb/rsync.spec87
-rw-r--r--packaging/lsb/rsync.xinetd13
-rw-r--r--packaging/openssl-rsync.cnf18
-rwxr-xr-xpackaging/patch-update244
-rw-r--r--packaging/pkglib.py266
-rwxr-xr-xpackaging/pre-push16
-rwxr-xr-xpackaging/prep-auto-dir43
-rwxr-xr-xpackaging/release-rsync399
-rwxr-xr-xpackaging/smart-make45
-rw-r--r--packaging/solaris/build_pkg.sh94
-rw-r--r--packaging/systemd/rsync.service32
-rw-r--r--packaging/systemd/rsync.socket10
-rw-r--r--packaging/systemd/rsync@.service28
-rwxr-xr-xpackaging/var-checker94
-rwxr-xr-xpackaging/year-tweak94
-rw-r--r--params.c645
-rw-r--r--pipe.c178
-rw-r--r--popt/CHANGES46
-rw-r--r--popt/COPYING22
-rw-r--r--popt/README18
-rw-r--r--popt/README.rsync4
-rw-r--r--popt/dummy.in0
-rw-r--r--popt/findme.c55
-rw-r--r--popt/findme.h20
-rw-r--r--popt/popt.c1283
-rw-r--r--popt/popt.h565
-rw-r--r--popt/poptconfig.c183
-rw-r--r--popt/popthelp.c826
-rw-r--r--popt/poptint.h122
-rw-r--r--popt/poptparse.c231
-rw-r--r--popt/system.h134
-rwxr-xr-xprepare-source72
-rw-r--r--prepare-source.mak13
-rw-r--r--progress.c241
-rw-r--r--receiver.c980
-rw-r--r--rounding.c38
-rw-r--r--rrsync.1166
-rw-r--r--rrsync.1.html164
-rwxr-xr-xrsync-ssl198
-rw-r--r--rsync-ssl.1144
-rw-r--r--rsync-ssl.1.html154
-rw-r--r--rsync-ssl.1.md140
-rw-r--r--rsync.15051
-rw-r--r--rsync.1.html4511
-rw-r--r--rsync.1.md4842
-rw-r--r--rsync.c828
-rw-r--r--rsync.h1486
-rw-r--r--rsync3.txt467
-rw-r--r--rsyncd.conf.51313
-rw-r--r--rsyncd.conf.5.html1205
-rw-r--r--rsyncd.conf.5.md1273
-rw-r--r--rsyncsh.txt26
-rwxr-xr-xruntests.sh360
-rw-r--r--sender.c461
-rwxr-xr-xshconfig.in15
-rw-r--r--simd-checksum-avx2.S177
-rw-r--r--simd-checksum-x86_64.cpp553
-rw-r--r--socket.c841
-rw-r--r--stunnel-rsyncd.conf.in30
-rw-r--r--support/Makefile6
-rwxr-xr-xsupport/atomic-rsync138
-rwxr-xr-xsupport/cvs2includes42
-rwxr-xr-xsupport/deny-rsync36
-rwxr-xr-xsupport/file-attr-restore173
-rwxr-xr-xsupport/files-to-excludes27
-rwxr-xr-xsupport/git-set-file-times94
-rwxr-xr-xsupport/instant-rsyncd126
-rwxr-xr-xsupport/json-rsync-version93
-rwxr-xr-xsupport/logfilter34
-rwxr-xr-xsupport/lsh108
-rwxr-xr-xsupport/lsh.sh37
-rwxr-xr-xsupport/mapfrom15
-rwxr-xr-xsupport/mapto15
-rwxr-xr-xsupport/mnt-excl49
-rwxr-xr-xsupport/munge-symlinks71
-rwxr-xr-xsupport/nameconvert50
-rwxr-xr-xsupport/rrsync379
-rw-r--r--support/rrsync.1.md166
-rwxr-xr-xsupport/rsync-no-vanished24
-rwxr-xr-xsupport/rsync-slash-strip24
-rwxr-xr-xsupport/rsyncstats312
-rw-r--r--support/savetransfer.c175
-rw-r--r--syscall.c714
-rw-r--r--t_stub.c113
-rw-r--r--t_unsafe.c44
-rw-r--r--tech_report.tex310
-rw-r--r--testhelp/maketree.py133
-rw-r--r--testrun.c61
-rw-r--r--testsuite/00-hello.test61
-rw-r--r--testsuite/README.testsuite28
-rw-r--r--testsuite/acls-default.test66
-rw-r--r--testsuite/acls.test62
-rw-r--r--testsuite/alt-dest.test68
-rw-r--r--testsuite/atimes.test19
-rw-r--r--testsuite/backup.test63
-rw-r--r--testsuite/batch-mode.test51
-rw-r--r--testsuite/chgrp.test29
-rw-r--r--testsuite/chmod-option.test71
-rw-r--r--testsuite/chmod-temp-dir.test41
-rw-r--r--testsuite/chmod.test30
-rw-r--r--testsuite/chown.test86
-rw-r--r--testsuite/crtimes.test26
-rw-r--r--testsuite/daemon-gzip-download.test37
-rw-r--r--testsuite/daemon-gzip-upload.test31
-rw-r--r--testsuite/daemon.test90
-rw-r--r--testsuite/delay-updates.test21
-rw-r--r--testsuite/delete.test57
-rw-r--r--testsuite/devices.test171
-rw-r--r--testsuite/dir-sgid.test48
-rw-r--r--testsuite/duplicates.test44
l---------testsuite/exclude-lsh.test1
-rw-r--r--testsuite/exclude.test252
-rw-r--r--testsuite/executability.test47
-rw-r--r--testsuite/files-from.test45
-rw-r--r--testsuite/fuzzy.test24
-rw-r--r--testsuite/hands.test38
-rw-r--r--testsuite/hardlinks.test81
-rw-r--r--testsuite/itemize.test246
-rw-r--r--testsuite/longdir.test26
-rw-r--r--testsuite/merge.test57
-rw-r--r--testsuite/missing.test34
-rw-r--r--testsuite/mkpath.test47
-rw-r--r--testsuite/protected-regular.test31
-rw-r--r--testsuite/relative.test60
-rw-r--r--testsuite/rsync.fns498
-rw-r--r--testsuite/ssh-basic.test34
-rw-r--r--testsuite/symlink-ignore.test34
-rw-r--r--testsuite/trimslash.test26
-rw-r--r--testsuite/unsafe-byname.test58
-rw-r--r--testsuite/unsafe-links.test65
-rw-r--r--testsuite/wildmatch.test23
-rw-r--r--testsuite/xattrs.test239
-rw-r--r--tls.c296
-rw-r--r--token.c1113
-rw-r--r--trimslash.c45
-rw-r--r--uidlist.c621
-rw-r--r--usage.c371
-rw-r--r--util1.c1705
-rw-r--r--util2.c145
-rw-r--r--version.h2
-rw-r--r--wildtest.c221
-rw-r--r--wildtest.txt165
-rw-r--r--xattrs.c1274
-rw-r--r--zlib/ChangeLog1472
-rw-r--r--zlib/README115
-rw-r--r--zlib/README.rsync31
-rw-r--r--zlib/adler32.c179
-rw-r--r--zlib/compress.c80
-rw-r--r--zlib/crc32.c423
-rw-r--r--zlib/crc32.h441
-rw-r--r--zlib/deflate.c2032
-rw-r--r--zlib/deflate.h343
-rw-r--r--zlib/dummy.in2
-rw-r--r--zlib/gzguts.h209
-rw-r--r--zlib/inffast.c321
-rw-r--r--zlib/inffast.h11
-rw-r--r--zlib/inffixed.h94
-rw-r--r--zlib/inflate.c1536
-rw-r--r--zlib/inflate.h126
-rw-r--r--zlib/inftrees.c304
-rw-r--r--zlib/inftrees.h62
-rw-r--r--zlib/trees.c1204
-rw-r--r--zlib/trees.h128
-rw-r--r--zlib/zconf.h511
-rw-r--r--zlib/zlib.h1769
-rw-r--r--zlib/zutil.c324
-rw-r--r--zlib/zutil.h251
266 files changed, 119697 insertions, 0 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
new file mode 100644
index 0000000..33e2685
--- /dev/null
+++ b/.cirrus.yml
@@ -0,0 +1,23 @@
+freebsd_task:
+ name: FreeBSD
+ freebsd_instance:
+ image_family: freebsd-13-1
+ env:
+ PATH: /usr/local/bin:$PATH
+ prep_script:
+ - dd if=/dev/zero of=/tmp/zpool bs=1M count=1024
+ - zpool create -m `pwd`/testtmp zpool /tmp/zpool
+ - pkg install -y bash autotools m4 xxhash zstd liblz4 wget
+ - wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
+ configure_script:
+ - CPPFLAGS=-I/usr/local/include/ LDFLAGS=-L/usr/local/lib/ ./configure --disable-md2man
+ make_script:
+ - make
+ install_script:
+ - make install
+ info_script:
+ - rsync --version
+ test_script:
+ - RSYNC_EXPECT_SKIPPED=acls-default,acls,crtimes,protected-regular make check
+ ssl_file_list_script:
+ - rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6313b56
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9e59c9c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,60 @@
+*.[oa]
+*~
+dummy
+ID
+Makefile
+Makefile.old
+configure.sh
+configure.sh.old
+config.cache
+config.h
+config.h.in
+config.h.in.old
+config.log
+config.status
+aclocal.m4
+/proto.h
+/proto.h-tstamp
+/rsync*.[15]
+/rrsync
+/rrsync*.1
+/rsync*.html
+/rrsync*.html
+/help-rsync*.h
+/default-cvsignore.h
+/default-dont-compress.h
+/daemon-parm.h
+/.md2man-works
+/autom4te*.cache
+/confdefs.h
+/conftest*
+/dox
+/getgroups
+/gists
+/gmon.out
+/rsync
+/stunnel-rsyncd.conf
+/shconfig
+/git-version.h
+/testdir
+/tests-dont-exist
+/testtmp
+/tls
+/testrun
+/trimslash
+/t_unsafe
+/wildtest
+/getfsdev
+/rounding.h
+/doc/rsync.pdf
+/doc/rsync.ps
+/support/savetransfer
+/testsuite/chown-fake.test
+/testsuite/devices-fake.test
+/testsuite/xattrs-hlink.test
+/patches
+/patches.gen
+/build
+/auto-build-save
+.deps
+/*.exe
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..fb19ef2
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,683 @@
+REGARDING OPENSSL AND XXHASH
+
+In addition, as a special exception, the copyright holders give
+permission to dynamically link rsync with the OpenSSL and xxhash
+libraries when those libraries are being distributed in compliance
+with their license terms, and to distribute a dynamically linked
+combination of rsync and these libraries. This is also considered
+to be covered under the GPL's System Libraries exception.
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 3 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, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..14e8d61
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,187 @@
+# Doxyfile 1.2.15
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = rsync
+PROJECT_NUMBER = HEAD
+OUTPUT_DIRECTORY = dox
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = NO
+STRIP_FROM_PATH = *source
+INTERNAL_DOCS = YES
+STRIP_CODE_COMMENTS = NO
+CASE_SENSE_NAMES = YES
+SHORT_NAMES = NO
+HIDE_SCOPE_NAMES = YES
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = YES
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = NO
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+ALIASES =
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = YES
+OPTIMIZE_OUTPUT_JAVA = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = NO
+WARN_IF_UNDOCUMENTED = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = .
+FILE_PATTERNS = *.c \
+ *.h
+RECURSIVE = YES
+EXCLUDE = proto.h \
+ zlib \
+ popt
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 3
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 3
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = YES
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = NO
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+TEMPLATE_RELATIONS = YES
+HIDE_UNDOC_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
+CGI_NAME = search.cgi
+CGI_URL =
+DOC_URL =
+DOC_ABSPATH =
+BIN_ABSPATH = /usr/local/bin/
+EXT_DOC_PATHS =
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 0000000..1605ab4
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,242 @@
+# How to build and install rsync
+
+When building rsync, you'll want to install various libraries in order to get
+all the features enabled. The configure script will alert you when the
+newest libraries are missing and tell you the appropriate `--disable-LIB`
+option to use if you want to just skip that feature. What follows are various
+support libraries that you may want to install to build rsync with the maximum
+features (the impatient can skip down to the package summary):
+
+## The basic setup
+
+You need to have a C compiler installed and optionally a C++ compiler in order
+to try to build some hardware-accelerated checksum routines. Rsync also needs
+a modern awk, which might be provided via gawk or nawk on some OSes.
+
+## Autoconf & manpages
+
+If you're installing from the git repo (instead of a release tar file) you'll
+also need the GNU autotools (autoconf & automake) and your choice of 2 python3
+markdown libraries: cmarkgfm or commonmark (needed to generate the manpages).
+If your OS doesn't provide a python3-cmarkgfm or python3-commonmark package,
+you can run the following to install the commonmark python library for your
+build user (after installing python3's pip package):
+
+> python3 -mpip install --user commonmark
+
+You can test if you've got it fixed by running (from the rsync checkout):
+
+> ./md-convert --test rsync-ssl.1.md
+
+Alternately, you can avoid generating the manpages by fetching the very latest
+versions (that match the latest git source) from the [generated-files][6] dir.
+One way to do that is to run:
+
+> ./prepare-source fetchgen
+
+[6]: https://download.samba.org/pub/rsync/generated-files/
+
+## ACL support
+
+To support copying ACL file information, make sure you have an acl
+development library installed. It also helps to have the helper programs
+installed to manipulate ACLs and to run the rsync testsuite.
+
+## Xattr support
+
+To support copying xattr file information, make sure you have an attr
+development library installed. It also helps to have the helper programs
+installed to manipulate xattrs and to run the rsync testsuite.
+
+## xxhash
+
+The [xxHash library][1] provides extremely fast checksum functions that can
+make the "rsync algorithm" run much more quickly, especially when matching
+blocks in large files. Installing this development library adds xxhash
+checksums as the default checksum algorithm. You'll need at least v0.8.0
+if you want rsync to include the full range of its checksum algorithms.
+
+[1]: https://cyan4973.github.io/xxHash/
+
+## zstd
+
+The [zstd library][2] compression algorithm that uses less CPU than
+the default zlib algorithm at the same compression level. Note that you
+need at least version 1.4, so you might need to skip the zstd compression if
+you can only install a 1.3 release. Installing this development library
+adds zstd compression as the default compression algorithm.
+
+[2]: http://facebook.github.io/zstd/
+
+## lz4
+
+The [lz4 library][3] compression algorithm that uses very little CPU, though
+it also has the smallest compression ratio of other algorithms. Installing
+this development library adds lz4 compression as an available compression
+algorithm.
+
+[3]: https://lz4.github.io/lz4/
+
+## openssl crypto
+
+The [openssl crypto library][4] provides some hardware accelerated checksum
+algorithms for MD4 and MD5. Installing this development library makes rsync
+use the (potentially) faster checksum routines when computing MD4 & MD5
+checksums.
+
+[4]: https://www.openssl.org/docs/man1.0.2/man3/crypto.html
+
+## Package summary
+
+To help you get the libraries installed, here are some package install commands
+for various OSes. The commands are split up to correspond with the above
+items, but feel free to combine the package names into a single install, if you
+like.
+
+ - For Debian and Ubuntu (Debian Buster users may want to briefly(?) enable
+ buster-backports to update zstd from 1.3 to 1.4):
+
+ > sudo apt install -y gcc g++ gawk autoconf automake python3-cmarkgfm
+ > sudo apt install -y acl libacl1-dev
+ > sudo apt install -y attr libattr1-dev
+ > sudo apt install -y libxxhash-dev
+ > sudo apt install -y libzstd-dev
+ > sudo apt install -y liblz4-dev
+ > sudo apt install -y libssl-dev
+
+ - For CentOS (use EPEL for python3-pip):
+
+ > sudo yum -y install epel-release
+ > sudo yum -y install gcc g++ gawk autoconf automake python3-pip
+ > sudo yum -y install acl libacl-devel
+ > sudo yum -y install attr libattr-devel
+ > sudo yum -y install xxhash-devel
+ > sudo yum -y install libzstd-devel
+ > sudo yum -y install lz4-devel
+ > sudo yum -y install openssl-devel
+ > python3 -mpip install --user commonmark
+
+ - For Fedora 33:
+
+ > sudo dnf -y install acl libacl-devel
+ > sudo dnf -y install attr libattr-devel
+ > sudo dnf -y install xxhash-devel
+ > sudo dnf -y install libzstd-devel
+ > sudo dnf -y install lz4-devel
+ > sudo dnf -y install openssl-devel
+
+ - For FreeBSD (this assumes that the python3 version is 3.7):
+
+ > sudo pkg install -y autotools python3 py37-CommonMark
+ > sudo pkg install -y xxhash
+ > sudo pkg install -y zstd
+ > sudo pkg install -y liblz4
+
+ - For macOS:
+
+ > brew install automake
+ > brew install xxhash
+ > brew install zstd
+ > brew install lz4
+ > brew install openssl
+
+ - For Cygwin (with all cygwin programs stopped, run the appropriate setup program from a cmd shell):
+
+ > setup-x86_64 --quiet-mode -P make,gawk,autoconf,automake,gcc-core,python38,python38-pip
+ > setup-x86_64 --quiet-mode -P attr,libattr-devel
+ > setup-x86_64 --quiet-mode -P libzstd-devel
+ > setup-x86_64 --quiet-mode -P liblz4-devel
+ > setup-x86_64 --quiet-mode -P libssl-devel
+
+ Sometimes cygwin has commonmark packaged and sometimes it doesn't. Now that
+ its python38 has stabilized, you could install python38-commonmark. Or just
+ avoid the issue by running this from a bash shell as your build user:
+
+ > python3 -mpip install --user commonmark
+
+## Build and install
+
+After installing the various libraries, you need to configure, build, and
+install the source:
+
+> ./configure
+> make
+> sudo make install
+
+The default install path is /usr/local/bin, but you can set the installation
+directory and other parameters using options to ./configure. To see them, use:
+
+> ./configure --help
+
+Configure tries to figure out if the local system uses group "nobody" or
+"nogroup" by looking in the /etc/group file. (This is only used for the
+default group of an rsync daemon, which attempts to run with "nobody"
+user and group permissions.) You can change the default user and group
+for the daemon by editing the NOBODY_USER and NOBODY_GROUP defines in
+config.h, or just override them in your /etc/rsyncd.conf file.
+
+As of 2.4.7, rsync uses Eric Troan's popt option-parsing library. A
+cut-down copy of a recent release is included in the rsync distribution,
+and will be used if there is no popt library on your build host, or if
+the `--with-included-popt` option is passed to ./configure.
+
+If you configure using `--enable-maintainer-mode`, then rsync will try
+to pop up an xterm on DISPLAY=:0 if it crashes. You might find this
+useful, but it should be turned off for production builds.
+
+If you want to automatically use a separate "build" directory based on
+the current git branch name, start with a pristine git checkout and run
+"mkdir auto-build-save" before you run the first ./configure command.
+That will cause a fresh build dir to spring into existence along with a
+special Makefile symlink that allows you to run "make" and "./configure"
+from the source dir (the "build" dir gets auto switched based on branch).
+This is helpful when using the branch-from-patch and patch-update scripts
+to maintain the official rsync patches. If you ever need to build from
+a "detached head" git position then you'll need to manually chdir into
+the build dir to run make. I also like to create 2 more symlinks in the
+source dir: `ln -s build/rsync . ; ln -s build/testtmp .`
+
+## Make compatibility
+
+Note that Makefile.in has a rule that uses a wildcard in a prerequisite. If
+your make has a problem with this rule, you will see an error like this:
+
+ Don't know how to make ./*.c
+
+You can change the "proto.h-tstamp" target in Makefile.in to list all the \*.c
+filenames explicitly in order to avoid this issue.
+
+## RPM notes
+
+Under packaging you will find .spec files for several distributions.
+The .spec file in packaging/lsb can be used for Linux systems that
+adhere to the Linux Standards Base (e.g., RedHat and others).
+
+## HP-UX notes
+
+The HP-UX 10.10 "bundled" C compiler seems not to be able to cope with
+ANSI C. You may see this error message in config.log if ./configure
+fails:
+
+ (Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
+
+Install gcc or HP's "ANSI/C Compiler".
+
+## Mac OS X notes
+
+Some versions of Mac OS X (Darwin) seem to have an IPv6 stack, but do
+not completely implement the "New Sockets" API.
+
+[This site][5] says that Apple started to support IPv6 in 10.2 (Jaguar). If
+your build fails, try again after running configure with `--disable-ipv6`.
+
+[5]: http://www.ipv6.org/impl/mac.html
+
+## IBM AIX notes
+
+IBM AIX has a largefile problem with mkstemp. See IBM PR-51921.
+The workaround is to append the following to config.h:
+
+> #ifdef _LARGE_FILES
+> #undef HAVE_SECURE_MKSTEMP
+> #endif
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..a1253e5
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,369 @@
+# The Makefile for rsync (configure creates it from Makefile.in).
+
+prefix=@prefix@
+datarootdir=@datarootdir@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+libdir=@libdir@/rsync
+mandir=@mandir@
+with_rrsync=@with_rrsync@
+
+LIBS=@LIBS@
+CC=@CC@
+AWK=@AWK@
+CFLAGS=@CFLAGS@
+CPPFLAGS=@CPPFLAGS@
+CXX=@CXX@
+CXXFLAGS=@CXXFLAGS@
+EXEEXT=@EXEEXT@
+LDFLAGS=@LDFLAGS@
+LIBOBJDIR=lib/
+
+INSTALLCMD=@INSTALL@
+INSTALLMAN=@INSTALL@
+
+srcdir=@srcdir@
+MKDIR_P=@MKDIR_P@
+VPATH=$(srcdir)
+SHELL=/bin/sh
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+ROLL_SIMD_x86_64=simd-checksum-x86_64.o
+ROLL_ASM_x86_64=simd-checksum-avx2.o
+MD5_ASM_x86_64=lib/md5-asm-x86_64.o
+
+GENFILES=configure.sh aclocal.m4 config.h.in rsync.1 rsync.1.html \
+ rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html \
+ @GEN_RRSYNC@
+HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \
+ lib/pool_alloc.h lib/mdigest.h lib/md-defines.h
+LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
+ lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
+zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
+ zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
+OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
+ util1.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
+OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
+ usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
+OBJS3=progress.o pipe.o @MD5_ASM@ @ROLL_SIMD@ @ROLL_ASM@
+DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
+popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+ popt/popthelp.o popt/poptparse.o
+OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
+
+TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
+
+# Programs we must have to run the test cases
+CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
+ testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(EXEEXT)
+
+CHECK_SYMLINKS = testsuite/chown-fake.test testsuite/devices-fake.test testsuite/xattrs-hlink.test
+
+# Objects for CHECK_PROGS to clean
+CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest.o
+
+# note that the -I. is needed to handle config.h when using VPATH
+.c.o:
+@OBJ_SAVE@
+ $(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) -c $< @CC_SHOBJ_FLAG@
+@OBJ_RESTORE@
+
+# NOTE: consider running "packaging/smart-make" instead of "make" to auto-handle
+# any changes to configure.sh and the main Makefile prior to a "make all".
+all: Makefile rsync$(EXEEXT) stunnel-rsyncd.conf @MAKE_RRSYNC@ @MAKE_MAN@
+.PHONY: all
+
+.PHONY: install
+install: all
+ -$(MKDIR_P) $(DESTDIR)$(bindir)
+ $(INSTALLCMD) $(INSTALL_STRIP) -m 755 rsync$(EXEEXT) $(DESTDIR)$(bindir)
+ $(INSTALLCMD) -m 755 $(srcdir)/rsync-ssl $(DESTDIR)$(bindir)
+ -$(MKDIR_P) $(DESTDIR)$(mandir)/man1
+ -$(MKDIR_P) $(DESTDIR)$(mandir)/man5
+ if test -f rsync.1; then $(INSTALLMAN) -m 644 rsync.1 $(DESTDIR)$(mandir)/man1; fi
+ if test -f rsync-ssl.1; then $(INSTALLMAN) -m 644 rsync-ssl.1 $(DESTDIR)$(mandir)/man1; fi
+ if test -f rsyncd.conf.5; then $(INSTALLMAN) -m 644 rsyncd.conf.5 $(DESTDIR)$(mandir)/man5; fi
+ if test "$(with_rrsync)" = yes; then \
+ $(INSTALLCMD) -m 755 rrsync $(DESTDIR)$(bindir); \
+ if test -f rrsync.1; then $(INSTALLMAN) -m 644 rrsync.1 $(DESTDIR)$(mandir)/man1; fi; \
+ fi
+
+install-ssl-daemon: stunnel-rsyncd.conf
+ -$(MKDIR_P) $(DESTDIR)/etc/stunnel
+ $(INSTALLCMD) -m 644 stunnel-rsyncd.conf $(DESTDIR)/etc/stunnel/rsyncd.conf
+ @if ! ls /etc/rsync-ssl/certs/server.* >/dev/null 2>/dev/null; then \
+ echo "Note that you'll need to install the certificate used by /etc/stunnel/rsyncd.conf"; \
+ fi
+
+install-all: install install-ssl-daemon
+
+install-strip:
+ $(MAKE) INSTALL_STRIP='-s' install
+
+rsync$(EXEEXT): $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+rrsync: support/rrsync
+ cp -p $(srcdir)/support/rrsync rrsync
+
+$(OBJS): $(HEADERS)
+$(CHECK_OBJS): $(HEADERS)
+tls.o xattrs.o: lib/sysxattrs.h
+usage.o: version.h latest-year.h help-rsync.h help-rsyncd.h git-version.h default-cvsignore.h
+loadparm.o: default-dont-compress.h daemon-parm.h
+
+flist.o: rounding.h
+
+default-cvsignore.h default-dont-compress.h: rsync.1.md define-from-md.awk
+ $(AWK) -f $(srcdir)/define-from-md.awk -v hfile=$@ $(srcdir)/rsync.1.md
+
+help-rsync.h help-rsyncd.h: rsync.1.md help-from-md.awk
+ $(AWK) -f $(srcdir)/help-from-md.awk -v hfile=$@ $(srcdir)/rsync.1.md
+
+daemon-parm.h: daemon-parm.txt daemon-parm.awk
+ $(AWK) -f $(srcdir)/daemon-parm.awk $(srcdir)/daemon-parm.txt
+
+rounding.h: rounding.c rsync.h proto.h
+ @for r in 0 1 3; do \
+ if $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o rounding -DEXTRA_ROUNDING=$$r -I. $(srcdir)/rounding.c >rounding.out 2>&1; then \
+ echo "#define EXTRA_ROUNDING $$r" >rounding.h; \
+ if test -f "$$HOME/build_farm/build_test.fns"; then \
+ echo "EXTRA_ROUNDING is $$r" >&2; \
+ fi; \
+ break; \
+ fi; \
+ done
+ @rm -f rounding
+ @if test -f rounding.h; then : ; else \
+ cat rounding.out 1>&2; \
+ echo "Failed to create rounding.h!" 1>&2; \
+ exit 1; \
+ fi
+ @rm -f rounding.out
+
+git-version.h: ALWAYS_RUN
+ $(srcdir)/mkgitver
+
+.PHONY: ALWAYS_RUN
+ALWAYS_RUN:
+
+simd-checksum-x86_64.o: simd-checksum-x86_64.cpp
+ @$(srcdir)/cmd-or-msg disable-roll-simd $(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $(srcdir)/simd-checksum-x86_64.cpp
+
+simd-checksum-avx2.o: simd-checksum-avx2.S
+ @$(srcdir)/cmd-or-msg disable-roll-asm $(CC) $(CFLAGS) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/simd-checksum-avx2.S
+
+lib/md5-asm-x86_64.o: lib/md5-asm-x86_64.S lib/md-defines.h
+ @$(srcdir)/cmd-or-msg disable-md5-asm $(CC) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/lib/md5-asm-x86_64.S
+
+tls$(EXEEXT): $(TLS_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TLS_OBJ) $(LIBS)
+
+testrun$(EXEEXT): testrun.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ testrun.o
+
+getgroups$(EXEEXT): getgroups.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getgroups.o $(LIBS)
+
+getfsdev$(EXEEXT): getfsdev.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
+
+TRIMSLASH_OBJ = trimslash.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o
+trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
+
+T_UNSAFE_OBJ = t_unsafe.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
+t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
+
+.PHONY: conf
+conf: configure.sh config.h.in
+
+.PHONY: gen
+gen: conf proto.h man git-version.h
+
+.PHONY: gensend
+gensend: gen
+ if ! diff git-version.h $(srcdir)/gists/rsync-git-version.h >/dev/null; then \
+ ./rsync -ai git-version.h $(srcdir)/gists/rsync-git-version.h && \
+ (cd $(srcdir)/gists && git commit --allow-empty-message -m '' rsync-git-version.h && git push) ; \
+ fi
+ rsync -aic $(GENFILES) git-version.h $${SAMBA_HOST-samba.org}:/home/ftp/pub/rsync/generated-files/ || true
+
+aclocal.m4: $(srcdir)/m4/*.m4
+ aclocal -I $(srcdir)/m4
+
+configure.sh config.h.in: configure.ac aclocal.m4
+ @if test -f configure.sh; then cp -p configure.sh configure.sh.old; else touch configure.sh.old; fi
+ @if test -f config.h.in; then cp -p config.h.in config.h.in.old; else touch config.h.in.old; fi
+ autoconf -o configure.sh
+ autoheader && touch config.h.in
+ @if diff configure.sh configure.sh.old >/dev/null 2>&1; then \
+ echo "configure.sh is unchanged."; \
+ rm configure.sh.old; \
+ else \
+ echo "configure.sh has CHANGED."; \
+ fi
+ @if diff config.h.in config.h.in.old >/dev/null 2>&1; then \
+ echo "config.h.in is unchanged."; \
+ rm config.h.in.old; \
+ else \
+ echo "config.h.in has CHANGED."; \
+ fi
+ @if test -f configure.sh.old || test -f config.h.in.old; then \
+ if test "$(MAKECMDGOALS)" = reconfigure; then \
+ echo 'Continuing with "make reconfigure".'; \
+ else \
+ echo 'You may need to run:'; \
+ echo ' make reconfigure'; \
+ exit 1; \
+ fi \
+ fi
+
+.PHONY: reconfigure
+reconfigure: configure.sh
+ ./config.status --recheck
+ ./config.status
+
+.PHONY: restatus
+restatus:
+ ./config.status
+
+Makefile: Makefile.in config.status configure.sh config.h.in
+ @if test -f Makefile; then cp -p Makefile Makefile.old; else touch Makefile.old; fi
+ @./config.status
+ @if diff Makefile Makefile.old >/dev/null 2>&1; then \
+ echo "Makefile is unchanged."; \
+ rm Makefile.old; \
+ else \
+ if test "$(MAKECMDGOALS)" = reconfigure; then \
+ echo 'Continuing with "make reconfigure".'; \
+ else \
+ echo "Makefile updated -- rerun your make command."; \
+ exit 1; \
+ fi \
+ fi
+
+stunnel-rsyncd.conf: $(srcdir)/stunnel-rsyncd.conf.in Makefile
+ sed 's;\@bindir\@;$(bindir);g' <$(srcdir)/stunnel-rsyncd.conf.in >stunnel-rsyncd.conf
+
+.PHONY: proto
+proto: proto.h-tstamp
+
+proto.h: proto.h-tstamp
+ @if test -f proto.h; then :; else cp -p $(srcdir)/proto.h .; fi
+
+proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
+ $(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
+
+.PHONY: man
+man: rsync.1 rsync-ssl.1 rsyncd.conf.5 @MAKE_RRSYNC_1@
+
+rsync.1: rsync.1.md md-convert version.h Makefile
+ @$(srcdir)/maybe-make-man rsync.1.md
+
+rsync-ssl.1: rsync-ssl.1.md md-convert version.h Makefile
+ @$(srcdir)/maybe-make-man rsync-ssl.1.md
+
+rsyncd.conf.5: rsyncd.conf.5.md md-convert version.h Makefile
+ @$(srcdir)/maybe-make-man rsyncd.conf.5.md
+
+rrsync.1: support/rrsync.1.md md-convert Makefile
+ @$(srcdir)/maybe-make-man support/rrsync.1.md
+
+.PHONY: clean
+clean: cleantests
+ rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) @MAKE_RRSYNC@ \
+ git-version.h rounding rounding.h *.old rsync*.1 rsync*.5 @MAKE_RRSYNC_1@ \
+ *.html daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp
+
+.PHONY: cleantests
+cleantests:
+ rm -rf ./testtmp*
+
+# We try to delete built files from both the source and build
+# directories, just in case somebody previously configured things in
+# the source directory.
+.PHONY: distclean
+distclean: clean
+ for dir in $(srcdir) . ; do \
+ (cd "$$dir" && rm -rf Makefile config.h config.status stunnel-rsyncd.conf \
+ lib/dummy popt/dummy zlib/dummy config.cache config.log shconfig \
+ $(GENFILES) autom4te.cache) ; \
+ done
+
+# this target is really just for my use. It only works on a limited
+# range of machines and is used to produce a list of potentially
+# dead (ie. unused) functions in the code. (tridge)
+.PHONY: finddead
+finddead:
+ nm *.o */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
+ nm *.o */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
+ comm -13 nmused.txt nmfns.txt
+ @rm nmused.txt nmfns.txt
+
+# 'check' is the GNU name, 'test' is the name for everybody else :-)
+.PHONY: test
+test: check
+
+# There seems to be no standard way to specify some variables as
+# exported from a Makefile apart from listing them like this.
+
+# This depends on building rsync; if we need any helper programs it
+# should depend on them too.
+
+# We try to run the scripts with POSIX mode on, in the hope that will
+# catch Bash-isms earlier even if we're running on GNU. Of course, we
+# might lose in the future where POSIX diverges from old sh.
+
+.PHONY: check
+check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
+
+.PHONY: check29
+check29: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=29
+
+.PHONY: check30
+check30: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=30
+
+wildtest.o: wildtest.c t_stub.o lib/wildmatch.c rsync.h config.h
+wildtest$(EXEEXT): wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@ $(LIBS)
+
+testsuite/chown-fake.test:
+ ln -s chown.test $(srcdir)/testsuite/chown-fake.test
+
+testsuite/devices-fake.test:
+ ln -s devices.test $(srcdir)/testsuite/devices-fake.test
+
+testsuite/xattrs-hlink.test:
+ ln -s xattrs.test $(srcdir)/testsuite/xattrs-hlink.test
+
+# This does *not* depend on building or installing: you can use it to
+# check a version installed from a binary or some other source tree,
+# if you want.
+
+.PHONY: installcheck
+installcheck: $(CHECK_PROGS) $(CHECK_SYMLINKS)
+ POSIXLY_CORRECT=1 TOOLDIR=`pwd` rsync_bin="$(bindir)/rsync$(EXEEXT)" srcdir="$(srcdir)" $(srcdir)/runtests.sh
+
+# TODO: Add 'dist' target; need to know which files will be included
+
+# Run the SPLINT (Secure Programming Lint) tool. <www.splint.org>
+.PHONY: splint
+splint:
+ splint +unixlib +gnuextensions -weak rsync.c
+
+.PHONY: doxygen
+doxygen:
+ cd $(srcdir) && rm dox/html/* && doxygen
+
+# for maintainers only
+.PHONY: doxygen-upload
+doxygen-upload:
+ rsync -avzv $(srcdir)/dox/html/ --delete \
+ $${SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..fb65628
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,4770 @@
+# NEWS for rsync 3.2.7 (20 Oct 2022)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+- Fixed the client-side validating of the remote sender's filtering behavior.
+
+- More fixes for the "unrequested file-list name" name, including a copy of
+ "/" with `--relative` enabled and a copy with a lot of related paths with
+ `--relative` enabled (often derived from a `--files-from` list).
+
+- When rsync gets an unpack error on an ACL, mention the filename.
+
+- Avoid over-setting sanitize_paths when a daemon is serving "/" (even if
+ "use chroot" is false).
+
+### ENHANCEMENTS:
+
+- Added negotiated daemon-auth support that allows a stronger checksum digest
+ to be used to validate a user's login to the daemon. Added SHA512, SHA256,
+ and SHA1 digests to MD5 & MD4. These new digests are at the highest priority
+ in the new daemon-auth negotiation list.
+
+- Added support for the SHA1 digest in file checksums. While this tends to be
+ overkill, it is available if someone really needs it. This overly-long
+ checksum is at the lowest priority in the normal checksum negotiation list.
+ See [`--checksum-choice`](rsync.1#opt) (`--cc`) and the `RSYNC_CHECKSUM_LIST`
+ environment var for how to customize this.
+
+- Improved the xattr hash table to use a 64-bit key without slowing down the
+ key's computation. This should make extra sure that a hash collision doesn't
+ happen.
+
+- If the `--version` option is repeated (e.g. `-VV`) then the information is
+ output in a (still readable) JSON format. Client side only.
+
+- The script `support/json-rsync-version` is available to get the JSON style
+ version output from any rsync. The script accepts either text on stdin
+ **or** an arg that specifies an rsync executable to run with a doubled
+ `--version` option. If the text we get isn't already in JSON format, it is
+ converted. Newer rsync versions will provide more complete json info than
+ older rsync versions. Various tweaks are made to keep the flag names
+ consistent across versions.
+
+- The [`use chroot`](rsyncd.conf.5#) daemon parameter now defaults to "unset"
+ so that rsync can use chroot when it works and a sanitized copy when chroot
+ is not supported (e.g., for a non-root daemon). Explicitly setting the
+ parameter to true or false (on or off) behaves the same way as before.
+
+- The `--fuzzy` option was optimized a bit to try to cut down on the amount of
+ computations when considering a big pool of files. The simple heuristic from
+ Kenneth Finnegan resuled in about a 2x speedup.
+
+- If rsync is forced to use protocol 29 or before (perhaps due to talking to an
+ rsync before 3.0.0), the modify time of a file is limited to 4-bytes. Rsync
+ now interprets this value as an unsigned integer so that a current year past
+ 2038 can continue to be represented. This does mean that years prior to 1970
+ cannot be represented in an older protocol, but this trade-off seems like the
+ right choice given that (1) 2038 is very rapidly approaching, and (2) newer
+ protocols support a much wider range of old and new dates.
+
+- The rsync client now treats an empty destination arg as an error, just like
+ it does for an empty source arg. This doesn't affect a `host:` arg (which is
+ treated the same as `host:.`) since the arg is not completely empty. The use
+ of [`--old-args`](rsync.1#opt) (including via `RSYNC_OLD_ARGS`) allows the
+ prior behavior of treating an empty destination arg as a ".".
+
+### PACKAGING RELATED:
+
+- The checksum code now uses openssl's EVP methods, which gets rid of various
+ deprecation warnings and makes it easy to support more digest methods. On
+ newer systems, the MD4 digest is marked as legacy in the openssl code, which
+ makes openssl refuse to support it via EVP. You can choose to ignore this
+ and allow rsync's MD4 code to be used for older rsync connections (when
+ talking to an rsync prior to 3.0.0) or you can choose to configure rsync to
+ tell openssl to enable legacy algorithms (see below).
+
+- A simple openssl config file is supplied that can be installed for rsync to
+ use. If you install packaging/openssl-rsync.cnf to a public spot (such as
+ `/etc/ssl/openssl-rsync.cnf`) and then run configure with the option
+ `--with-openssl-conf=/path/name.cnf`, this will cause rsync to export the
+ configured path in the OPENSSL_CONF environment variable (when the variable
+ is not already set). This will enable openssl's MD4 code for rsync to use.
+
+- The packager may wish to include an explicit "use chroot = true" in the top
+ section of their supplied /etc/rsyncd.conf file if the daemon is being
+ installed to run as the root user (though rsync should behave the same even
+ with the value unset, a little extra paranoia doesn't hurt).
+
+- I've noticed that some packagers haven't installed support/nameconvert for
+ users to use in their chrooted rsync configs. Even if it is not installed
+ as an executable script (to avoid a python3 dependency) it would be good to
+ install it with the other rsync-related support scripts.
+
+- It would be good to add support/json-rsync-version to the list of installed
+ support scripts.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.2.6 (9 Sep 2022)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+- More path-cleaning improvements in the file-list validation code to avoid
+ rejecting of valid args.
+
+- A file-list validation fix for a [`--files-from`](rsync.1#opt) file that ends
+ without a line-terminating character.
+
+- Added a safety check that prevents the sender from removing destination files
+ when a local copy using [`--remove-source-files`](rsync.1#opt) has some files
+ that are shared between the sending & receiving hierarchies, including the
+ case where the source dir & destination dir are identical.
+
+- Fixed a bug in the internal MD4 checksum code that could cause the digest
+ to be sporadically incorrect (the openssl version was/is fine).
+
+- A minor tweak to rrsync added "copy-devices" to the list of known args, but
+ left it disabled by default.
+
+### ENHANCEMENTS:
+
+- Rename `--protect-args` to [`--secluded-args`](rsync.1#opt) to make it
+ clearer how it differs from the default backslash-escaped arg-protecting
+ behavior of rsync. The old option names are still accepted. The
+ environment-variable override did not change its name.
+
+### PACKAGING RELATED:
+
+- The configure option `--with-protected-args` was renamed to
+ `--with-secluded-args`. This option makes `--secluded-args` the default
+ rsync behavior instead of using backslash escaping for protecting args.
+
+- The mkgitver script now makes sure that a `.git` dir/file is in the top-level
+ source dir before calling `git describe`. It also runs a basic check on the
+ version value. This should avoid using an unrelated git description for
+ rsync's version.
+
+### DEVELOPER RELATED:
+
+- The configure script no longer sets the -pedantic-errors CFLAG (which it
+ used to try to do only for gcc).
+
+- The name_num_obj struct was modified to allow its dynamic name_num_item list
+ to be initialized in a better way.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.2.5 (14 Aug 2022)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+- Added some file-list safety checking that helps to ensure that a rogue
+ sending rsync can't add unrequested top-level names and/or include recursive
+ names that should have been excluded by the sender. These extra safety
+ checks only require the receiver rsync to be updated. When dealing with an
+ untrusted sending host, it is safest to copy into a dedicated destination
+ directory for the remote content (i.e. don't copy into a destination
+ directory that contains files that aren't from the remote host unless you
+ trust the remote host). Fixes CVE-2022-29154.
+
+ - A fix for CVE-2022-37434 in the bundled zlib (buffer overflow issue).
+
+### BUG FIXES:
+
+- Fixed the handling of filenames specified with backslash-quoted wildcards
+ when the default remote-arg-escaping is enabled.
+
+- Fixed the configure check for signed char that was causing a host that
+ defaults to unsigned characters to generate bogus rolling checksums. This
+ made rsync send mostly literal data for a copy instead of finding matching
+ data in the receiver's basis file (for a file that contains high-bit
+ characters).
+
+- Lots of manpage improvements, including an attempt to better describe how
+ include/exclude filters work.
+
+- If rsync is compiled with an xxhash 0.8 library and then moved to a system
+ with a dynamically linked xxhash 0.7 library, we now detect this and disable
+ the XX3 hashes (since these routines didn't stabilize until 0.8).
+
+### ENHANCEMENTS:
+
+- The [`--trust-sender`](rsync.1#opt) option was added as a way to bypass the
+ extra file-list safety checking (should that be required).
+
+### PACKAGING RELATED:
+
+- A note to those wanting to patch older rsync versions: the changes in this
+ release requires the quoted argument change from 3.2.4. Then, you'll want
+ every single code change from 3.2.5 since there is no fluff in this release.
+
+- The build date that goes into the manpages is now based on the developer's
+ release date, not on the build's local-timezone interpretation of the date.
+
+### DEVELOPER RELATED:
+
+- Configure now defaults GETGROUPS_T to gid_t when cross compiling.
+
+- Configure now looks for the bsd/string.h include file in order to fix the
+ build on a host that has strlcpy() in the main libc but not defined in the
+ main string.h file.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.2.4 (15 Apr 2022)
+
+## Changes in this version:
+
+### BEHAVIOR CHANGES:
+
+ - A new form of arg protection was added that works similarly to the older
+ `--protect-args` ([`-s`](rsync.1#opt)) option but in a way that avoids
+ breaking things like rrsync (the restricted rsync script): rsync now uses
+ backslash escaping for sending "shell-active" characters to the remote
+ shell. This includes spaces, so fetching a remote file via a simple quoted
+ filename value now works by default without any extra quoting:
+
+ ```shell
+ rsync -aiv host:'a simple file.pdf' .
+ ```
+
+ Wildcards are not escaped in filename args, but they are escaped in options
+ like the [`--suffix`](rsync.1#opt) and [`--usermap`](rsync.1#opt) values.
+ If your rsync script depends on the old arg-splitting behavior, either run
+ it with the [`--old-args`](rsync.1#opt) option or `export RSYNC_OLD_ARGS=1`
+ in the script's environment. See also the [ADVANCED USAGE](rsync.1#)
+ section of rsync's manpage for how to use a more modern arg style.
+
+ - A long-standing bug was preventing rsync from figuring out the current
+ locale's decimal point character, which made rsync always output numbers
+ using the "C" locale. Since this is now fixed in 3.2.4, a script that
+ parses rsync's decimal numbers (e.g. from the verbose footer) may want to
+ setup the environment in a way that the output continues to be in the C
+ locale. For instance, one of the following should work fine:
+
+ ```shell
+ export LC_ALL=C.UTF-8
+ ```
+
+ or if iconv translations are needed:
+
+ ```shell
+ if [ "${LC_ALL:-}" ]; then
+ export LANG="$LC_ALL"
+ export LC_CTYPE="$LC_ALL"
+ unset LC_ALL
+ fi
+ export LC_NUMERIC=C.UTF-8
+ ```
+
+### SECURITY FIXES:
+
+ - A fix for CVE-2018-25032 in the bundled zlib (memory corruption issue).
+
+### BUG FIXES:
+
+ - Fixed a bug with [`--inplace`](rsync.1#opt) + [`--sparse`](rsync.1#opt) (and
+ a lack of [`--whole-file`](rsync.1#opt)) where the destination file could
+ get reconstructed with bogus data. Since the bug can also be avoided by
+ using (the seemingly redundant) [`--no-W`](rsync.1#opt) on the receiving
+ side, the latest rsync will now send `--no-W` to a remote receiver when this
+ option combination occurs. If your client rsync is not new enough to do
+ this for you (or if you're just paranoid), you can manually specify `--no-W
+ -M--no-W` (when not using [`--whole-file`](rsync.1#opt)) to make sure the
+ bug is avoided.
+
+ - Fixed a bug with [`--mkpath`](rsync.1#opt) if a single-file copy specifies
+ an existing destination dir with a non-existing destination filename.
+
+ - Fixed `--update -vv` to output "is uptodate" instead of "is newer" messages
+ for files that are being skipped due to an identical modify time. (This was
+ a new output quirk in 3.2.3.)
+
+ - When doing an append transfer, the sending side's file must not get shorter
+ or it is skipped. Fixes a crash that could occur when the size changes to 0
+ in the middle of the send negotiations.
+
+ - When dealing with special files (see [`--specials`](rsync.1#opt)) in an
+ alt-dest hierarchy, rsync now checks the non-permission mode bits to ensure
+ that the 2 special files are really the same before hard-linking them
+ together.
+
+ - Fixed a bug where [`--delay-updates`](rsync.1#opt) with stale partial data
+ could cause a file to fail to update.
+
+ - Fixed a few places that would output an INFO message with
+ [`--info=NAME`](rsync.1#opt) that should only have been output given
+ [`--verbose`](rsync.1#opt) or [`--itemize-changes`](rsync.1#opt).
+
+ - Avoid a weird failure if you run a local copy with a (useless)
+ [`--rsh`](rsync.1#opt) option that contains a `V` in the command.
+
+ - Fixed a long-standing compression bug where the compression level of the
+ first file transferred affected the level for all future files. Also, the
+ per-file compression skipping has apparently never worked, so it is now
+ documented as being ineffective.
+
+ - Fixed a truncate error when a `--write-devices` copy wrote a file onto a
+ device that was shorter than the device.
+
+ - Made `--write-devices` support both `--checksum` and `--no-whole-file` when
+ copying to a device.
+
+ - Improved how the [`--stop-at`](rsync.1#opt), [`--stop-after`](rsync.1#opt),
+ and (the deprecated) [`--time-limit`](rsync.1#opt) options check to see if
+ the allowed time is over, which should make rsync exit more consistently.
+
+ - Tweak --progress to display "`??:??:??`" when the time-remaining value is so
+ large as to be meaningless.
+
+ - Silence some chmod warnings about symlinks when it looks like we have a
+ function to set their permissions but they can't really be set.
+
+ - Fixed a potential issue in git-set-file-times when handling commits with
+ high-bit characters in the description & when handling a description that
+ might mimic the git raw-commit deliniators. (See the support dir.)
+
+ - The bundled systemd/rsync.service file now includes `Restart=on-failure`.
+
+### ENHANCEMENTS:
+
+ - Use openssl's `-verify_hostname` option in the rsync-ssl script.
+
+ - Added extra info to the "FILENAME exists" output of
+ [`--ignore-existing`](rsync.1#opt) when [`--info=skip2`](rsync.1#opt) is
+ used. The skip message becomes "FILENAME exists (INFO)" where the INFO is
+ one of "type change", "sum change" (requires [`--checksum`](rsync.1#opt)),
+ "file change" (based on the quick check), "attr change", or "uptodate".
+ Prior versions only supported `--info=skip1`.
+
+ - Added the [`--fsync`](rsync.1#opt) option (promoted from the patches repo).
+
+ - Added the [`--copy-devices`](rsync.1#opt) option. Compared to the
+ historical version from the rsync-patches repo, this version: properly
+ handles `--checksum`; fixes a truncation bug when doing an `--inplace` copy
+ onto a longer file; fixes several bugs in the `--itemize` output; and only
+ the sending side needs the enhanced rsync for the copy to work.
+
+ - Reduced memory usage for an incremental transfer that has a bunch of small
+ directories.
+
+ - The rsync daemon can now handle a client address with an implied "%scope"
+ suffix.
+
+ - Added support for [`--atimes`](rsync.1#opt) on macOS and fixed a bug where
+ it wouldn't work without [`--times`](rsync.1#opt).
+
+ - Rsync can now update the xattrs on a read-only file when your user can
+ temporarily add user-write permission to the file. (It always worked for a
+ root transfer.)
+
+ - Rsync can now work around an [`--inplace`](rsync.1#opt) update of a file
+ that is being refused due to the Linux fs.protected_regular sysctl setting.
+
+ - When [`--chown`](rsync.1#opt), [`--usermap`](rsync.1#opt), or
+ [`--groupmap`](rsync.1#opt) is specified, rsync now makes sure that the
+ appropriate [`--owner`](rsync.1#opt) and/or [`--group`](rsync.1#opt) options
+ are enabled.
+
+ - Added the [`--info=NONREG`](rsync.1#opt) setting to control if rsync should
+ warn about non-regular files in the transfer. This is enabled by default
+ (keeping the behavior the same as before), so specifying `--info=nonreg0`
+ can be used to turn the warnings off.
+
+ - An optional asm optimization for the rolling checksum from Shark64. Enable
+ it with `./configure --enable-roll-asm`.
+
+ - Using `--debug=FILTER` now outputs a caution message if a filter rule
+ has trailing whitespace.
+
+ - Transformed rrsync into a python script with improvements:
+ - Security has been beefed up.
+ - The known rsync options were updated to include recent additions.
+ - Make rrsync reject [`--copy-links`](rsync.1#opt) (`-L`),
+ [`--copy-dirlinks`](rsync.1#opt) (`-k`), &
+ [`--keep-dirlinks`](rsync.1#opt) (`-K`) by default to make it harder to
+ exploit any out-of-subdir symlinks.
+ - A new rrsync option of [`-munge`](rrsync.1#opt) tells rrsync to always
+ enable rsync's [`--munge-links`](rsync.1#opt) option on the server side.
+ - A new rrsync option of [`-no-lock`](rrsync.1#opt) disables a new
+ single-use locking idiom that is the default when [`-ro`](rrsync.1#opt) is
+ not used (useful with [`-munge`](rrsync.1#opt)).
+ - A new rrsync option of [`-no-del`](rrsync.1#opt) disables all `--remove*`
+ and `--delete*` rsync options on the server side.
+ - The log format has been tweaked slightly to add seconds to the timestamp
+ and to output the command executed as a tuple (making the args clearer).
+ - An rrsync.1 manpage was added (in the support dir with rrsync).
+
+ - Added options to the lsh script to facilitate rrsync testing. (See the
+ support dir.)
+
+ - Transformed the atomic-rsync script into a python script and added the
+ ability to ignore one or more non-zero exit codes. By default, it now
+ ignores code 24, the file-vanished exit code. (See the support dir.)
+
+ - Transformed the munge-symlinks script into python. (See the support dir.)
+
+ - Improved the rsync-no-vanished script to not join stdout & stderr together.
+ (See the support dir.)
+
+ - Work around a glibc bug where lchmod() breaks in a chroot w/o /proc mounted.
+
+ - Try to support a client that sent a remote rsync a wacko stderr file handle
+ (such as an older File::RsyncP perl library used by BackupPC).
+
+ - Lots of manpage improvements, including better HTML versions.
+
+### PACKAGING RELATED:
+
+ - Give configure the `--with-rrsync` option if you want `make install` to
+ install the (now python3) rrsync script and its new manpage.
+
+ - If the rrsync script is installed, its package should be changed to depend
+ on python3 and the (suggested but not mandatory) python3 braceexpand lib.
+
+ - When creating a package from a non-release version (w/o a git checkout), the
+ packager can elect to create git-version.h and define RSYNC_GITVER to the
+ string they want `--version` to output. (The file is still auto-generated
+ using the output of `git describe` when building inside a non-shallow git
+ checkout, though.)
+
+ - Renamed configure's `--enable-simd` option to `--enable-roll-simd` and added
+ the option `--enable-roll-asm` to use the new asm version of the code. Both
+ are x86_64/amd64 only.
+
+ - Renamed configure's `--enable-asm` option to `--enable-md5-asm` to avoid
+ confusion with the asm option for the rolling checksum. It is also honored
+ even when openssl crypto is in use. This allows: normal MD4 & MD5, normal
+ MD4 + asm MD5, openssl MD4 & MD5, or openssl MD4 + asm MD5 depending on the
+ configure options selected.
+
+ - Made SIMD & asm configure checks default to "no" on non-Linux hosts due to
+ various reports of problems on NetBSD & macOS hosts. These were also
+ tweaked to allow enabling the feature on a host_cpu of amd64 (was only
+ allowed on x86_64 before).
+
+ - Fixed configure to not fail at the SIMD check when cross-compiling.
+
+ - Improved the IPv6 determination in configure.
+
+ - Compile the C files with `-pedantic-errors` (when possible) so that we will
+ get warned if a static initialization overflows in the future (among other
+ things).
+
+ - When linking with an external zlib, rsync renames its `read_buf()` function
+ to `read_buf_()` to avoid a symbol clash on an unpatched zlib.
+
+ - Added a SECURITY.md file.
+
+### DEVELOPER RELATED:
+
+ - Made it easier to write rsync tests that diff the output while also checking
+ the status code, and used the idiom to improve the existing tests. (See the
+ `checkdiff` and `checkdiff2` idioms in the `testsuite/*.test` files.
+
+ - The packaging scripts & related python lib got some minor enhancements.
+
+### INTERNAL
+
+ - Use setenv() instead of putenv() when it is available.
+
+ - Improve the logic in compat.c so that we don't need to try to remember to
+ sprinkle `!local_server` exceptions throughout the protocol logic.
+
+ - One more C99 Flexible Array improvement (started in the last release) and
+ make use of the C99 `%zd` format string when printing size_t values (when
+ possible).
+
+ - Use mallinfo2() instead of mallinfo(), when available.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.2.3 (6 Aug 2020)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a bug in the xattr code that was freeing the wrong object when trying
+ to cleanup the xattr list.
+
+ - Fixed a bug in the xattr code that was not leaving room for the "rsync."
+ prefix in some instances where it needed to be added.
+
+ - Restored the ability to use [`--bwlimit=0`](rsync.1#opt) to specify no
+ bandwidth limit. (It was accidentally broken in 3.2.2.)
+
+ - Fixed a bug when combining [`--delete-missing-args`](rsync.1#opt) with
+ [`--no-implied-dirs`](rsync.1#opt) & [`-R`](rsync.1#opt) where rsync might
+ create the destination path of a missing arg. The code also avoids some
+ superfluous warnings for nested paths of removed args.
+
+ - Fixed an issue where hard-linked devices could cause the rdev_major value to
+ get out of sync between the sender and the receiver, which could cause a
+ device to get created with the wrong major value in its major,minor pair.
+
+ - Rsync now complains about a missing [`--temp-dir`](rsync.1#opt) before
+ starting any file transfers.
+
+ - A completely empty source arg is now a fatal error. This doesn't change
+ the handling of implied dot-dir args such as "localhost:" and such.
+
+### ENHANCEMENTS:
+
+ - Allow [`--max-alloc=0`](rsync.1#opt) to specify no limit to the alloc sanity
+ check.
+
+ - Allow [`--block-size=SIZE`](rsync.1#opt) to specify the size using units
+ (e.g. "100K").
+
+ - The name of the id-0 user & group are now sent to the receiver along with
+ the other user/group names in the transfer (instead of assuming that both
+ sides have the same id-0 names).
+
+ - Added the [`--stop-after`](rsync.1#opt) and [`--stop-at`](rsync.1#opt)
+ options (with a [`--time-limit`](rsync.1#opt) alias for `--stop-after`).
+ This is an enhanced version of the time-limit patch from the patches repo.
+
+ - Added the [`name converter`](rsyncd.conf.5#opt) daemon parameter to make it
+ easier to convert user & group names inside a chrooted daemon module. This
+ is based on the nameconverter patch with some improvements, including a
+ tweak to the request protocol (so if you used this patch in the past, be
+ sure to update your converter script to use newlines instead of null chars).
+
+ - Added [`--crtimes`](rsync.1#opt) (`-N`) option for preserving the file's
+ create time (I believe that this is macOS only at the moment).
+
+ - Added [`--mkpath`](rsync.1#opt) option to tell rsync that it should create a
+ non-existing path component of the destination arg.
+
+ - Added [`--stderr=errors|all|client`](rsync.1#opt) to replace the
+ `--msgs2stderr` and `--no-msgs2stderr` options (which are still accepted).
+ The default use of stderr was changed to be `--stderr=errors` where all the
+ processes that have stderr available output directly to stderr, which should
+ help error messages get to the user more quickly, especially when doing a
+ push (which includes local copying). This also allows rsync to exit quickly
+ when a receiver failure occurs, since rsync doesn't need to try to keep the
+ connection alive long enough for the fatal error to go from the receiver to
+ the generator to the sender. The old default can be requested via
+ `--stderr=client`. Also changed is that a non-default stderr mode is
+ conveyed to the remote rsync (using the older option names) instead of
+ requiring the user to use [`--remote-option`](rsync.1#opt) (`-M`) to tell
+ the remote rsync what to do.
+
+ - Added the ability to specify "@netgroup" names to the [`hosts
+ allow`](rsyncd.conf.5#opt) and [`hosts deny`](rsyncd.conf.5#opt) daemon
+ parameters. This is a finalized version of the netgroup-auth patch from the
+ patches repo.
+
+ - Rsync can now hard-link symlinks on FreeBSD due to it making use of the
+ linkat() function when it is available.
+
+ - Output file+line info on out-of-memory & overflow errors while also avoiding
+ the output of alternate build-dir path info that is not useful to the user.
+
+ - Change configure to know that Cygwin supports Linux xattrs.
+
+ - Improved the testsuite on FreeBSD & Cygwin.
+
+ - Added some compatibility code for HPE NonStop platforms.
+
+ - Improved the INSTALL.md info.
+
+ - Added a few more suffixes to the default skip-compress list.
+
+ - Improved configure's error handling to notify about several issues at once
+ instead of one by one (for the newest optional features).
+
+### INTERNAL:
+
+ - Use a simpler overflow check idiom in a few spots.
+
+ - Use a C99 Flexible Array for a trailing variable-size filename in a struct
+ (with a fallback to the old 1-char string kluge for older compilers).
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.2.2 (4 Jul 2020)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Avoid a crash when a daemon module enables `transfer logging` without
+ setting a `log format` value.
+
+ - Fixed installing rsync-ssl script from an alternate build dir.
+
+ - Fixed the updating of configure.sh from an alternate build dir.
+
+ - Apple requires the asm function name to begin with an underscore.
+
+ - Avoid a test failure in the daemon test when `--atimes` is disabled.
+
+### ENHANCEMENTS:
+
+ - Allow the server side to restrict checksum & compression choices via the
+ same environment variables the client uses. The env vars can be divided
+ into "client list & server list" by the "`&`" char or the same list can
+ apply to both.
+
+ - Simplify how the negotiation environment variables apply when interacting
+ with an older rsync and also when a list contains only invalid names.
+
+ - Do not allow a negotiated checksum or compression choice of "none" unless
+ the user authorized it via an environment variable or command-line option.
+
+ - Added the `--max-alloc=SIZE` option to be able to override the memory
+ allocator's sanity-check limit. It defaults to 1G (as before) but the error
+ message when exceeding it specifically mentions the new option so that you
+ can differentiate an out-of-memory error from a failure of this limit. It
+ also allows you to specify the value via the RSYNC_MAX_ALLOC environment
+ variable.
+
+ - Add the "open atime" daemon parameter to allow a daemon to always enable or
+ disable the use of O_NOATIME (the default is to let the user control it).
+
+ - The default systemd config was changed to remove the `ProtectHome=on`
+ setting since rsync is often used to serve files in /home and /root and this
+ seemed a bit too strict. Feel free to use `systemctl edit rsync` to add
+ that restriction (or maybe `ProtectHome=read-only`), if you like. See the
+ 3.2.0 NEWS for the other restrictions that were added compared to 3.1.3.
+
+ - The memory allocation functions now automatically check for a failure and
+ die when out of memory. This eliminated some caller-side check-and-die
+ code and added some missing sanity-checking of allocations.
+
+ - Put optimizations into their own list in the `--version` output.
+
+ - Improved the manpage a bit more.
+
+### PACKAGING RELATED:
+
+ - Prepared the checksum code for an upcoming xxHash release that provides new
+ XXH3 (64-bit) & XXH128 (128-bit) checksum routines. These will not be
+ compiled into rsync until the xxhash v0.8.0 include files are installed on
+ the build host, and that release is a few weeks away at the time this was
+ written. So, if it's now the future and you have packaged and installed
+ xxhash-0.8.0-devel, a fresh rebuild of rsync 3.2.2 will give you the new
+ checksum routines. Just make sure that the new rsync package depends on
+ xxhash >= 0.8.0.
+
+### DEVELOPER RELATED:
+
+ - Moved the version number out of configure.ac into its own version.h file so
+ that we don't need to reconfigure just because the version number changes.
+
+ - Moved the daemon parameter list into daemon-parm.txt so that an awk script
+ can create the interrelated structs and accessors that loadparm.c needs.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.2.1 (22 Jun 2020)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a potential build issue with the MD5 assembly-language code by
+ removing some non-portable directives.
+
+ - Use the preprocessor with the asm file to ensure that if the code is
+ unneeded, it doesn't get built.
+
+ - Avoid the stack getting set to executable when including the asm code.
+
+ - Some improvements in the SIMD configure testing to try to avoid build
+ issues, such as avoiding a clang++ core dump when `-g` is combined with
+ `-O2`. Note that clang++ is quite buggy in this area, and it does still
+ crash for some folks, so just use `--disable-simd` if you need to avoid
+ their buggy compiler (since the configure test is apparently not finding
+ all the compilers that will to crash and burn).
+
+ - Fixed an issue in the md2man script when building from an alternate dir.
+
+ - Disable `--atimes` on macOS (it apparently just ignores the atime change).
+
+### ENHANCEMENTS:
+
+ - The use of `--backup-dir=STR` now implies `--backup`.
+
+ - Added `--zl=NUM` as a short-hand for `--compress-level=NUM`.
+
+ - Added `--early-input=FILE` option that allows the client to send some
+ data to a daemon's (optional) "early exec" script on its stdin.
+
+ - Mention atimes in the capabilities list that `--version` outputs.
+
+ - Mention either "default protect-args" or "optional protect-args" in the
+ `--version` capabilities depending on how rsync was configured.
+
+ - Some info on optimizations is now elided from the `--version` capabilities
+ since they aren't really user-facing capabilities. You can still see the
+ info (plus the status of a couple extra optimizations) by repeating the
+ `--version` option (e.g. `-VV`).
+
+ - Updated various URLs to be https instead of http.
+
+ - Some documentation improvements.
+
+### PACKAGING RELATED:
+
+ - If you had to use `--disable-simd` for 3.2.0, you might want to try removing
+ that and see if it will succeed or auto-disable. Some buggy clang++
+ compilers are still not auto disabled, though.
+
+ - The MD5 asm code is now under its own configure flag (not shared with the
+ SIMD setting), so if you have any issues compiling it, re-run configure with
+ `--disable-asm`.
+
+ - Merged the OLDNEWS.md file into NEWS.md.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.2.0 (19 Jun 2020)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Avoid a potential out-of-bounds read in daemon mode if argc can be made to
+ become 0.
+
+ - Fix the default list of skip-compress files for non-daemon transfers.
+
+ - Fix xattr filter rules losing an 'x' attribute in a non-local transfer.
+
+ - Avoid an error when a check for a potential fuzzy file happens to reference
+ a directory.
+
+ - Make the atomic-rsync helper script have a more consistent error-exit.
+
+ - Make sure that a signal handler's use of exit_cleanup() calls `_exit()`
+ instead of exit().
+
+ - Various zlib fixes, including security fixes for CVE-2016-9843,
+ CVE-2016-9842, CVE-2016-9841, and CVE-2016-9840.
+
+ - Fixed an issue with `--remove-source-files` not removing a source symlink
+ when combined with `--copy-links`.
+
+ - Fixed a bug where the daemon would fail to write early fatal error messages
+ to the client, such as refused or unknown command-line options.
+
+ - Fixed the block-size validation logic when dealing with older protocols.
+
+ - Some rrsync fixes and enhancements to handle the latest options.
+
+ - Fixed a problem with the `--link-dest`|`--copy-dest` code when `--xattrs`
+ was specified along with multiple alternate-destination directories (it
+ could possibly choose a bad file match while trying to find a better xattr
+ match).
+
+ - Fixed a couple bugs in the handling of files with the `--sparse` option.
+
+ - Fixed a bug in the writing of the batch.sh file (w/`--write-batch`) when the
+ source & destination args were not last on the command-line.
+
+ - Avoid a hang when an overabundance of messages clogs up all the I/O buffers.
+
+ - Fixed a mismatch in the RSYNC_PID values put into the environment of
+ `pre-xfer exec` and a `post-xfer exec`.
+
+ - Fixed a crash in the `--iconv` code.
+
+ - Fixed a rare crash in the popt_unalias() code.
+
+### ENHANCEMENTS:
+
+ - The default systemd config was made stricter by default. For instance,
+ `ProtectHome=on` (which hides content in /root and /home/USER dirs),
+ `ProtectSystem=full` (which makes /usr, /boot, & /etc dirs read-only), and
+ `PrivateDevices=on` (which hides devices). You can override any of these
+ using the standard `systemctl edit rsync` and add one or more directives
+ under a `[Service]` heading (and restart the rsync service).
+
+ - Various checksum enhancements, including the optional use of openssl's MD4 &
+ MD5 checksum algorithms, some x86-64 optimizations for the rolling checksum,
+ some x86-64 optimizations for the (non-openssl) MD5 checksum, the addition
+ of xxHash checksum support, and a negotiation heuristic that ensures that it
+ is easier to add new checksum algorithms in the future. The environment
+ variable `RSYNC_CHECKSUM_LIST` can be used to customize the preference order
+ of the negotiation, or use `--checksum-choice` (`--cc`) to force a choice.
+
+ - Various compression enhancements, including the addition of zstd and lz4
+ compression algorithms and a negotiation heuristic that picks the best
+ compression option supported by both sides. The environment variable
+ `RSYNC_COMPRESS_LIST` can be used to customize the preference order of the
+ negotiation, or use `--compress-choice` (`--zc`) to force a choice.
+
+ - Added a `--debug=NSTR` option that outputs details of the new negotiation
+ strings (for checksums and compression). The first level just outputs the
+ result of each negotiation on the client, level 2 outputs the values of the
+ strings that were sent to and received from the server, and level 3 outputs
+ all those values on the server side too (when the server was given the debug
+ option).
+
+ - The `--debug=OPTS` command-line option is no longer auto-forwarded to the
+ remote rsync which allows for the client and server to have different levels
+ of debug specified. This also allows for newer debug options to be
+ specified, such as using `--debug=NSTR` to see the negotiated hash result,
+ without having the command fail if the server version is too old to handle
+ that debug item. Use `-M--debug=OPTS` to send the options to the remote side.
+
+ - Added the `--atimes` option based on the long-standing patch (just with some
+ fixes that the patch has been needing).
+
+ - Added `--open-noatime` option to open files using `O_NOATIME`.
+
+ - Added the `--write-devices` option based on the long-standing patch.
+
+ - Added openssl & preliminary gnutls support to the rsync-ssl script, which is
+ now installed by default. This was unified with the old stunnel-rsync
+ helper script to simplify packaging. Note that the script accepts the use
+ of `--type=gnutls` for gnutls testing, but does not look for gnutls-cli on
+ the path yet. The use of `--type=gnutls` will not work right until
+ gnutls-cli no longer drops data.
+
+ - Rsync was enhanced to set the `RSYNC_PORT` environment variable when running
+ a daemon-over-rsh script. Its value is the user-specified port number (set
+ via `--port` or an rsync:// URL) or 0 if the user didn't override the port.
+
+ - Added the `proxy protocol` daemon parameter that allows your rsyncd to know
+ the real remote IP when it is setup behind a proxy.
+
+ - Added negated matching to the daemon's `refuse options` setting by using
+ match strings that start with a `!` (such as `!compress*`). This lets you
+ refuse all options except for a particular approved list, for example. It
+ also lets rsync refuse certain options by default (such as `write-devices`)
+ while allowing the config to override that, as desired.
+
+ - Added the `early exec` daemon parameter that runs a script before the
+ transfer parameters are known, allowing some early setup based on module
+ name.
+
+ - Added status output in response to a signal (via both SIGINFO & SIGVTALRM).
+
+ - Added `--copy-as=USER` option to give some extra security to root-run rsync
+ commands into/from untrusted directories (such as backups and restores).
+
+ - When resuming the transfer of a file in the `--partial-dir`, rsync will now
+ update that partial file in-place instead of creating yet another tmp file
+ copy. This requires both sender & receiver to be at least v3.2.0.
+
+ - Added support for `RSYNC_SHELL` & `RSYNC_NO_XFER_EXEC` environment variables
+ that affect the early, pre-xfer, and post-xfer exec rsync daemon parameters.
+
+ - Optimize the `--fuzzy --fuzzy` heuristic to avoid the fuzzy directory scan
+ until all other basis-file options are exhausted (such as `--link-dest`).
+
+ - Have the daemon log include the normal-exit sent/received stats when the
+ transfer exited with an error when possible (i.e. if it is the sender).
+
+ - The daemon now locks its pid file (when configured to use one) so that it
+ will not fail to start when the file exists but no daemon is running.
+
+ - Various manpage improvements, including some html representations (that
+ aren't installed by default).
+
+ - Made `-V` the short option for `--version` and improved its information.
+
+ - Pass the `-4` or `-6` option to the ssh command, making it easier to type
+ than `--rsh='ssh -4'` (or the `-6` equivalent).
+
+ - Added example config for rsyncd SSL proxy configs to rsyncd.conf.
+
+ - More errors messages now mention if the error is coming from the sender or
+ the receiver.
+
+### PACKAGING RELATED:
+
+ - Add installed bash script: /usr/bin/rsync-ssl
+
+ - Add installed manpage: /usr/man/man1/rsync-ssl.1
+
+ - Tweak auxiliary doc file names, such as: README.md, INSTALL.md, & NEWS.md.
+
+ - The rsync-ssl script wants to run openssl or stunnel4, so consider adding a
+ dependency for one of those options (though it's probably fine to just let
+ it complain about being unable to find the program and let the user decide
+ if they want to install one or the other).
+
+ - If you packaged rsync + rsync-ssl + rsync-ssl-daemon as separate packages,
+ the rsync-ssl package is now gone (rsync-ssl should be considered to be
+ mainstream now that Samba requires SSL for its rsync daemon).
+
+ - Add _build_ dependency for liblz4-dev, libxxhash-dev, libzstd-dev, and
+ libssl-dev. These development libraries will give rsync extra compression
+ algorithms, extra checksum algorithms, and allow use of openssl's crypto
+ lib for (potentially) faster MD4/MD5 checksums.
+
+ - Add _build_ dependency for g++ or clang++ on x86_64 systems to enable the
+ SIMD checksum optimizations.
+
+ - Add _build_ dependency for _either_ python3-cmarkcfm or python3-commonmark
+ to allow for patching of manpages or building a git release. This is not
+ required for a release-tar build, since it comes with pre-built manpages.
+ Note that cmarkcfm is faster than commonmark, but they generate the same
+ data. The commonmark dependency is easiest to install since it's native
+ python, and can even be installed via `pip3 install --user commonmark` if
+ you want to just install it for the build user.
+
+ - Remove yodl _build_ dependency (if it was even listed before).
+
+### DEVELOPER RELATED:
+
+ - Silenced some annoying warnings about major() & minor() by improving an
+ autoconf include-file check.
+
+ - Converted the manpages from yodl to markdown. They are now processed via a
+ simple python3 script using the cmarkgfm **or** commonmark library. This
+ should make it easier to package rsync, since yodl is rather obscure.
+
+ - Improved some configure checks to work better with strict C99 compilers.
+
+ - Some perl building/packaging scripts were recoded into awk and python3.
+
+ - Some defines in byteorder.h were changed into static inline functions that
+ will help to ensure that the args don't get evaluated multiple times on
+ "careful alignment" hosts.
+
+ - Some code typos were fixed (as pointed out by a Fossies run).
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.1.3 (28 Jan 2018)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+ - Fixed a buffer overrun in the protocol's handling of xattr names and ensure
+ that the received name is null terminated.
+
+ - Fix an issue with `--protect-args` where the user could specify the arg in the
+ protected-arg list and short-circuit some of the arg-sanitizing code.
+
+### BUG FIXES:
+
+ - Don't output about a new backup dir without appropriate info verbosity.
+
+ - Fixed some issues with the sort functions in the rsyncstats script (in the
+ support dir).
+
+ - Added a way to specify daemon config lists (e.g. users, groups, etc) that
+ contain spaces (see `auth users` in the latest rsyncd.conf manpage).
+
+ - If a backup fails (e.g. full disk) rsync exits with an error.
+
+ - Fixed a problem with a doubled `--fuzzy` option combined with `--link-dest`.
+
+ - Avoid invalid output in the summary if either the start or end time had an
+ error.
+
+ - We don't allow a popt alias to affect the `--daemon` or `--server` options.
+
+ - Fix daemon exclude code to disallow attribute changes in addition to
+ disallowing transfers.
+
+ - Don't force nanoseconds to match if a non-transferred, non-checksummed file
+ only passed the quick-check w/o comparing nanoseconds.
+
+### ENHANCEMENTS:
+
+ - Added the ability for rsync to compare nanosecond times in its file-check
+ comparisons, and added support nanosecond times on Mac OS X.
+
+ - Added a short-option (`-@`) for `--modify-window`.
+
+ - Added the `--checksum-choice=NAME[,NAME]` option to choose the checksum
+ algorithms.
+
+ - Added hashing of xattr names (with using `-X`) to improve the handling of
+ files with large numbers of xattrs.
+
+ - Added a way to filter xattr names using include/exclude/filter rules (see
+ the `--xattrs` option in the manpage for details).
+
+ - Added `daemon chroot|uid|gid` to the daemon config (in addition to the old
+ chroot|uid|gid settings that affect the daemon's transfer process).
+
+ - Added `syslog tag` to the daemon configuration.
+
+ - Some manpage improvements.
+
+### DEVELOPER RELATED:
+
+ - Tweak the `make` output when yodl isn't around to create the manpages.
+
+ - Changed an obsolete autoconf compile macro.
+
+ - Support newer yodl versions when converting manpages.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.1.2 (21 Dec 2015)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+ - Make sure that all transferred files use only path names from inside the
+ transfer. This makes it impossible for a malicious sender to try to make the
+ receiver use an unsafe destination path for a transferred file, such as a
+ just-sent symlink.
+
+### BUG FIXES:
+
+ - Change the checksum seed order in the per-block checksums. This prevents
+ someone from trying to create checksum blocks that match in sum but not
+ content.
+
+ - Fixed a with the per-dir filter files (using `-FF`) that could trigger an
+ assert failure.
+
+ - Only skip `set_modtime()` on a transferred file if the time is exactly
+ right.
+
+ - Don't create an empty backup dir for a transferred file that doesn't exist
+ yet.
+
+ - Fixed a bug where `--link-dest` and `--xattrs` could cause rsync to exit if
+ a filename had a matching dir of the same name in the alt-dest area.
+
+ - Allow more than 32 group IDs per user in the daemon's gid=LIST config.
+
+ - Fix the logging of %b & %c via `--log-file` (daemon logging was already
+ correct, as was `--out-format='%b/%c'`).
+
+ - Fix erroneous acceptance of `--info=5` & `--debug=5` (an empty flag name is
+ not valid).
+
+### ENHANCEMENTS:
+
+ - Added `(DRY RUN)` info to the `--debug=exit` output line.
+
+ - Use usleep() for our msleep() function if it is available.
+
+ - Added a few extra long-option names to rrsync script, which will make
+ BackupPC happier.
+
+ - Made configure choose to use Linux xattrs on NetBSD (rather than not
+ supporting xattrs).
+
+ - Added `-wo` (write-only) option to rrsync support script.
+
+ - Misc. manpage tweaks.
+
+### DEVELOPER RELATED:
+
+ - Fixed a bug with the Makefile's use of `INSTALL_STRIP`.
+
+ - Improve a test in the suite that could get an erroneous timestamp error.
+
+ - Tweaks for newer versions of git in the packaging tools.
+
+ - Improved the m4 generation rules and some autoconf idioms.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.1.1 (22 Jun 2014)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - If the receiver gets bogus filenames from the sender (an unexpected leading
+ slash or a `..` infix dir), exit with an error. This prevents a malicious
+ sender from trying to inject filenames that would affect an area outside the
+ destination directories.
+
+ - Fixed a failure to remove the partial-transfer temp file when interrupted
+ (and rsync is not saving the partial files).
+
+ - Changed the chown/group/xattr-set order to avoid losing some security-
+ related xattr info (that would get cleared by a chown).
+
+ - Fixed a bug in the xattr-finding code that could make a non-root-run
+ receiver not able to find some xattr numbers.
+
+ - Fixed a bug in the early daemon protocol where a timeout failed to be
+ honored (e.g. if the remote side fails to send us the initial protocol
+ greeting).
+
+ - Fixed unintended inclusion of commas in file numbers in the daemon log.
+
+ - We once again send the 'f' sub-flag (of `-e`) to the server side so it knows
+ that we can handle incremental-recursion directory errors properly in older
+ protocols.
+
+ - Fixed an issue with too-aggressive keep-alive messages causing a problem for
+ older rsync versions early in the transfer.
+
+ - Fixed an incorrect message about backup-directory-creation when using
+ `--dry-run` and the backup dir is not an absolute path.
+
+ - Fixed a bug where a failed deletion and/or a failed sender-side removal
+ would not affect the exit code.
+
+ - Fixed a bug that caused a failure when combining `--delete-missing-args`
+ with `--xattrs` and/or `--acls`.
+
+ - Fixed a strange `dir_depth` assertion error that was caused by empty-dir
+ removals and/or duplicate files in the transfer.
+
+ - Fixed a problem with `--info=progress2`'s output stats where rsync would
+ only update the stats at the end of each file's transfer. It now uses the
+ data that is flowing for the current file, making the stats more accurate
+ and less jumpy.
+
+ - Fixed an itemize bug that affected the combo of `--link-dest`, `-X`, and
+ `-n`.
+
+ - Fixed a problem with delete messages not appearing in the log file when the
+ user didn't use `--verbose`.
+
+ - Improve chunked xattr reading for OS X.
+
+ - Removed an attempted hard-link xattr optimization that was causing a
+ transfer failure. This removal is flagged in the compatibility code, so if a
+ better fix can be discovered, we have a way to flip it on again.
+
+ - Fixed a bug when the receiver is not configured to be able to hard link
+ symlimks/devices/special-file items but the sender sent some of these items
+ flagged as hard-linked.
+
+ - We now generate a better error if the buffer overflows in `do_mknod()`.
+
+ - Fixed a problem reading more than 16 ACLs on some OSes.
+
+ - Fixed the reading of the secrets file to avoid an infinite wait when the
+ username is missing.
+
+ - Fixed a parsing problem in the `--usermap`/`--groupmap` options when using
+ MIN-MAX numbers.
+
+ - Switched Cygwin back to using socketpair `pipes` to try to speed it up.
+
+ - Added knowledge of a few new options to rrsync.
+
+### ENHANCEMENTS:
+
+ - Tweaked the temp-file naming when `--temp-dir=DIR` is used: the temp-file
+ names will not get a '.' prepended.
+
+ - Added support for a new-compression idiom that does not compress all the
+ matching data in a transfer. This can help rsync to use less cpu when a
+ transfer has a lot of matching data, and also makes rsync compatible with a
+ non-bundled zlib. See the `--new-compress` and `--old-compress` options in
+ the manpage.
+
+ - Added the rsync-no-vanished shell script. (See the support dir.)
+
+ - Made configure more prominently mention when we failed to find yodl (in case
+ the user wants to be able to generate manpages from `*.yo` files).
+
+ - Have manpage mention how a daemon's max-verbosity setting affects info and
+ debug options. Also added more clarification on backslash removals for
+ excludes that contain wildcards.
+
+ - Have configure check if for the attr lib (for getxattr) for those systems
+ that need to link against it explicitly.
+
+ - Change the early dir-creation logic to only use that idiom in an
+ inc-recursive copy that is preserving directory times. e.g. using
+ `--omit-dir-times` will avoid these early directories being created.
+
+ - Fix a bug in `cmp_time()` that would return a wrong result if the 2 times
+ differed by an amount greater than what a `time_t` can hold.
+
+### DEVELOPER RELATED:
+
+ - We now include an example systemd file (in packaging/systemd).
+
+ - Tweaked configure to make sure that any intended use of the included popt
+ and/or zlib code is put early in the CFLAGS.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.1.0 (28 Sep 2013)
+
+## Changes in this version:
+
+### PROTOCOL NUMBER:
+
+ - The protocol number was changed to 31.
+
+### OUTPUT CHANGES:
+
+ - Output numbers in 3-digit groups by default (e.g. 1,234,567). See the
+ `--human-readable` option for a way to turn it off. See also the daemon's
+ `log format` parameter and related command-line options (including
+ `--out-format`) for a modifier that can be used to request digit-grouping or
+ human-readable output in log escapes. (Note that log output is unchanged by
+ default.)
+
+ - The `--list-only` option is now affected by the `--human-readable` setting.
+ It will display digit groupings by default, and unit suffixes if higher
+ levels of readability are requested. Also, the column width for the size
+ output has increased from 11 to 14 characters when human readability is
+ enabled. Use `--no-h` to get the old-style output and column size.
+
+ - The output of the `--progress` option has changed: the string `xfer` was
+ shortened to `xfr`, and the string `to-check` was shortened to `to-chk`,
+ both designed to make room for the (by default) wider display of file size
+ numbers without making the total line-length longer. Also, when incremental
+ recursion is enabled, the string `ir-chk` will be used instead of `to-chk`
+ up until the incremental-recursion scan is done, letting you know that the
+ value to check and the total value will still be increasing as new files are
+ found.
+
+ - Enhanced the `--stats` output: 1) to mention how many files were created
+ (protocol >= 28), 2) to mention how many files were deleted (a new line for
+ protocol 31, but only output when `--delete` is in effect), and 3) to follow
+ the file-count, created-count, and deleted-count with a subcount list that
+ shows the counts by type. The wording of the transferred count has also
+ changed so that it is clearer that it is only a count of regular files.
+
+### BUG FIXES:
+
+ - Fixed a bug in the iconv code when EINVAL or EILSEQ is returned with a full
+ output buffer.
+
+ - Fixed some rare bugs in `--iconv` processing that might cause a multi-byte
+ character to get translated incorrectly.
+
+ - Fixed a bogus `vanished file` error if some files were specified with `./`
+ prefixes and others were not.
+
+ - Fixed a bug in `--sparse` where an extra gap could get inserted after a
+ partial write.
+
+ - Changed the way `--progress` overwrites its prior output in order to make it
+ nearly impossible for the progress to get overwritten by an error.
+
+ - Improved the propagation of abnormal-exit error messages. This should help
+ the client side to receive errors from the server when it is exiting
+ abnormally, and should also avoid dying with an `connection unexpectedly
+ closed` exit when the closed connection is really expected.
+
+ - The sender now checks each file it plans to remove to ensure that it hasn't
+ changed from the first stat's info. This helps to avoid losing file data
+ when the user is not using the option in a safe manner.
+
+ - Fixed a data-duplication bug in the compress option that made compression
+ less efficient. This improves protocol 31 onward, while behaving in a
+ compatible (buggy) manner with older rsync protocols.
+
+ - When creating a temp-file, rsync is now a bit smarter about it dot-char
+ choices, which can fix a problem on OS X with names that start with `..`.
+
+ - Rsync now sets a cleanup flag for `--inplace` and `--append` transfers that
+ will flush the write buffer if the transfer aborts. This ensures that more
+ received data gets written out to the disk on an aborted transfer (which is
+ quite helpful on a slow, flaky connection).
+
+ - The reads that `map_ptr()` now does are aligned on 1K boundaries. This helps
+ some filesystems and/or files that don't like unaligned reads.
+
+ - Fix an issue in the msleep() function if time jumps backwards.
+
+ - Fix daemon-server module-name splitting bug where an arg would get split
+ even if `--protect-args` was used.
+
+### ENHANCEMENTS:
+
+ - Added the `--remote-option=OPT` (`-M OPT`) command-line option that is
+ useful for things like sending a remote `--log-file=FILE` or `--fake-super`
+ option.
+
+ - Added the `--info=FLAGS` and `--debug=FLAGS` options to allow finer-grained
+ control over what is output. Added an extra type of `--progress` output
+ using `--info=progress2`.
+
+ - The `--msgs2stderr` option can help with debugging rsync by allowing the
+ debug messages to get output to stderr rather than travel via the socket
+ protocol.
+
+ - Added the `--delete-missing-args` and `--ignore-missing-args` options to
+ either delete or ignore user-specified files on the receiver that are
+ missing on the sender (normally the absence of user-specified files
+ generates an error).
+
+ - Added a `T` (terabyte) category to the `--human-readable` size suffixes.
+
+ - Added the `--usermap`/`--groupmap`/`--chown` options for manipulating file
+ ownership during the copy.
+
+ - Added the `%C` escape to the log-output handling, which will output the MD5
+ checksum of any transferred file, or all files if `--checksum` was specified
+ (when protocol 30 or above is in effect).
+
+ - Added the `reverse lookup` parameter to the rsync daemon config file to
+ allow reverse-DNS lookups to be disabled.
+
+ - Added a forward-DNS lookup for the daemon's hosts allow/deny config. Can be
+ disabled via `forward lookup` parameter (defaults to enabled).
+
+ - Added a way for more than one group to be specified in the daemon's config
+ file, including a way to specify that you want all of the specified user's
+ groups without having to name them. Also changed the daemon to complain
+ about an inability to set explicitly-specified uid/gid values, even when not
+ run by a super-user.
+
+ - The daemon now tries to send the user the error messages from the pre-xfer
+ exec script when it fails.
+
+ - Improved the use of alt-dest options into an existing hierarchy of files: If
+ a match is found in an alt-dir, it takes precedence over an existing file.
+ (We'll need to wait for a future version before attribute-changes on
+ otherwise unchanged files are safe when using an existing hierarchy.)
+
+ - Added per-user authorization options and group-authorization support to the
+ daemon's `auth users` parameter.
+
+ - Added a way to reference environment variables in a daemon's config file
+ (using %VAR% references).
+
+ - When replacing a non-dir with a symlink/hard-link/device/special-file, the
+ update should now be done in an atomic manner.
+
+ - Avoid re-sending xattr info for hard-linked files w/the same xattrs
+ (protocol 31).
+
+ - The backup code was improved to use better logic maintaining the backup
+ directory hierarchy. Also, when a file is being backed up, rsync tries to
+ hard-link it into place so that the upcoming replacement of the destination
+ file will be atomic (for the normal, non-inplace logic).
+
+ - Added the ability to synchronize nanosecond modified times.
+
+ - Added a few more default suffixes for the `dont compress` settings.
+
+ - Added the checking of the `RSYNC_PROTECT_ARGS` environment variable to allow
+ the default for the `--protect-args` command-line option to be overridden.
+
+ - Added the `--preallocate` command-line option.
+
+ - Allow `--password-file=-` to read the password from stdin (filename `-`).
+
+ - Rsync now comes packaged with an rsync-ssl helper script that can be used to
+ contact a remote rsync daemon using a piped-stunnel command. It also
+ includes an stunnel config file to run the server side to support ssl daemon
+ connections. See the packaging/lsb/rsync.spec file for one way to package
+ the resulting files. (Suggestions for how to make this even easier to
+ install & use are welcomed.)
+
+ - Improved the speed of some `--inplace` updates when there are lots of
+ identical checksum blocks that end up being unusable.
+
+ - Added the `--outbuf=N|L|B` option for choosing the output buffering.
+
+ - Repeating the `--fuzzy` option now causes the code to look for fuzzy matches
+ inside alt-dest directories too.
+
+ - The `--chmod` option now supports numeric modes, e.g. `--chmod=644,D755`
+
+ - Added some Solaris xattr code.
+
+ - Made an rsync daemon (the listening process) exit with a 0 status when it
+ was signaled to die. This helps launchd.
+
+ - Improved the `RSYNC_*` environment variables for the pre-xfer exec script:
+ when a daemon is sent multiple request args, they are now joined into a
+ single return value (separated by spaces) so that the `RSYNC_REQUEST`
+ environment variable is accurate for any `pre-xfer exec`. The values in
+ `RSYNC_ARG#` vars are no longer truncated at the `.` arg (prior to the
+ request dirs/files), so that all the requested values are also listed
+ (separately) in `RSYNC_ARG#` variables.
+
+### EXTRAS:
+
+ - Added an `instant-rsyncd` script to the support directory, which makes it
+ easy to configure a simple rsync daemon in the current directory.
+
+ - Added the `mapfrom` and `mapto` scripts to the support directory, which
+ makes it easier to do user/group mapping in a local transfer based on
+ passwd/group files from another machine.
+
+ - There's a new, improved version of the lsh script in the support dir: it's
+ written in perl and supports `-u` without resorting to using sudo (when run
+ as root). The old shell version is now named lsh.sh.
+
+ - There is a helper script named rsync-slash-strip in the support directory
+ for anyone that wants to change the way rsync handles args with trailing
+ slashes. (e.g. arg/ would get stripped to arg while arg/. would turn into
+ arg/).
+
+### INTERNAL:
+
+ - The I/O code was rewritten to be simpler and do bigger buffered reads over
+ the socket. The I/O between the receiver and the generator was changed to be
+ standard multiplexed-I/O (like that over the socket).
+
+ - The sender tries to use any dead time while the generator is looking for
+ files to transfer in order to do sender-side directory scanning in a more
+ parallel manner.
+
+ - A daemon can now inform a client about a daemon-configured timeout value so
+ that the client can assist in the keep-alive activity (protocol 31).
+
+ - The filter code received some refactoring to make it more extendable, to
+ read better, and do better sanity checking.
+
+ - Really big numbers are now output using our own big-num routine rather than
+ casting them to a double and using a %.0f conversion.
+
+ - The `pool_alloc` library has received some minor improvements in alignment
+ handling.
+
+ - Added `init_stat_x()` function to avoid duplication of acl/xattr init code.
+
+ - The included zlib was upgraded from 1.2.3 to 1.2.8.
+
+ - Rsync can now be compiled to use an unmodified zlib library instead of the
+ tweaked one that is included with rsync. This will eventually become the
+ default, at which point we'll start the countdown to removing the included
+ zlib. Until then, feel free to configure using:
+
+ ./configure `--with-included-zlib=no`
+
+### DEVELOPER RELATED:
+
+ - Added more conditional debug output.
+
+ - Fixed some build issues for Android and Minix.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.9 (23 Sep 2011)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fix a crash bug in checksum scanning when `--inplace` is used.
+
+ - Fix a hang if a hard-linked file cannot be opened by the sender (e.g. if it
+ has no read permission).
+
+ - Fix preservation of a symlink's system xattrs (e.g. selinux) on Linux.
+
+ - Fix a memory leak in the xattr code.
+
+ - Fixed a bug with `--delete-excluded` when a filter merge file has a rule
+ that specifies a receiver-only side restriction.
+
+ - Fix a bug with the modifying of unwritable directories.
+
+ - Fix `--fake-super`'s interaction with `--link-dest` same-file comparisons.
+
+ - Fix the updating of the `curr_dir` buffer to avoid a duplicate slash.
+
+ - Fix the directory permissions on an implied dot-dir when using `--relative`
+ (e.g. /outside/path/././send/path).
+
+ - Fixed some too-long sleeping instances when using `--bwlimit`.
+
+ - Fixed when symlink ownership difference-checking gets compiled into
+ `unchanged_attrs()`.
+
+ - Improved the socket-error reporting when multiple protocols fail.
+
+ - Fixed a case where a socket error could reference just-freed memory.
+
+ - Failing to use a password file that was specified on the command-line is now
+ a fatal error.
+
+ - Fix the non-root updating of directories that don't have the read and/or
+ execute permission.
+
+ - Make daemon-excluded file errors more error-like.
+
+ - Fix a compilation issue on older C compilers (due to a misplaced var
+ declaration).
+
+ - Make configure avoid finding socketpair on Cygwin.
+
+ - Avoid trying to reference `SO_BROADCAST` if the OS doesn't support it.
+
+ - Fix some issues with the post-processing of the manpages.
+
+ - Fixed the user home-dir handling in the lsh script. (See the support dir.)
+
+ - Some minor manpage improvements.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.8 (26 Mar 2011)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed two buffer-overflow issues: one where a directory path that is exactly
+ MAXPATHLEN was not handled correctly, and one handling a `--backup-dir` that
+ is extra extra large.
+
+ - Fixed a data-corruption issue when preserving hard-links without preserving
+ file ownership, and doing deletions either before or during the transfer
+ (CVE-2011-1097). This fixes some assert errors in the hard-linking code, and
+ some potential failed checksums (via `-c`) that should have matched.
+
+ - Fixed a potential crash when an rsync daemon has a filter/exclude list and
+ the transfer is using ACLs or xattrs.
+
+ - Fixed a hang if a really large file is being processed by an rsync that
+ can't handle 64-bit numbers. Rsync will now complain about the file being
+ too big and skip it.
+
+ - For devices and special files, we now avoid gathering useless ACL and/or
+ xattr information for files that aren't being copied. (The un-copied files
+ are still put into the file list, but there's no need to gather data that is
+ not going to be used.) This ensures that if the user uses `--no-D`, that
+ rsync can't possibly complain about being unable to gather extended
+ information from special files that are in the file list (but not in the
+ transfer).
+
+ - Properly handle requesting remote filenames that start with a dash. This
+ avoids a potential error where a filename could be interpreted as a (usually
+ invalid) option.
+
+ - Fixed a bug in the comparing of upper-case letters in file suffixes for
+ `--skip-compress`.
+
+ - If an rsync daemon has a module configured without a path setting, rsync
+ will now disallow access to that module.
+
+ - If the destination arg is an empty string, it will be treated as a reference
+ to the current directory (as 2.x used to do).
+
+ - If rsync was compiled with a newer time-setting function (such as lutimes),
+ rsync will fall-back to an older function (such as utimes) on a system where
+ the newer function is not around. This helps to make the rsync binary more
+ portable in mixed-OS-release situations.
+
+ - Fixed a batch-file writing bug that would not write out the full set of
+ compatibility flags that the transfer was using. This fixes a potential
+ protocol problem for a batch file that contains a sender-side I/O error: it
+ would have been sent in a way that the batch-reader wasn't expecting.
+
+ - Some improvements to the hard-linking code to ensure that device-number
+ hashing is working right, and to supply more information if the hard-link
+ code fails.
+
+ - The `--inplace` code was improved to not search for an impossible checksum
+ position. The quadruple-verbose chunk[N] message will now mention when an
+ inplace chunk was handled by a seek rather than a read+write.
+
+ - Improved ACL mask handling, e.g. for Solaris.
+
+ - Fixed a bug that prevented `--numeric-ids` from disabling the translation of
+ user/group IDs for ACLs.
+
+ - Fixed an issue where an xattr and/or ACL transfer that used an alt-dest
+ option (e.g. `--link-dest`) could output an error trying to itemize the
+ changes against the alt-dest directory's xattr/ACL info but was instead
+ trying to access the not-yet-existing new destination directory.
+
+ - Improved xattr system-error messages to mention the full path to the file.
+
+ - The `--link-dest` checking for identical symlinks now avoids considering
+ attribute differences that cannot be changed on the receiver.
+
+ - Avoid trying to read/write xattrs on certain file types for certain OSes.
+ Improved configure to set `NO_SYMLINK_XATTRS`, `NO_DEVICE_XATTRS`, and/or
+ `NO_SPECIAL_XATTRS` defines in config.h.
+
+ - Improved the unsafe-symlink errors messages.
+
+ - Fixed a bug setting xattrs on new files that aren't user writable.
+
+ - Avoid re-setting xattrs on a hard-linked file w/the same xattrs.
+
+ - Fixed a bug with `--fake-super` when copying files and dirs that aren't user
+ writable.
+
+ - Fixed a bug where a sparse file could have its last sparse block turned into
+ a real block when rsync sets the file size (requires ftruncate).
+
+ - If a temp-file name is too long, rsync now avoids truncating the name in the
+ middle of adjacent high-bit characters. This prevents a potential filename
+ error if the filesystem doesn't allow a name to contain an invalid
+ multi-byte sequence.
+
+ - If a muli-protocol socket connection fails (i.e., when contacting a daemon),
+ we now report all the failures, not just the last one. This avoids losing a
+ relevant error (e.g. an IPv4 connection-refused error) that happened before
+ the final error (e.g. an IPv6 protocol-not-supported error).
+
+ - Generate a transfer error if we try to call chown with a `-1` for a uid or a
+ gid (which is not settable).
+
+ - Fixed the working of `--force` when used with `--one-file-system`.
+
+ - Fix the popt arg parsing so that an option that doesn't take an arg will
+ reject an attempt to supply one (can configure `--with-included-popt` if
+ your system's popt library doesn't yet have this fix).
+
+ - A couple minor option tweaks to the rrsync script, and also some regex
+ changes that make vim highlighting happier. (See the support dir.)
+
+ - Fixed some issues in the mnt-excl script. (See the support dir.)
+
+ - Various manpage improvements.
+
+### ENHANCEMENTS:
+
+ - Added `.hg/` to the default cvs excludes (see `-C` & `--cvs-exclude`).
+
+### DEVELOPER RELATED:
+
+ - Use lchmod() whenever it is available (not just on symlinks).
+
+ - A couple fixes to the `socketpair_tcp()` routine.
+
+ - Updated the helper scripts in the packaging subdirectory.
+
+ - Renamed configure.in to configure.ac.
+
+ - Fixed configure's checking for iconv routines for newer OS X versions.
+
+ - Fixed the testsuite/xattrs.test script on OS X.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.7 (31 Dec 2009)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a bogus free when using `--xattrs` with `--backup`.
+
+ - Avoid an error when `--dry-run` was trying to stat a prior hard-link file
+ that hasn't really been created.
+
+ - Fixed a problem with `--compress` (`-z`) where the receiving side could
+ return the error "`inflate (token) returned -5`".
+
+ - Fixed a bug where `--delete-during` could delete in a directory before it
+ noticed that the sending side sent an I/O error for that directory (both
+ sides of the transfer must be at least 3.0.7).
+
+ - Improved `--skip-compress`'s error handling of bad character-sets and got
+ rid of a lingering debug fprintf().
+
+ - Fixed the daemon's conveyance of `io_error` value from the sender.
+
+ - An rsync daemon use seteuid() (when available) if it used setuid().
+
+ - Get the permissions right on a `--fake-super` transferred directory that
+ needs more owner permissions to emulate root behavior.
+
+ - An absolute-path filter rule (i.e. with a '/' modifier) no longer loses its
+ modifier when sending the filter rules to the remote rsync.
+
+ - Improved the "`--delete does not work without -r or -d`" message.
+
+ - Improved rsync's handling of `--timeout` to avoid a weird timeout case where
+ the sender could timeout even though it has recently written data to the
+ socket (but hasn't read data recently, due to the writing).
+
+ - Some misc manpage improvements.
+
+ - Fixed the chmod-temp-dir testsuite on a system without /var/tmp.
+
+ - Make sure that a timeout specified in the daemon's config is used as a
+ maximum timeout value when the user also specifies a timeout.
+
+ - Improved the error-exit reporting when rsync gets an error trying to cleanup
+ after an error: the initial error is reported.
+
+ - Improved configure's detection of IPv6 for Solaris and Cygwin.
+
+ - The AIX sysacls routines will now return ENOSYS if ENOTSUP is missing.
+
+ - Made our (only used if missing) getaddrinfo() routine use `inet_pton()`
+ (which we also provide) instead of `inet_aton()`.
+
+ - The exit-related debug messages now mention the program's role so it is
+ clear who output what message.
+
+### DEVELOPER RELATED:
+
+ - Got rid of type-punned compiler warnings output by newer gcc versions.
+
+ - The Makefile now ensures that proto.h will be rebuilt if config.h changes.
+
+ - The testsuite no longer uses `id -u`, so it works better on Solaris.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.6 (8 May 2009)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a `--read-batch` hang when rsync is reading a batch file that was
+ created from an incremental-recursion transfer.
+
+ - Fixed the daemon's socket code to handle the simultaneous arrival of
+ multiple connections.
+
+ - Fix `--safe-links`/`--copy-unsafe-links` to properly handle symlinks that
+ have consecutive slashes in the value.
+
+ - Fixed the parsing of an `[IPv6_LITERAL_ADDR]` when a USER@ is prefixed.
+
+ - The sender now skips a (bogus) symlink that has a 0-length value, which
+ avoids a transfer error in the receiver.
+
+ - Fixed a case where the sender could die with a tag-0 error if there was an
+ I/O during the sending of the file list.
+
+ - Fixed the rrsync script to avoid a server-side problem when `-e` is at the
+ start of the short options.
+
+ - Fixed a problem where a vanished directory could turn into an exit code 23
+ instead of the proper exit code 24.
+
+ - Fixed the `--iconv` conversion of symlinks when doing a local copy.
+
+ - Fixed a problem where `--one-file-system` was not stopping deletions on the
+ receiving side when a mount-point directory did not match a directory in the
+ transfer.
+
+ - Fixed the dropping of an ACL mask when no named ACL values were present.
+
+ - Fixed an ACL/xattr corruption issue where the `--backup` option could cause
+ rsync to associate the wrong ACL/xattr information with received files.
+
+ - Fixed the use of `--xattrs` with `--only-write-batch`.
+
+ - Fixed the use of `--dry-run` with `--read-batch`.
+
+ - Fixed configure's erroneous use of target.
+
+ - Fixed configure's `--disable-debug` option.
+
+ - Fixed a run-time issue for systems that can't find `iconv_open()` by adding
+ the `--disable-iconv-open` configure option.
+
+ - Complain and die if the user tries to combine `--remove-source-files` (or
+ the deprecated `--remove-sent-files`) with `--read-batch`.
+
+ - Fixed an failure transferring special files from Solaris to Linux.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.5 (28 Dec 2008)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Initialize xattr data in a couple spots in the hlink code, which avoids a
+ crash when the xattr pointer's memory happens to start out non-zero. Also
+ fixed the itemizing of an alt-dest file's xattrs when hard-linking.
+
+ - Don't send a bogus `-` option to an older server if there were no short
+ options specified.
+
+ - Fixed skipping of unneeded updates in a batch file when incremental
+ recursion is active. Added a test for this. Made batch-mode handle `redo`
+ files properly (and without hanging).
+
+ - Fix the %P logfile escape when the daemon logs from inside a chroot.
+
+ - Fixed the use of `-s` (`--protect-args`) when used with a remote source or
+ destination that had an empty path (e.g. `host:`). Also fixed a problem when
+ `-s` was used when accessing a daemon via a remote-shell.
+
+ - Fixed the use of a dot-dir path (e.g. foo/./bar) inside a `--files-from`
+ file when the root of the transfer isn't the current directory.
+
+ - Fixed a bug with `-K --delete` removing symlinks to directories when
+ incremental recursion is active.
+
+ - Fixed a hard to trigger hang when using `--remove-source-files`.
+
+ - Got rid of an annoying delay when accessing a daemon via a remote-shell.
+
+ - Properly ignore (superfluous) source args on a `--read-batch` command.
+
+ - Improved the manpage's description of the `*` wildcard to remove the
+ confusing `non-empty` qualifier.
+
+ - Fixed reverse lookups in the compatibility-library version of getnameinfo().
+
+ - Fixed a bug when using `--sparse` on a sparse file that has over 2GB of
+ consecutive sparse data.
+
+ - Avoid a hang when using at least 3 `--verbose` options on a transfer with a
+ client sender (which includes local copying).
+
+ - Fixed a problem with `--delete-delay` reporting an error when it was ready
+ to remove a directory that was now gone.
+
+ - Got rid of a bunch of `warn_unused_result` compiler warnings.
+
+ - If an ftruncate() on a received file fails, it now causes a partial-
+ transfer warning.
+
+ - Allow a path with a leading `//` to be preserved (CYGWIN only).
+
+### ENHANCEMENTS:
+
+ - Made the atomic-rsync script able to perform a fully atomic update of the
+ copied hierarchy when the destination is setup using a particular symlink
+ idiom. (See the support dir.)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.4 (6 Sep 2008)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a bug in the hard-linking code where it would sometimes try to
+ allocate 0 bytes of memory (which fails on some OSes, such as AIX).
+
+ - Fixed the hard-linking of files from a device that has a device number of 0
+ (which seems to be a common device number on NetBSD).
+
+ - Fixed the handling of a `--partial-dir` that cannot be created. This
+ particularly impacts the `--delay-updates` option (since the files cannot be
+ delayed without a partial-dir), and was potentially destructive if the
+ `--remove-source-files` was also specified.
+
+ - Fixed a couple issues in the `--fake-super` handling of xattrs when the
+ destination files have root-level attributes (e.g. selinux values) that a
+ non-root copy can't affect.
+
+ - Improved the keep-alive check in the generator to fire consistently in
+ incremental-recursion mode when `--timeout` is enabled.
+
+ - The `--iconv` option now converts the content of a symlink too, instead of
+ leaving it in the wrong character-set (requires 3.0.4 on both sides of the
+ transfer).
+
+ - When using `--iconv`, if a filename fails to convert on the receiving side,
+ this no longer makes deletions in the root-dir of the transfer fail silently
+ (the user now gets a warning about deletions being disabled due to IO error
+ as long as `--ignore-errors` was not specified).
+
+ - When using `--iconv`, if a server-side receiver can't convert a filename,
+ the error message sent back to the client no longer mangles the name with
+ the wrong charset conversion.
+
+ - Fixed a potential alignment issue in the IRIX ACL code when allocating the
+ initial `struct acl` object. Also, cast mallocs to avoid warnings.
+
+ - Changed some errors that were going to stdout to go to stderr.
+
+ - Made `human_num()` and `human_dnum()` able to output a negative number
+ (rather than outputting a cryptic string of punctuation).
+
+### ENHANCEMENTS:
+
+ - Rsync will avoid sending an `-e` option to the server if an older protocol
+ is requested (and thus the option would not be useful). This lets the user
+ specify the `--protocol=29` option to access an overly-restrictive server
+ that is rejecting the protocol-30 use of `-e` to the server.
+
+ - Improved the message output for an `RERR_PARTIAL` exit.
+
+### DEVELOPER RELATED:
+
+ - The Makefile will not halt for just a timestamp change on the Makefile or
+ the configure files, only for actual changes in content.
+
+ - Changed some commands in the testsuite's xattrs.test that called `rsync`
+ instead of `$RSYNC`.
+
+ - Enhanced the release scripts to be able to handle a branch release and to do
+ even more consistency checks on the files.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.3 (29 Jun 2008)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a wildcard matching problem in the daemon when a module has `use
+ chroot` enabled.
+
+ - Fixed a crash bug in the hard-link code.
+
+ - Fixed the sending of xattr directory information when the code finds a
+ `--link-dest` or `--copy-dest` directory with unchanged xattrs -- the
+ destination directory now gets these unchanged xattrs properly applied.
+
+ - Fixed an xattr-sending glitch that could cause an `Internal abbrev` error.
+
+ - Fixed the combination of `--xattrs` and `--backup`.
+
+ - The generator no longer allows a '.' dir to be excluded by a daemon-exclude
+ rule.
+
+ - Fixed deletion handling when copying a single, empty directory (with no
+ files) to a differently named, non-existent directory.
+
+ - Fixed the conversion of spaces into dashes in the %M log escape.
+
+ - Fixed several places in the code that were not returning the right errno
+ when a function failed.
+
+ - Fixed the backing up of a device or special file into a backup dir.
+
+ - Moved the setting of the socket options prior to the connect().
+
+ - If rsync exits in the middle of a `--progress` output, it now outputs a
+ newline to help prevent the progress line from being overwritten.
+
+ - Fixed a problem with how a destination path with a trailing slash or a
+ trailing dot-dir was compared against the daemon excludes.
+
+ - Fixed the sending of large (size > 16GB) files when talking to an older
+ rsync (protocols < 30): we now use a compatible block size limit.
+
+ - If a file's length is so huge that we overflow a checksum buffer count (i.e.
+ several hundred TB), warn the user and avoid sending an invalid checksum
+ struct over the wire.
+
+ - If a source arg is excluded, `--relative` no longer adds the excluded arg's
+ implied dirs to the transfer. This fix also made the exclude check happen in
+ the better place in the sending code.
+
+ - Use the `overflow_exit()` function for overflows, not `out_of_memory()`.
+
+ - Improved the code to better handle a system that has only 32-bit file
+ offsets.
+
+### ENHANCEMENTS:
+
+ - The rsyncd.conf manpage now consistently refers to the parameters in the
+ daemon config file as `parameters`.
+
+ - The description of the `--inplace` option was improved.
+
+### EXTRAS:
+
+ - Added a new script in the support directory, deny-rsync, which allows an
+ admin to (temporarily) replace the rsync command with a script that sends an
+ error message to the remote client via the rsync protocol.
+
+### DEVELOPER RELATED:
+
+ - Fixed a testcase failure if the tests are run as root and made some
+ compatibility improvements.
+
+ - Improved the daemon tests, including checking module comments, the listing
+ of files, and the ensuring that daemon excludes can't affect a dot-dir arg.
+
+ - Improved some build rules for those that build in a separate directory from
+ the source, including better install rules for the manpages, and the fixing
+ of a proto.h-tstamp rule that could make the binaries get rebuild without
+ cause.
+
+ - Improved the testsuite to work around a problem with some utilities (e.g.
+ `cp -p` & `touch -r`) rounding sub-second timestamps.
+
+ - Ensure that the early patches don't cause any generated-file hunks to
+ bleed-over into patches that follow.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.2 (8 Apr 2008)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a potential buffer overflow in the xattr code.
+
+### ENHANCEMENTS:
+
+ - None.
+
+### DEVELOPER RELATED:
+
+ - The RPM spec file was improved to install more useful files.
+
+ - A few developer-oriented scripts were moved from the support dir to the
+ packaging dir.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.1 (3 Apr 2008)
+
+## Changes in this version:
+
+### NOTABLE CHANGES IN BEHAVIOR:
+
+ - Added the 'c'-flag to the itemizing of non-regular files so that the
+ itemized output doesn't get hidden if there were no attribute changes, and
+ also so that the itemizing of a `--copy-links` run will distinguish between
+ copying an identical non-regular file and the creation of a revised version
+ with a new value (e.g. a changed symlink referent, a new device number,
+ etc.).
+
+### BUG FIXES:
+
+ - Fixed a crash bug when a single-use rsync daemon (via remote shell) was run
+ without specifying a `--config=FILE` option.
+
+ - Fixed a crash when backing up a directory that has a default ACL.
+
+ - Fixed a bug in the handling of xattr values that could cause rsync to not
+ think that a file's extended attributes are up-to-date.
+
+ - Fixed the working of `--fake-super` with `--link-dest` and `--xattrs`.
+
+ - Fixed a hang when combining `--dry-run` with `--remove-source-files`.
+
+ - Fixed a bug with `--iconv`'s handling of files that cannot be converted: a
+ failed name can no longer cause a transfer failure.
+
+ - Fixed the building of the rounding.h file on systems that need custom
+ CPPFLAGS to be used. Also improved the error reporting if the building of
+ rounding.h fails.
+
+ - Fixed the use of the `--protect-args` (`-s`) option when talking to a
+ daemon.
+
+ - Fixed the `--ignore-existing` option's protection of files on the receiver
+ that are non-regular files on the sender (e.g. if a symlink or a dir on the
+ sender is trying to replace a file on the receiver). The reverse protection
+ (protecting a dir/symlink/device from being replaced by a file) was already
+ working.
+
+ - Fixed an assert failure if `--hard-links` is combined with an option that
+ can skip a file in a set of hard-linked files (i.e. `--ignore-existing`,
+ `--append`, etc.), without skipping all the files in the set.
+
+ - Avoid setting the modify time on a directory that already has the right
+ modify time set. This avoids tweaking the dir's ctime.
+
+ - Improved the daemon-exclude handling to do a better job of applying the
+ exclude rules to path entries. It also sends the user an error just as if
+ the files were actually missing (instead of silently ignoring the user's
+ args), and avoids sending the user the filter-action messages for these
+ non-user-initiated rules.
+
+ - Fixed some glitches with the dry-run code's missing-directory handling,
+ including a problem when combined with `--fuzzy`.
+
+ - Fixed some glitches with the skipped-directory handling.
+
+ - Fixed the 'T'-flag itemizing of symlinks when `--time` isn't preserved.
+
+ - Fixed a glitch in the itemizing of permissions with the `-E` option.
+
+ - The `--append` option's restricting of transfers to those that add data no
+ longer prevents the updating of non-content changes to otherwise up-to-date
+ files (i.e. those with the same content but differing permissions,
+ ownership, xattrs, etc.).
+
+ - Don't allow `--fake-super` to be specified with `-XX` (double `--xattrs`)
+ because the options conflict. If a daemon has `fake super` enabled, it
+ automatically downgrades a `-XX` request to `-X`.
+
+ - Fixed a couple bugs in the parsing of daemon-config excludes that could make
+ a floating exclude rule get treated as matching an absolute path.
+
+ - A daemon doesn't try to auto-refuse the `iconv` option if iconv-support
+ wasn't compiled in to the daemon (avoiding a warning in the logs).
+
+ - Fixed the inclusion of per-dir merge files from implied dirs.
+
+ - Fixed the rrsync script to work with the latest options that rsync sends,
+ including its flag-specifying use of `-e` to the server. (See the support
+ dir.)
+
+### ENHANCEMENTS:
+
+ - Added the `--old-dirs` (`--old-d`) option to make it easier for a user to
+ ask for file-listings with older rsync versions (this is easier than having
+ to type `-r --exclude='/*/*'` manually).
+
+ - When getting an error while asking an older rsync daemon for a file listing,
+ rsync will try to notice if the error is a rejection of the `--dirs` (`-d`)
+ option and let the user know how to work around the issue.
+
+ - Added a few more `--no-OPTION` overrides.
+
+ - Improved the documentation of the `--append` option.
+
+ - Improved the documentation of the filter/exclude/include daemon parameters.
+
+### INTERNAL:
+
+ - Fixed a couple minor bugs in the included popt library (ones which I sent to
+ the official popt project for inclusion in the 1.14 release).
+
+ - Fixed a stat() call that should have been `do_stat()` so that the proper
+ normal/64-bit stat() function gets called. (Was in an area that should not
+ have caused problems, though.)
+
+ - Changed the file-glob code to do a directory scan without using the `glob`
+ and `glob.h`. This lets us do the globbing with less memory churn, and also
+ avoid adding daemon-excluded items to the returned args.
+
+### DEVELOPER RELATED:
+
+ - The configure script tries to get the user's compiler to not warn about
+ unused function parameters if the build is not including one or more of the
+ ACL/xattrs/iconv features.
+
+ - The configure script now has better checks for figuring out if the included
+ popt code should be used or not.
+
+ - Fixed two testsuite glitches: avoid a failure if someone's `cd` command
+ outputs the current directory when cd-ing to a relative path, and made the
+ itemized test query how rsync was built to determine if it should expect
+ hard-linked symlinks or not.
+
+ - Updated the testsuite to verify that various bug fixes remain fixed.
+
+ - The RPM spec file was updated to have: (1) comments for how to use the
+ rsync-patch tar file, and (2) an /etc/xinetd.d/rsync file.
+
+ - Updated the build scripts to work with a revised FTP directory structure.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 3.0.0 (1 Mar 2008)
+
+## Changes in this version:
+
+### PROTOCOL NUMBER:
+
+ - The protocol number was changed to 30.
+
+### NOTABLE CHANGES IN BEHAVIOR:
+
+ - The handling of implied directories when using `--relative` has changed to
+ send them as directories (e.g. no implied dir is ever sent as a symlink).
+ This avoids unexpected behavior and should not adversely affect most people.
+ If you're one of those rare individuals who relied upon having an implied
+ dir be duplicated as a symlink, you should specify the transfer of the
+ symlink and the transfer of the referent directory as separate args. (See
+ also `--keep-dirlinks` and `--no-implied-dirs`.) Also, exclude rules no
+ longer have a partial effect on implied dirs.
+
+ - Requesting a remote file-listing without specifying `-r` (`--recursive`) now
+ sends the `-d` (`--dirs`) option to the remote rsync rather than sending
+ `-r` along with an extra exclude of `/*/*`. If the remote rsync does not
+ understand the `-d` option (i.e. it is 2.6.3 or older), you will need to
+ either turn off `-d` (`--no-d`), or specify `-r --exclude='/*/*'` manually.
+
+ - In `--dry-run` mode, the last line of the verbose summary text is output
+ with a "(DRY RUN)" suffix to help remind you that no updates were made.
+ Similarly, `--only-write-batch` outputs `(BATCH ONLY)`.
+
+ - A writable rsync daemon with `use chroot` disabled now defaults to a
+ symlink-munging behavior designed to make symlinks safer while also allowing
+ absolute symlinks to be stored and retrieved. This also has the effect of
+ making symlinks unusable while they're in the daemon's hierarchy. See the
+ daemon's `munge symlinks` parameter for details.
+
+ - Starting up an extra copy of an rsync daemon will not clobber the pidfile
+ for the running daemon -- if the pidfile exists, the new daemon will exit
+ with an error. This means that your wrapper script that starts the rsync
+ daemon should be made to handle lock-breaking (if you want any automatic
+ breaking of locks to be done).
+
+### BUG FIXES:
+
+ - A daemon with `use chroot = no` and excluded items listed in the daemon
+ config file now properly checks an absolute-path arg specified for these
+ options: `--compare-dest`, `--link-dest`, `--copy-dest`, `--partial-dir`,
+ `--backup-dir`, `--temp-dir`, and `--files-from`.
+
+ - A daemon can now be told to disable all user- and group-name translation on
+ a per-module basis. This avoids a potential problem with a writable daemon
+ module that has `use chroot` enabled -- if precautions weren't taken, a user
+ could try to add a missing library and get rsync to use it. This makes rsync
+ safer by default, and more configurable when id-translation is not desired.
+ See the daemon's `numeric ids` parameter for full details.
+
+ - A chroot daemon can now indicate which part of its path should affect the
+ chroot call, and which part should become an inside-chroot path for the
+ module. This allows you to have outside-the-transfer paths (such as for
+ libraries) even when you enable chroot protection. The idiom used in the
+ rsyncd.conf file is: `path = /chroot/dirs/./dirs/inside`
+
+ - If a file's data arrived successfully on the receiving side but the rename
+ of the temporary file to the destination file failed AND the
+ `--remove-source-files` (or the deprecated `--remove-sent-files`) option was
+ specified, rsync no longer erroneously removes the associated source file.
+
+ - Fixed the output of `-ii` when combined with one of the `--*-dest` options:
+ it now itemizes all the items, not just the changed ones.
+
+ - Made the output of all file types consistent when using a `--*-dest` option.
+ Prior versions would output too many creation events for matching items.
+
+ - The code that waits for a child pid now handles being interrupted by a
+ signal. This fixes a problem with the pre-xfer exec function not being able
+ to get the exit status from the script.
+
+ - A negated filter rule (i.e. with a '!' modifier) no longer loses the
+ negation when sending the filter rules to the remote rsync.
+
+ - Fixed a problem with the `--out-format` (aka `--log-format`) option %f: it
+ no longer outputs superfluous directory info for a non-daemon rsync.
+
+ - Fixed a problem with `-vv` (double `--verbose`) and `--stats` when `pushing`
+ files (which includes local copies). Version 2.6.9 would complete the copy,
+ but exit with an error when the receiver output its memory stats.
+
+ - If `--password-file` is used on a non-daemon transfer, rsync now complains
+ and exits. This should help users figure out that they can't use this option
+ to control a remote shell's password prompt.
+
+ - Make sure that directory permissions of a newly-created destination
+ directory are handled right when `--perms` is left off.
+
+ - The itemized output of a newly-created destination directory is now output
+ as a creation event, not a change event.
+
+ - Improved `--hard-link` so that more corner cases are handled correctly when
+ combined with options such as `--link-dest` and/or `--ignore-existing`.
+
+ - The `--append` option no longer updates a file that has the same size.
+
+ - Fixed a bug when combining `--backup` and `--backup-dir` with `--inplace`:
+ any missing backup directories are now created.
+
+ - Fixed a bug when using `--backup` and `--inplace` with `--whole-file` or
+ `--read-batch`: backup files are actually created now.
+
+ - The daemon pidfile is checked and created sooner in the startup sequence.
+
+ - If a daemon module's `path` value is not an absolute pathname, the code now
+ makes it absolute internally (making it work properly).
+
+ - Ensure that a temporary file always has owner-write permission while we are
+ writing to it. This avoids problems with some network filesystems when
+ transferring read-only files.
+
+ - Any errors output about password-file reading no longer cause an error at
+ the end of the run about a partial transfer.
+
+ - The `--read-batch` option for protocol 30 now ensures that several more
+ options are set correctly for the current batch file: `--iconv`, `--acls`,
+ `--xattrs`, `--inplace`, `--append`, and `--append-verify`.
+
+ - Using `--only-write-batch` to a daemon receiver now works properly (older
+ versions would update some files while writing the batch).
+
+ - Avoid outputting a "file has vanished" message when the file is a broken
+ symlink and `--copy-unsafe-links` or `--copy-dirlinks` is used (the code
+ already handled this for `--copy-links`).
+
+ - Fixed the combination of `--only-write-batch` and `--dry-run`.
+
+ - Fixed rsync's ability to remove files that are not writable by the file's
+ owner when rsync is running as the same user.
+
+ - When transferring large files, the sender's hashtable of checksums is kept
+ at a more reasonable state of fullness (no more than 80% full) so that the
+ scanning of the hashtable will not bog down as the number of blocks
+ increases.
+
+### ENHANCEMENTS:
+
+ - A new incremental-recursion algorithm is now used when rsync is talking to
+ another 3.x version. This starts the transfer going more quickly (before all
+ the files have been found), and requires much less memory. See the
+ `--recursive` option in the manpage for some restrictions.
+
+ - Lowered memory use in the non-incremental-recursion algorithm for typical
+ option values (usually saving from 21-29 bytes per file).
+
+ - The default `--delete` algorithm is now `--delete-during` when talking to a
+ 3.x rsync. This is a faster scan than using `--delete-before` (which is the
+ default when talking to older rsync versions), and is compatible with the
+ new incremental recursion mode.
+
+ - Rsync now allows multiple remote-source args to be specified rather than
+ having to rely on a special space-splitting side-effect of the remote-
+ shell. Additional remote args must specify the same host or an empty one
+ (e.g. empty: `:file1` or `::module/file2`). For example, this means that
+ local use of brace expansion now works: `rsync -av host:dir/{f1,f2} .`
+
+ - Added the `--protect-args` (`-s`) option, that tells rsync to send most of
+ the command-line args at the start of the transfer rather than as args to
+ the remote-shell command. This protects them from space-splitting, and only
+ interprets basic wildcard special shell characters (`*?[`).
+
+ - Added the `--delete-delay` option, which is a more efficient way to delete
+ files at the end of the transfer without needing a separate delete pass.
+
+ - Added the `--acls` (`-A`) option to preserve Access Control Lists. This is
+ an improved version of the prior patch that was available, and it even
+ supports OS X ACLs. If you need to have backward compatibility with old,
+ ACL-patched versions of rsync, apply the acls.diff file from the patches
+ dir.
+
+ - Added the `--xattrs` (`-X`) option to preserve extended attributes. This is
+ an improved version of the prior patch that was available, and it even
+ supports OS X xattrs (which includes their resource fork data). If you need
+ to have backward compatibility with old, xattr-patched versions of rsync,
+ apply the xattrs.diff file from the patches dir.
+
+ - Added the `--fake-super` option that allows a non-super user to preserve all
+ attributes of a file by using a special extended-attribute idiom. It even
+ supports the storing of foreign ACL data on your backup server. There is
+ also an analogous `fake super` parameter for an rsync daemon.
+
+ - Added the `--iconv` option, which allows rsync to convert filenames from one
+ character-set to another during the transfer. The default is to make this
+ feature available as long as your system has `iconv_open()`. If compilation
+ fails, specify `--disable-iconv` to configure, and then rebuild. If you want
+ rsync to perform character-set conversions by default, you can specify
+ `--enable-iconv=CONVERT_STRING` with the default value for the `--iconv`
+ option that you wish to use. For example, `--enable-iconv=.` is a good
+ choice. See the rsync manpage for an explanation of the `--iconv` option's
+ settings.
+
+ - A new daemon config parameter, `charset`, lets you control the character-
+ set that is used during an `--iconv` transfer to/from a daemon module. You
+ can also set your daemon to refuse `no-iconv` if you want to force the
+ client to use an `--iconv` transfer (requiring an rsync 3.x client).
+
+ - Added the `--skip-compress=LIST` option to override the default list of file
+ suffixes that will not be compressed when using `--compress` (`-z`).
+
+ - The daemon's default for `dont compress` was extended to include: `*.7z`
+ `*.mp[34]` `*.mov` `*.avi` `*.ogg` `*.jpg` `*.jpeg` and the name-matching routine was also
+ optimized to run more quickly.
+
+ - The `--max-delete` option now outputs a warning if it skipped any file
+ deletions, including a count of how many deletions were skipped. (Older
+ versions just silently stopped deleting things.)
+
+ - You may specify `--max-delete=0` to a 3.0.0 client to request that it warn
+ about extraneous files without deleting anything. If you're not sure what
+ version the client is, you can use the less-obvious `--max-delete=-1`, as
+ both old and new versions will treat that as the same request (though older
+ versions don't warn).
+
+ - The `--hard-link` option now uses less memory on both the sending and
+ receiving side for all protocol versions. For protocol 30, the use of a
+ hashtable on the sending side allows us to more efficiently convey to the
+ receiver what files are linked together. This reduces the amount of data
+ sent over the socket by a considerable margin (rather than adding more
+ data), and limits the in-memory storage of the device+inode information to
+ just the sending side for the new protocol 30, or to the receiving side when
+ speaking an older protocol (note that older rsync versions kept the
+ device+inode information on both sides).
+
+ - The filter rules now support a perishable (`p`) modifier that marks rules
+ that should not have an effect in a directory that is being deleted. e.g.
+ `-f '-p .svn/'` would only affect `live` .svn directories.
+
+ - Rsync checks all the alternate-destination args for validity (e.g.
+ `--link-dest`). This lets the user know when they specified a directory that
+ does not exist.
+
+ - If we get an ENOSYS error setting the time on a symlink, we don't complain
+ about it anymore (for those systems that even support the setting of the
+ modify-time on a symlink).
+
+ - Protocol 30 now uses MD5 checksums instead of MD4.
+
+ - Changed the `--append` option to not checksum the existing data in the
+ destination file, which speeds up file appending.
+
+ - Added the `--append-verify` option, which works like the older `--append`
+ option (verifying the existing data in the destination file). For
+ compatibility with older rsync versions, any use of `--append` that is
+ talking protocol 29 or older will revert to the `--append-verify` method.
+
+ - Added the `--contimeout=SECONDS` option that lets the user specify a
+ connection timeout for rsync daemon access.
+
+ - Documented and extended the support for the `RSYNC_CONNECT_PROG` variable
+ that can be used to enhance the client side of a daemon connection.
+
+ - Improved the dashes and double-quotes in the nroff manpage output.
+
+ - Rsync now supports a lot more `--no-OPTION` override options.
+
+### INTERNAL:
+
+ - The file-list sorting algorithm now uses a sort that keeps any same-named
+ items in the same order as they were specified. This allows rsync to always
+ ensure that the first of the duplicates is the one that will be included in
+ the copy. The new sort is also faster than the glibc version of qsort() and
+ mergesort().
+
+ - Rsync now supports the transfer of 64-bit timestamps (`time_t` values).
+
+ - Made the file-deletion code use a little less stack when recursing through a
+ directory hierarchy of extraneous files.
+
+ - Fixed a build problem with older (2.x) versions of gcc.
+
+ - Added some isType() functions that make dealing with signed characters
+ easier without forcing variables via casts.
+
+ - Changed strcat/strcpy/sprintf function calls to use safer versions.
+
+ - Upgraded the included popt version to 1.10.2 and improved its use of
+ string-handling functions.
+
+ - Added missing prototypes for compatibility functions from the lib dir.
+
+ - Configure determines if iconv() has a const arg, allowing us to avoid a
+ compiler warning.
+
+ - Made the sending of some numbers more efficient for protocol 30.
+
+ - Make sure that a daemon process doesn't mind if the client was weird and
+ omitted the `--server` option.
+
+ - There are more internal logging categories available in protocol 30 than the
+ age-old FINFO and FERROR, including `FERROR_XFER` and FWARN. These new
+ categories allow some errors and warnings to go to stderr without causing an
+ erroneous end-of-run warning about some files not being able to be
+ transferred.
+
+ - Improved the use of `const` on pointers.
+
+ - Improved J.W.'s `pool_alloc` routines to add a way of incrementally freeing
+ older sections of a pool's memory.
+
+ - The getaddrinfo.c compatibility code in the `lib` dir was replaced with some
+ new code (derived from samba, derived from PostgreSQL) that has a better
+ license than the old code.
+
+### DEVELOPER RELATED:
+
+ - Rsync is now licensed under the GPLv3 or later.
+
+ - Rsync is now being maintained in a `git` repository instead of CVS (though
+ the old CVS repository still exists for historical access). Several
+ maintenance scripts were updated to work with git.
+
+ - Generated files are no longer committed into the source repository. The
+ autoconf and autoheader commands are now automatically run during the normal
+ use of `configure` and `make`. The latest dev versions of all generated
+ files can also be copied from the samba.org web site (see the prepare-source
+ script's fetch option).
+
+ - The `patches` directory of diff files is now built from branches in the
+ rsync git repository (branch patch/FOO creates file patches/FOO.diff). This
+ directory is now distributed in a separate separate tar file named
+ rsync-patches-VERSION.tar.gz instead of the main rsync-VERSION.tar.gz.
+
+ - The proto.h file is now built using a simple perl script rather than a
+ complex awk script, which proved to be more widely compatible.
+
+ - When running the tests, we now put our per-test temp dirs into a sub-
+ directory named testtmp (which is created, if missing). This allows someone
+ to symlink the testtmp directory to another filesystem (which is useful if
+ the build dir's filesystem does not support ACLs and xattrs, but another
+ filesystem does).
+
+ - Rsync now has a way of handling protocol-version changes during the
+ development of a new protocol version. This causes any out-of-sync versions
+ to speak an older protocol rather than fail in a cryptic manner. This
+ addition makes it safer to deploy a pre-release version that may interact
+ with the public. This new exchange of sub-version info does not interfere
+ with the `{MIN,MAX}_PROTOCOL_VERSION` checking algorithm (which does not
+ have enough range to allow the main protocol number to be incremented for
+ every minor tweak in that happens during development).
+
+ - The csprotocol.txt file was updated to mention the daemon protocol change in
+ the 3.0.0 release.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.9 (6 Nov 2006)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - If rsync is interrupted via a handled signal (such as SIGINT), it will once
+ again clean-up its temp file from the destination dir.
+
+ - Fixed an overzealous sanitizing bug in the handling of the `--link-dest`,
+ `--copy-dest`, and `--compare-dest` options to a daemon without chroot: if
+ the copy's destination dir is deeper than the top of the module's path,
+ these options now accept a safe number of parent-dir (../) references (since
+ these options are relative to the destination dir). The old code incorrectly
+ chopped off all `../` prefixes for these options, no matter how deep the
+ destination directory was in the module's hierarchy.
+
+ - Fixed a bug where a deferred info/error/log message could get sent directly
+ to the sender instead of being handled by rwrite() in the generator. This
+ fixes an `unexpected tag 3` fatal error, and should also fix a potential
+ problem where a deferred info/error message from the receiver might bypass
+ the log file and get sent only to the client process. (These problems could
+ only affect an rsync daemon that was receiving files.)
+
+ - Fixed a bug when `--inplace` was combined with a `--*-dest` option and we
+ update a file's data using an alternate basis file. The code now notices
+ that it needs to copy the matching data from the basis file instead of
+ (wrongly) assuming that it was already present in the file.
+
+ - Fixed a bug where using `--dry-run` with a `--*-dest` option with a path
+ relative to a directory that does not yet exist: the affected option gets
+ its proper path value so that the output of the dry-run is right.
+
+ - Fixed a bug in the %f logfile escape when receiving files: the destination
+ path is now included in the output (e.g. you can now tell when a user
+ specifies a subdir inside a module).
+
+ - If the receiving side fails to create a directory, it will now skip trying
+ to update everything that is inside that directory.
+
+ - If `--link-dest` is specified with `--checksum` but without `--times`, rsync
+ will now allow a hard-link to be created to a matching link-dest file even
+ when the file's modify-time doesn't match the server's file.
+
+ - The daemon now calls more timezone-using functions prior to doing a chroot.
+ This should help some C libraries to generate proper timestamps from inside
+ a chrooted daemon (and to not try to access /etc/timezone over and over
+ again).
+
+ - Fixed a bug in the handling of an absolute `--partial-dir=ABS_PATH` option:
+ it now deletes an alternate basis file from the partial-dir that was used to
+ successfully update a destination file.
+
+ - Fixed a bug in the handling of `--delete-excluded` when using a per-dir
+ merge file: the merge file is now honored on the receiving side, and only
+ its unqualified include/exclude commands are ignored (just as is done for
+ global include/excludes).
+
+ - Fixed a recent bug where `--delete` was not working when transferring from
+ the root (/) of the filesystem with `--relative` enabled.
+
+ - Fixed a recent bug where an `--exclude='*'` could affect the root (/) of the
+ filesystem with `--relative` enabled.
+
+ - When `--inplace` creates a file, it is now created with owner read/write
+ permissions (0600) instead of no permissions at all. This avoids a problem
+ continuing a transfer that was interrupted (since `--inplace` will not
+ update a file that has no write permissions).
+
+ - If either `--remove-source-files` or `--remove-sent-files` is enabled and we
+ are unable to remove the source file, rsync now outputs an error.
+
+ - Fixed a bug in the daemon's `incoming chmod` rule: newly-created directories
+ no longer get the 'F' (file) rules applied to them.
+
+ - Fixed an infinite loop bug when a filter rule was rejected due to being
+ overly long.
+
+ - When the server receives a `--partial-dir` option from the client, it no
+ longer runs the client-side code that adds an assumed filter rule (since the
+ client will be sending us the rules in the usual manner, and they may have
+ chosen to override the auto-added rule).
+
+### ENHANCEMENTS:
+
+ - Added the `--log-file=FILE` and `--log-file-format=FORMAT` options. These
+ can be used to tell any rsync to output what it is doing to a log file.
+ They work with a client rsync, a non-daemon server rsync (see the manpage
+ for instructions), and also allows the overriding of rsyncd.conf settings
+ when starting a daemon.
+
+ - The `--log-format` option was renamed to be `--out-format` to avoid
+ confusing it with affecting the log-file output. (The old option remains as
+ an alias for the new to preserve backward compatibility.)
+
+ - Made `log file` and `syslog facility` settable on a per-module basis in the
+ daemon's config file.
+
+ - Added the `--remove-source-files` option as a replacement for the (now
+ deprecated) `--remove-sent-files` option. This new option removes all
+ non-dirs from the source directories, even if the file was already
+ up-to-date. This fixes a problem where interrupting an rsync that was using
+ `--remove-sent-files` and restarting it could leave behind a file that the
+ earlier rsync synchronized, but didn't get to remove. (The deprecated
+ `--remove-sent-files` is still understood for now, and still behaves in the
+ same way as before.)
+
+ - Added the option `--no-motd` to suppress the message-of-the-day output from
+ a daemon when doing a copy. (See the manpage for a caveat.)
+
+ - Added a new environment variable to the pre-/post-xfer exec commands (in the
+ daemon's config file): `RSYNC_PID`. This value will be the same in both the
+ pre- and post-xfer commands, so it can be used as a unique ID if the
+ pre-xfer command wants to cache some arg/request info for the post-xfer
+ command.
+
+### INTERNAL:
+
+ - Did a code audit using IBM's code-checker program and made several changes,
+ including: replacing most of the strcpy() and sprintf() calls with
+ strlcpy(), snprintf(), and memcpy(), adding a 0-value to an enum that had
+ been intermingling a literal 0 with the defined enum values, silencing some
+ uninitialized memory checks, marking some functions with a `noreturn`
+ attribute, and changing an `if` that could never succeed on some platforms
+ into a pre-processor directive that conditionally compiles the code.
+
+ - Fixed a potential bug in `f_name_cmp()` when both the args are a top-level
+ `.` dir (which doesn't happen in normal operations).
+
+ - Changed `exit_cleanup()` so that it can never return instead of exit. The
+ old code might return if it found the `exit_cleanup()` function was being
+ called recursively. The new code is segmented so that any recursive calls
+ move on to the next step of the exit-processing.
+
+ - The macro WIFEXITED(stat) will now be defined if the OS didn't already
+ define it.
+
+### DEVELOPER RELATED:
+
+ - The acls.diff and xattrs.diff patches have received a bunch of work to make
+ them much closer to being acceptable in the main distribution. The xattrs
+ patch also has some preliminary Mac OS X and FreeBSD compatibility code that
+ various system types to exchange extended file-attributes.
+
+ - A new diff in the patches dir, fake-root.diff, allows rsync to maintain a
+ backup hierarchy with full owner, group, and device info without actually
+ running as root. It does this using a special extended attribute, so it
+ depends on xattrs.diff (which depends on acls.diff).
+
+ - The rsync.yo and rsyncd.conf.yo files have been updated to work better with
+ the latest yodl 2.x releases.
+
+ - Updated config.guess and config.sub to their 2006-07-02 versions.
+
+ - Updated various files to include the latest FSF address and to have
+ consistent opening comments.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.8 (22 Apr 2006)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a bug in the exclude code where an anchored exclude without any
+ wildcards fails to match an absolute source arg, but only when `--relative`
+ is in effect.
+
+ - Improved the I/O code for the generator to fix a potential hang when the
+ receiver gets an EOF on the socket but the generator's select() call never
+ indicates that the socket is writable for it to be notified about the EOF.
+ (This can happen when using stunnel).
+
+ - Fixed a problem with the file-reading code where a failed read (such as that
+ caused by a bad sector) would not advance the file's read-position beyond
+ the failed read's data.
+
+ - Fixed a logging bug where the `log file` directive was not being honored in
+ a single-use daemon (one spawned by a remote-shell connection or by init).
+
+ - If rsync cannot honor the `--delete` option, we output an error and exit
+ instead of silently ignoring the option.
+
+ - Fixed a bug in the `--link-dest` code that prevented special files (such as
+ fifos) from being linked.
+
+ - The ability to hard-link symlinks and special files is now determined at
+ configure time instead of at runtime. This fixes a bug with `--link-dest`
+ creating a hard-link to a symlink's referent on a BSD system.
+
+### ENHANCEMENTS:
+
+ - In daemon mode, if rsync fails to bind to the requested port, the error(s)
+ returned by socket() and/or bind() are now logged.
+
+ - When we output a fatal error, we now output the version of rsync in the
+ message.
+
+ - Improved the documentation for the `--owner` and `--group` options.
+
+ - The rsyncstats script in `support` has an improved line-parsing regex that
+ is easier to read and also makes it to parse syslog-generated lines.
+
+ - A new script in `support`: file-attr-restore, can be used to restore the
+ attributes of a file-set (the permissions, ownership, and group info) taken
+ from the cached output of a `find ARG... -ls` command.
+
+### DEVELOPER RELATED:
+
+ - Removed the unused function `write_int_named()`, the unused variable
+ `io_read_phase`, and the rarely used variable `io_write_phase`. This also
+ elides the confusing 'phase "unknown"' part of one error message.
+
+ - Removed two unused configure checks and two related (also unused)
+ compatibility functions.
+
+ - The xattrs.diff patch received a security fix that prevents a potential
+ buffer overflow in the `receive_xattr()` code.
+
+ - The acls.diff patch has been improved quite a bit, with more to come.
+
+ - A new patch was added: log-file.diff. This contains an early version of a
+ future option, `--log-file=FILE`, that will allow any rsync to log its
+ actions to a file (something that only a daemon supports at present).
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.7 (11 Mar 2006)
+
+## Changes in this version:
+
+### OUTPUT CHANGES:
+
+ - The letter 'D' in the itemized output was being used for both devices
+ (character or block) as well as other special files (such as fifos and named
+ sockets). This has changed to separate non-device special files under the
+ 'S' designation (e.g. `cS+++++++ path/fifo`). See also the `--specials`
+ option, below.
+
+ - The way rsync escapes unreadable characters has changed. First, rsync now
+ has support for recognizing valid multi-byte character sequences in your
+ current locale, allowing it to escape fewer characters than before for a
+ locale such as UTF-8. Second, it now uses an escape idiom of `\#123`, which
+ is the literal string `\#` followed by exactly 3 octal digits. Rsync no
+ longer doubles a backslash character in a filename (e.g. it used to output
+ `foo\\bar` when copying `foo\bar`) -- now it only escapes a backslash that
+ is followed by a hash-sign and 3 digits (0-9) (e.g. it will output
+ `foo\#134#789` when copying `foo\#789`). See also the `--8-bit-output`
+ (`-8`) option, mentioned below.
+
+ Script writers: the local rsync is the one that outputs escaped names, so if
+ you need to support unescaping of filenames for older rsyncs, I'd suggest
+ that you parse the output of `rsync --version` and only use the old
+ unescaping rules for 2.6.5 and 2.6.6.
+
+### BUG FIXES:
+
+ - Fixed a really old bug that caused `--checksum` (`-c`) to checksum all the
+ files encountered during the delete scan (ouch).
+
+ - Fixed a potential hang in a remote generator: when the receiver gets a
+ read-error on the socket, it now signals the generator about this so that
+ the generator does not try to send any of the terminating error messages to
+ the client (avoiding a potential hang in some setups).
+
+ - Made hard-links work with symlinks and devices again.
+
+ - If the sender gets an early EOF reading a source file, we propagate this
+ error to the receiver so that it can discard the file and try requesting it
+ again (which is the existing behavior for other kinds of read errors).
+
+ - If a device-file/special-file changes permissions, rsync now updates the
+ permissions without recreating the file.
+
+ - If the user specifies a remote-host for both the source and destination, we
+ now output a syntax error rather than trying to open the destination
+ hostspec as a filename.
+
+ - When `--inplace` creates a new destination file, rsync now creates it with
+ permissions 0600 instead of 0000 -- this makes restarting possible when the
+ transfer gets interrupted in the middle of sending a new file.
+
+ - Reject the combination of `--inplace` and `--sparse` since the sparse-output
+ algorithm doesn't work when overwriting existing data.
+
+ - Fixed the directory name in the error that is output when `pop_dir()` fails.
+
+ - Really fixed the parsing of a `!` entry in .cvsignore files this time.
+
+ - If the generator gets a stat() error on a file, output it (this used to
+ require at least `-vv` for the error to be seen).
+
+ - If waitpid() fails or the child rsync didn't exit cleanly, we now handle the
+ exit status properly and generate a better error.
+
+ - Fixed some glitches in the double-verbose output when using `--copy-dest`,
+ `--link-dest`, or `--compare-dest`. Also improved how the verbose output
+ handles hard-links (within the transfer) that had an up-to-date alternate
+ `dest` file, and copied files (via `--copy-dest`).
+
+ - Fixed the matching of the dont-compress items (e.g. `*.gz`) against files
+ that have a path component containing a slash.
+
+ - If the code reading a filter/exclude file gets an EINTR error, rsync now
+ clears the error flag on the file handle so it can keep on reading.
+
+ - If `--relative` is active, the sending side cleans up trailing `/` or `/.`
+ suffixes to avoid triggering a bug in older rsync versions. Also, we now
+ reject a `..` dir if it would be sent as a relative dir.
+
+ - If a non-directory is in the way of a directory and rsync is run with
+ `--dry-run` and `--delete`, rsync no longer complains about not being able
+ to opendir() the not-yet present directory.
+
+ - When `--list-only` is used and a non-existent local destination dir was also
+ specified as a destination, rsync no longer generates a warning about being
+ unable to create the missing directory.
+
+ - Fixed some problems with `--relative --no-implied-dirs` when the destination
+ directory did not yet exist: we can now create a symlink or device when it
+ is the first thing in the missing dir, and `--fuzzy` no longer complains
+ about being unable to open the missing dir.
+
+ - Fixed a bug where the `--copy-links` option would not affect implied
+ directories without `--copy-unsafe-links` (see `--relative`).
+
+ - Got rid of the need for `--force` to be used in some circumstances with
+ `--delete-after` (making it consistent with
+ `--delete-before`/`--delete-during`).
+
+ - Rsync now ignores the SIGXFSZ signal, just in case your OS sends this when a
+ file is too large (rsync handles the write error).
+
+ - Fixed a bug in the Proxy-Authorization header's base64-encoded value: it was
+ not properly padded with trailing '=' chars. This only affects a user that
+ need to use a password-authenticated proxy for an outgoing daemon-rsync
+ connection.
+
+ - If we're transferring an empty directory to a new name, rsync no longer
+ forces `S_IWUSR` if it wasn't already set, nor does it accidentally leave it
+ set.
+
+ - Fixed a bug in the debug output (`-vvvvv`) that could mention the wrong
+ checksum for the current file offset.
+
+ - Rsync no longer allows a single directory to be copied over a non-directory
+ destination arg.
+
+### ENHANCEMENTS:
+
+ - Added the `--append` option that makes rsync append data onto files that are
+ longer on the source than the destination (this includes new files).
+
+ - Added the `--min-size=SIZE` option to exclude small files from the transfer.
+
+ - Added the `--compress-level` option to allow you to set how aggressive
+ rsync's compression should be (this option implies `--compress`).
+
+ - Enhanced the parsing of the SIZE value for `--min-size` and `--max-size` to
+ allow easy entry of multiples of 1000 (instead of just multiples of 1024)
+ and off-by-one values too (e.g. `--max-size=8mb-1`).
+
+ - Added the `--8-bit-output` (`-8`) option, which tells rsync to avoid
+ escaping high-bit characters that it thinks are unreadable in the current
+ locale.
+
+ - The new option `--human-readable` (`-h`) changes the output of `--progress`,
+ `--stats`, and the end-of-run summary to be easier to read. If repeated, the
+ units become powers of 1024 instead of powers of 1000. (The old meaning of
+ `-h`, as a shorthand for `--help`, still works as long as you just use it on
+ its own, as in `rsync -h`.)
+
+ - If lutimes() and/or lchmod() are around, use them to allow the preservation
+ of attributes on symlinks.
+
+ - The `--link-dest` option now affects symlinks and devices (when possible).
+
+ - Added two config items to the rsyncd.conf parsing: `pre-xfer exec` and
+ `post-xfer exec`. These allow a command to be specified on a per-module
+ basis that will be run before and/or after a daemon-mode transfer. (See the
+ manpage for a list of the environment variables that are set with
+ information about the transfer.)
+
+ - When using the `--relative` option, you can now insert a dot dir in the
+ source path to indicate where the replication of the source dirs should
+ start. For example, if you specify a source path of
+ rsync://host/module/foo/bar/./baz/dir with `-R`, rsync will now only
+ replicate the `baz/dir` part of the source path (note: a trailing dot dir is
+ unaffected unless it also has a trailing slash).
+
+ - Added some new `--no-FOO` options that make it easier to override unwanted
+ implied or default options. For example, `-a --no-o` (aka `--archive
+ --no-owner`) can be used to turn off the preservation of file ownership that
+ is implied by `-a`.
+
+ - Added the `--chmod=MODE` option that allows the destination permissions to
+ be changed from the source permissions. E.g. `--chmod=g+w,o-rwx`
+
+ - Added the `incoming chmod` and `outgoing chmod` daemon options that allow a
+ module to specify what permissions changes should be applied to all files
+ copied to and from the daemon.
+
+ - Allow the `--temp-dir` option to be specified when starting a daemon, which
+ sets the default temporary directory for incoming files.
+
+ - If `--delete` is combined with `--dirs` without `--recursive`, rsync will
+ now delete in any directory whose content is being synchronized.
+
+ - If `--backup` is combined with `--delete` without `--backup-dir` (and
+ without `--delete-excluded`), we add a `protect` filter-rule to ensure that
+ files with the backup suffix are not deleted.
+
+ - The file-count stats that are output by `--progress` were improved to better
+ indicate what the numbers mean. For instance, the output: `(xfer#5,
+ to-check=8383/9999)` indicates that this was the fifth file to be
+ transferred, and we still need to check 8383 more files out of a total of
+ 9999.
+
+ - The include/exclude code now allows a `dir/***` directive (with 3 trailing
+ stars) to match both the dir itself as well as all the content below the dir
+ (`dir/**` would not match the dir).
+
+ - Added the `--prune-empty-dirs` (`-m`) option that makes the receiving rsync
+ discard empty chains of directories from the file-list. This makes it easier
+ to selectively copy files from a source hierarchy and end up with just the
+ directories needed to hold the resulting files.
+
+ - If the `--itemize-changes` (`-i`) option is repeated, rsync now includes
+ unchanged files in the itemized output (similar to `-vv`, but without all
+ the other verbose messages that can get in the way). Of course, the client
+ must be version 2.6.7 for this to work, but the remote rsync only needs to
+ be 2.6.7 if you're pushing files.
+
+ - Added the `--specials` option to tell rsync to copy non-device special files
+ (which rsync now attempts even as a normal user). The `--devices` option now
+ requests the copying of just devices (character and block). The `-D` option
+ still requests both (e.g. `--devices` and `--specials`), `-a` still implies
+ `-D`, and non-root users still get a silent downgrade that omits device
+ copying.
+
+ - Added the `--super` option to make the receiver always attempt super-user
+ activities. This is useful for systems that allow things such as devices to
+ be created or ownership to be set without being UID 0, and is also useful
+ for someone who wants to ensure that errors will be output if the receiving
+ rsync isn't being run as root.
+
+ - Added the `--sockopts` option for those few who want to customize the TCP
+ options used to contact a daemon rsync.
+
+ - Added a way for the `--temp-dir` option to be combined with a partial-dir
+ setting that lets rsync avoid non-atomic updates (for those times when
+ `--temp-dir` is not being used because space is tight).
+
+ - A new support script, files-to-excludes, will transform a list of files into
+ a set of include/exclude directives that will copy those files.
+
+ - A new option, `--executability` (`-E`) can be used to preserve just the
+ execute bit on files, for those times when using the `--perms` option is not
+ desired.
+
+ - The daemon now logs each connection and also each module-list request that
+ it receives.
+
+ - New log-format options: %M (modtime), %U (uid), %G (gid), and %B (permission
+ bits, e.g. `rwxr-xrwt`).
+
+ - The `--dry-run` option no longer forces the enabling of `--verbose`.
+
+ - The `--remove-sent-files` option now does a better job of incrementally
+ removing the sent files on the sending side (older versions tended to clump
+ up all the removals at the end).
+
+ - A daemon now supersedes its minimal SIGCHLD handler with the standard
+ PID-remembering version after forking. This ensures that the generator can
+ get the child-exit status from the receiver.
+
+ - Use of the `--bwlimit` option no longer interferes with the remote rsync
+ sending error messages about invalid/refused options.
+
+ - Rsync no longer returns a usage error when used with one local source arg
+ and no destination: this now implies the `--list-only` option, just like the
+ comparable situation with a remote source arg.
+
+ - Added the `--copy-dirlinks` option, a more limited version of
+ `--copy-links`.
+
+ - Various documentation improvements, including: a better synopsis, some
+ improved examples, a better discussion of the presence and absence of
+ `--perms` (including how it interacts with the new `--executability` and
+ `--chmod` options), an extended discussion of `--temp-dir`, an improved
+ discussion of `--partial-dir`, a better description of rsync's pattern
+ matching characters, an improved `--no-implied-dirs` section, and the
+ documenting of what the `--stats` option outputs.
+
+ - Various new and updated diffs in the patches dir, including: acls.diff,
+ xattrs.diff, atimes.diff, detect-renamed.diff, and slp.diff.
+
+### INTERNAL:
+
+ - We now use sigaction() and sigprocmask() if possible, and fall back on
+ signal() if not. Using sigprocmask() ensures that rsync enables all the
+ signals that it needs, just in case it was started in a masked state.
+
+ - Some buffer sizes were expanded a bit, particularly on systems where
+ MAXPATHLEN is overly small (e.g. Cygwin).
+
+ - If `io_printf()` tries to format more data than fits in the buffer, exit
+ with an error instead of transmitting a truncated buffer.
+
+ - If a `va_copy` macro is defined, lib/snprintf.c will use it when defining
+ the `VA_COPY` macro.
+
+ - Reduced the amount of stack memory needed for each level of directory
+ recursion by nearly MAXPATHLEN bytes.
+
+ - The wildmatch function was extended to allow an array of strings to be
+ supplied as the string to match. This allows the exclude code to do less
+ string copying.
+
+ - Got rid of the `safe_fname()` function (and all the myriad calls) and
+ replaced it with a new function in the log.c code that filters all the
+ output going to the terminal.
+
+ - Unified the `f_name()` and the `f_name_to()` functions.
+
+ - Improved the hash-table code the sender uses to handle checksums to make it
+ use slightly less memory and run just a little faster.
+
+### DEVELOPER RELATED:
+
+ - The diffs in the patches dir now require `patch -p1 <DIFF` instead of the
+ previous `-p0`. Also, the version included in the release tar now affect
+ generated files (e.g. configure, rsync.1, proto.h, etc.), so it is no longer
+ necessary to run autoconf and/or yodl unless you're applying a patch that
+ was checked out from CVS.
+
+ - Several diffs in the patches dir now use the proper `--enable-FOO` configure
+ option instead of `--with-FOO` to turn on the inclusion of the newly patched
+ feature.
+
+ - There is a new script, `prepare-source` than can be used to update the
+ various generated files (proto.h, configure, etc.) even before configure has
+ created the Makefile (this is mainly useful when patching the source with a
+ patch that doesn't affect generated files).
+
+ - The testsuite now sets HOME so that it won't be affected by a file such as
+ ~/.popt.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.6 (28 Jul 2005)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+ - The zlib code was upgraded to version 1.2.3 in order to make it more secure.
+ While the widely-publicized security problem in zlib 1.2.2 did not affect
+ rsync, another security problem surfaced that affects rsync's zlib 1.1.4.
+
+### BUG FIXES:
+
+ - The setting of `flist->high` in `clean_flist()` was wrong for an empty list.
+ This could cause `flist_find()` to crash in certain rare circumstances (e.g.
+ if just the right directory setup was around when `--fuzzy` was combined
+ with `--link-dest`).
+
+ - The outputting of hard-linked files when verbosity was > 1 was not right:
+ (1) Without `-i` it would output the name of each hard-linked file as though
+ it had been changed; it now outputs a `is hard linked` message for the file.
+ (2) With `-i` it would output all dots for the unchanged attributes of a
+ hard-link; it now changes those dots to spaces, as is done for other totally
+ unchanged items.
+
+ - When backing up a changed symlink or device, get rid of any old backup item
+ so that we don't get an `already exists` error.
+
+ - A couple places that were comparing a local and a remote modification-time
+ were not honoring the `--modify-window` option.
+
+ - Fixed a bug where the 'p' (permissions) itemized-changes flag might get set
+ too often (if some non-significant mode bits differed).
+
+ - Fixed a really old, minor bug that could cause rsync to warn about being
+ unable to mkdir() a path that ends in `/.` because it just created the
+ directory (required `--relative`, `--no-implied-dirs`, a source path that
+ ended in either a trailing slash or a trailing `/.`, and a non-existing
+ destination dir to tickle the bug in a recent version).
+
+### ENHANCEMENTS:
+
+ - Made the `max verbosity` setting in the rsyncd.conf file settable on a
+ per-module basis (which now matches the documentation).
+
+ - The rrsync script has been upgraded to verify the args of options that take
+ args (instead of rejecting any such options). It was also changed to try to
+ be more secure and to fix a problem in the parsing of a pull operation that
+ has multiple source args. (See the support dir.)
+
+ - Improved the documentation that explains the difference between a normal
+ daemon transfer and a daemon-over remote-shell transfer.
+
+ - Some of the diffs supplied in the patches dir were fixed and/or improved.
+
+### BUILD CHANGES:
+
+ - Made configure define `NOBODY_USER` (currently hard-wired to `nobody`) and
+ `NOBODY_GROUP` (set to either `nobody` or `nogroup` depending on what we
+ find in the /etc/group file).
+
+ - Added a test to the test suite, itemized.test, that tests the output of `-i`
+ (log-format w/%i) and some double-verbose messages.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.5 (1 Jun 2005)
+
+## Changes in this version:
+
+### OUTPUT CHANGES:
+
+ - Non-printable chars in filenames are now output using backslash-escaped
+ characters rather than '?'s. Any non-printable character is output using 3
+ digits of octal (e.g. `\n` -> `\012`), and a backslash is now output as
+ `\\`. Rsync also uses your locale setting, which can make it treat fewer
+ high-bit characters as non-printable.
+
+ - If rsync received an empty file-list when pulling files, it would output a
+ `nothing to do` message and exit with a 0 (success) exit status, even if the
+ remote rsync returned an error (it did not do this under the same conditions
+ when pushing files). This was changed to make the pulling behavior the same
+ as the pushing behavior: we now do the normal end-of-run outputting
+ (depending on options) and exit with the appropriate exit status.
+
+### BUG FIXES:
+
+ - A crash bug was fixed when a daemon had its `path` set to `/`, did not have
+ chroot enabled, and used some anchored excludes in the rsyncd.conf file.
+
+ - Fixed a bug in the transfer of a single file when `-H` is specified (rsync
+ would either infinite loop or perhaps crash).
+
+ - Fixed a case where the generator might try (and fail) to tweak the
+ write-permissions of a read-only directory in list-only mode (this only
+ caused an annoying warning message).
+
+ - If `--compare-dest` or `--link-dest` uses a locally-copied file as the basis
+ for an updated version, log this better when `--verbose` or `-i` is in
+ effect.
+
+ - Fixed the accidental disabling of `--backup` during the `--delete-after`
+ processing.
+
+ - Restored the ability to use the `--address` option in client mode (in
+ addition to its use in daemon mode).
+
+ - Make sure that some temporary progress information from the delete
+ processing does not get left on the screen when it is followed by a newline.
+
+ - When `--existing` skips a directory with extra verbosity, refer to it as a
+ `directory`, not a `file`.
+
+ - When transferring a single file to a different-named file, any generator
+ messages that are source-file related no longer refer to the file by the
+ destination filename.
+
+ - Fixed a bug where hard-linking a group of files might fail if the generator
+ hasn't created a needed destination directory yet.
+
+ - Fixed a bug where a hard-linked group of files that is newly-linked to a
+ file in a `--link-dest` dir doesn't link the files from the rest of the
+ cluster.
+
+ - When deleting files with the `--one-file-system` (`-x`) option set, rsync no
+ longer tries to remove files from inside a mount-point on the receiving
+ side. Also, we don't complain about being unable to remove the mount-point
+ dir.
+
+ - Fixed a compatibility problem when using `--cvs-ignore` (`-C`) and sending
+ files to an older rsync without using `--delete`.
+
+ - Make sure that a `- !` or `+ !` include/exclude pattern does not trigger the
+ list-clearing action that is reserved for `!`.
+
+ - Avoid a timeout in the generator when the sender/receiver aren't handling
+ the generator's checksum output quickly enough.
+
+ - Fixed the omission of some directories in the delete processing when
+ `--relative` (`-R`) was combined with a source path that had a trailing
+ slash.
+
+ - Fixed a case where rsync would erroneously delete some files and then
+ re-transfer them when the options `--relative` (`-R`) and `--recursive`
+ (`-r`) were both enabled (along with `--delete`) and a source path had a
+ trailing slash.
+
+ - Make sure that `--max-size` doesn't affect a device or a symlink.
+
+ - Make sure that a system with a really small MAXPATHLEN does not cause the
+ buffers in `readfd_unbuffered()` to be too small to receive normal messages.
+ (This mainly affected Cygwin.)
+
+ - If a source pathname ends with a filename of `..`, treat it as if `../` had
+ been specified (so that we don't copy files to the parent dir of the
+ destination).
+
+ - If `--delete` is combined with a file-listing rsync command (i.e. no
+ transfer is happening), avoid outputting a warning that we couldn't delete
+ anything.
+
+ - If `--stats` is specified with `--delete-after`, ensure that all the
+ `deleting` messages are output before the statistics.
+
+ - Improved one `if` in the deletion code that was only checking errno for
+ ENOTEMPTY when it should have also been checking for EEXIST (for
+ compatibility with OS variations).
+
+### ENHANCEMENTS:
+
+ - Added the `--only-write-batch=FILE` option that may be used (instead of
+ `--write-batch=FILE`) to create a batch file without doing any actual
+ updating of the destination. This allows you to divert all the file-updating
+ data away from a slow data link (as long as you are pushing the data to the
+ remote server when creating the batch).
+
+ - When the generator is taking a long time to fill up its output buffer (e.g.
+ if the transferred files are few, small, or missing), it now periodically
+ flushes the output buffer so that the sender/receiver can get started on the
+ files sooner rather than later.
+
+ - Improved the keep-alive code to handle a long silence between the sender and
+ the receiver that can occur when the sender is receiving the checksum data
+ for a large file.
+
+ - Improved the auth-errors that are logged by the daemon to include some
+ information on why the authorization failed: wrong user, password mismatch,
+ etc. (The client-visible message is unchanged!)
+
+ - Improved the client's handling of an `@ERROR` from a daemon so that it does
+ not complain about an unexpectedly closed socket (since we really did expect
+ the socket to close).
+
+ - If the daemon can't open the log-file specified in rsyncd.conf, fall back to
+ using syslog and log an appropriate warning. This is better than what was
+ typically a totally silent (and fatal) failure (since a daemon is not
+ usually run with the `--no-detach` option that was necessary to see the
+ error on stderr).
+
+ - The manpages now consistently refer to an rsync daemon as a `daemon`
+ instead of a `server` (to distinguish it from the server process in a
+ non-daemon transfer).
+
+ - Made a small change to the rrsync script (restricted rsync -- in the support
+ dir) to make a read-only server reject all `--remove-*` options when sending
+ files (to future-proof it against the possibility of other similar options
+ being added at some point).
+
+### INTERNAL:
+
+ - Rsync now calls `setlocale(LC_CTYPE, "")`. This enables isprint() to better
+ discern which filename characters need to be escaped in messages (which
+ should result in fewer escaped characters in some locales).
+
+ - Improved the naming of the log-file open/reopen/close functions.
+
+ - Removed some protocol-compatibility code that was only needed to help
+ someone running a pre-release of 2.6.4.
+
+### BUILD CHANGES:
+
+ - Added configure option `--disable-locale` to disable any use of setlocale()
+ in the binary.
+
+ - Fixed a bug in the `SUPPORT{,_HARD}_LINKS` #defines which prevented rsync
+ from being built without symlink or hard-link support.
+
+ - Only #define `HAVE_REMSH` if it is going to be set to 1.
+
+ - Configure now disables the use of mkstemp() under HP-UX (since they refuse
+ to fix its broken handling of large files).
+
+ - Configure now explicitly checks for the lseek64() function so that the code
+ can use `HAVE_LSEEK64` instead of inferring lseek64()'s presence based on
+ the presence of the `off64_t` type.
+
+ - Configure no longer mentions the change in the default remote-shell (from
+ rsh to ssh) that occurred for the 2.6.0 release.
+
+ - Some minor enhancements to the test scripts.
+
+ - Added a few new `*.diff` files to the patches dir, including a patch that
+ enables the optional copying of extended attributes.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.4 (30 March 2005)
+
+## Changes in this version:
+
+### PROTOCOL NUMBER:
+
+ - The protocol number was changed to 29.
+
+### OUTPUT CHANGES:
+
+ - When rsync deletes a directory and outputs a verbose message about it, it
+ now appends a trailing slash to the name instead of (only sometimes)
+ outputting a preceding "directory " string.
+
+ - The `--stats` output will contain file-list time-statistics if both sides
+ are 2.6.4, or if the local side is 2.6.4 and the files are being pushed
+ (since the stats come from the sending side). (Requires protocol 29 for a
+ pull.)
+
+ - The `%o` (operation) log-format escape now has a third value (besides `send`
+ and `recv`): `del.` (with trailing dot to make it 4 chars). This changes
+ the way deletions are logged in the daemon's log file.
+
+ - When the `--log-format` option is combined with `--verbose`, rsync now
+ avoids outputting the name of the file twice in most circumstances. As long
+ as the `--log-format` item does not refer to any post-transfer items (such
+ as %b or %c), the `--log-format` message is output prior to the transfer, so
+ `--verbose` is now the equivalent of a `--log-format` of '%n%L' (which
+ outputs the name and any link info). If the log output must occur after the
+ transfer to be complete, the only time the name is also output prior to the
+ transfer is when `--progress` was specified (so that the name will precede
+ the progress stats, and the full `--log-format` output will come after).
+
+ - Non-printable characters in filenames are replaced with a '?' to avoid
+ corrupting the screen or generating empty lines in the output.
+
+### BUG FIXES:
+
+ - Restore the list-clearing behavior of `!` in a .cvsignore file (2.6.3 was
+ only treating it as a special token in an rsync include/exclude file).
+
+ - The combination of `--verbose` and `--dry-run` now mentions the full list of
+ changes that would be output without `--dry-run`.
+
+ - Avoid a mkdir warning when removing a directory in the destination that
+ already exists in the `--backup-dir`.
+
+ - An OS that has a binary mode for its files (such as Cygwin) needed
+ `setmode(fd, O_BINARY)` called on the temp-file we opened with mkstemp().
+ (Fix derived from Cygwin's 2.6.3 rsync package.)
+
+ - Fixed a potential hang when verbosity is high, the client side is the
+ sender, and the file-list is large.
+
+ - Fixed a potential protocol-corrupting bug where the generator could merge a
+ message from the receiver into the middle of a multiplexed packet of data if
+ only part of that data had been written out to the socket when the message
+ from the generator arrived.
+
+ - We now check if the OS doesn't support using mknod() for creating FIFOs and
+ sockets, and compile-in some compatibility code using mkfifo() and socket()
+ when necessary.
+
+ - Fixed an off-by-one error in the handling of `--max-delete=N`. Also, if the
+ `--max-delete` limit is exceeded during a run, we now output a warning about
+ this at the end of the run and exit with a new error code (25).
+
+ - One place in the code wasn't checking if fork() failed.
+
+ - The `ignore nonreadable` daemon parameter used to erroneously affect
+ readable symlinks that pointed to a non-existent file.
+
+ - If the OS does not have lchown() and a chown() of a symlink will affect the
+ referent of a symlink (as it should), we no longer try to set the user and
+ group of a symlink.
+
+ - The generator now properly runs the hard-link loop and the dir-time
+ rewriting loop after we're sure that the redo phase is complete.
+
+ - When `--backup` was specified with `--partial-dir=DIR`, where DIR is a
+ relative path, the backup code was erroneously trying to backup a file that
+ was put into the partial-dir.
+
+ - If a file gets resent in a single transfer and the `--backup` option is
+ enabled along with `--inplace`, rsync no longer performs a duplicate backup
+ (it used to overwrite the first backup with the failed file).
+
+ - One call to `flush_write_file()` was not being checked for an error.
+
+ - The `--no-relative` option was not being sent from the client to a server
+ sender.
+
+ - If an rsync daemon specified `dont compress = ...` for a file and the client
+ tried to specify `--compress`, the libz code was not handling a compression
+ level of 0 properly. This could cause a transfer failure if the block-size
+ for a file was large enough (e.g. rsync might have exited with an error for
+ large files).
+
+ - Fixed a bug that would sometimes surface when using `--compress` and sending
+ a file with a block-size larger than 64K (either manually specified, or
+ computed due to the file being really large). Prior versions of rsync would
+ sometimes fail to decompress the data properly, and thus the transferred
+ file would fail its verification.
+
+ - If a daemon can't open the specified log file (i.e. syslog is not being
+ used), die without crashing. We also output an error about the failure on
+ stderr (which will only be seen if `--no-detach` was specified) and exit
+ with a new error code (6).
+
+ - A local transfer no longer duplicates all its include/exclude options (since
+ the forked process already has a copy of the exclude list, there's no need
+ to send them a set of duplicates).
+
+ - The output of the items that are being updated by the generator (dirs,
+ symlinks, devices) is now intermingled in the proper order with the output
+ from the items that the receiver is updating (regular files) when pulling.
+ This misordering was particularly bad when `--progress` was specified.
+ (Requires protocol 29.)
+
+ - When `--timeout` is specified, lulls that occur in the transfer while the
+ generator is doing work that does not generate socket traffic (looking for
+ changed files, deleting files, doing directory-time touch-ups, etc.) will
+ cause a new keep-alive packet to be sent that should keep the transfer going
+ as long as the generator continues to make progress. (Requires protocol 29.)
+
+ - The stat size of a device is not added to the total file size of the items
+ in the transfer (the size might be undefined on some OSes).
+
+ - Fixed a problem with refused-option messages sometimes not making it back to
+ the client side when a remote `--files-from` was in effect and the daemon
+ was the receiver.
+
+ - The `--compare-dest` option was not updating a file that differed in (the
+ preserved) attributes from the version in the compare-dest DIR.
+
+ - When rsync is copying files into a write-protected directory, fixed the
+ change-report output for the directory so that we don't report an identical
+ directory as changed.
+
+### ENHANCEMENTS:
+
+ - Rsync now supports popt's option aliases, which means that you can use
+ /etc/popt and/or ~/.popt to create your own option aliases.
+
+ - Added the `--delete-during` (`--del`) option which will delete files from
+ the receiving side incrementally as each directory in the transfer is being
+ processed. This makes it more efficient than the default,
+ before-the-transfer behavior, which is now also available as
+ `--delete-before` (and is still the default `--delete-WHEN` option that will
+ be chosen if `--delete` or `--delete-excluded` is specified without a
+ `--delete-WHEN` choice). All the `--del*` options infer `--delete`, so an
+ rsync daemon that refuses `delete` will still refuse to allow any
+ file-deleting options (including the new `--remove-sent-files` option).
+
+ - All the `--delete-WHEN` options are now more memory efficient: Previously an
+ duplicate set of file-list objects was created on the receiving side for the
+ entire destination hierarchy. The new algorithm only creates one directory
+ of objects at a time (for files inside the transfer).
+
+ - Added the `--copy-dest` option, which works like `--link-dest` except that
+ it locally copies identical files instead of hard-linking them.
+
+ - Added support for specifying multiple `--compare-dest`, `--copy-dest`, or
+ `--link-dest` options, but only of a single type. (Promoted from the patches
+ dir and enhanced.) (Requires protocol 29.)
+
+ - Added the `--max-size` option. (Promoted from the patches dir.)
+
+ - The daemon-mode options are now separated from the normal rsync options so
+ that they can't be mixed together. This makes it impossible to start a
+ daemon that has improper default option values (which could cause problems
+ when a client connects, such as hanging or crashing).
+
+ - The `--bwlimit` option may now be used in combination with `--daemon` to
+ specify both a default value for the daemon side and a value that cannot be
+ exceeded by a user-specified `--bwlimit` option.
+
+ - Added the `port` parameter to the rsyncd.conf file. (Promoted from the
+ patches dir.) Also added `address`. The command-line options take precedence
+ over a config-file option, as expected.
+
+ - In `_exit_cleanup()`: when we are exiting with a partially-received file, we
+ now flush any data in the write-cache before closing the partial file.
+
+ - The `--inplace` support was enhanced to work with `--compare-dest`,
+ `--link-dest`, and (the new) `--copy-dest` options. (Requires protocol 29.)
+
+ - Added the `--dirs` (`-d`) option for an easier way to copy directories
+ without recursion. Any directories that are encountered are created on the
+ destination. Specifying a directory with a trailing slash copies its
+ immediate contents to the destination.
+
+ - The `--files-from` option now implies `--dirs` (`-d`).
+
+ - Added the `--list-only` option, which is mainly a way for the client to put
+ the server into listing mode without needing to resort to any internal
+ option kluges (e.g. the age-old use of `-r --exclude='/*/*'` for a
+ non-recursive listing). This option is used automatically (behind the
+ scenes) when a modern rsync speaks to a modern daemon, but may also be
+ specified manually if you want to force the use of the `--list-only` option
+ over a remote-shell connection.
+
+ - Added the `--omit-dir-times` (`-O`) option, which will avoid updating the
+ modified time for directories when `--times` was specified. This option will
+ avoid an extra pass through the file-list at the end of the transfer (to
+ tweak all the directory times), which may provide an appreciable speedup for
+ a really large transfer. (Promoted from the patches dir.)
+
+ - Added the `--filter` (`-f`) option and its helper option, `-F`. Filter rules
+ are an extension to the existing include/exclude handling that also supports
+ nested filter files as well as per-directory filter files (like .cvsignore,
+ but with full filter-rule parsing). This new option was chosen in order to
+ ensure that all existing include/exclude processing remained 100% compatible
+ with older versions. Protocol 29 is needed for full filter-rule support, but
+ backward-compatible rules work with earlier protocol versions. (Promoted
+ from the patches dir and enhanced.)
+
+ - Added the `--delay-updates` option that puts all updated files into a
+ temporary directory (by default `.~tmp~`, but settable via the
+ `--partial-dir=DIR` option) until the end of the transfer. This makes the
+ updates a little more atomic for a large transfer.
+
+ - If rsync is put into the background, any output from `--progress` is
+ reduced.
+
+ - Documented the `max verbosity` setting for rsyncd.conf. (This setting was
+ added a couple releases ago, but left undocumented.)
+
+ - The sender and the generator now double-check the file-list index they are
+ given, and refuse to try to do a file transfer on a non-file index (since
+ that would indicate that something had gone very wrong).
+
+ - Added the `--itemize-changes` (`-i`) option, which is a way to output a more
+ detailed list of what files changed and in what way. The effect is the same
+ as specifying a `--log-format` of `%i %n%L` (see both the rsync and
+ rsyncd.conf manpages). Works with `--dry-run` too.
+
+ - Added the `--fuzzy` (`-y`) option, which attempts to find a basis file for a
+ file that is being created from scratch. The current algorithm only looks in
+ the destination directory for the created file, but it does attempt to find
+ a match based on size/mod-time (in case the file was renamed with no other
+ changes) as well as based on a fuzzy name-matching algorithm. This option
+ requires protocol 29 because it needs the new file-sorting order. (Promoted
+ from patches dir and enhanced.) (Requires protocol 29.)
+
+ - Added the `--remove-sent-files` option, which lets you move files between
+ systems.
+
+ - The hostname in HOST:PATH or HOST::PATH may now be an IPv6 literal enclosed
+ in '[' and ']' (e.g. `[::1]`). (We already allowed IPv6 literals in the
+ rsync://HOST:PORT/PATH format.)
+
+ - When rsync recurses to build the file list, it no longer keeps open one or
+ more directory handles from the dir's parent dirs.
+
+ - When building under windows, the default for `--daemon` is now to avoid
+ detaching, requiring the new `--detach` option to force rsync to detach.
+
+ - The `--dry-run` option can now be combined with either `--write-batch` or
+ `--read-batch`, allowing you to run a do-nothing test command to see what
+ would happen without `--dry-run`.
+
+ - The daemon's `read only` config item now sets an internal `read_only`
+ variable that makes extra sure that no write/delete calls on the read-only
+ side can succeed.
+
+ - The log-format % escapes can now have a numeric field width in between the %
+ and the escape letter (e.g. `%-40n %08p`).
+
+ - Improved the option descriptions in the `--help` text.
+
+### SUPPORT FILES:
+
+ - Added atomic-rsync to the support dir: a perl script that will transfer some
+ files using rsync, and then move the updated files into place all at once at
+ the end of the transfer. Only works when pulling, and uses `--link-dest` and
+ a parallel hierarchy of files to effect its update.
+
+ - Added mnt-excl to the support dir: a perl script that takes the /proc/mounts
+ file and translates it into a set of excludes that will exclude all mount
+ points (even mapped mounts to the same disk). The excludes are made relative
+ to the specified source dir and properly anchored.
+
+ - Added savetransfer.c to the support dir: a C program that can make a copy of
+ all the data that flows over the wire. This lets you test for data
+ corruption (by saving the data on both the sending side and the receiving
+ side) and provides one way to debug a protocol error.
+
+ - Added rrsync to the support dir: this is an updated version of Joe Smith's
+ restricted rsync perl script. This helps to ensure that only certain rsync
+ commands can be run by an ssh invocation.
+
+### INTERNAL:
+
+ - Added better checking of the checksum-header values that come over the
+ socket.
+
+ - Merged a variety of file-deleting functions into a single function so that
+ it is easier to maintain.
+
+ - Improved the type of some variables (particularly blocksize vars) for
+ consistency and proper size.
+
+ - Got rid of the uint64 type (which we didn't need).
+
+ - Use a slightly more compatible set of core #include directives.
+
+ - Defined int32 in a way that ensures that the build dies if we can't find a
+ variable with at least 32 bits.
+
+### PROTOCOL DIFFERENCES FOR VERSION 29:
+
+ - A 16-bit flag-word is transmitted after every file-list index. This
+ indicates what is changing between the sender and the receiver. The
+ generator now transmits an index and a flag-word to indicate when dirs and
+ symlinks have changed (instead of producing a message), which makes the
+ outputting of the information more consistent and less prone to screen
+ corruption (because the local receiver/sender is now outputting all the
+ file-change info messages).
+
+ - If a file is being hard-linked, the `ITEM_XNAME_FOLLOWS` bit is enabled in
+ the flag-word and the name of the file that was linked immediately follows
+ in vstring format (see below).
+
+ - If a file is being transferred with an alternate-basis file, the
+ `ITEM_BASIS_TYPE_FOLLOWS` bit is enabled in the flag-word and a single byte
+ follows, indicating what type of basis file was chosen. If that indicates
+ that a fuzzy-match was selected, the `ITEM_XNAME_FOLLOWS` bit is set in the
+ flag-word and the name of the match in vstring format follows the basis
+ byte. A vstring is a variable length string that has its size written prior
+ to the string, and no terminating null. If the string is from 1-127 bytes,
+ the length is a single byte. If it is from 128-32767 bytes, the length is
+ written as ((len >> 8) | 0x80) followed by (len % 0x100).
+
+ - The sending of exclude names is done using filter-rule syntax. This means
+ that all names have a prefixed rule indicator, even excludes (which used to
+ be sent as a bare pattern, when possible). The `-C` option will include the
+ per-dir .cvsignore merge file in the list of filter rules so it is
+ positioned correctly (unlike in some older transfer scenarios).
+
+ - Rsync sorts the filename list in a different way: it sorts the subdir names
+ after the non-subdir names for each dir's contents, and it always puts a
+ dir's contents immediately after the dir's name in the list. (Previously an
+ item named `foo.txt` would sort in between directory `foo/` and `foo/bar`.)
+
+ - When talking to a protocol 29 rsync daemon, a list-only request is able to
+ note this before the options are sent over the wire and the new
+ `--list-only` option is included in the options.
+
+ - When the `--stats` bytes are sent over the wire (or stored in a batch), they
+ now include two elapsed-time values: one for how long it took to build the
+ file-list, and one for how long it took to send it over the wire (each
+ expressed in thousandths of a second).
+
+ - When `--delete-excluded` is specified with some filter rules (AKA excludes),
+ a client sender will now initiate a send of the rules to the receiver (older
+ protocols used to omit the sending of excludes in this situation since there
+ were no receiver-specific rules that survived `--delete-excluded` back
+ then). Note that, as with all the filter-list sending, only items that are
+ significant to the other side will actually be sent over the wire, so the
+ filter-rule list that is sent in this scenario is often empty.
+
+ - An index equal to the file-list count is sent as a keep-alive packet from
+ the generator to the sender, which then forwards it on to the receiver. This
+ normally invalid index is only a valid keep-alive packet if the 16-bit
+ flag-word that follows it contains a single bit (`ITEM_IS_NEW`, which is
+ normally an illegal flag to appear alone).
+
+ - A protocol-29 batch file includes a bit for the setting of the `--dirs`
+ option and for the setting of the `--compress` option. Also, the shell
+ script created by `--write-batch` will use the `--filter` option instead of
+ `--exclude-from` to capture any filter rules.
+
+### BUILD CHANGES:
+
+ - Handle an operating system that use mkdev() in place of makedev().
+
+ - Improved configure to better handle cross-compiling.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.3 (30 Sep 2004)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+ - A bug in the `sanitize_path` routine (which affects a non-chrooted rsync
+ daemon) could allow a user to craft a pathname that would get transformed
+ into an absolute path for certain options (but not for file-transfer names).
+ If you're running an rsync daemon with chroot disabled, **please upgrade**,
+ ESPECIALLY if the user privs you run rsync under is anything above `nobody`.
+
+ OUTPUT CHANGES (ATTN: those using a script to parse the verbose output):
+
+ - Please note that the 2-line footer (output when verbose) now uses the term
+ `sent` instead of `wrote` and `received` instead of `read`. If you are not
+ parsing the numeric values out of this footer, a script would be better off
+ using the empty line prior to the footer as the indicator that the verbose
+ output is over.
+
+ - The output from the `--stats` option was similarly affected to change
+ `written` to `sent` and `read` to `received`.
+
+ - Rsync ensures that a filename that contains a newline gets mentioned with
+ each newline transformed into a question mark (which prevents a filename
+ from causing an empty line to be output).
+
+ - The `backed up ...` message that is output when at least 2 `--verbose`
+ options are specified is now the same both with and without the
+ `--backup-dir` option.
+
+### BUG FIXES:
+
+ - Fixed a crash bug that might appear when `--delete` was used and multiple
+ source directories were specified.
+
+ - Fixed a 32-bit truncation of the file length when generating the checksums.
+
+ - The `--backup` code no longer attempts to create some directories over and
+ over again (generating warnings along the way).
+
+ - Fixed a bug in the reading of the secrets file (by the daemon) and the
+ password file (by the client): the files no longer need to be terminated by
+ a newline for their content to be read in.
+
+ - If a file has a read error on the sending side or the reconstructed data
+ doesn't match the expected checksum (perhaps due to the basis file changing
+ during the transfer), the receiver will no longer retain the resulting file
+ unless the `--partial` option was specified. (Note: for the read-error
+ detection to work, neither side can be older than 2.6.3 -- older receivers
+ will always retain the file, and older senders don't tell the receiver that
+ the file had a read error.)
+
+ - If a file gets resent in a single transfer and the `--backup` option is
+ enabled, rsync no longer performs a duplicate backup (it used to overwrite
+ the original file in the backup area).
+
+ - Files specified in the daemon's `exclude` or `exclude from` config items are
+ now excluded from being uploaded (assuming that the module allows uploading
+ at all) in addition to the old download exclusion.
+
+ - Got rid of a potential hang in the receiver when near the end of a phase.
+
+ - When using `--backup` without a `--backup-dir`, rsync no longer preserves
+ the modify time on directories. This avoids confusing NFS.
+
+ - When `--copy-links` (`-L`) is specified, we now output a separate error for
+ a symlink that has no referent instead of claiming that a file `vanished`.
+
+ - The `--copy-links` (`-L`) option no longer has the side-effect of telling
+ the receiving side to follow symlinks. See the `--keep-dirlinks` option
+ (mentioned below) for a way to specify that behavior.
+
+ - Error messages from the daemon server's option-parsing (such as refused
+ options) are now successfully transferred back to the client (the server
+ used to fail to send the message because the socket wasn't in the right
+ state for the message to get through).
+
+ - Most transfer errors that occur during a daemon transfer are now returned to
+ the user in addition to being logged (some messages are intended to be
+ daemon-only and are not affected by this).
+
+ - Fixed a bug in the daemon authentication code when using one of the
+ batch-processing options.
+
+ - We try to work around some buggy IPv6 implementations that fail to implement
+ `IPV6_V6ONLY`. This should fix the `address in use` error that some daemons
+ get when running on an OS with a buggy IPv6 implementation. Also, if the new
+ code gets this error, we might suggest that the user specify `--ipv4` or
+ `--ipv6` (if we think it will help).
+
+ - When the remote rsync dies, make a better effort to recover any error
+ messages it may have sent before dying (the local rsync used to just die
+ with a socket-write error).
+
+ - When using `--delete` and a `--backup-dir` that contains files that are
+ hard-linked to their destination equivalents, rsync now makes sure that
+ removed files really get removed (avoids a really weird rename() behavior).
+
+ - Avoid a bogus run-time complaint about a lack of 64-bit integers when the
+ int64 type is defined as an `off_t` and it actually has 64-bits.
+
+ - Added a configure check for open64() without mkstemp64() so that we can
+ avoid using mkstemp() when such a combination is encountered. This bypasses
+ a problem writing out large temp files on OSes such as AIX and HP-UX.
+
+ - Fixed an age-old crash problem with `--read-batch` on a local copy (rsync
+ was improperly assuming `--whole-file` for the local copy).
+
+ - When `--dry-run` (`-n`) is used and the destination directory does not
+ exist, rsync now produces a correct report of files that would be sent
+ instead of dying with a chdir() error.
+
+ - Fixed a bug that could cause a slow-to-connect rsync daemon to die with an
+ error instead of waiting for the connection to finish.
+
+ - Fixed an ssh interaction that could cause output to be lost when the user
+ chose to combine the output of rsync's stdout and stderr (e.g. using the
+ `2>&1`).
+
+ - Fixed an option-parsing bug when `--files-from` got passed to a daemon.
+
+### ENHANCEMENTS:
+
+ - Added the `--partial-dir=DIR` option that lets you specify where to
+ (temporarily) put a partially transferred file (instead of overwriting the
+ destination file). E.g. `--partial-dir=.rsync-partial` Also added support
+ for the `RSYNC_PARTIAL_DIR` environment variable that, when found,
+ transforms a regular `--partial` option (such as the convenient `-P` option)
+ into one that also specifies a directory.
+
+ - Added `--keep-dirlinks` (`-K`), which allows you to symlink a directory onto
+ another partition on the receiving side and have rsync treat it as matching
+ a normal directory from the sender.
+
+ - Added the `--inplace` option that tells rsync to write each destination file
+ without using a temporary file. The matching of existing data in the
+ destination file can be severely limited by this, but there are also cases
+ where this is more efficient (such as appending data). Use only when needed
+ (see the manpage for more details).
+
+ - Added the `write only` option for the daemon's config file.
+
+ - Added long-option names for `-4` and `-6` (namely `--ipv4` and `--ipv6`) and
+ documented all these options in the manpage.
+
+ - Improved the handling of the `--bwlimit` option so that it's less bursty,
+ more accurate, and works properly over a larger range of values.
+
+ - The rsync daemon-over-ssh code now looks for `SSH_CONNECTION` and
+ `SSH2_CLIENT` in addition to `SSH_CLIENT` to figure out the IP address.
+
+ - Added the `--checksum-seed=N` option for advanced users.
+
+ - Batch writing/reading has a brand-new implementation that is simpler, fixes
+ a few weird problems with the old code (such as no longer sprinkling the
+ batch files into different dirs or even onto different systems), and is much
+ less intrusive into the code (making it easier to maintain for the future).
+ The new code generates just one data file instead of three, which makes it
+ possible to read the batch on stdin via a remote shell. Also, the old
+ requirement of forcing the same fixed checksum-seed for all batch processing
+ has been removed.
+
+ - If an rsync daemon has a module set with `list = no` (which hides its
+ presence in the list of available modules), a user that fails to
+ authenticate gets the same `unknown module` error that they would get if the
+ module were actually unknown (while still logging the real error to the
+ daemon's log file). This prevents fishing for module names.
+
+ - The daemon's `refuse options` config item now allows you to match option
+ names using wildcards and/or the single-letter option names.
+
+ - Each transferred file now gets its permissions and modified-time updated
+ before the temp-file gets moved into place. Previously, the finished file
+ would have a very brief window where its permissions disallowed all group
+ and world access.
+
+ - Added the ability to parse a literal IPv6 address in an `rsync:` URL (e.g.
+ rsync://[2001:638:500:101::21]:873/module/dir).
+
+ - The daemon's wildcard expanding code can now handle more than 1000 filenames
+ (it's now limited by memory instead of having a hard-wired limit).
+
+### INTERNAL:
+
+ - Some cleanup in the exclude code has saved some per-exclude memory and made
+ the code easier to maintain.
+
+ - Improved the argv-overflow checking for a remote command that has a lot of
+ args.
+
+ - Use rsyserr() in the various places that were still calling rprintf() with
+ strerror() as an arg.
+
+ - If an rsync daemon is listening on multiple sockets (to handle both IPv4 and
+ IPv6 to a single port), we now close all the unneeded file handles after we
+ accept a connection (we used to close just one of them).
+
+ - Optimized the handling of larger block sizes (rsync used to slow to a crawl
+ if the block size got too large).
+
+ - Optimized away a loop in `hash_search()`.
+
+ - Some improvements to the `sanitize_path()` and `clean_fname()` functions
+ makes them more efficient and produce better results (while still being
+ compatible with the file-name cleaning that gets done on both sides when
+ sending the file-list).
+
+ - Got rid of `alloc_sanitize_path()` after adding a destination-buffer arg to
+ `sanitize_path()` made it possible to put all the former's functionality
+ into the latter.
+
+ - The file-list that is output when at least 4 verbose options are specified
+ reports the uid value on the sender even when rsync is not running as root
+ (since we might be sending to a root receiver).
+
+### BUILD CHANGES:
+
+ - Added a `gen` target to rebuild most of the generated files, including
+ configure, config.h.in, the manpages, and proto.h.
+
+ - If `make proto` doesn't find some changes in the prototypes, the proto.h
+ file is left untouched (its time-stamp used to always be updated).
+
+ - The variable `$STRIP` (that is optionally set by the install-strip target's
+ rule) was changed to `$INSTALL_STRIP` because some systems have `$STRIP`
+ already set in the environment.
+
+ - Fixed a build problem when `SUPPORT_HARD_LINKS` isn't defined.
+
+ - When cross-compiling, the gettimeofday() function is now assumed to be a
+ modern version that takes two-args (since we can't test it).
+
+### DEVELOPER RELATED:
+
+ - The scripts in the testsuite dir were cleaned up a bit and a few new tests
+ added.
+
+ - Some new diffs were added to the patches dir, and some accepted ones were
+ removed.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.2 (30 Apr 2004)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fixed a major bug in the sorting of the filenames when `--relative` is used
+ for some sources (just sources such as `/` and `/*` were affected). This fix
+ ensures that we ask for the right file-list item when requesting changes
+ from the sender.
+
+ - Rsync now checks the return value of the close() function to better report
+ disk-full problems on an NFS file system.
+
+ - Restored the old daemon-server behavior of logging error messages rather
+ than returning them to the user. (A better long-term fix will be sought in
+ the future.)
+
+ - An obscure uninitialized-variable bug was fixed in the uid/gid code. (This
+ bug probably had no ill effects.)
+
+### BUILD CHANGES:
+
+ - Got rid of the configure check for sys/sysctl.h (it wasn't used and was
+ causing a problem on some systems). Also improved the
+ broken-largefile-locking test to try to avoid failure due to an NFS
+ build-dir.
+
+ - Fixed a compile problem on systems that don't define `AI_NUMERICHOST`.
+
+ - Fixed a compile problem in the popt source for compilers that don't support
+ `__attribute__`.
+
+### DEVELOPER RELATED:
+
+ - Improved the testsuite's `merge` test to work on OSF1.
+
+ - Two new diffs were added to the patches dir.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.1 (26 Apr 2004)
+
+## Changes in this version:
+
+### PROTOCOL NUMBER:
+
+ - The protocol number was changed to 28.
+
+### SECURITY FIXES:
+
+ - Paths sent to an rsync daemon are more thoroughly sanitized when chroot is
+ not used. If you're running a non-read-only rsync daemon with chroot
+ disabled, **please upgrade**, ESPECIALLY if the user privs you run rsync
+ under is anything above `nobody`.
+
+### ENHANCEMENTS:
+
+ - Lower memory use, more optimal transfer of data over the socket, and lower
+ CPU usage (see the INTERNAL section for details).
+
+ - The `RSYNC_PROXY` environment variable can now contain a `USER:PASS@` prefix
+ before the `HOST:PORT` information. (Bardur Arantsson)
+
+ - The `--progress` output now mentions how far along in the transfer we are,
+ including both a count of files transferred and a percentage of the total
+ file-count that we've processed. It also shows better
+ current-rate-of-transfer and remaining-transfer-time values.
+
+ - Documentation changes now attempt to describe some often misunderstood
+ features more clearly.
+
+### BUG FIXES:
+
+ - When `-x` (`--one-file-system`) is combined with `-L` (`--copy-links`) or
+ `--copy-unsafe-links,` no symlinked files are skipped, even if the referent
+ file is on a different filesystem.
+
+ - The `--link-dest` code now works properly for a non-root user when (1) the
+ UIDs of the source and destination differ and `-o` was specified, or (2)
+ when the group of the source can't be used on the destination and `-g` was
+ specified.
+
+ - Fixed a bug in the handling of `-H` (hard-links) that might cause the
+ expanded PATH/NAME value of the current item to get overwritten (due to an
+ expanded-name caching bug).
+
+ - We now reset the `new data has been sent` flag at the start of each file we
+ send. This makes sure that an interrupted transfer with the `--partial`
+ option set doesn't keep a shorter temp file than the current basis file when
+ no new data has been transferred over the wire for that file.
+
+ - Fixed a byte-order problem in `--batch-mode` on big-endian machines. (Jay
+ Fenlason)
+
+ - When using `--cvs-exclude`, the exclude items we get from a per-directory's
+ .cvsignore file once again only affect that one directory (not all following
+ directories too). The items are also now properly word-split and parsed
+ without any +/- prefix parsing.
+
+ - When specifying the USER@HOST: prefix for a file, the USER part can now
+ contain an '@', if needed (i.e. the last '@' is used to find the HOST, not
+ the first).
+
+ - Fixed some bugs in the handling of group IDs for non-root users: (1) It
+ properly handles a group that the sender didn't have a name for (it would
+ previously skip changing the group on any files in that group). (2) If
+ `--numeric-ids` is used, rsync no longer attempts to set groups that the
+ user doesn't have the permission to set.
+
+ - Fixed the `refuse options` setting in the rsyncd.conf file.
+
+ - Improved the `-x` (`--one-file-system`) flag's handling of any mount-point
+ directories we encounter. It is both more optimal (in that it no longer does
+ a useless scan of the contents of the mount-point dirs) and also fixes a
+ bug where a remapped mount of the original filesystem could get discovered
+ in a subdir we should be ignoring.
+
+ - Rsync no longer discards a double-slash at the start of a filename when
+ trying to open the file. It also no longer constructs names that start with
+ a double slash (unless the user supplied them).
+
+ - Path-specifying options to a daemon should now work the same with or without
+ chroot turned on. Previously, such a option (such as `--link-dest`) would
+ get its absolute path munged into a relative one if chroot was not on,
+ making that setting fairly useless. Rsync now transforms the path into one
+ that is based on the module's base dir when chroot is not enabled.
+
+ - Fixed a compatibility problem interacting with older rsync versions that
+ might send us an empty `--suffix` value without telling us that
+ `--backup-dir` was specified.
+
+ - The `hosts allow` option for a daemon-over-remote-shell process now has
+ improved support for IPv6 addresses and a fix for systems that have a length
+ field in their socket structs.
+
+ - Fixed the ability to request an empty backup `--suffix` when sending files
+ to an rsync daemon.
+
+ - Fixed an option-parsing bug when `--files-from` was sent to a server sender.
+
+### INTERNAL:
+
+ - Most of the I/O is now buffered, which results in a pretty large speedup
+ when running under MS Windows. (Craig Barratt)
+
+ - Optimizations to the name-handling/comparing code have made some significant
+ reductions in user-CPU time for large file sets.
+
+ - Some cleanup of the variable types make the code more consistent.
+
+ - Reduced memory requirements of hard link preservation. (J.W. Schultz)
+
+ - Implemented a new algorithm for hard-link handling that speeds up the code
+ significantly. (J.W. Schultz and Wayne Davison)
+
+ - The `--hard-link` option now uses the first existing file in the group of
+ linked files as the basis for the transfer. This prevents the sub-optimal
+ transfer of a file's data when a new hardlink is added on the sending side
+ and it sorts alphabetically earlier in the list than the files that are
+ already present on the receiving side.
+
+ - Dropped support for protocol versions less than 20 (2.3.0 released 15 Mar
+ 1999) and activated warnings for protocols less than 25 (2.5.0 released 23
+ Aug 2001). (Wayne Davison and J.W. Schultz, severally)
+
+ - More optimal data transmission for `--hard-links` (protocol 28).
+
+ - More optimal data transmission for `--checksum` (protocol 28).
+
+ - Less memory is used when `--checksum` is specified.
+
+ - Less memory is used in the file list (a per-file savings).
+
+ - The generator is now better about not modifying the file list during the
+ transfer in order to avoid a copy-on-write memory bifurcation (on systems
+ where fork() uses shared memory). Previously, rsync's shared memory would
+ slowly become unshared, resulting in real memory usage nearly doubling on
+ the receiving side by the end of the transfer. Now, as long as permissions
+ are being preserved, the shared memory should remain that way for the entire
+ transfer.
+
+ - Changed hardlink info and `file_struct` + strings to use allocation pools.
+ This reduces memory use for large file-sets and permits freeing memory to
+ the OS. (J.W. Schultz)
+
+ - The 2 pipes used between the receiver and generator processes (which are
+ forked on the same machine) were reduced to 1 pipe and the protocol improved
+ so that (1) it is now impossible to have the `redo` pipe fill up and hang
+ rsync, and (2) trailing messages from the receiver don't get lost on their
+ way through the generator over to the sender (which mainly affected
+ hard-link messages and verbose `--stats` output).
+
+ - Improved the internal uid/gid code to be more portable and a little more
+ optimized.
+
+ - The device numbers sent when using `--devices` are now sent as separate
+ major/minor values with 32-bit accuracy (protocol 28). Previously, the
+ copied devices were sent as a single 32-bit number. This will make
+ inter-operation of 64-bit binaries more compatible with their 32-bit
+ brethren (with both ends of the connection are using protocol 28). Note that
+ optimizations in the binary protocol for sending the device numbers often
+ results in fewer bytes being used than before, even though more precision is
+ now available.
+
+ - Some cleanup of the exclude/include structures and its code made things
+ clearer (internally), simpler, and more efficient.
+
+ - The reading & writing of the file-list in batch-mode is now handled by the
+ same code that sends & receives the list over the wire. This makes it much
+ easier to maintain. (Note that the batch code is still considered to be
+ experimental.)
+
+### BUILD CHANGES:
+
+ - The configure script now accepts `--with-rsyncd-conf=PATH` to override the
+ default value of the /etc/rsyncd.conf file.
+
+ - Fixed configure bug when running `./configure --disable-ipv6`.
+
+ - Fixed compilation problem on Tru64 Unix (having to do with `sockaddr.sa_len`
+ and `sockaddr.sin_len`).
+
+### DEVELOPER RELATED:
+
+ - Fixed `make test` bug when build dir is not the source dir.
+
+ - Added a couple extra diffs in the `patches` dir, removed the ones that got
+ applied, and rebuilt the rest.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.6.0 (1 Jan 2004)
+
+## Changes in this version:
+
+### PROTOCOL NUMBER:
+
+ - The protocol number was changed to 27. The maximum accepted protocol number
+ was increased from 30 to 40.
+
+### ENHANCEMENTS:
+
+ - `ssh` is now the default remote shell for rsync. If you want to change this,
+ configure like this: `./configure --with-rsh=rsh`.
+
+ - Added `--files-from`, `--no-relative`, `--no-implied-dirs`, and `--from0`.
+ Note that `--from0` affects the line-ending character for all the files read
+ by the `--*-from` options. (Wayne Davison)
+
+ - Length of csum2 is now per-file starting with protocol version
+ 27. (J.W. Schultz)
+
+ - Per-file dynamic block size is now sqrt(file length). The per-file checksum
+ size is determined according to an algorithm provided by Donovan Baarda
+ which reduces the probability of rsync algorithm corrupting data and falling
+ back using the whole md4 checksums. (J.W. Schultz, Donovan Baarda)
+
+ - The `--stats` option no longer includes the (debug) malloc summary unless
+ the verbose option was specified at least twice.
+
+ - Added a new error/warning code for when files vanish from the sending side.
+ Made vanished source files not interfere with the file-deletion pass when
+ `--delete-after` was specified.
+
+ - Various trailing-info sections are now preceded by a newline.
+
+### BUG FIXES:
+
+ - Fixed several exclude/include matching bugs when using wild-cards. This has
+ a several user-visible effects, all of which make the matching more
+ consistent and intuitive. This should hopefully not cause anyone problems
+ since it makes the matching work more like what people are expecting. (Wayne
+ Davison)
+
+ - A pattern with a `**` no longer causes a `*` to match slashes. For example,
+ with `/*/foo/**`, `foo` must be 2 levels deep. [If your string has BOTH `*`
+ and `**` wildcards, changing the `*` wildcards to `**` will provide the old
+ behavior in all versions.]
+
+ - `**/foo` now matches at the base of the transfer (like /foo does). [Use
+ `/**/foo` to get the old behavior in all versions.]
+
+ - A non-anchored wildcard term floats to match beyond the base of the
+ transfer. E.g. `CVS/R*` matches at the end of the path, just like the
+ non-wildcard term `CVS/Root` does. [Use `/CVS/R*` to get the old behavior in
+ all versions.]
+
+ - Including a `**` in the match term causes it to be matched against the
+ entire path, not just the name portion, even if there aren't any interior
+ slashes in the term. E.g. `foo**bar` would exclude `/path/foo-bar` (just
+ like before) as well as `/foo-path/baz-bar` (unlike before). [Use `foo*bar`
+ to get the old behavior in all versions.]
+
+ - The exclude list specified in the daemon's config file is now properly
+ applied to the pulled items no matter how deep the user's file-args are in
+ the source tree. (Wayne Davison)
+
+ - For protocol version >= 27, `mdfour_tail()` is called when the block size
+ (including `checksum_seed`) is a multiple of 64. Previously it was not
+ called, giving the wrong MD4 checksum. (Craig Barratt)
+
+ - For protocol version >= 27, a 64 bit bit counter is used in mdfour.c as
+ required by the RFC. Previously only a 32 bit bit counter was used, causing
+ incorrect MD4 file checksums for file sizes >= 512MB - 4. (Craig Barratt)
+
+ - Fixed a crash bug when interacting with older rsync versions and multiple
+ files of the same name are destined for the same dir. (Wayne Davison)
+
+ - Keep tmp names from overflowing MAXPATHLEN.
+
+ - Make `--link-dest` honor the absence of `-p`, `-o`, and `-g`.
+
+ - Made rsync treat a trailing slash in the destination in a more consistent
+ manner.
+
+ - Fixed file I/O error detection. (John Van Essen)
+
+ - Fixed bogus `malformed address {hostname}` message in rsyncd log when
+ checking IP address against hostnames from `hosts allow` and `hosts deny`
+ parameters in config file.
+
+ - Print heap statistics when verbose >= 2 instead of when >= 1.
+
+ - Fixed a compression (`-z`) bug when syncing a mostly-matching file that
+ contains already-compressed data. (Yasuoka Masahiko and Wayne Davison)
+
+ - Fixed a bug in the `--backup` code that could cause deleted files to not get
+ backed up.
+
+ - When the backup code makes new directories, create them with mode 0700
+ instead of 0755 (since the directory permissions in the backup tree are not
+ yet copied from the main tree).
+
+ - Call setgroups() in a more portable manner.
+
+ - Improved file-related error messages to better indicate exactly what
+ pathname failed. (Wayne Davison)
+
+ - Fixed some bugs in the handling of `--delete` and `--exclude` when using the
+ `--relative` (`-R`) option. (Wayne Davison)
+
+ - Fixed bug that prevented regular files from replacing special files and
+ caused a directory in `--link-dest` or `--compare-dest` to block the
+ creation of a file with the same path. A directory still cannot be replaced
+ by a regular file unless `--delete` specified. (J.W. Schultz)
+
+ - Detect and report when open or opendir succeed but read and readdir fail
+ caused by network filesystem issues and truncated files. (David Norwood,
+ Michael Brown, J.W. Schultz)
+
+ - Added a fix that should give ssh time to restore the tty settings if the
+ user presses Ctrl-C at an ssh password prompt.
+
+### INTERNAL:
+
+ - Eliminated vestigial support for old versions that we stopped supporting.
+ (J.W. Schultz)
+
+ - Simplified some of the option-parsing code. (Wayne Davison)
+
+ - Some cleanup made to the exclude code, as well as some new defines added to
+ enhance readability. (Wayne Davison)
+
+ - Changed the protocol-version code so that it can interact at a lower
+ protocol level than the maximum supported by both sides. Added an
+ undocumented option, `--protocol=N`, to force the value we advertise to the
+ other side (primarily for testing purposes). (Wayne Davison)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.7 (4 Dec 2003)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+ - Fix buffer handling bugs. (Andrew Tridgell, Martin Pool, Paul Russell,
+ Andrea Barisani)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.6, aka "the dwd-between-jobs release" (26 Jan 2003)
+
+## Changes in this version:
+
+### ENHANCEMENTS:
+
+ - The `--delete-after` option now implies `--delete`. (Wayne Davison)
+
+ - The `--suffix` option can now be used with `--backup-dir`. (Michael
+ Zimmerman)
+
+ - Combining `::` syntax with the `--rsh`/`-e` option now uses the specified
+ remote-shell as a transport to talk to a (newly-spawned) server-daemon. This
+ allows someone to use daemon features, such as modules, over a secure
+ protocol, such as ssh. (JD Paul)
+
+ - The rsync:// syntax for daemon connections is now accepted in the
+ destination field.
+
+ - If the file name given to `--include-from` or `--exclude-from` is `-`, rsync
+ will read from standard input. (J.W. Schultz)
+
+ - New option `--link-dest` which is like `--compare-dest` except that
+ unchanged files are hard-linked in to the destination directory. (J.W.
+ Schultz)
+
+ - Don't report an error if an excluded file disappears during an rsync run.
+ (Eugene Chupriyanov and Bo Kersey)
+
+ - Added .svn to `--cvs-exclude` list to support subversion. (Jon Middleton)
+
+ - Properly support IPv6 addresses in the rsyncd.conf `hosts allow` and `hosts
+ deny` fields. (Hideaki Yoshifuji)
+
+ - Changed exclude file handling to permit DOS or MAC style line terminations.
+ (J.W. Schultz)
+
+ - Ignore errors from chmod when `-p`/`-a`/`--preserve-perms` is not set.
+ (Dave Dykstra)
+
+### BUG FIXES:
+
+ - Fix `forward name lookup failed` errors on AIX 4.3.3. (John L. Allen, Martin
+ Pool)
+
+ - Generate each file's rolling-checksum data as we send it, not in a separate
+ (memory-eating) pass before hand. This prevents timeout errors on really
+ large files. (Stefan Nehlsen)
+
+ - Fix compilation on Tru64. (Albert Chin, Zoong Pham)
+
+ - Better handling of some client-server errors. (Martin Pool)
+
+ - Fixed a crash that would occur when sending a list of files that contains a
+ duplicate name (if it sorts to the end of the file list) and using
+ `--delete`. (Wayne Davison)
+
+ - Fixed the file-name duplicate-removal code when dealing with multiple dups
+ in a row. (Wayne Davison)
+
+ - Fixed a bug that caused rsync to lose the exit status of its child processes
+ and sometimes return an exit code of 0 instead of showing an error. (David
+ R. Staples, Dave Dykstra)
+
+ - Fixed bug in `--copy-unsafe-links` that caused it to be completely broken.
+ (Dave Dykstra)
+
+ - Prevent infinite recursion in cleanup code under certain circumstances.
+ (Sviatoslav Sviridov and Marc Espie)
+
+ - Fixed a bug that prevented rsync from creating intervening directories when
+ `--relative-paths`/`-R` is set. (Craig Barratt)
+
+ - Prevent `Connection reset by peer` messages from Cygwin. (Randy O'Meara)
+
+### INTERNAL:
+
+ - Many code cleanups and improved internal documentation. (Martin Pool, Nelson
+ Beebe)
+
+ - Portability fixes. (Dave Dykstra and Wayne Davison)
+
+ - More test cases. (Martin Pool)
+
+ - Some test-case fixes. (Brian Poole, Wayne Davison)
+
+ - Updated included popt to the latest vendor drop, version 1.6.4. (Jos
+ Backus)
+
+ - Updated config.guess and config.sub to latest versions; this means rsync
+ should build on more platforms. (Paul Green)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.5, aka Snowy River (2 Apr 2002)
+
+## Changes in this version:
+
+### ENHANCEMENTS:
+
+ - With `--progress`, when a transfer is complete show the time taken;
+ otherwise show expected time to complete. (Cameron Simpson)
+
+ - Make `make install-strip` works properly, and `make install` accepts a
+ DESTDIR variable for help in building binary packages. (Peter
+ Breitenlohner, Greg Louis)
+
+ - If configured with `--enable-maintainer-mode`, then on receipt of a fatal
+ signal rsync will try to open an xterm running gdb, similarly to Samba's
+ `panic action` or GNOME's bug-buddy. (Martin Pool)
+
+### BUG FIXES:
+
+ - Fix situation where failure to fork (e.g. because out of process slots)
+ would cause rsync to kill all processes owned by the current user. Yes,
+ really! (Paul Haas, Martin Pool)
+
+ - Fix test suite on Solaris. (Jos Backus, Martin Pool)
+
+ - Fix minor memory leak in socket code. (Dave Dykstra, Martin Pool.)
+
+ - Fix `--whole-file` problem that caused it to be the default even for remote
+ connections. (Martin Pool, Frank Schulz)
+
+ - Work around bug in Mac OS X mkdir(2), which cannot handle trailing slashes.
+ <http://www.opensource.apple.com/bugs/X/BSD%20Kernel/2734739.html> (Martin
+ Pool)
+
+ - Improved network error handling. (Greg A. Woods)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.4, aka "Imitation lizard skin" (13 Mar 2002)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Additional fix for zlib double-free bug. (Martin Pool, Andrew Tridgell) (CVE
+ CAN-2002-0059)
+
+### ENHANCEMENTS:
+
+ - Merge in changes from zlib 1.1.3 to zlib 1.1.4. (Jos Backus) (Note that
+ rsync still uses a custom version of zlib; you can not just link against a
+ system library. See zlib/README.rsync)
+
+ - Additional test cases for `--compress`. (Martin Pool)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.3, aka "Happy 26" (11 Mar 2002)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+ - Make sure that supplementary groups are removed from a server
+ process after changing uid and gid. (Ethan Benson) (Debian bug
+ #132272, CVE CAN-2002-0080)
+
+### BUG FIXES:
+
+ - Fix zlib double-free bug. (Owen Taylor, Mark J Cox) (CVE CAN-2002-0059)
+
+ - Fixed problem that in many cases caused the error message unexpected read
+ size of 0 in `map_ptr` and resulted in the wrong data being copied.
+
+ - Fixed compilation errors on some systems caused by the use of `unsigned
+ int64` in rsync.h.
+
+ - Fixed problem on systems such as Sunos4 that do not support realloc on a
+ NULL pointer; error was 'out of memory in "flist_expand"'.
+
+ - Fix for rsync server processes hanging around after the client unexpectedly
+ disconnects. (Colin Walters) (Debian bug #128632)
+
+ - Cope with BSD systems on which mkdir() will not accept a trailing slash.
+
+### ENHANCEMENTS:
+
+ - Merge in changes from zlib 1.1.2 to zlib 1.1.3. (Note that rsync still uses
+ a custom version of zlib; you can not just link against a system library.
+ See zlib/README.rsync)
+
+ - Command to initiate connections is only shown with `-vv`, rather than `-v`
+ as in 2.5.2. Output from plain `-v` is more similar to what was historically
+ used so as not to break scripts that try to parse the output.
+
+ - Added `--no-whole-file` and `--no-blocking-io` options (Dave Dykstra)
+
+ - Made the `--write-batch` and `--read-batch` options actually work and added
+ documentation in the manpage (Jos Backus)
+
+ - If the daemon is unable to fork a child to accept a connection, print an
+ error message. (Colin Walters)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.2 (26 Jan 2002)
+
+## Changes in this version:
+
+### SECURITY FIXES:
+
+ - Signedness security patch from Sebastian Krahmer <krahmer@suse.de> -- in
+ some cases we were not sufficiently careful about reading integers from the
+ network.
+
+### PROTOCOL NUMBER:
+
+ - The protocol number was changed to 26.
+
+### BUG FIXES:
+
+ - Fix possible string mangling in log files.
+
+ - Fix for setting local address of outgoing sockets.
+
+ - Better handling of hardlinks and devices on platforms with 64-bit `dev_t` or
+ `ino_t`.
+
+ - Name resolution on machines supporting IPv6 is improved.
+
+ - Fix for device nodes. (dann frazier) (Debian #129135)
+
+### ENHANCEMENTS:
+
+ - With `-v`, rsync now shows the command used to initiate an ssh/rsh
+ connection.
+
+ - `--statistics` now shows memory heap usage on platforms that support
+ mallinfo().
+
+ - "The Ted T'so school of program optimization": make progress visible and
+ people will think it's faster. (With `--progress`, rsync will show you how
+ many files it has seen as it builds the `file_list`, giving some indication
+ that it has not hung.)
+
+ - Improvements to batch mode support. This is still experimental but testing
+ would be welcome. (Jos Backus)
+
+ - New `--ignore-existing` option, patch previously distributed with Vipul's
+ Razor. (Debian #124286)
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.1 (3 Jan 2002)
+
+## Changes in this version:
+
+### BUG FIXES:
+
+ - Fix for segfault in `--daemon` mode configuration parser. (Paul Mackerras)
+
+ - Correct `string<->address` parsing for both IPv4 and 6. (YOSHIFUJI Hideaki,
+ SUMIKAWA Munechika and Jun-ichiro `itojun` Hagino)
+
+ - Various fixes for IPv6 support. (Dave Dykstra)
+
+ - rsync.1 typo fix. (Matt Kraai)
+
+ - Test suite typo fixes. (Tom Schmidt)
+
+ - rsync.1 grammar and clarity improvements. (Edward Welbourne)
+
+ - Correction to ./configure tests for `inet_ntop`. (Jeff Garzik)
+
+### ENHANCEMENTS:
+
+ - `--progress` and `-P` now show estimated data transfer rate (in a multiple
+ of bytes/s) and estimated time to completion. (Rik Faith)
+
+ - `--no-detach` option, required to run as a W32 service and also useful when
+ running on Unix under daemontools, AIX's SRC, or a debugger. (Max Bowsher,
+ Jos Backus)
+
+ - Clearer error messages for some conditions.
+
+------------------------------------------------------------------------------
+
+# NEWS for rsync 2.5.0 (30 Nov 2001)
+
+## Changes in this version:
+
+### PROTOCOL NUMBER:
+
+ - The protocol number was changed to 25.
+
+### ANNOUNCEMENTS:
+
+ - Martin Pool <mbp@samba.org> is now a co-maintainer.
+
+### NEW FEATURES:
+
+ - Support for LSB-compliant packaging <http://www.linuxbase.org/>
+
+ - Shell wildcards are allowed in `auth users` lines.
+
+ - Merged UNC rsync+ patch to support creation of standalone patch sets. By
+ Bert J. Dempsey and Debra Weiss, updated by Jos Backus.
+ <http://www.ils.unc.edu/i2dsi/unc_rsync+.html>
+
+ - IPv6 support based on a patch from KAME.net, on systems including modern
+ versions of Linux, Solaris, and HP-UX. Also includes IPv6 compatibility
+ functions for old OSs by the Internet Software Consortium, Paul Vixie, the
+ OpenSSH portability project, and OpenBSD.
+
+### ENHANCEMENTS:
+
+ - Include/exclude cluestick: with `-vv`, print out whether files are included
+ or excluded and why.
+
+ - Many error messages have more friendly explanations and more details.
+
+ - Manual page improvements plus scanty protocol documentation.
+
+ - When running as `--daemon` in the background and using a `log file`
+ rsyncd.conf directive, close the log file every time it is open when going
+ to sleep on the socket. This allows the log file to get cleaned out by
+ another process.
+
+ - Change to using libpopt rather than getopt for processing options. This
+ makes the code cleaner and the behaviour more consistent across platforms.
+ popt is included and built if not installed on the platform.
+
+ - More details in `--version`, including note about whether 64-bit files,
+ symlinks and hardlinks are supported.
+
+ - MD4 code may use less CPU cycles.
+
+ - Use mkstemp on systems where it is secure. If we use mktemp, explain that we
+ do it in a secure way.
+
+ - `--whole-file` is the default when source and target are on the local
+ machine.
+
+### BUG FIXES:
+
+ - Fix for various bugs causing rsync to hang.
+
+ - Attempt to fix Large File Summit support on AIX.
+
+ - Attempt to fix error handling lockup bug.
+
+ - Give a non-0 exit code if **any** of the files we have been asked to
+ transfer fail to transfer.
+
+ - For log messages containing ridiculously long strings that might overflow a
+ buffer rsync no longer aborts, but rather prints an ellipsis at the end of
+ the string. (Patch from Ed Santiago.)
+
+### PLATFORMS:
+
+ - Improved support for UNICOS (tested on Cray T3E and Cray SV1)
+
+ - autoconf2.52 (or later) is now required to rebuild the autoconf scripts. It
+ is not required to simply build rsync.
+
+ - Platforms thought to work in this release:
+
+ - Cray SV1 UNICOS 10.0.0.8 cc
+ - Debian Linux 2.2 UltraSparc gcc
+ - Debian Linux testing/unstable ARM gcc
+ - FreeBSD 3.3-RELEASE i386 cc
+ - FreeBSD 4.1.1-RELEASE i386 cc
+ - FreeBSD 4.3-STABLE i386 cc
+ - HP PA-RISC HP-UX 10.20 gcc
+ - HP PA-RISC HP-UX 11.11 cc
+ - IRIX 6.5 MIPS cc
+ - IRIX 6.5 MIPS gcc
+ - Mac OS X PPC (`--disable-ipv6`) cc
+ - NetBSD 1.5 i386 gcc
+ - NetBSD Current i386 cc
+ - OpenBSD 2.5 Sparc gcc
+ - OpenBSD 2.9 i386 cc
+ - OpenBSD Current i386 cc
+ - RedHat 6.2 i386 gcc
+ - RedHat 6.2 i386 insure++
+ - RedHat 7.0 i386 gcc
+ - RedHat 7.1 i386 (Kernel 2.4.10) gcc
+ - Slackware 8.0 i686 (Kernel 2.4.10)
+ - Solaris 8 UltraSparc cc
+ - Solaris 8 UltraSparc gcc
+ - Solaris 8 i386 gcc
+ - SuSE 7.1 i386 gcc2.95.2
+ - SuSE 7.1 ppc gcc2.95.2
+ - i386-pc-sco3.2v5.0.5 cc
+ - i386-pc-sco3.2v5.0.5 gcc
+ - powerpc-ibm-aix4.3.3.0 cc
+ - i686-unknown-sysv5UnixWare7.1.0 gcc
+ - i686-unknown-sysv5UnixWare7.1.0 cc
+
+### TESTING:
+
+ - The existing test.sh script by Phil Hands has been merged into a
+ test framework that works from both `make check` and the Samba
+ build farm.
+
+------------------------------------------------------------------------------
+
+## Partial Protocol History
+
+| RELEASE DATE | VER. | DATE OF COMMIT\* | PROTOCOL |
+|--------------|--------|------------------|-------------|
+| 20 Oct 2022 | 3.2.7 | | 31 |
+| 09 Sep 2022 | 3.2.6 | | 31 |
+| 14 Aug 2022 | 3.2.5 | | 31 |
+| 15 Apr 2022 | 3.2.4 | | 31 |
+| 06 Aug 2020 | 3.2.3 | | 31 |
+| 04 Jul 2020 | 3.2.2 | | 31 |
+| 22 Jun 2020 | 3.2.1 | | 31 |
+| 19 Jun 2020 | 3.2.0 | | 31 |
+| 28 Jan 2018 | 3.1.3 | | 31 |
+| 21 Dec 2015 | 3.1.2 | | 31 |
+| 22 Jun 2014 | 3.1.1 | | 31 |
+| 28 Sep 2013 | 3.1.0 | 31 Aug 2008 | 31 |
+| 23 Sep 2011 | 3.0.9 | | 30 |
+| 26 Mar 2011 | 3.0.8 | | 30 |
+| 31 Dec 2009 | 3.0.7 | | 30 |
+| 08 May 2009 | 3.0.6 | | 30 |
+| 28 Dec 2008 | 3.0.5 | | 30 |
+| 06 Sep 2008 | 3.0.4 | | 30 |
+| 29 Jun 2008 | 3.0.3 | | 30 |
+| 08 Apr 2008 | 3.0.2 | | 30 |
+| 03 Apr 2008 | 3.0.1 | | 30 |
+| 01 Mar 2008 | 3.0.0 | 11 Nov 2006 | 30 |
+| 06 Nov 2006 | 2.6.9 | | 29 |
+| 22 Apr 2006 | 2.6.8 | | 29 |
+| 11 Mar 2006 | 2.6.7 | | 29 |
+| 28 Jul 2005 | 2.6.6 | | 29 |
+| 01 Jun 2005 | 2.6.5 | | 29 |
+| 30 Mar 2005 | 2.6.4 | 17 Jan 2005 | 29 |
+| 30 Sep 2004 | 2.6.3 | | 28 |
+| 30 Apr 2004 | 2.6.2 | | 28 |
+| 26 Apr 2004 | 2.6.1 | 08 Jan 2004 | 28 |
+| 01 Jan 2004 | 2.6.0 | 10 Apr 2003 | 27 (MAX=40) |
+| 04 Dec 2003 | 2.5.7 | | 26 |
+| 26 Jan 2003 | 2.5.6 | | 26 |
+| 02 Apr 2002 | 2.5.5 | | 26 |
+| 13 Mar 2002 | 2.5.4 | | 26 |
+| 11 Mar 2002 | 2.5.3 | | 26 |
+| 26 Jan 2002 | 2.5.2 | 11 Jan 2002 | 26 |
+| 03 Jan 2002 | 2.5.1 | | 25 |
+| 30 Nov 2001 | 2.5.0 | 23 Aug 2001 | 25 |
+| 06 Sep 2000 | 2.4.6 | | 24 |
+| 19 Aug 2000 | 2.4.5 | | 24 |
+| 29 Jul 2000 | 2.4.4 | | 24 |
+| 09 Apr 2000 | 2.4.3 | | 24 |
+| 30 Mar 2000 | 2.4.2 | | 24 |
+| 30 Jan 2000 | 2.4.1 | 29 Jan 2000 | 24 |
+| 29 Jan 2000 | 2.4.0 | 28 Jan 2000 | 23 |
+| 25 Jan 2000 | 2.3.3 | 23 Jan 2000 | 22 |
+| 08 Nov 1999 | 2.3.2 | 26 Jun 1999 | 21 |
+| 06 Apr 1999 | 2.3.1 | | 20 |
+| 15 Mar 1999 | 2.3.0 | 15 Mar 1999 | 20 |
+| 25 Nov 1998 | 2.2.1 | | 19 |
+| 03 Nov 1998 | 2.2.0 | | 19 |
+| 09 Sep 1998 | 2.1.1 | | 19 |
+| 20 Jul 1998 | 2.1.0 | | 19 |
+| 17 Jul 1998 | 2.0.19 | | 19 |
+| 18 Jun 1998 | 2.0.17 | | 19 |
+| 01 Jun 1998 | 2.0.16 | | 19 |
+| 27 May 1998 | 2.0.13 | 27 May 1998 | 19 |
+| 26 May 1998 | 2.0.12 | | 18 |
+| 22 May 1998 | 2.0.11 | | 18 |
+| 18 May 1998 | 2.0.9 | 18 May 1998 | 18 |
+| 17 May 1998 | 2.0.8 | | 17 |
+| 15 May 1998 | 2.0.1 | | 17 |
+| 14 May 1998 | 2.0.0 | | 17 |
+| 17 Apr 1998 | 1.7.4 | | 17 |
+| 13 Apr 1998 | 1.7.3 | | 17 |
+| 05 Apr 1998 | 1.7.2 | | 17 |
+| 26 Mar 1998 | 1.7.1 | | 17 |
+| 26 Mar 1998 | 1.7.0 | 26 Mar 1998 | 17 (MAX=30) |
+| 13 Jan 1998 | 1.6.9 | 13 Jan 1998 | 15 (MAX=20) |
+
+\* DATE OF COMMIT is the date the protocol change was committed to version
+control.
+
+@USE_GFM_PARSER@
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a86c771
--- /dev/null
+++ b/README.md
@@ -0,0 +1,144 @@
+WHAT IS RSYNC?
+--------------
+
+Rsync is a fast and extraordinarily versatile file copying tool for
+both remote and local files.
+
+Rsync uses a delta-transfer algorithm which provides a very fast method
+for bringing remote files into sync. It does this by sending just the
+differences in the files across the link, without requiring that both
+sets of files are present at one of the ends of the link beforehand. At
+first glance this may seem impossible because the calculation of diffs
+between two files normally requires local access to both files.
+
+A technical report describing the rsync algorithm is included with this
+package.
+
+
+USAGE
+-----
+
+Basically you use rsync just like scp, but rsync has many additional
+options. To get a complete list of supported options type:
+
+ rsync --help
+
+See the [manpage][0] for more detailed information.
+
+[0]: https://download.samba.org/pub/rsync/rsync.1
+
+BUILDING AND INSTALLING
+-----------------------
+
+If you need to build rsync yourself, check out the [INSTALL][1] page for
+information on what libraries and packages you can use to get the maximum
+features in your build.
+
+[1]: https://github.com/WayneD/rsync/blob/master/INSTALL.md
+
+SETUP
+-----
+
+Rsync normally uses ssh or rsh for communication with remote systems.
+It does not need to be setuid and requires no special privileges for
+installation. You must, however, have a working ssh or rsh system.
+Using ssh is recommended for its security features.
+
+Alternatively, rsync can run in `daemon' mode, listening on a socket.
+This is generally used for public file distribution, although
+authentication and access control are available.
+
+To install rsync, first run the "configure" script. This will create a
+Makefile and config.h appropriate for your system. Then type "make".
+
+Note that on some systems you will have to force configure not to use
+gcc because gcc may not support some features (such as 64 bit file
+offsets) that your system may support. Set the environment variable CC
+to the name of your native compiler before running configure in this
+case.
+
+Once built put a copy of rsync in your search path on the local and
+remote systems (or use "make install"). That's it!
+
+
+RSYNC DAEMONS
+-------------
+
+Rsync can also talk to "rsync daemons" which can provide anonymous or
+authenticated rsync. See the rsyncd.conf(5) manpage for details on how
+to setup an rsync daemon. See the rsync(1) manpage for info on how to
+connect to an rsync daemon.
+
+
+WEB SITE
+--------
+
+For more information, visit the [main rsync web site][2].
+
+[2]: https://rsync.samba.org/
+
+You'll find a FAQ list, downloads, resources, HTML versions of the
+manpages, etc.
+
+
+MAILING LISTS
+-------------
+
+There is a mailing list for the discussion of rsync and its applications
+that is open to anyone to join. New releases are announced on this
+list, and there is also an announcement-only mailing list for those that
+want official announcements. See the [mailing-list page][3] for full
+details.
+
+[3]: https://rsync.samba.org/lists.html
+
+
+BUG REPORTS
+-----------
+
+The [bug-tracking web page][4] has full details on bug reporting.
+
+[4]: https://rsync.samba.org/bug-tracking.html
+
+That page contains links to the current bug list, and information on how to
+do a good job when reporting a bug. You might also like to try searching
+the Internet for the error message you've received, or looking in the
+[mailing list archives][5].
+
+[5]: https://mail-archive.com/rsync@lists.samba.org/
+
+To send a bug report, follow the instructions on the bug-tracking
+page of the web site.
+
+Alternately, email your bug report to <rsync@lists.samba.org>.
+
+
+GIT REPOSITORY
+--------------
+
+If you want to get the very latest version of rsync direct from the
+source code repository, then you will need to use git. The git repo
+is hosted [on GitHub][6] and [on Samba's site][7].
+
+[6]: https://github.com/WayneD/rsync
+[7]: https://git.samba.org/?p=rsync.git;a=summary
+
+See [the download page][8] for full details on all the ways to grab the
+source.
+
+[8]: https://rsync.samba.org/download.html
+
+
+COPYRIGHT
+---------
+
+Rsync was originally written by Andrew Tridgell and is currently
+maintained by Wayne Davison. It has been improved by many developers
+from around the world.
+
+Rsync may be used, modified and redistributed only under the terms of
+the GNU General Public License, found in the file [COPYING][9] in this
+distribution, or at [the Free Software Foundation][10].
+
+[9]: https://github.com/WayneD/rsync/blob/master/COPYING
+[10]: https://www.fsf.org/licenses/gpl.html
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..c243574
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Security Policy
+
+## Supported Versions
+
+Only the current release of the software is actively supported. If you need
+help backporting fixes into an older release, feel free to ask.
+
+## Reporting a Vulnerability
+
+Email your vulnerability information to rsync's maintainer:
+
+ Wayne Davison <wayne@opencoder.net>
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..66ae838
--- /dev/null
+++ b/TODO
@@ -0,0 +1,528 @@
+-*- indented-text -*-
+
+FEATURES ------------------------------------------------------------
+Use chroot only if supported
+Allow supplementary groups in rsyncd.conf 2002/04/09
+Handling IPv6 on old machines
+Other IPv6 stuff
+Add ACL support 2001/12/02
+proxy authentication 2002/01/23
+SOCKS 2002/01/23
+FAT support
+--diff david.e.sewell 2002/03/15
+Add daemon --no-fork option
+Create more granular verbosity 2003/05/15
+
+DOCUMENTATION --------------------------------------------------------
+Keep list of open issues and todos on the web site
+Perhaps redo manual as SGML
+
+LOGGING --------------------------------------------------------------
+Memory accounting
+Improve error messages
+Better statistics Rasmus 2002/03/08
+Perhaps flush stdout like syslog
+Log child death on signal
+verbose output David Stein 2001/12/20
+internationalization
+
+DEVELOPMENT --------------------------------------------------------
+Handling duplicate names
+Use generic zlib 2002/02/25
+TDB 2002/03/12
+Splint 2002/03/12
+
+PERFORMANCE ----------------------------------------------------------
+Traverse just one directory at a time
+Allow skipping MD4 file_sum 2002/04/08
+Accelerate MD4
+
+TESTING --------------------------------------------------------------
+Torture test
+Cross-test versions 2001/08/22
+Test on kernel source
+Test large files
+Create mutator program for testing
+Create configure option to enable dangerous tests
+Create pipe program for testing
+Create test makefile target for some tests
+
+RELATED PROJECTS -----------------------------------------------------
+rsyncsh
+https://rsync.samba.org/rsync-and-debian/
+rsyncable gzip patch
+rsyncsplit as alternative to real integration with gzip?
+reverse rsync over HTTP Range
+
+
+
+FEATURES ------------------------------------------------------------
+
+
+Use chroot only if supported
+
+ If the platform doesn't support it, then don't even try.
+
+ If running as non-root, then don't fail, just give a warning.
+ (There was a thread about this a while ago?)
+
+ https://lists.samba.org/pipermail/rsync/2001-August/thread.html
+ https://lists.samba.org/pipermail/rsync/2001-September/thread.html
+
+ -- --
+
+
+Allow supplementary groups in rsyncd.conf 2002/04/09
+
+ Perhaps allow supplementary groups to be specified in rsyncd.conf;
+ then make the first one the primary gid and all the rest be
+ supplementary gids.
+
+ -- --
+
+
+Handling IPv6 on old machines
+
+ The KAME IPv6 patch is nice in theory but has proved a bit of a
+ nightmare in practice. The basic idea of their patch is that rsync
+ is rewritten to use the new getaddrinfo()/getnameinfo() interface,
+ rather than gethostbyname()/gethostbyaddr() as in rsync 2.4.6.
+ Systems that don't have the new interface are handled by providing
+ our own implementation in lib/, which is selectively linked in.
+
+ The problem with this is that it is really hard to get right on
+ platforms that have a half-working implementation, so redefining
+ these functions clashes with system headers, and leaving them out
+ breaks. This affects at least OSF/1, RedHat 5, and Cobalt, which
+ are moderately important.
+
+ Perhaps the simplest solution would be to have two different files
+ implementing the same interface, and choose either the new or the
+ old API. This is probably necessary for systems that e.g. have
+ IPv6, but gethostbyaddr() can't handle it. The Linux manpage claims
+ this is currently the case.
+
+ In fact, our internal sockets interface (things like
+ open_socket_out(), etc) is much narrower than the getaddrinfo()
+ interface, and so probably simpler to get right. In addition, the
+ old code is known to work well on old machines.
+
+ We could drop the rather large lib/getaddrinfo files.
+
+ -- --
+
+
+Other IPv6 stuff
+
+ Implement suggestions from http://www.kame.net/newsletter/19980604/
+ and ftp://ftp.iij.ad.jp/pub/RFC/rfc2553.txt
+
+ If a host has multiple addresses, then listen try to connect to all
+ in order until we get through. (getaddrinfo may return multiple
+ addresses.) This is kind of implemented already.
+
+ Possibly also when starting as a server we may need to listen on
+ multiple passive addresses. This might be a bit harder, because we
+ may need to select on all of them. Hm.
+
+ -- --
+
+
+Add ACL support 2001/12/02
+
+ Transfer ACLs. Need to think of a standard representation.
+ Probably better not to even try to convert between NT and POSIX.
+ Possibly can share some code with Samba.
+ NOTE: there is a patch that implements this in the "patches" subdir.
+
+ -- --
+
+
+proxy authentication 2002/01/23
+
+ Allow RSYNC_PROXY to be http://user:pass@proxy.foo:3128/, and do
+ HTTP Basic Proxy-Authentication.
+
+ Multiple schemes are possible, up to and including the insanity that
+ is NTLM, but Basic probably covers most cases.
+
+ -- --
+
+
+SOCKS 2002/01/23
+
+ Add --with-socks, and then perhaps a command-line option to put them
+ on or off. This might be more reliable than LD_PRELOAD hacks.
+
+ -- --
+
+
+FAT support
+
+ rsync to a FAT partition on a Unix machine doesn't work very well at
+ the moment. I think we get errors about invalid filenames and
+ perhaps also trying to do atomic renames.
+
+ I guess the code to do this is currently #ifdef'd on Windows;
+ perhaps we ought to intelligently fall back to it on Unix too.
+
+ -- --
+
+
+--diff david.e.sewell 2002/03/15
+
+ Allow people to specify the diff command. (Might want to use wdiff,
+ gnudiff, etc.)
+
+ Just diff the temporary file with the destination file, and delete
+ the tmp file rather than moving it into place.
+
+ Interaction with --partial.
+
+ Security interactions with daemon mode?
+
+ -- --
+
+
+Add daemon --no-fork option
+
+ Very useful for debugging. Also good when running under a
+ daemon-monitoring process that tries to restart the service when the
+ parent exits.
+
+ -- --
+
+
+Create more granular verbosity 2003/05/15
+
+ Control output with the --report option.
+
+ The option takes as a single argument (no whitespace) a
+ comma delimited lists of keywords.
+
+ This would separate debugging from "logging" as well as
+ fine grained selection of statistical reporting and what
+ actions are logged.
+
+ https://lists.samba.org/archive/rsync/2003-May/006059.html
+
+ -- --
+
+DOCUMENTATION --------------------------------------------------------
+
+
+Keep list of open issues and todos on the web site
+
+ -- --
+
+
+Perhaps redo manual as SGML
+
+ The man page is getting rather large, and there is more information
+ that ought to be added.
+
+ TexInfo source is probably a dying format.
+
+ Linuxdoc looks like the most likely contender. I know DocBook is
+ favoured by some people, but it's so bloody verbose, even with emacs
+ support.
+
+ -- --
+
+LOGGING --------------------------------------------------------------
+
+
+Memory accounting
+
+ At exit, show how much memory was used for the file list, etc.
+
+ We also do a weird exponential-growth allocation in flist.c. I'm
+ not sure this makes sense with modern mallocs. At any rate it will
+ make us allocate a huge amount of memory for large file lists.
+
+ -- --
+
+
+Improve error messages
+
+ If we hang or get SIGINT, then explain where we were up to. Perhaps
+ have a static buffer that contains the current function name, or
+ some kind of description of what we were trying to do. This is a
+ little easier on people than needing to run strace/truss.
+
+ "The dungeon collapses! You are killed." Rather than "unexpected
+ eof" give a message that is more detailed if possible and also more
+ helpful.
+
+ If we get an error writing to a socket, then we should perhaps
+ continue trying to read to see if an error message comes across
+ explaining why the socket is closed. I'm not sure if this would
+ work, but it would certainly make our messages more helpful.
+
+ What happens if a directory is missing -x attributes. Do we lose
+ our load? (Debian #28416) Probably fixed now, but a test case would
+ be good.
+
+ -- --
+
+
+Better statistics Rasmus 2002/03/08
+
+ <Rasmus>
+ hey, how about an rsync option that just gives you the
+ summary without the list of files? And perhaps gives
+ more information like the number of new files, number
+ of changed, deleted, etc. ?
+
+ <mbp>
+ nice idea there is --stats but at the moment it's very
+ tridge-oriented rather than user-friendly it would be
+ nice to improve it that would also work well with
+ --dryrun
+
+ -- --
+
+
+Perhaps flush stdout like syslog
+
+ Perhaps flush stdout after each filename, so that people trying to
+ monitor progress in a log file can do so more easily. See
+ https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=48108
+
+ -- --
+
+
+Log child death on signal
+
+ If a child of the rsync daemon dies with a signal, we should notice
+ that when we reap it and log a message.
+
+ -- --
+
+
+verbose output David Stein 2001/12/20
+
+ At end of transfer, show how many files were or were not transferred
+ correctly.
+
+ -- --
+
+
+internationalization
+
+ Change to using gettext(). Probably need to ship this for platforms
+ that don't have it.
+
+ Solicit translations.
+
+ Does anyone care? Before we bother modifying the code, we ought to
+ get the manual translated first, because that's possibly more useful
+ and at any rate demonstrates desire.
+
+ -- --
+
+DEVELOPMENT --------------------------------------------------------
+
+Handling duplicate names
+
+ Some folks would like rsync to be deterministic in how it handles
+ duplicate names that come from mering multiple source directories
+ into a single destination directory; e.g. the last name wins. We
+ could do this by switching our sort algorithm to one that will
+ guarantee that the names won't be reordered. Alternately, we could
+ assign an ever-increasing number to each item as we insert it into
+ the list and then make sure that we leave the largest number when
+ cleaning the file list (see clean_flist()). Another solution would
+ be to add a hash table, and thus never put any duplicate names into
+ the file list (and bump the protocol to handle this).
+
+ -- --
+
+
+Use generic zlib 2002/02/25
+
+ Perhaps don't use our own zlib.
+
+ Advantages:
+
+ - will automatically be up to date with bugfixes in zlib
+
+ - can leave it out for small rsync on e.g. recovery disks
+
+ - can use a shared library
+
+ - avoids people breaking rsync by trying to do this themselves and
+ messing up
+
+ Should we ship zlib for systems that don't have it, or require
+ people to install it separately?
+
+ Apparently this will make us incompatible with versions of rsync
+ that use the patched version of rsync. Probably the simplest way to
+ do this is to just disable gzip (with a warning) when talking to old
+ versions.
+
+ -- --
+
+
+Splint 2002/03/12
+
+ Build rsync with SPLINT to try to find security holes. Add
+ annotations as necessary. Keep track of the number of warnings
+ found initially, and see how many of them are real bugs, or real
+ security bugs. Knowing the percentage of likely hits would be
+ really interesting for other projects.
+
+ -- --
+
+PERFORMANCE ----------------------------------------------------------
+
+Allow skipping MD4 file_sum 2002/04/08
+
+ If we're doing a local transfer, or using -W, then perhaps don't
+ send the file checksum. If we're doing a local transfer, then
+ calculating MD4 checksums uses 90% of CPU and is unlikely to be
+ useful.
+
+ We should not allow it to be disabled separately from -W, though
+ as it is the only thing that lets us know when the rsync algorithm
+ got out of sync and messed the file up (i.e. if the basis file
+ changed between checksum generation and reception).
+
+ -- --
+
+
+Accelerate MD4
+
+ Perhaps borrow an assembler MD4 from someone?
+
+ Make sure we call MD4 with properly-sized blocks whenever possible
+ to avoid copying into the residue region?
+
+ -- --
+
+TESTING --------------------------------------------------------------
+
+Torture test
+
+ Something that just keeps running rsync continuously over a data set
+ likely to generate problems.
+
+ -- --
+
+
+Cross-test versions 2001/08/22
+
+ Part of the regression suite should be making sure that we
+ don't break backwards compatibility: old clients vs new
+ servers and so on. Ideally we would test both up and down
+ from the current release to all old versions.
+
+ Run current rsync versions against significant past releases.
+
+ We might need to omit broken old versions, or versions in which
+ particular functionality is broken
+
+ It might be sufficient to test downloads from well-known public
+ rsync servers running different versions of rsync. This will give
+ some testing and also be the most common case for having different
+ versions and not being able to upgrade.
+
+ The new --protocol option may help in this.
+
+ -- --
+
+
+Test on kernel source
+
+ Download all versions of kernel; unpack, sync between them. Also
+ sync between uncompressed tarballs. Compare directories after
+ transfer.
+
+ Use local mode; ssh; daemon; --whole-file and --no-whole-file.
+
+ Use awk to pull out the 'speedup' number for each transfer. Make
+ sure it is >= x.
+
+ -- --
+
+
+Test large files
+
+ Sparse and non-sparse
+
+ -- --
+
+
+Create mutator program for testing
+
+ Insert bytes, delete bytes, swap blocks, ...
+
+ -- --
+
+
+Create configure option to enable dangerous tests
+
+ -- --
+
+
+Create pipe program for testing
+
+ Create pipe program that makes slow/jerky connections for
+ testing Versions of read() and write() that corrupt the
+ stream, or abruptly fail
+
+ -- --
+
+
+Create test makefile target for some tests
+
+ Separate makefile target to run rough tests -- or perhaps
+ just run them every time?
+
+ -- --
+
+RELATED PROJECTS -----------------------------------------------------
+
+rsyncsh
+
+ Write a small emulation of interactive ftp as a Pythonn program
+ that calls rsync. Commands such as "cd", "ls", "ls *.c" etc map
+ fairly directly into rsync commands: it just needs to remember the
+ current host, directory and so on. We can probably even do
+ completion of remote filenames.
+
+ -- --
+
+
+https://rsync.samba.org/rsync-and-debian/
+
+
+ -- --
+
+
+rsyncable gzip patch
+
+ Exhaustive, tortuous testing
+
+ Cleanups?
+
+ -- --
+
+
+rsyncsplit as alternative to real integration with gzip?
+
+ -- --
+
+
+reverse rsync over HTTP Range
+
+ Goswin Brederlow suggested this on Debian; I think tridge and I
+ talked about it previous in relation to rproxy.
+
+ Addendum: It looks like someone is working on a version of this:
+
+ http://zsync.moria.org.uk/
+
+ -- --
+
diff --git a/access.c b/access.c
new file mode 100644
index 0000000..b6afce3
--- /dev/null
+++ b/access.c
@@ -0,0 +1,292 @@
+/*
+ * Routines to authenticate access to a daemon (hosts allow/deny).
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2004-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#ifdef HAVE_NETGROUP_H
+#include <netgroup.h>
+#endif
+
+static int allow_forward_dns;
+
+extern const char undetermined_hostname[];
+
+static int match_hostname(const char **host_ptr, const char *addr, const char *tok)
+{
+ struct hostent *hp;
+ unsigned int i;
+ const char *host = *host_ptr;
+
+ if (!host || !*host)
+ return 0;
+
+#ifdef HAVE_INNETGR
+ if (*tok == '@' && tok[1])
+ return innetgr(tok + 1, host, NULL, NULL);
+#endif
+
+ /* First check if the reverse-DNS-determined hostname matches. */
+ if (iwildmatch(tok, host))
+ return 1;
+
+ if (!allow_forward_dns)
+ return 0;
+
+ /* Fail quietly if tok is an address or wildcarded entry, not a simple hostname. */
+ if (!tok[strspn(tok, ".0123456789")] || tok[strcspn(tok, ":/*?[")])
+ return 0;
+
+ /* Now try forward-DNS on the token (config-specified hostname) and see if the IP matches. */
+ if (!(hp = gethostbyname(tok)))
+ return 0;
+
+ for (i = 0; hp->h_addr_list[i] != NULL; i++) {
+ if (strcmp(addr, inet_ntoa(*(struct in_addr*)(hp->h_addr_list[i]))) == 0) {
+ /* If reverse lookups are off, we'll use the conf-specified
+ * hostname in preference to UNDETERMINED. */
+ if (host == undetermined_hostname)
+ *host_ptr = strdup(tok);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int match_binary(const char *b1, const char *b2, const char *mask, int addrlen)
+{
+ int i;
+
+ for (i = 0; i < addrlen; i++) {
+ if ((b1[i] ^ b2[i]) & mask[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+static void make_mask(char *mask, int plen, int addrlen)
+{
+ int w, b;
+
+ w = plen >> 3;
+ b = plen & 0x7;
+
+ if (w)
+ memset(mask, 0xff, w);
+ if (w < addrlen)
+ mask[w] = 0xff & (0xff<<(8-b));
+ if (w+1 < addrlen)
+ memset(mask+w+1, 0, addrlen-w-1);
+
+ return;
+}
+
+static int match_address(const char *addr, const char *tok)
+{
+ char *p;
+ struct addrinfo hints, *resa, *rest;
+ int gai;
+ int ret = 0;
+ int addrlen = 0;
+#ifdef HAVE_STRTOL
+ long int bits;
+#else
+ int bits;
+#endif
+ char mask[16];
+ char *a = NULL, *t = NULL;
+
+ if (!addr || !*addr)
+ return 0;
+
+ p = strchr(tok,'/');
+ if (p)
+ *p = '\0';
+
+ /* Fail quietly if tok is a hostname, not an address. */
+ if (tok[strspn(tok, ".0123456789")] && strchr(tok, ':') == NULL) {
+ if (p)
+ *p = '/';
+ return 0;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_NUMERICHOST
+ hints.ai_flags = AI_NUMERICHOST;
+#endif
+
+ if (getaddrinfo(addr, NULL, &hints, &resa) != 0) {
+ if (p)
+ *p = '/';
+ return 0;
+ }
+
+ gai = getaddrinfo(tok, NULL, &hints, &rest);
+ if (p)
+ *p++ = '/';
+ if (gai != 0) {
+ rprintf(FLOG, "error matching address %s: %s\n",
+ tok, gai_strerror(gai));
+ freeaddrinfo(resa);
+ return 0;
+ }
+
+ if (rest->ai_family != resa->ai_family) {
+ ret = 0;
+ goto out;
+ }
+
+ switch(resa->ai_family) {
+ case PF_INET:
+ a = (char *)&((struct sockaddr_in *)resa->ai_addr)->sin_addr;
+ t = (char *)&((struct sockaddr_in *)rest->ai_addr)->sin_addr;
+ addrlen = 4;
+
+ break;
+
+#ifdef INET6
+ case PF_INET6: {
+ struct sockaddr_in6 *sin6a, *sin6t;
+
+ sin6a = (struct sockaddr_in6 *)resa->ai_addr;
+ sin6t = (struct sockaddr_in6 *)rest->ai_addr;
+
+ a = (char *)&sin6a->sin6_addr;
+ t = (char *)&sin6t->sin6_addr;
+
+ addrlen = 16;
+
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ if (sin6t->sin6_scope_id && sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
+ ret = 0;
+ goto out;
+ }
+#endif
+
+ break;
+ }
+#endif
+ default:
+ rprintf(FLOG, "unknown family %u\n", rest->ai_family);
+ ret = 0;
+ goto out;
+ }
+
+ bits = -1;
+ if (p) {
+ if (inet_pton(resa->ai_addr->sa_family, p, mask) <= 0) {
+#ifdef HAVE_STRTOL
+ char *ep = NULL;
+#else
+ unsigned char *pp;
+#endif
+
+#ifdef HAVE_STRTOL
+ bits = strtol(p, &ep, 10);
+ if (!*p || *ep) {
+ rprintf(FLOG, "malformed mask in %s\n", tok);
+ ret = 0;
+ goto out;
+ }
+#else
+ for (pp = (unsigned char *)p; *pp; pp++) {
+ if (!isascii(*pp) || !isdigit(*pp)) {
+ rprintf(FLOG, "malformed mask in %s\n", tok);
+ ret = 0;
+ goto out;
+ }
+ }
+ bits = atoi(p);
+#endif
+ if (bits == 0) {
+ ret = 1;
+ goto out;
+ }
+ if (bits < 0 || bits > (addrlen << 3)) {
+ rprintf(FLOG, "malformed mask in %s\n", tok);
+ ret = 0;
+ goto out;
+ }
+ }
+ } else {
+ bits = 128;
+ }
+
+ if (bits >= 0)
+ make_mask(mask, bits, addrlen);
+
+ ret = match_binary(a, t, mask, addrlen);
+
+ out:
+ freeaddrinfo(resa);
+ freeaddrinfo(rest);
+ return ret;
+}
+
+static int access_match(const char *list, const char *addr, const char **host_ptr)
+{
+ char *tok;
+ char *list2 = strdup(list);
+
+ strlower(list2);
+
+ for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
+ if (match_hostname(host_ptr, addr, tok) || match_address(addr, tok)) {
+ free(list2);
+ return 1;
+ }
+ }
+
+ free(list2);
+ return 0;
+}
+
+int allow_access(const char *addr, const char **host_ptr, int i)
+{
+ const char *allow_list = lp_hosts_allow(i);
+ const char *deny_list = lp_hosts_deny(i);
+
+ if (allow_list && !*allow_list)
+ allow_list = NULL;
+ if (deny_list && !*deny_list)
+ deny_list = NULL;
+
+ allow_forward_dns = lp_forward_lookup(i);
+
+ /* If we match an allow-list item, we always allow access. */
+ if (allow_list) {
+ if (access_match(allow_list, addr, host_ptr))
+ return 1;
+ /* For an allow-list w/o a deny-list, disallow non-matches. */
+ if (!deny_list)
+ return 0;
+ }
+
+ /* If we match a deny-list item (and got past any allow-list
+ * items), we always disallow access. */
+ if (deny_list && access_match(deny_list, addr, host_ptr))
+ return 0;
+
+ /* Allow all other access. */
+ return 1;
+}
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..62b3bc3
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,17 @@
+# generated automatically by aclocal 1.16.5 -*- Autoconf -*-
+
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+m4_include([../m4/have_type.m4])
+m4_include([../m4/header_major_fixed.m4])
+m4_include([../m4/socklen_t.m4])
diff --git a/acls.c b/acls.c
new file mode 100644
index 0000000..3cf12ee
--- /dev/null
+++ b/acls.c
@@ -0,0 +1,1140 @@
+/*
+ * Handle passing Access Control Lists between systems.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2006-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "lib/sysacls.h"
+
+#ifdef SUPPORT_ACLS
+
+extern int dry_run;
+extern int am_root;
+extern int read_only;
+extern int list_only;
+extern int orig_umask;
+extern int numeric_ids;
+extern int inc_recurse;
+extern int preserve_devices;
+extern int preserve_specials;
+
+/* Flags used to indicate what items are being transmitted for an entry. */
+#define XMIT_USER_OBJ (1<<0)
+#define XMIT_GROUP_OBJ (1<<1)
+#define XMIT_MASK_OBJ (1<<2)
+#define XMIT_OTHER_OBJ (1<<3)
+#define XMIT_NAME_LIST (1<<4)
+
+#define NO_ENTRY ((uchar)0x80) /* Default value of a NON-name-list entry. */
+
+#define NAME_IS_USER (1u<<31) /* Bit used only on a name-list entry. */
+
+/* When we send the access bits over the wire, we shift them 2 bits to the
+ * left and use the lower 2 bits as flags (relevant only to a name entry).
+ * This makes the protocol more efficient than sending a value that would
+ * be likely to have its highest bits set. */
+#define XFLAG_NAME_FOLLOWS 0x0001u
+#define XFLAG_NAME_IS_USER 0x0002u
+
+/* === ACL structures === */
+
+typedef struct {
+ id_t id;
+ uint32 access;
+} id_access;
+
+typedef struct {
+ id_access *idas;
+ int count;
+} ida_entries;
+
+typedef struct {
+ char *name;
+ uchar len;
+} idname;
+
+typedef struct rsync_acl {
+ ida_entries names;
+ /* These will be NO_ENTRY if there's no such entry. */
+ uchar user_obj;
+ uchar group_obj;
+ uchar mask_obj;
+ uchar other_obj;
+} rsync_acl;
+
+typedef struct {
+ rsync_acl racl;
+ SMB_ACL_T sacl;
+} acl_duo;
+
+static const rsync_acl empty_rsync_acl = {
+ {NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY
+};
+
+static item_list access_acl_list = EMPTY_ITEM_LIST;
+static item_list default_acl_list = EMPTY_ITEM_LIST;
+
+static size_t prior_access_count = (size_t)-1;
+static size_t prior_default_count = (size_t)-1;
+
+/* === Calculations on ACL types === */
+
+static const char *str_acl_type(SMB_ACL_TYPE_T type)
+{
+ switch (type) {
+ case SMB_ACL_TYPE_ACCESS:
+#ifdef HAVE_OSX_ACLS
+ return "ACL_TYPE_EXTENDED";
+#else
+ return "ACL_TYPE_ACCESS";
+#endif
+ case SMB_ACL_TYPE_DEFAULT:
+ return "ACL_TYPE_DEFAULT";
+ default:
+ break;
+ }
+ return "unknown ACL type!";
+}
+
+static int calc_sacl_entries(const rsync_acl *racl)
+{
+ /* A System ACL always gets user/group/other permission entries. */
+ return racl->names.count
+#ifdef ACLS_NEED_MASK
+ + 1
+#else
+ + (racl->mask_obj != NO_ENTRY)
+#endif
+ + 3;
+}
+
+/* Extracts and returns the permission bits from the ACL. This cannot be
+ * called on an rsync_acl that has NO_ENTRY in any spot but the mask. */
+static int rsync_acl_get_perms(const rsync_acl *racl)
+{
+ return (racl->user_obj << 6)
+ + ((racl->mask_obj != NO_ENTRY ? racl->mask_obj : racl->group_obj) << 3)
+ + racl->other_obj;
+}
+
+/* Removes the permission-bit entries from the ACL because these
+ * can be reconstructed from the file's mode. */
+static void rsync_acl_strip_perms(stat_x *sxp)
+{
+ rsync_acl *racl = sxp->acc_acl;
+
+ racl->user_obj = NO_ENTRY;
+ if (racl->mask_obj == NO_ENTRY)
+ racl->group_obj = NO_ENTRY;
+ else {
+ int group_perms = (sxp->st.st_mode >> 3) & 7;
+ if (racl->group_obj == group_perms)
+ racl->group_obj = NO_ENTRY;
+#ifndef HAVE_SOLARIS_ACLS
+ if (racl->names.count != 0 && racl->mask_obj == group_perms)
+ racl->mask_obj = NO_ENTRY;
+#endif
+ }
+ racl->other_obj = NO_ENTRY;
+}
+
+/* Given an empty rsync_acl, fake up the permission bits. */
+static void rsync_acl_fake_perms(rsync_acl *racl, mode_t mode)
+{
+ racl->user_obj = (mode >> 6) & 7;
+ racl->group_obj = (mode >> 3) & 7;
+ racl->other_obj = mode & 7;
+}
+
+/* === Rsync ACL functions === */
+
+static rsync_acl *create_racl(void)
+{
+ rsync_acl *racl = new(rsync_acl);
+
+ *racl = empty_rsync_acl;
+
+ return racl;
+}
+
+static BOOL ida_entries_equal(const ida_entries *ial1, const ida_entries *ial2)
+{
+ id_access *ida1, *ida2;
+ int count = ial1->count;
+ if (count != ial2->count)
+ return False;
+ ida1 = ial1->idas;
+ ida2 = ial2->idas;
+ for (; count--; ida1++, ida2++) {
+ if (ida1->access != ida2->access || ida1->id != ida2->id)
+ return False;
+ }
+ return True;
+}
+
+static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2)
+{
+ return racl1->user_obj == racl2->user_obj
+ && racl1->group_obj == racl2->group_obj
+ && racl1->mask_obj == racl2->mask_obj
+ && racl1->other_obj == racl2->other_obj
+ && ida_entries_equal(&racl1->names, &racl2->names);
+}
+
+/* Are the extended (non-permission-bit) entries equal? If so, the rest of
+ * the ACL will be handled by the normal mode-preservation code. This is
+ * only meaningful for access ACLs! Note: the 1st arg is a fully-populated
+ * rsync_acl, but the 2nd parameter can be a condensed rsync_acl, which means
+ * that it might have several of its permission objects set to NO_ENTRY. */
+static BOOL rsync_acl_equal_enough(const rsync_acl *racl1,
+ const rsync_acl *racl2, mode_t m)
+{
+ if ((racl1->mask_obj ^ racl2->mask_obj) & NO_ENTRY)
+ return False; /* One has a mask and the other doesn't */
+
+ /* When there's a mask, the group_obj becomes an extended entry. */
+ if (racl1->mask_obj != NO_ENTRY) {
+ /* A condensed rsync_acl with a mask can only have no
+ * group_obj when it was identical to the mask. This
+ * means that it was also identical to the group attrs
+ * from the mode. */
+ if (racl2->group_obj == NO_ENTRY) {
+ if (racl1->group_obj != ((m >> 3) & 7))
+ return False;
+ } else if (racl1->group_obj != racl2->group_obj)
+ return False;
+ }
+ return ida_entries_equal(&racl1->names, &racl2->names);
+}
+
+static void rsync_acl_free(rsync_acl *racl)
+{
+ if (racl->names.idas)
+ free(racl->names.idas);
+ *racl = empty_rsync_acl;
+}
+
+void free_acl(stat_x *sxp)
+{
+ if (sxp->acc_acl) {
+ rsync_acl_free(sxp->acc_acl);
+ free(sxp->acc_acl);
+ sxp->acc_acl = NULL;
+ }
+ if (sxp->def_acl) {
+ rsync_acl_free(sxp->def_acl);
+ free(sxp->def_acl);
+ sxp->def_acl = NULL;
+ }
+}
+
+#ifdef SMB_ACL_NEED_SORT
+static int id_access_sorter(const void *r1, const void *r2)
+{
+ id_access *ida1 = (id_access *)r1;
+ id_access *ida2 = (id_access *)r2;
+ id_t rid1 = ida1->id, rid2 = ida2->id;
+ if ((ida1->access ^ ida2->access) & NAME_IS_USER)
+ return ida1->access & NAME_IS_USER ? -1 : 1;
+ return rid1 == rid2 ? 0 : rid1 < rid2 ? -1 : 1;
+}
+#endif
+
+/* === System ACLs === */
+
+/* Unpack system ACL -> rsync ACL verbatim. Return whether we succeeded. */
+static BOOL unpack_smb_acl(SMB_ACL_T sacl, rsync_acl *racl)
+{
+ static item_list temp_ida_list = EMPTY_ITEM_LIST;
+ SMB_ACL_ENTRY_T entry;
+ const char *errfun;
+ int rc;
+
+ errfun = "sys_acl_get_entry";
+ for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry);
+ rc == 1;
+ rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {
+ SMB_ACL_TAG_T tag_type;
+ uint32 access;
+ id_t g_u_id;
+ id_access *ida;
+ if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0) {
+ errfun = "sys_acl_get_info";
+ break;
+ }
+ /* continue == done with entry; break == store in temporary ida list */
+ switch (tag_type) {
+#ifndef HAVE_OSX_ACLS
+ case SMB_ACL_USER_OBJ:
+ if (racl->user_obj == NO_ENTRY)
+ racl->user_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate USER_OBJ entry ignored\n");
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ if (racl->group_obj == NO_ENTRY)
+ racl->group_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate GROUP_OBJ entry ignored\n");
+ continue;
+ case SMB_ACL_MASK:
+ if (racl->mask_obj == NO_ENTRY)
+ racl->mask_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate MASK entry ignored\n");
+ continue;
+ case SMB_ACL_OTHER:
+ if (racl->other_obj == NO_ENTRY)
+ racl->other_obj = access;
+ else
+ rprintf(FINFO, "unpack_smb_acl: warning: duplicate OTHER entry ignored\n");
+ continue;
+#endif
+ case SMB_ACL_USER:
+ access |= NAME_IS_USER;
+ break;
+ case SMB_ACL_GROUP:
+ break;
+ default:
+ rprintf(FINFO, "unpack_smb_acl: warning: entry with unrecognized tag type ignored\n");
+ continue;
+ }
+ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10);
+ ida->id = g_u_id;
+ ida->access = access;
+ }
+ if (rc) {
+ rsyserr(FERROR_XFER, errno, "unpack_smb_acl: %s()", errfun);
+ rsync_acl_free(racl);
+ return False;
+ }
+
+ /* Transfer the count id_access items out of the temp_ida_list
+ * into the names ida_entries list in racl. */
+ if (temp_ida_list.count) {
+#ifdef SMB_ACL_NEED_SORT
+ if (temp_ida_list.count > 1) {
+ qsort(temp_ida_list.items, temp_ida_list.count, sizeof (id_access), id_access_sorter);
+ }
+#endif
+ racl->names.idas = new_array(id_access, temp_ida_list.count);
+ memcpy(racl->names.idas, temp_ida_list.items, temp_ida_list.count * sizeof (id_access));
+ } else
+ racl->names.idas = NULL;
+
+ racl->names.count = temp_ida_list.count;
+
+ /* Truncate the temporary list now that its idas have been saved. */
+ temp_ida_list.count = 0;
+
+ return True;
+}
+
+/* Synactic sugar for system calls */
+
+#define CALL_OR_ERROR(func,args,str) \
+ do { \
+ if (func args) { \
+ errfun = str; \
+ goto error_exit; \
+ } \
+ } while (0)
+
+#define COE(func,args) CALL_OR_ERROR(func,args,#func)
+#define COE2(func,args) CALL_OR_ERROR(func,args,NULL)
+
+#ifndef HAVE_OSX_ACLS
+/* Store the permissions in the system ACL entry. */
+static int store_access_in_entry(uint32 access, SMB_ACL_ENTRY_T entry)
+{
+ if (sys_acl_set_access_bits(entry, access)) {
+ rsyserr(FERROR_XFER, errno, "store_access_in_entry sys_acl_set_access_bits()");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+/* Pack rsync ACL -> system ACL verbatim. Return whether we succeeded. */
+static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl)
+{
+#ifdef ACLS_NEED_MASK
+ uchar mask_bits;
+#endif
+ size_t count;
+ id_access *ida;
+ const char *errfun = NULL;
+ SMB_ACL_ENTRY_T entry;
+
+ if (!(*smb_acl = sys_acl_init(calc_sacl_entries(racl)))) {
+ rsyserr(FERROR_XFER, errno, "pack_smb_acl: sys_acl_init()");
+ return False;
+ }
+
+#ifndef HAVE_OSX_ACLS
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_USER_OBJ, racl->user_obj & ~NO_ENTRY, 0) );
+#endif
+
+ for (ida = racl->names.idas, count = racl->names.count; count; ida++, count--) {
+#ifdef SMB_ACL_NEED_SORT
+ if (!(ida->access & NAME_IS_USER))
+ break;
+#endif
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,
+ (entry,
+ ida->access & NAME_IS_USER ? SMB_ACL_USER : SMB_ACL_GROUP,
+ ida->access & ~NAME_IS_USER, ida->id) );
+ }
+
+#ifndef HAVE_OSX_ACLS
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_GROUP_OBJ, racl->group_obj & ~NO_ENTRY, 0) );
+
+#ifdef SMB_ACL_NEED_SORT
+ for ( ; count; ida++, count--) {
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_GROUP, ida->access, ida->id) );
+ }
+#endif
+
+#ifdef ACLS_NEED_MASK
+ mask_bits = racl->mask_obj == NO_ENTRY ? racl->group_obj & ~NO_ENTRY : racl->mask_obj;
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_MASK, mask_bits, 0) );
+#else
+ if (racl->mask_obj != NO_ENTRY) {
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_MASK, racl->mask_obj, 0) );
+ }
+#endif
+
+ COE( sys_acl_create_entry,(smb_acl, &entry) );
+ COE( sys_acl_set_info,(entry, SMB_ACL_OTHER, racl->other_obj & ~NO_ENTRY, 0) );
+#endif
+
+#ifdef DEBUG
+ if (sys_acl_valid(*smb_acl) < 0)
+ rprintf(FERROR_XFER, "pack_smb_acl: warning: system says the ACL I packed is invalid\n");
+#endif
+
+ return True;
+
+ error_exit:
+ if (errfun) {
+ rsyserr(FERROR_XFER, errno, "pack_smb_acl %s()", errfun);
+ }
+ sys_acl_free_acl(*smb_acl);
+ return False;
+}
+
+static int find_matching_rsync_acl(const rsync_acl *racl, SMB_ACL_TYPE_T type,
+ const item_list *racl_list)
+{
+ static int access_match = -1, default_match = -1;
+ int *match = type == SMB_ACL_TYPE_ACCESS ? &access_match : &default_match;
+ size_t count = racl_list->count;
+
+ /* If this is the first time through or we didn't match the last
+ * time, then start at the end of the list, which should be the
+ * best place to start hunting. */
+ if (*match == -1)
+ *match = racl_list->count - 1;
+ while (count--) {
+ rsync_acl *base = racl_list->items;
+ if (rsync_acl_equal(base + *match, racl))
+ return *match;
+ if (!(*match)--)
+ *match = racl_list->count - 1;
+ }
+
+ *match = -1;
+ return *match;
+}
+
+static int get_rsync_acl(const char *fname, rsync_acl *racl,
+ SMB_ACL_TYPE_T type, mode_t mode)
+{
+ SMB_ACL_T sacl;
+
+#ifdef SUPPORT_XATTRS
+ /* --fake-super support: load ACLs from an xattr. */
+ if (am_root < 0) {
+ char *buf;
+ size_t len;
+ int cnt;
+
+ if ((buf = get_xattr_acl(fname, type == SMB_ACL_TYPE_ACCESS, &len)) == NULL)
+ return 0;
+ cnt = (len - 4*4) / (4+4);
+ if (len < 4*4 || len != (size_t)cnt*(4+4) + 4*4) {
+ free(buf);
+ return -1;
+ }
+
+ racl->user_obj = IVAL(buf, 0);
+ if (racl->user_obj == NO_ENTRY)
+ racl->user_obj = (mode >> 6) & 7;
+ racl->group_obj = IVAL(buf, 4);
+ if (racl->group_obj == NO_ENTRY)
+ racl->group_obj = (mode >> 3) & 7;
+ racl->mask_obj = IVAL(buf, 8);
+ racl->other_obj = IVAL(buf, 12);
+ if (racl->other_obj == NO_ENTRY)
+ racl->other_obj = mode & 7;
+
+ if (cnt) {
+ char *bp = buf + 4*4;
+ id_access *ida = racl->names.idas = new_array(id_access, cnt);
+ racl->names.count = cnt;
+ for ( ; cnt--; ida++, bp += 4+4) {
+ ida->id = IVAL(bp, 0);
+ ida->access = IVAL(bp, 4);
+ }
+ }
+ free(buf);
+ return 0;
+ }
+#endif
+
+ if ((sacl = sys_acl_get_file(fname, type)) != 0) {
+ BOOL ok = unpack_smb_acl(sacl, racl);
+
+ sys_acl_free_acl(sacl);
+ if (!ok) {
+ rsyserr(FERROR_XFER, errno, "get_acl: unpack_smb_acl(%s)", fname);
+ return -1;
+ }
+ } else if (no_acl_syscall_error(errno)) {
+ /* ACLs are not supported, so pretend we have a basic ACL. */
+ if (type == SMB_ACL_TYPE_ACCESS)
+ rsync_acl_fake_perms(racl, mode);
+ } else {
+ rsyserr(FERROR_XFER, errno, "get_acl: sys_acl_get_file(%s, %s)",
+ fname, str_acl_type(type));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Return the Access Control List for the given filename. */
+int get_acl(const char *fname, stat_x *sxp)
+{
+ sxp->acc_acl = create_racl();
+
+ if (S_ISREG(sxp->st.st_mode) || S_ISDIR(sxp->st.st_mode)) {
+ /* Everyone supports this. */
+ } else if (S_ISLNK(sxp->st.st_mode)) {
+ return 0;
+ } else if (IS_SPECIAL(sxp->st.st_mode)) {
+#ifndef NO_SPECIAL_ACLS
+ if (!preserve_specials)
+#endif
+ return 0;
+ } else if (IS_DEVICE(sxp->st.st_mode)) {
+#ifndef NO_DEVICE_ACLS
+ if (!preserve_devices)
+#endif
+ return 0;
+ } else if (IS_MISSING_FILE(sxp->st))
+ return 0;
+
+ if (get_rsync_acl(fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS,
+ sxp->st.st_mode) < 0) {
+ free_acl(sxp);
+ return -1;
+ }
+
+ if (S_ISDIR(sxp->st.st_mode)) {
+ sxp->def_acl = create_racl();
+ if (get_rsync_acl(fname, sxp->def_acl, SMB_ACL_TYPE_DEFAULT,
+ sxp->st.st_mode) < 0) {
+ free_acl(sxp);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* === Send functions === */
+
+/* Send the ida list over the file descriptor. */
+static void send_ida_entries(int f, const ida_entries *idal)
+{
+ id_access *ida;
+ size_t count = idal->count;
+
+ write_varint(f, idal->count);
+
+ for (ida = idal->idas; count--; ida++) {
+ uint32 xbits = ida->access << 2;
+ const char *name;
+ if (ida->access & NAME_IS_USER) {
+ xbits |= XFLAG_NAME_IS_USER;
+ name = numeric_ids ? NULL : add_uid(ida->id);
+ } else
+ name = numeric_ids ? NULL : add_gid(ida->id);
+ write_varint(f, ida->id);
+ if (inc_recurse && name) {
+ int len = strlen(name);
+ write_varint(f, xbits | XFLAG_NAME_FOLLOWS);
+ write_byte(f, len);
+ write_buf(f, name, len);
+ } else
+ write_varint(f, xbits);
+ }
+}
+
+static void send_rsync_acl(int f, rsync_acl *racl, SMB_ACL_TYPE_T type,
+ item_list *racl_list)
+{
+ int ndx = find_matching_rsync_acl(racl, type, racl_list);
+
+ /* Send 0 (-1 + 1) to indicate that literal ACL data follows. */
+ write_varint(f, ndx + 1);
+
+ if (ndx < 0) {
+ rsync_acl *new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000);
+ uchar flags = 0;
+
+ if (racl->user_obj != NO_ENTRY)
+ flags |= XMIT_USER_OBJ;
+ if (racl->group_obj != NO_ENTRY)
+ flags |= XMIT_GROUP_OBJ;
+ if (racl->mask_obj != NO_ENTRY)
+ flags |= XMIT_MASK_OBJ;
+ if (racl->other_obj != NO_ENTRY)
+ flags |= XMIT_OTHER_OBJ;
+ if (racl->names.count)
+ flags |= XMIT_NAME_LIST;
+
+ write_byte(f, flags);
+
+ if (flags & XMIT_USER_OBJ)
+ write_varint(f, racl->user_obj);
+ if (flags & XMIT_GROUP_OBJ)
+ write_varint(f, racl->group_obj);
+ if (flags & XMIT_MASK_OBJ)
+ write_varint(f, racl->mask_obj);
+ if (flags & XMIT_OTHER_OBJ)
+ write_varint(f, racl->other_obj);
+ if (flags & XMIT_NAME_LIST)
+ send_ida_entries(f, &racl->names);
+
+ /* Give the allocated data to the new list object. */
+ *new_racl = *racl;
+ *racl = empty_rsync_acl;
+ }
+}
+
+/* Send the ACL from the stat_x structure down the indicated file descriptor.
+ * This also frees the ACL data. */
+void send_acl(int f, stat_x *sxp)
+{
+ if (!sxp->acc_acl) {
+ sxp->acc_acl = create_racl();
+ rsync_acl_fake_perms(sxp->acc_acl, sxp->st.st_mode);
+ }
+ /* Avoid sending values that can be inferred from other data. */
+ rsync_acl_strip_perms(sxp);
+
+ send_rsync_acl(f, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);
+
+ if (S_ISDIR(sxp->st.st_mode)) {
+ if (!sxp->def_acl)
+ sxp->def_acl = create_racl();
+
+ send_rsync_acl(f, sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list);
+ }
+}
+
+/* === Receive functions === */
+
+static uint32 recv_acl_access(int f, uchar *name_follows_ptr)
+{
+ uint32 access = read_varint(f);
+
+ if (name_follows_ptr) {
+ int flags = access & 3;
+ access >>= 2;
+ if (am_root >= 0 && access & ~SMB_ACL_VALID_NAME_BITS)
+ goto value_error;
+ if (flags & XFLAG_NAME_FOLLOWS)
+ *name_follows_ptr = 1;
+ else
+ *name_follows_ptr = 0;
+ if (flags & XFLAG_NAME_IS_USER)
+ access |= NAME_IS_USER;
+ } else if (am_root >= 0 && access & ~SMB_ACL_VALID_OBJ_BITS) {
+ value_error:
+ rprintf(FERROR_XFER, "recv_acl_access: value out of range: %x\n",
+ access);
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ return access;
+}
+
+static uchar recv_ida_entries(int f, ida_entries *ent)
+{
+ uchar computed_mask_bits = 0;
+ int i, count = read_varint(f);
+
+ ent->idas = count ? new_array(id_access, count) : NULL;
+ ent->count = count;
+
+ for (i = 0; i < count; i++) {
+ uchar has_name;
+ id_t id = read_varint(f);
+ uint32 access = recv_acl_access(f, &has_name);
+
+ if (has_name) {
+ if (access & NAME_IS_USER)
+ id = recv_user_name(f, id);
+ else
+ id = recv_group_name(f, id, NULL);
+ } else if (access & NAME_IS_USER) {
+ if (inc_recurse && am_root && !numeric_ids)
+ id = match_uid(id);
+ } else {
+ if (inc_recurse && (!am_root || !numeric_ids))
+ id = match_gid(id, NULL);
+ }
+
+ ent->idas[i].id = id;
+ ent->idas[i].access = access;
+ computed_mask_bits |= access;
+ }
+
+ return computed_mask_bits & ~NO_ENTRY;
+}
+
+static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode_t mode)
+{
+ uchar computed_mask_bits = 0;
+ acl_duo *duo_item;
+ uchar flags;
+ int ndx = read_varint(f);
+
+ if (ndx < 0 || (size_t)ndx > racl_list->count) {
+ rprintf(FERROR_XFER, "recv_acl_index: %s ACL index %d > %d\n",
+ str_acl_type(type), ndx, (int)racl_list->count);
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ if (ndx != 0)
+ return ndx - 1;
+
+ ndx = racl_list->count;
+ duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
+ duo_item->racl = empty_rsync_acl;
+
+ flags = read_byte(f);
+
+ if (flags & XMIT_USER_OBJ)
+ duo_item->racl.user_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_GROUP_OBJ)
+ duo_item->racl.group_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_MASK_OBJ)
+ duo_item->racl.mask_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_OTHER_OBJ)
+ duo_item->racl.other_obj = recv_acl_access(f, NULL);
+ if (flags & XMIT_NAME_LIST)
+ computed_mask_bits |= recv_ida_entries(f, &duo_item->racl.names);
+
+#ifdef HAVE_OSX_ACLS
+ /* If we received a superfluous mask, throw it away. */
+ duo_item->racl.mask_obj = NO_ENTRY;
+ (void)mode;
+#else
+ if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
+ /* Mask must be non-empty with lists. */
+ if (type == SMB_ACL_TYPE_ACCESS)
+ computed_mask_bits = (mode >> 3) & 7;
+ else
+ computed_mask_bits |= duo_item->racl.group_obj & ~NO_ENTRY;
+ duo_item->racl.mask_obj = computed_mask_bits;
+ }
+#endif
+
+ duo_item->sacl = NULL;
+
+ return ndx;
+}
+
+/* Receive the ACL info the sender has included for this file-list entry. */
+void receive_acl(int f, struct file_struct *file)
+{
+ F_ACL(file) = recv_rsync_acl(f, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode);
+
+ if (S_ISDIR(file->mode))
+ F_DIR_DEFACL(file) = recv_rsync_acl(f, &default_acl_list, SMB_ACL_TYPE_DEFAULT, 0);
+}
+
+static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type, item_list *racl_list)
+{
+ int ndx;
+
+ if (!racl)
+ ndx = -1;
+ else if ((ndx = find_matching_rsync_acl(racl, type, racl_list)) == -1) {
+ acl_duo *new_duo;
+ ndx = racl_list->count;
+ new_duo = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
+ new_duo->racl = *racl;
+ new_duo->sacl = NULL;
+ *racl = empty_rsync_acl;
+ }
+
+ return ndx;
+}
+
+/* Turn the ACL data in stat_x into cached ACL data, setting the index
+ * values in the file struct. */
+void cache_tmp_acl(struct file_struct *file, stat_x *sxp)
+{
+ if (prior_access_count == (size_t)-1)
+ prior_access_count = access_acl_list.count;
+
+ F_ACL(file) = cache_rsync_acl(sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);
+
+ if (S_ISDIR(sxp->st.st_mode)) {
+ if (prior_default_count == (size_t)-1)
+ prior_default_count = default_acl_list.count;
+ F_DIR_DEFACL(file) = cache_rsync_acl(sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list);
+ }
+}
+
+static void uncache_duo_acls(item_list *duo_list, size_t start)
+{
+ acl_duo *duo_item = duo_list->items;
+ acl_duo *duo_start = duo_item + start;
+
+ duo_item += duo_list->count;
+ duo_list->count = start;
+
+ while (duo_item-- > duo_start) {
+ rsync_acl_free(&duo_item->racl);
+ if (duo_item->sacl)
+ sys_acl_free_acl(duo_item->sacl);
+ }
+}
+
+void uncache_tmp_acls(void)
+{
+ if (prior_access_count != (size_t)-1) {
+ uncache_duo_acls(&access_acl_list, prior_access_count);
+ prior_access_count = (size_t)-1;
+ }
+
+ if (prior_default_count != (size_t)-1) {
+ uncache_duo_acls(&default_acl_list, prior_default_count);
+ prior_default_count = (size_t)-1;
+ }
+}
+
+#ifndef HAVE_OSX_ACLS
+static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode, mode_t mode)
+{
+ SMB_ACL_ENTRY_T entry;
+ const char *errfun;
+ int rc;
+
+ if (S_ISDIR(mode)) {
+ /* If the sticky bit is going on, it's not safe to allow all
+ * the new ACL to go into effect before it gets set. */
+#ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS
+ if (mode & S_ISVTX)
+ mode &= ~0077;
+#else
+ if (mode & S_ISVTX && !(old_mode & S_ISVTX))
+ mode &= ~0077;
+ } else {
+ /* If setuid or setgid is going off, it's not safe to allow all
+ * the new ACL to go into effect before they get cleared. */
+ if ((old_mode & S_ISUID && !(mode & S_ISUID))
+ || (old_mode & S_ISGID && !(mode & S_ISGID)))
+ mode &= ~0077;
+#endif
+ }
+
+ errfun = "sys_acl_get_entry";
+ for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry);
+ rc == 1;
+ rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {
+ SMB_ACL_TAG_T tag_type;
+ if ((rc = sys_acl_get_tag_type(entry, &tag_type)) != 0) {
+ errfun = "sys_acl_get_tag_type";
+ break;
+ }
+ switch (tag_type) {
+ case SMB_ACL_USER_OBJ:
+ COE2( store_access_in_entry,((mode >> 6) & 7, entry) );
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ /* group is only empty when identical to group perms. */
+ if (racl->group_obj != NO_ENTRY)
+ break;
+ COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
+ break;
+ case SMB_ACL_MASK:
+#ifndef HAVE_SOLARIS_ACLS
+#ifndef ACLS_NEED_MASK
+ /* mask is only empty when we don't need it. */
+ if (racl->mask_obj == NO_ENTRY)
+ break;
+#endif
+ COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
+#endif
+ break;
+ case SMB_ACL_OTHER:
+ COE2( store_access_in_entry,(mode & 7, entry) );
+ break;
+ }
+ }
+ if (rc) {
+ error_exit:
+ if (errfun) {
+ rsyserr(FERROR_XFER, errno, "change_sacl_perms: %s()",
+ errfun);
+ }
+ return (mode_t)-1;
+ }
+
+#ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS
+ /* Ensure that chmod() will be called to restore any lost setid bits. */
+ if (old_mode & (S_ISUID | S_ISGID | S_ISVTX)
+ && BITS_EQUAL(old_mode, mode, CHMOD_BITS))
+ old_mode &= ~(S_ISUID | S_ISGID | S_ISVTX);
+#endif
+
+ /* Return the mode of the file on disk, as we will set them. */
+ return (old_mode & ~ACCESSPERMS) | (mode & ACCESSPERMS);
+}
+#endif
+
+static int set_rsync_acl(const char *fname, acl_duo *duo_item,
+ SMB_ACL_TYPE_T type, stat_x *sxp, mode_t mode)
+{
+ if (type == SMB_ACL_TYPE_DEFAULT
+ && duo_item->racl.user_obj == NO_ENTRY) {
+ int rc;
+#ifdef SUPPORT_XATTRS
+ /* --fake-super support: delete default ACL from xattrs. */
+ if (am_root < 0)
+ rc = del_def_xattr_acl(fname);
+ else
+#endif
+ rc = sys_acl_delete_def_file(fname);
+ if (rc < 0) {
+ rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_delete_def_file(%s)",
+ fname);
+ return -1;
+ }
+#ifdef SUPPORT_XATTRS
+ } else if (am_root < 0) {
+ /* --fake-super support: store ACLs in an xattr. */
+ int cnt = duo_item->racl.names.count;
+ size_t len = 4*4 + cnt * (4+4);
+ char *buf = new_array(char, len);
+ int rc;
+
+ SIVAL(buf, 0, duo_item->racl.user_obj);
+ SIVAL(buf, 4, duo_item->racl.group_obj);
+ SIVAL(buf, 8, duo_item->racl.mask_obj);
+ SIVAL(buf, 12, duo_item->racl.other_obj);
+
+ if (cnt) {
+ char *bp = buf + 4*4;
+ id_access *ida = duo_item->racl.names.idas;
+ for ( ; cnt--; ida++, bp += 4+4) {
+ SIVAL(bp, 0, ida->id);
+ SIVAL(bp, 4, ida->access);
+ }
+ }
+ rc = set_xattr_acl(fname, type == SMB_ACL_TYPE_ACCESS, buf, len);
+ free(buf);
+ return rc;
+#endif
+ } else {
+ mode_t cur_mode = sxp->st.st_mode;
+ if (!duo_item->sacl
+ && !pack_smb_acl(&duo_item->sacl, &duo_item->racl))
+ return -1;
+#ifdef HAVE_OSX_ACLS
+ mode = 0; /* eliminate compiler warning */
+#else
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl, cur_mode, mode);
+ if (cur_mode == (mode_t)-1)
+ return 0;
+ }
+#endif
+ if (sys_acl_set_file(fname, type, duo_item->sacl) < 0) {
+ rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_set_file(%s, %s)",
+ fname, str_acl_type(type));
+ return -1;
+ }
+ if (type == SMB_ACL_TYPE_ACCESS)
+ sxp->st.st_mode = cur_mode;
+ }
+
+ return 0;
+}
+
+/* Given a fname, this sets extended access ACL entries, the default ACL (for a
+ * dir), and the regular mode bits on the file. Call this with fname set to
+ * NULL to just check if the ACL is different.
+ *
+ * If the ACL operation has a side-effect of changing the file's mode, the
+ * sxp->st.st_mode value will be changed to match.
+ *
+ * Returns 0 for an unchanged ACL, 1 for changed, -1 for failed. */
+int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp, mode_t new_mode)
+{
+ int changed = 0;
+ int32 ndx;
+ BOOL eq;
+
+ if (!dry_run && (read_only || list_only)) {
+ errno = EROFS;
+ return -1;
+ }
+
+ ndx = F_ACL(file);
+ if (ndx >= 0 && (size_t)ndx < access_acl_list.count) {
+ acl_duo *duo_item = access_acl_list.items;
+ duo_item += ndx;
+ eq = sxp->acc_acl
+ && rsync_acl_equal_enough(sxp->acc_acl, &duo_item->racl, new_mode);
+ if (!eq) {
+ changed = 1;
+ if (!dry_run && fname
+ && set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_ACCESS,
+ sxp, new_mode) < 0)
+ return -1;
+ }
+ }
+
+ if (!S_ISDIR(new_mode))
+ return changed;
+
+ ndx = F_DIR_DEFACL(file);
+ if (ndx >= 0 && (size_t)ndx < default_acl_list.count) {
+ acl_duo *duo_item = default_acl_list.items;
+ duo_item += ndx;
+ eq = sxp->def_acl && rsync_acl_equal(sxp->def_acl, &duo_item->racl);
+ if (!eq) {
+ changed = 1;
+ if (!dry_run && fname
+ && set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_DEFAULT,
+ sxp, new_mode) < 0)
+ return -1;
+ }
+ }
+
+ return changed;
+}
+
+/* Non-incremental recursion needs to convert all the received IDs.
+ * This is done in a single pass after receiving the whole file-list. */
+static void match_racl_ids(const item_list *racl_list)
+{
+ int list_cnt, name_cnt;
+ acl_duo *duo_item = racl_list->items;
+ for (list_cnt = racl_list->count; list_cnt--; duo_item++) {
+ ida_entries *idal = &duo_item->racl.names;
+ id_access *ida = idal->idas;
+ for (name_cnt = idal->count; name_cnt--; ida++) {
+ if (ida->access & NAME_IS_USER)
+ ida->id = match_uid(ida->id);
+ else
+ ida->id = match_gid(ida->id, NULL);
+ }
+ }
+}
+
+void match_acl_ids(void)
+{
+ match_racl_ids(&access_acl_list);
+ match_racl_ids(&default_acl_list);
+}
+
+/* This is used by dest_mode(). */
+int default_perms_for_dir(const char *dir)
+{
+ rsync_acl racl;
+ SMB_ACL_T sacl;
+ BOOL ok;
+ int perms;
+
+ if (dir == NULL)
+ dir = ".";
+ perms = ACCESSPERMS & ~orig_umask;
+ /* Read the directory's default ACL. If it has none, this will successfully return an empty ACL. */
+ sacl = sys_acl_get_file(dir, SMB_ACL_TYPE_DEFAULT);
+ if (sacl == NULL) {
+ /* Couldn't get an ACL. Darn. */
+ switch (errno) {
+ case EINVAL:
+ /* If SMB_ACL_TYPE_DEFAULT isn't valid, then the ACLs must be non-POSIX. */
+ break;
+#ifdef ENOTSUP
+ case ENOTSUP:
+#endif
+ case ENOSYS:
+ /* No ACLs are available. */
+ break;
+ default:
+ if (dry_run && errno == ENOENT) {
+ /* We're doing a dry run, so the containing directory
+ * wasn't actually created. Don't worry about it. */
+ break;
+ }
+ rprintf(FWARNING,
+ "default_perms_for_dir: sys_acl_get_file(%s, %s): %s, falling back on umask\n",
+ dir, str_acl_type(SMB_ACL_TYPE_DEFAULT), strerror(errno));
+ }
+ return perms;
+ }
+
+ /* Convert it. */
+ racl = empty_rsync_acl;
+ ok = unpack_smb_acl(sacl, &racl);
+ sys_acl_free_acl(sacl);
+ if (!ok) {
+ rprintf(FWARNING, "default_perms_for_dir: unpack_smb_acl failed, falling back on umask\n");
+ return perms;
+ }
+
+ /* Apply the permission-bit entries of the default ACL, if any. */
+ if (racl.user_obj != NO_ENTRY) {
+ perms = rsync_acl_get_perms(&racl);
+ if (DEBUG_GTE(ACL, 1))
+ rprintf(FINFO, "got ACL-based default perms %o for directory %s\n", perms, dir);
+ }
+
+ rsync_acl_free(&racl);
+ return perms;
+}
+
+#endif /* SUPPORT_ACLS */
diff --git a/authenticate.c b/authenticate.c
new file mode 100644
index 0000000..b7f6ead
--- /dev/null
+++ b/authenticate.c
@@ -0,0 +1,376 @@
+/*
+ * Support rsync daemon authentication.
+ *
+ * Copyright (C) 1998-2000 Andrew Tridgell
+ * Copyright (C) 2002-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "ifuncs.h"
+
+extern int read_only;
+extern char *password_file;
+extern struct name_num_obj valid_auth_checksums;
+
+/***************************************************************************
+encode a buffer using base64 - simple and slow algorithm. null terminates
+the result.
+ ***************************************************************************/
+void base64_encode(const char *buf, int len, char *out, int pad)
+{
+ char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int bit_offset, byte_offset, idx, i;
+ const uchar *d = (const uchar *)buf;
+ int bytes = (len*8 + 5)/6;
+
+ for (i = 0; i < bytes; i++) {
+ byte_offset = (i*6)/8;
+ bit_offset = (i*6)%8;
+ if (bit_offset < 3) {
+ idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
+ } else {
+ idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
+ if (byte_offset+1 < len) {
+ idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
+ }
+ }
+ out[i] = b64[idx];
+ }
+
+ while (pad && (i % 4))
+ out[i++] = '=';
+
+ out[i] = '\0';
+}
+
+/* Generate a challenge buffer and return it base64-encoded. */
+static void gen_challenge(const char *addr, char *challenge)
+{
+ char input[32];
+ char digest[MAX_DIGEST_LEN];
+ struct timeval tv;
+ int len;
+
+ memset(input, 0, sizeof input);
+
+ strlcpy(input, addr, 17);
+ sys_gettimeofday(&tv);
+ SIVAL(input, 16, tv.tv_sec);
+ SIVAL(input, 20, tv.tv_usec);
+ SIVAL(input, 24, getpid());
+
+ len = sum_init(valid_auth_checksums.negotiated_nni, 0);
+ sum_update(input, sizeof input);
+ sum_end(digest);
+
+ base64_encode(digest, len, challenge, 0);
+}
+
+/* Generate an MD4 hash created from the combination of the password
+ * and the challenge string and return it base64-encoded. */
+static void generate_hash(const char *in, const char *challenge, char *out)
+{
+ char buf[MAX_DIGEST_LEN];
+ int len;
+
+ len = sum_init(valid_auth_checksums.negotiated_nni, 0);
+ sum_update(in, strlen(in));
+ sum_update(challenge, strlen(challenge));
+ sum_end(buf);
+
+ base64_encode(buf, len, out, 0);
+}
+
+/* Return the secret for a user from the secret file, null terminated.
+ * Maximum length is len (not counting the null). */
+static const char *check_secret(int module, const char *user, const char *group,
+ const char *challenge, const char *pass)
+{
+ char line[1024];
+ char pass2[MAX_DIGEST_LEN*2];
+ const char *fname = lp_secrets_file(module);
+ STRUCT_STAT st;
+ int ok = 1;
+ int user_len = strlen(user);
+ int group_len = group ? strlen(group) : 0;
+ char *err;
+ FILE *fh;
+
+ if (!fname || !*fname || (fh = fopen(fname, "r")) == NULL)
+ return "no secrets file";
+
+ if (do_fstat(fileno(fh), &st) == -1) {
+ rsyserr(FLOG, errno, "fstat(%s)", fname);
+ ok = 0;
+ } else if (lp_strict_modes(module)) {
+ if ((st.st_mode & 06) != 0) {
+ rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
+ ok = 0;
+ } else if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
+ rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
+ ok = 0;
+ }
+ }
+ if (!ok) {
+ fclose(fh);
+ return "ignoring secrets file";
+ }
+
+ if (*user == '#') {
+ /* Reject attempt to match a comment. */
+ fclose(fh);
+ return "invalid username";
+ }
+
+ /* Try to find a line that starts with the user (or @group) name and a ':'. */
+ err = "secret not found";
+ while ((user || group) && fgets(line, sizeof line, fh) != NULL) {
+ const char **ptr, *s = strtok(line, "\n\r");
+ int len;
+ if (!s)
+ continue;
+ if (*s == '@') {
+ ptr = &group;
+ len = group_len;
+ s++;
+ } else {
+ ptr = &user;
+ len = user_len;
+ }
+ if (!*ptr || strncmp(s, *ptr, len) != 0 || s[len] != ':')
+ continue;
+ generate_hash(s+len+1, challenge, pass2);
+ if (strcmp(pass, pass2) == 0) {
+ err = NULL;
+ break;
+ }
+ err = "password mismatch";
+ *ptr = NULL; /* Don't look for name again. */
+ }
+
+ fclose(fh);
+
+ force_memzero(line, sizeof line);
+ force_memzero(pass2, sizeof pass2);
+
+ return err;
+}
+
+static const char *getpassf(const char *filename)
+{
+ STRUCT_STAT st;
+ char buffer[512], *p;
+ int n;
+
+ if (!filename)
+ return NULL;
+
+ if (strcmp(filename, "-") == 0) {
+ n = fgets(buffer, sizeof buffer, stdin) == NULL ? -1 : (int)strlen(buffer);
+ } else {
+ int fd;
+
+ if ((fd = open(filename,O_RDONLY)) < 0) {
+ rsyserr(FERROR, errno, "could not open password file %s", filename);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (do_stat(filename, &st) == -1) {
+ rsyserr(FERROR, errno, "stat(%s)", filename);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if ((st.st_mode & 06) != 0) {
+ rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
+ rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ n = read(fd, buffer, sizeof buffer - 1);
+ close(fd);
+ }
+
+ if (n > 0) {
+ buffer[n] = '\0';
+ if ((p = strtok(buffer, "\n\r")) != NULL)
+ return strdup(p);
+ }
+
+ rprintf(FERROR, "ERROR: failed to read a password from %s\n", filename);
+ exit_cleanup(RERR_SYNTAX);
+}
+
+/* Possibly negotiate authentication with the client. Use "leader" to
+ * start off the auth if necessary.
+ *
+ * Return NULL if authentication failed. Return "" if anonymous access.
+ * Otherwise return username.
+ */
+char *auth_server(int f_in, int f_out, int module, const char *host,
+ const char *addr, const char *leader)
+{
+ char *users = lp_auth_users(module);
+ char challenge[MAX_DIGEST_LEN*2];
+ char line[BIGPATHBUFLEN];
+ const char **auth_uid_groups = NULL;
+ int auth_uid_groups_cnt = -1;
+ const char *err = NULL;
+ int group_match = -1;
+ char *tok, *pass;
+ char opt_ch = '\0';
+
+ /* if no auth list then allow anyone in! */
+ if (!users || !*users)
+ return "";
+
+ negotiate_daemon_auth(f_out, 0);
+ gen_challenge(addr, challenge);
+
+ io_printf(f_out, "%s%s\n", leader, challenge);
+
+ if (!read_line_old(f_in, line, sizeof line, 0)
+ || (pass = strchr(line, ' ')) == NULL) {
+ rprintf(FLOG, "auth failed on module %s from %s (%s): "
+ "invalid challenge response\n",
+ lp_name(module), host, addr);
+ return NULL;
+ }
+ *pass++ = '\0';
+
+ users = strdup(users);
+
+ for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
+ char *opts;
+ /* See if the user appended :deny, :ro, or :rw. */
+ if ((opts = strchr(tok, ':')) != NULL) {
+ *opts++ = '\0';
+ opt_ch = isUpper(opts) ? toLower(opts) : *opts;
+ if (opt_ch == 'r') { /* handle ro and rw */
+ opt_ch = isUpper(opts+1) ? toLower(opts+1) : opts[1];
+ if (opt_ch == 'o')
+ opt_ch = 'r';
+ else if (opt_ch != 'w')
+ opt_ch = '\0';
+ } else if (opt_ch != 'd') /* if it's not deny, ignore it */
+ opt_ch = '\0';
+ } else
+ opt_ch = '\0';
+ if (*tok != '@') {
+ /* Match the username */
+ if (wildmatch(tok, line))
+ break;
+ } else {
+#ifdef HAVE_GETGROUPLIST
+ int j;
+ /* See if authorizing user is a real user, and if so, see
+ * if it is in a group that matches tok+1 wildmat. */
+ if (auth_uid_groups_cnt < 0) {
+ item_list gid_list = EMPTY_ITEM_LIST;
+ uid_t auth_uid;
+ if (!user_to_uid(line, &auth_uid, False)
+ || getallgroups(auth_uid, &gid_list) != NULL)
+ auth_uid_groups_cnt = 0;
+ else {
+ gid_t *gid_array = gid_list.items;
+ auth_uid_groups_cnt = gid_list.count;
+ auth_uid_groups = new_array(const char *, auth_uid_groups_cnt);
+ for (j = 0; j < auth_uid_groups_cnt; j++)
+ auth_uid_groups[j] = gid_to_group(gid_array[j]);
+ }
+ }
+ for (j = 0; j < auth_uid_groups_cnt; j++) {
+ if (auth_uid_groups[j] && wildmatch(tok+1, auth_uid_groups[j])) {
+ group_match = j;
+ break;
+ }
+ }
+ if (group_match >= 0)
+ break;
+#else
+ rprintf(FLOG, "your computer doesn't support getgrouplist(), so no @group authorization is possible.\n");
+#endif
+ }
+ }
+
+ free(users);
+
+ if (!tok)
+ err = "no matching rule";
+ else if (opt_ch == 'd')
+ err = "denied by rule";
+ else {
+ const char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
+ err = check_secret(module, line, group, challenge, pass);
+ }
+
+ force_memzero(challenge, sizeof challenge);
+ force_memzero(pass, strlen(pass));
+
+ if (auth_uid_groups) {
+ int j;
+ for (j = 0; j < auth_uid_groups_cnt; j++) {
+ if (auth_uid_groups[j])
+ free((char*)auth_uid_groups[j]);
+ }
+ free(auth_uid_groups);
+ }
+
+ if (err) {
+ rprintf(FLOG, "auth failed on module %s from %s (%s) for %s: %s\n",
+ lp_name(module), host, addr, line, err);
+ return NULL;
+ }
+
+ if (opt_ch == 'r')
+ read_only = 1;
+ else if (opt_ch == 'w')
+ read_only = 0;
+
+ return strdup(line);
+}
+
+void auth_client(int fd, const char *user, const char *challenge)
+{
+ const char *pass;
+ char pass2[MAX_DIGEST_LEN*2];
+
+ if (!user || !*user)
+ user = "nobody";
+ negotiate_daemon_auth(-1, 1);
+
+ if (!(pass = getpassf(password_file))
+ && !(pass = getenv("RSYNC_PASSWORD"))) {
+ /* XXX: cyeoh says that getpass is deprecated, because
+ * it may return a truncated password on some systems,
+ * and it is not in the LSB.
+ *
+ * Andrew Klein says that getpassphrase() is present
+ * on Solaris and reads up to 256 characters.
+ *
+ * OpenBSD has a readpassphrase() that might be more suitable.
+ */
+ pass = getpass("Password: ");
+ }
+
+ if (!pass)
+ pass = "";
+
+ generate_hash(pass, challenge, pass2);
+ io_printf(fd, "%s %s\n", user, pass2);
+}
diff --git a/backup.c b/backup.c
new file mode 100644
index 0000000..686cb29
--- /dev/null
+++ b/backup.c
@@ -0,0 +1,355 @@
+/*
+ * Backup handling code.
+ *
+ * Copyright (C) 1999 Andrew Tridgell
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+
+extern int am_root;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_devices;
+extern int preserve_specials;
+extern int preserve_links;
+extern int safe_symlinks;
+extern int backup_dir_len;
+extern unsigned int backup_dir_remainder;
+extern char backup_dir_buf[MAXPATHLEN];
+extern char *backup_suffix;
+extern char *backup_dir;
+
+/* Returns -1 on error, 0 on missing dir, and 1 on present dir. */
+static int validate_backup_dir(void)
+{
+ STRUCT_STAT st;
+
+ if (do_lstat(backup_dir_buf, &st) < 0) {
+ if (errno == ENOENT)
+ return 0;
+ rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
+ return -1;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ int flags = get_del_for_flag(st.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
+ if (delete_item(backup_dir_buf, st.st_mode, flags) == 0)
+ return 0;
+ return -1;
+ }
+ return 1;
+}
+
+/* Create a backup path from the given fname, putting the result into
+ * backup_dir_buf. Any new directories (compared to the prior backup
+ * path) are ensured to exist as directories, replacing anything else
+ * that may be in the way (e.g. a symlink). */
+static BOOL copy_valid_path(const char *fname)
+{
+ const char *f;
+ int val;
+ BOOL ret = True;
+ stat_x sx;
+ char *b, *rel = backup_dir_buf + backup_dir_len, *name = rel;
+
+ for (f = fname, b = rel; *f && *f == *b; f++, b++) {
+ if (*b == '/')
+ name = b + 1;
+ }
+
+ if (stringjoin(rel, backup_dir_remainder, fname, backup_suffix, NULL) >= backup_dir_remainder) {
+ rprintf(FERROR, "backup filename too long\n");
+ *name = '\0';
+ return False;
+ }
+
+ for ( ; ; name = b + 1) {
+ if ((b = strchr(name, '/')) == NULL)
+ return True;
+ *b = '\0';
+
+ val = validate_backup_dir();
+ if (val == 0)
+ break;
+ if (val < 0) {
+ *name = '\0';
+ return False;
+ }
+
+ *b = '/';
+ }
+
+ init_stat_x(&sx);
+
+ for ( ; b; name = b + 1, b = strchr(name, '/')) {
+ *b = '\0';
+
+ while (do_mkdir(backup_dir_buf, ACCESSPERMS) < 0) {
+ if (errno == EEXIST) {
+ val = validate_backup_dir();
+ if (val > 0)
+ break;
+ if (val == 0)
+ continue;
+ } else
+ rsyserr(FERROR, errno, "backup mkdir %s failed", backup_dir_buf);
+ *name = '\0';
+ ret = False;
+ goto cleanup;
+ }
+
+ /* Try to transfer the directory settings of the actual dir
+ * that the files are coming from. */
+ if (x_stat(rel, &sx.st, NULL) < 0)
+ rsyserr(FERROR, errno, "backup stat %s failed", full_fname(rel));
+ else {
+ struct file_struct *file;
+ if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
+ continue;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ get_acl(rel, &sx);
+ cache_tmp_acl(file, &sx);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ get_xattr(rel, &sx);
+ cache_tmp_xattr(file, &sx);
+ free_xattr(&sx);
+ }
+#endif
+ set_file_attrs(backup_dir_buf, file, NULL, NULL, 0);
+ unmake_file(file);
+ }
+
+ *b = '/';
+ }
+
+ cleanup:
+
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+
+ return ret;
+}
+
+/* Make a complete pathname for backup file and verify any new path elements. */
+char *get_backup_name(const char *fname)
+{
+ if (backup_dir) {
+ static int initialized = 0;
+ if (!initialized) {
+ int ret;
+ if (backup_dir_len > 1)
+ backup_dir_buf[backup_dir_len-1] = '\0';
+ ret = make_path(backup_dir_buf, 0);
+ if (backup_dir_len > 1)
+ backup_dir_buf[backup_dir_len-1] = '/';
+ if (ret < 0)
+ return NULL;
+ initialized = 1;
+ }
+ /* copy fname into backup_dir_buf while validating the dirs. */
+ if (copy_valid_path(fname))
+ return backup_dir_buf;
+ /* copy_valid_path() has printed an error message. */
+ return NULL;
+ }
+
+ if (stringjoin(backup_dir_buf, MAXPATHLEN, fname, backup_suffix, NULL) < MAXPATHLEN)
+ return backup_dir_buf;
+
+ rprintf(FERROR, "backup filename too long\n");
+ return NULL;
+}
+
+/* Has same return codes as make_backup(). */
+static inline int link_or_rename(const char *from, const char *to,
+ BOOL prefer_rename, STRUCT_STAT *stp)
+{
+#ifdef SUPPORT_HARD_LINKS
+ if (!prefer_rename) {
+#ifndef CAN_HARDLINK_SYMLINK
+ if (S_ISLNK(stp->st_mode))
+ return 0; /* Use copy code. */
+#endif
+#ifndef CAN_HARDLINK_SPECIAL
+ if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
+ return 0; /* Use copy code. */
+#endif
+ if (do_link(from, to) == 0) {
+ if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
+ return 2;
+ }
+ /* We prefer to rename a regular file rather than copy it. */
+ if (!S_ISREG(stp->st_mode) || errno == EEXIST || errno == EISDIR)
+ return 0;
+ }
+#endif
+ if (do_rename(from, to) == 0) {
+ if (stp->st_nlink > 1 && !S_ISDIR(stp->st_mode)) {
+ /* If someone has hard-linked the file into the backup
+ * dir, rename() might return success but do nothing! */
+ robust_unlink(from); /* Just in case... */
+ }
+ if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: RENAME %s successful.\n", from);
+ return 1;
+ }
+ return 0;
+}
+
+/* Hard-link, rename, or copy an item to the backup name. Returns 0 for
+ * failure, 1 if item was moved, 2 if item was duplicated or hard linked
+ * into backup area, or 3 if item doesn't exist or isn't a regular file. */
+int make_backup(const char *fname, BOOL prefer_rename)
+{
+ stat_x sx;
+ struct file_struct *file;
+ int save_preserve_xattrs;
+ char *buf;
+ int ret = 0;
+
+ init_stat_x(&sx);
+ /* Return success if no file to keep. */
+ if (x_lstat(fname, &sx.st, NULL) < 0)
+ return 3;
+
+ if (!(buf = get_backup_name(fname)))
+ return 0;
+
+ /* Try a hard-link or a rename first. Using rename is not atomic, but
+ * is more efficient than forcing a copy for larger files when no hard-
+ * linking is possible. */
+ if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
+ goto success;
+ if (errno == EEXIST || errno == EISDIR) {
+ STRUCT_STAT bakst;
+ if (do_lstat(buf, &bakst) == 0) {
+ int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
+ if (delete_item(buf, bakst.st_mode, flags) != 0)
+ return 0;
+ }
+ if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
+ goto success;
+ }
+
+ /* Fall back to making a copy. */
+ if (!(file = make_file(fname, NULL, &sx.st, 0, NO_FILTERS)))
+ return 3; /* the file could have disappeared */
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ get_acl(fname, &sx);
+ cache_tmp_acl(file, &sx);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ get_xattr(fname, &sx);
+ cache_tmp_xattr(file, &sx);
+ free_xattr(&sx);
+ }
+#endif
+
+ /* Check to see if this is a device file, or link */
+ if ((am_root && preserve_devices && IS_DEVICE(file->mode))
+ || (preserve_specials && IS_SPECIAL(file->mode))) {
+ if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
+ rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf));
+ else if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname);
+ ret = 2;
+ }
+
+#ifdef SUPPORT_LINKS
+ if (!ret && preserve_links && S_ISLNK(file->mode)) {
+ const char *sl = F_SYMLINK(file);
+ if (safe_symlinks && unsafe_symlink(sl, fname)) {
+ if (INFO_GTE(SYMSAFE, 1)) {
+ rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n",
+ fname, sl);
+ }
+ ret = 2;
+ } else {
+ if (do_symlink(sl, buf) < 0)
+ rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl);
+ else if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname);
+ ret = 2;
+ }
+ }
+#endif
+
+ if (!ret && !S_ISREG(file->mode)) {
+ if (INFO_GTE(NONREG, 1))
+ rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
+ unmake_file(file);
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+ return 3;
+ }
+
+ /* Copy to backup tree if a file. */
+ if (!ret) {
+ if (copy_file(fname, buf, -1, file->mode) < 0) {
+ rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
+ full_fname(fname), buf);
+ unmake_file(file);
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+ return 0;
+ }
+ if (DEBUG_GTE(BACKUP, 1))
+ rprintf(FINFO, "make_backup: COPY %s successful.\n", fname);
+ ret = 2;
+ }
+
+ save_preserve_xattrs = preserve_xattrs;
+ preserve_xattrs = 0;
+ set_file_attrs(buf, file, NULL, fname, ATTRS_ACCURATE_TIME);
+ preserve_xattrs = save_preserve_xattrs;
+
+ unmake_file(file);
+#ifdef SUPPORT_ACLS
+ uncache_tmp_acls();
+#endif
+#ifdef SUPPORT_XATTRS
+ uncache_tmp_xattrs();
+#endif
+
+ success:
+ if (INFO_GTE(BACKUP, 1))
+ rprintf(FINFO, "backed up %s to %s\n", fname, buf);
+ return ret;
+}
diff --git a/batch.c b/batch.c
new file mode 100644
index 0000000..accc4c6
--- /dev/null
+++ b/batch.c
@@ -0,0 +1,312 @@
+/*
+ * Support for the batch-file options.
+ *
+ * Copyright (C) 1999 Weiss
+ * Copyright (C) 2004 Chris Shoemaker
+ * Copyright (C) 2004-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include <zlib.h>
+#include <time.h>
+
+extern int eol_nulls;
+extern int recurse;
+extern int xfer_dirs;
+extern int preserve_links;
+extern int preserve_hard_links;
+extern int preserve_devices;
+extern int preserve_uid;
+extern int preserve_gid;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int always_checksum;
+extern int do_compression;
+extern int inplace;
+extern int append_mode;
+extern int write_batch;
+extern int protocol_version;
+extern int raw_argc, cooked_argc;
+extern char **raw_argv, **cooked_argv;
+extern char *batch_name;
+#ifdef ICONV_OPTION
+extern char *iconv_opt;
+#endif
+
+extern filter_rule_list filter_list;
+
+int batch_fd = -1;
+int batch_sh_fd = -1;
+int batch_stream_flags;
+
+static int tweaked_append;
+static int tweaked_append_verify;
+static int tweaked_iconv;
+
+static int *flag_ptr[] = {
+ &recurse, /* 0 */
+ &preserve_uid, /* 1 */
+ &preserve_gid, /* 2 */
+ &preserve_links, /* 3 */
+ &preserve_devices, /* 4 */
+ &preserve_hard_links, /* 5 */
+ &always_checksum, /* 6 */
+ &xfer_dirs, /* 7 (protocol 29) */
+ &do_compression, /* 8 (protocol 29) */
+ &tweaked_iconv, /* 9 (protocol 30) */
+ &preserve_acls, /* 10 (protocol 30) */
+ &preserve_xattrs, /* 11 (protocol 30) */
+ &inplace, /* 12 (protocol 30) */
+ &tweaked_append, /* 13 (protocol 30) */
+ &tweaked_append_verify, /* 14 (protocol 30) */
+ NULL
+};
+
+static char *flag_name[] = {
+ "--recurse (-r)",
+ "--owner (-o)",
+ "--group (-g)",
+ "--links (-l)",
+ "--devices (-D)",
+ "--hard-links (-H)",
+ "--checksum (-c)",
+ "--dirs (-d)",
+ "--compress (-z)",
+ "--iconv",
+ "--acls (-A)",
+ "--xattrs (-X)",
+ "--inplace",
+ "--append",
+ "--append-verify",
+ NULL
+};
+
+void write_stream_flags(int fd)
+{
+ int i, flags;
+
+ tweaked_append = append_mode == 1;
+ tweaked_append_verify = append_mode == 2;
+#ifdef ICONV_OPTION
+ tweaked_iconv = iconv_opt != NULL;
+#endif
+
+ /* Start the batch file with a bitmap of data-stream-affecting
+ * flags. */
+ for (i = 0, flags = 0; flag_ptr[i]; i++) {
+ if (*flag_ptr[i])
+ flags |= 1 << i;
+ }
+ write_int(fd, flags);
+}
+
+void read_stream_flags(int fd)
+{
+ batch_stream_flags = read_int(fd);
+}
+
+void check_batch_flags(void)
+{
+ int i;
+
+ if (protocol_version < 29)
+ flag_ptr[7] = NULL;
+ else if (protocol_version < 30)
+ flag_ptr[9] = NULL;
+ tweaked_append = append_mode == 1;
+ tweaked_append_verify = append_mode == 2;
+#ifdef ICONV_OPTION
+ tweaked_iconv = iconv_opt != NULL;
+#endif
+ for (i = 0; flag_ptr[i]; i++) {
+ int set = batch_stream_flags & (1 << i) ? 1 : 0;
+ if (*flag_ptr[i] != set) {
+ if (i == 9) {
+ rprintf(FERROR,
+ "%s specify the --iconv option to use this batch file.\n",
+ set ? "Please" : "Do not");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (INFO_GTE(MISC, 1)) {
+ rprintf(FINFO,
+ "%sing the %s option to match the batchfile.\n",
+ set ? "Sett" : "Clear", flag_name[i]);
+ }
+ *flag_ptr[i] = set;
+ }
+ }
+ if (protocol_version < 29) {
+ if (recurse)
+ xfer_dirs |= 1;
+ else if (xfer_dirs < 2)
+ xfer_dirs = 0;
+ }
+
+ if (tweaked_append)
+ append_mode = 1;
+ else if (tweaked_append_verify)
+ append_mode = 2;
+}
+
+static int write_arg(const char *arg)
+{
+ const char *x, *s;
+ int len, err = 0;
+
+ if (*arg == '-' && (x = strchr(arg, '=')) != NULL) {
+ err |= write(batch_sh_fd, arg, x - arg + 1) != x - arg + 1;
+ arg += x - arg + 1;
+ }
+
+ if (strpbrk(arg, " \"'&;|[]()$#!*?^\\") != NULL) {
+ err |= write(batch_sh_fd, "'", 1) != 1;
+ for (s = arg; (x = strchr(s, '\'')) != NULL; s = x + 1) {
+ err |= write(batch_sh_fd, s, x - s + 1) != x - s + 1;
+ err |= write(batch_sh_fd, "'", 1) != 1;
+ }
+ len = strlen(s);
+ err |= write(batch_sh_fd, s, len) != len;
+ err |= write(batch_sh_fd, "'", 1) != 1;
+ return err;
+ }
+
+ len = strlen(arg);
+ err |= write(batch_sh_fd, arg, len) != len;
+
+ return err;
+}
+
+/* Writes out a space and then an option (or other string) with an optional "=" + arg suffix. */
+static int write_opt(const char *opt, const char *arg)
+{
+ int len = strlen(opt);
+ int err = write(batch_sh_fd, " ", 1) != 1;
+ err = write(batch_sh_fd, opt, len) != len ? 1 : 0;
+ if (arg) {
+ err |= write(batch_sh_fd, "=", 1) != 1;
+ err |= write_arg(arg);
+ }
+ return err;
+}
+
+static void write_filter_rules(int fd)
+{
+ filter_rule *ent;
+
+ write_sbuf(fd, " <<'#E#'\n");
+ for (ent = filter_list.head; ent; ent = ent->next) {
+ unsigned int plen;
+ char *p = get_rule_prefix(ent, "- ", 0, &plen);
+ write_buf(fd, p, plen);
+ write_sbuf(fd, ent->pattern);
+ if (ent->rflags & FILTRULE_DIRECTORY)
+ write_byte(fd, '/');
+ write_byte(fd, eol_nulls ? 0 : '\n');
+ }
+ if (eol_nulls)
+ write_sbuf(fd, ";\n");
+ write_sbuf(fd, "#E#");
+}
+
+/* This sets batch_fd and (for --write-batch) batch_sh_fd. */
+void open_batch_files(void)
+{
+ if (write_batch) {
+ char filename[MAXPATHLEN];
+
+ stringjoin(filename, sizeof filename, batch_name, ".sh", NULL);
+
+ batch_sh_fd = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
+ if (batch_sh_fd < 0) {
+ rsyserr(FERROR, errno, "Batch file %s open error", full_fname(filename));
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ batch_fd = do_open(batch_name, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ } else if (strcmp(batch_name, "-") == 0)
+ batch_fd = STDIN_FILENO;
+ else
+ batch_fd = do_open(batch_name, O_RDONLY, S_IRUSR | S_IWUSR);
+
+ if (batch_fd < 0) {
+ rsyserr(FERROR, errno, "Batch file %s open error", full_fname(batch_name));
+ exit_cleanup(RERR_FILEIO);
+ }
+}
+
+/* This routine tries to write out an equivalent --read-batch command
+ * given the user's --write-batch args. However, it doesn't really
+ * understand most of the options, so it uses some overly simple
+ * heuristics to munge the command line into something that will
+ * (hopefully) work. */
+void write_batch_shell_file(void)
+{
+ int i, j, len, err = 0;
+ char *p, *p2;
+
+ /* Write argvs info to BATCH.sh file */
+ err |= write_arg(raw_argv[0]);
+ if (filter_list.head) {
+ if (protocol_version >= 29)
+ err |= write_opt("--filter", "._-");
+ else
+ err |= write_opt("--exclude-from", "-");
+ }
+
+ /* Elide the filename args from the option list, but scan for them in reverse. */
+ for (i = raw_argc-1, j = cooked_argc-1; i > 0 && j >= 0; i--) {
+ if (strcmp(raw_argv[i], cooked_argv[j]) == 0) {
+ raw_argv[i] = NULL;
+ j--;
+ }
+ }
+
+ for (i = 1; i < raw_argc; i++) {
+ if (!(p = raw_argv[i]))
+ continue;
+ if (strncmp(p, "--files-from", 12) == 0
+ || strncmp(p, "--filter", 8) == 0
+ || strncmp(p, "--include", 9) == 0
+ || strncmp(p, "--exclude", 9) == 0) {
+ if (strchr(p, '=') == NULL)
+ i++;
+ continue;
+ }
+ if (strcmp(p, "-f") == 0) {
+ i++;
+ continue;
+ }
+ if (strncmp(p, "--write-batch", len = 13) == 0
+ || strncmp(p, "--only-write-batch", len = 18) == 0)
+ err |= write_opt("--read-batch", p[len] == '=' ? p + len + 1 : NULL);
+ else {
+ err |= write(batch_sh_fd, " ", 1) != 1;
+ err |= write_arg(p);
+ }
+ }
+ if (!(p = check_for_hostspec(cooked_argv[cooked_argc - 1], &p2, &i)))
+ p = cooked_argv[cooked_argc - 1];
+ err |= write_opt("${1:-", NULL);
+ err |= write_arg(p);
+ err |= write(batch_sh_fd, "}", 1) != 1;
+ if (filter_list.head)
+ write_filter_rules(batch_sh_fd);
+ if (write(batch_sh_fd, "\n", 1) != 1 || close(batch_sh_fd) < 0 || err) {
+ rsyserr(FERROR, errno, "Batch file %s.sh write error", batch_name);
+ exit_cleanup(RERR_FILEIO);
+ }
+ batch_sh_fd = -1;
+}
diff --git a/byteorder.h b/byteorder.h
new file mode 100644
index 0000000..059cc70
--- /dev/null
+++ b/byteorder.h
@@ -0,0 +1,131 @@
+/*
+ * Simple byteorder handling.
+ *
+ * Copyright (C) 1992-1995 Andrew Tridgell
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#undef CAREFUL_ALIGNMENT
+
+/* We know that the x86 can handle misalignment and has the same
+ * byte order (LSB-first) as the 32-bit numbers we transmit. */
+#if defined __i386__ || defined __i486__ || defined __i586__ || defined __i686__ || __amd64
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define UVAL(buf,pos) ((uint32)CVAL(buf,pos))
+
+#if CAREFUL_ALIGNMENT
+
+static inline uint32
+IVALu(const uchar *buf, int pos)
+{
+ return UVAL(buf, pos)
+ | UVAL(buf, pos + 1) << 8
+ | UVAL(buf, pos + 2) << 16
+ | UVAL(buf, pos + 3) << 24;
+}
+
+static inline void
+SIVALu(uchar *buf, int pos, uint32 val)
+{
+ CVAL(buf, pos) = val;
+ CVAL(buf, pos + 1) = val >> 8;
+ CVAL(buf, pos + 2) = val >> 16;
+ CVAL(buf, pos + 3) = val >> 24;
+}
+
+static inline int64
+IVAL64(const char *buf, int pos)
+{
+ return IVALu((uchar*)buf, pos) | (int64)IVALu((uchar*)buf, pos + 4) << 32;
+}
+
+static inline void
+SIVAL64(char *buf, int pos, int64 val)
+{
+ SIVALu((uchar*)buf, pos, val);
+ SIVALu((uchar*)buf, pos + 4, val >> 32);
+}
+
+#else /* !CAREFUL_ALIGNMENT */
+
+/* This handles things for architectures like the 386 that can handle alignment errors.
+ * WARNING: This section is dependent on the length of an int32 (and thus a uint32)
+ * being correct (4 bytes)! Set CAREFUL_ALIGNMENT if it is not. */
+
+static inline uint32
+IVALu(const uchar *buf, int pos)
+{
+ union {
+ const uchar *b;
+ const uint32 *num;
+ } u;
+ u.b = buf + pos;
+ return *u.num;
+}
+
+static inline void
+SIVALu(uchar *buf, int pos, uint32 val)
+{
+ union {
+ uchar *b;
+ uint32 *num;
+ } u;
+ u.b = buf + pos;
+ *u.num = val;
+}
+
+static inline int64
+IVAL64(const char *buf, int pos)
+{
+ union {
+ const char *b;
+ const int64 *num;
+ } u;
+ u.b = buf + pos;
+ return *u.num;
+}
+
+static inline void
+SIVAL64(char *buf, int pos, int64 val)
+{
+ union {
+ char *b;
+ int64 *num;
+ } u;
+ u.b = buf + pos;
+ *u.num = val;
+}
+
+#endif /* !CAREFUL_ALIGNMENT */
+
+static inline uint32
+IVAL(const char *buf, int pos)
+{
+ return IVALu((uchar*)buf, pos);
+}
+
+static inline void
+SIVAL(char *buf, int pos, uint32 val)
+{
+ SIVALu((uchar*)buf, pos, val);
+}
diff --git a/case_N.h b/case_N.h
new file mode 100644
index 0000000..f6e0fda
--- /dev/null
+++ b/case_N.h
@@ -0,0 +1,92 @@
+/*
+ * Allow an arbitrary sequence of case labels.
+ *
+ * Copyright (C) 2006-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/* This is included multiple times, once for every segment in a switch statement.
+ * This produces the next "case N:" statement in sequence. */
+
+#if !defined CASE_N_STATE_0
+#define CASE_N_STATE_0
+ case 0:
+#elif !defined CASE_N_STATE_1
+#define CASE_N_STATE_1
+ /* FALLTHROUGH */
+ case 1:
+#elif !defined CASE_N_STATE_2
+#define CASE_N_STATE_2
+ /* FALLTHROUGH */
+ case 2:
+#elif !defined CASE_N_STATE_3
+#define CASE_N_STATE_3
+ /* FALLTHROUGH */
+ case 3:
+#elif !defined CASE_N_STATE_4
+#define CASE_N_STATE_4
+ /* FALLTHROUGH */
+ case 4:
+#elif !defined CASE_N_STATE_5
+#define CASE_N_STATE_5
+ /* FALLTHROUGH */
+ case 5:
+#elif !defined CASE_N_STATE_6
+#define CASE_N_STATE_6
+ /* FALLTHROUGH */
+ case 6:
+#elif !defined CASE_N_STATE_7
+#define CASE_N_STATE_7
+ /* FALLTHROUGH */
+ case 7:
+#elif !defined CASE_N_STATE_8
+#define CASE_N_STATE_8
+ /* FALLTHROUGH */
+ case 8:
+#elif !defined CASE_N_STATE_9
+#define CASE_N_STATE_9
+ /* FALLTHROUGH */
+ case 9:
+#elif !defined CASE_N_STATE_10
+#define CASE_N_STATE_10
+ /* FALLTHROUGH */
+ case 10:
+#elif !defined CASE_N_STATE_11
+#define CASE_N_STATE_11
+ /* FALLTHROUGH */
+ case 11:
+#elif !defined CASE_N_STATE_12
+#define CASE_N_STATE_12
+ /* FALLTHROUGH */
+ case 12:
+#elif !defined CASE_N_STATE_13
+#define CASE_N_STATE_13
+ /* FALLTHROUGH */
+ case 13:
+#elif !defined CASE_N_STATE_14
+#define CASE_N_STATE_14
+ /* FALLTHROUGH */
+ case 14:
+#elif !defined CASE_N_STATE_15
+#define CASE_N_STATE_15
+ /* FALLTHROUGH */
+ case 15:
+#elif !defined CASE_N_STATE_16
+#define CASE_N_STATE_16
+ /* FALLTHROUGH */
+ case 16:
+#else
+#error Need to add more case statements!
+#endif
diff --git a/checksum.c b/checksum.c
new file mode 100644
index 0000000..60de365
--- /dev/null
+++ b/checksum.c
@@ -0,0 +1,799 @@
+/*
+ * Routines to support checksumming of bytes.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2004-2022 Wayne Davison
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to dynamically link rsync with the OpenSSL and xxhash
+ * libraries when those libraries are being distributed in compliance
+ * with their license terms, and to distribute a dynamically linked
+ * combination of rsync and these libraries. This is also considered
+ * to be covered under the GPL's System Libraries exception.
+ *
+ * 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+#ifdef SUPPORT_XXHASH
+#include <xxhash.h>
+# if XXH_VERSION_NUMBER >= 800
+# define SUPPORT_XXH3 1
+# endif
+#endif
+
+extern int am_server;
+extern int whole_file;
+extern int checksum_seed;
+extern int protocol_version;
+extern int proper_seed_order;
+extern const char *checksum_choice;
+
+#define NNI_BUILTIN (1<<0)
+#define NNI_EVP (1<<1)
+#define NNI_EVP_OK (1<<2)
+
+struct name_num_item valid_checksums_items[] = {
+#ifdef SUPPORT_XXH3
+ { CSUM_XXH3_128, 0, "xxh128", NULL },
+ { CSUM_XXH3_64, 0, "xxh3", NULL },
+#endif
+#ifdef SUPPORT_XXHASH
+ { CSUM_XXH64, 0, "xxh64", NULL },
+ { CSUM_XXH64, 0, "xxhash", NULL },
+#endif
+ { CSUM_MD5, NNI_BUILTIN|NNI_EVP, "md5", NULL },
+ { CSUM_MD4, NNI_BUILTIN|NNI_EVP, "md4", NULL },
+#ifdef SHA_DIGEST_LENGTH
+ { CSUM_SHA1, NNI_EVP, "sha1", NULL },
+#endif
+ { CSUM_NONE, 0, "none", NULL },
+ { 0, 0, NULL, NULL }
+};
+
+struct name_num_obj valid_checksums = {
+ "checksum", NULL, 0, 0, valid_checksums_items
+};
+
+struct name_num_item valid_auth_checksums_items[] = {
+#ifdef SHA512_DIGEST_LENGTH
+ { CSUM_SHA512, NNI_EVP, "sha512", NULL },
+#endif
+#ifdef SHA256_DIGEST_LENGTH
+ { CSUM_SHA256, NNI_EVP, "sha256", NULL },
+#endif
+#ifdef SHA_DIGEST_LENGTH
+ { CSUM_SHA1, NNI_EVP, "sha1", NULL },
+#endif
+ { CSUM_MD5, NNI_BUILTIN|NNI_EVP, "md5", NULL },
+ { CSUM_MD4, NNI_BUILTIN|NNI_EVP, "md4", NULL },
+ { 0, 0, NULL, NULL }
+};
+
+struct name_num_obj valid_auth_checksums = {
+ "daemon auth checksum", NULL, 0, 0, valid_auth_checksums_items
+};
+
+/* These cannot make use of openssl, so they're marked just as built-in */
+struct name_num_item implied_checksum_md4 =
+ { CSUM_MD4, NNI_BUILTIN, "md4", NULL };
+struct name_num_item implied_checksum_md5 =
+ { CSUM_MD5, NNI_BUILTIN, "md5", NULL };
+
+struct name_num_item *xfer_sum_nni; /* used for the transfer checksum2 computations */
+int xfer_sum_len;
+struct name_num_item *file_sum_nni; /* used for the pre-transfer --checksum computations */
+int file_sum_len, file_sum_extra_cnt;
+
+#ifdef USE_OPENSSL
+const EVP_MD *xfer_sum_evp_md;
+const EVP_MD *file_sum_evp_md;
+EVP_MD_CTX *ctx_evp = NULL;
+#endif
+
+static int initialized_choices = 0;
+
+struct name_num_item *parse_csum_name(const char *name, int len)
+{
+ struct name_num_item *nni;
+
+ if (len < 0 && name)
+ len = strlen(name);
+
+ init_checksum_choices();
+
+ if (!name || (len == 4 && strncasecmp(name, "auto", 4) == 0)) {
+ if (protocol_version >= 30) {
+ if (!proper_seed_order)
+ return &implied_checksum_md5;
+ name = "md5";
+ len = 3;
+ } else {
+ if (protocol_version >= 27)
+ implied_checksum_md4.num = CSUM_MD4_OLD;
+ else if (protocol_version >= 21)
+ implied_checksum_md4.num = CSUM_MD4_BUSTED;
+ else
+ implied_checksum_md4.num = CSUM_MD4_ARCHAIC;
+ return &implied_checksum_md4;
+ }
+ }
+
+ nni = get_nni_by_name(&valid_checksums, name, len);
+
+ if (!nni) {
+ rprintf(FERROR, "unknown checksum name: %s\n", name);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ return nni;
+}
+
+#ifdef USE_OPENSSL
+static const EVP_MD *csum_evp_md(struct name_num_item *nni)
+{
+ const EVP_MD *emd;
+ if (!(nni->flags & NNI_EVP))
+ return NULL;
+
+#ifdef USE_MD5_ASM
+ if (nni->num == CSUM_MD5)
+ emd = NULL;
+ else
+#endif
+ emd = EVP_get_digestbyname(nni->name);
+ if (emd && !(nni->flags & NNI_EVP_OK)) { /* Make sure it works before we advertise it */
+ if (!ctx_evp && !(ctx_evp = EVP_MD_CTX_create()))
+ out_of_memory("csum_evp_md");
+ /* Some routines are marked as legacy and are not enabled in the openssl.cnf file.
+ * If we can't init the emd, we'll fall back to our built-in code. */
+ if (EVP_DigestInit_ex(ctx_evp, emd, NULL) == 0)
+ emd = NULL;
+ else
+ nni->flags = (nni->flags & ~NNI_BUILTIN) | NNI_EVP_OK;
+ }
+ if (!emd)
+ nni->flags &= ~NNI_EVP;
+ return emd;
+}
+#endif
+
+void parse_checksum_choice(int final_call)
+{
+ if (valid_checksums.negotiated_nni)
+ xfer_sum_nni = file_sum_nni = valid_checksums.negotiated_nni;
+ else {
+ char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
+ if (cp) {
+ xfer_sum_nni = parse_csum_name(checksum_choice, cp - checksum_choice);
+ file_sum_nni = parse_csum_name(cp+1, -1);
+ } else
+ xfer_sum_nni = file_sum_nni = parse_csum_name(checksum_choice, -1);
+ if (am_server && checksum_choice)
+ validate_choice_vs_env(NSTR_CHECKSUM, xfer_sum_nni->num, file_sum_nni->num);
+ }
+ xfer_sum_len = csum_len_for_type(xfer_sum_nni->num, 0);
+ file_sum_len = csum_len_for_type(file_sum_nni->num, 0);
+#ifdef USE_OPENSSL
+ xfer_sum_evp_md = csum_evp_md(xfer_sum_nni);
+ file_sum_evp_md = csum_evp_md(file_sum_nni);
+#endif
+
+ file_sum_extra_cnt = (file_sum_len + EXTRA_LEN - 1) / EXTRA_LEN;
+
+ if (xfer_sum_nni->num == CSUM_NONE)
+ whole_file = 1;
+
+ /* Snag the checksum name for both write_batch's option output & the following debug output. */
+ if (valid_checksums.negotiated_nni)
+ checksum_choice = valid_checksums.negotiated_nni->name;
+ else if (checksum_choice == NULL)
+ checksum_choice = xfer_sum_nni->name;
+
+ if (final_call && DEBUG_GTE(NSTR, am_server ? 3 : 1)) {
+ rprintf(FINFO, "%s%s checksum: %s\n",
+ am_server ? "Server" : "Client",
+ valid_checksums.negotiated_nni ? " negotiated" : "",
+ checksum_choice);
+ }
+}
+
+int csum_len_for_type(int cst, BOOL flist_csum)
+{
+ switch (cst) {
+ case CSUM_NONE:
+ return 1;
+ case CSUM_MD4_ARCHAIC:
+ /* The oldest checksum code is rather weird: the file-list code only sent
+ * 2-byte checksums, but all other checksums were full MD4 length. */
+ return flist_csum ? 2 : MD4_DIGEST_LEN;
+ case CSUM_MD4:
+ case CSUM_MD4_OLD:
+ case CSUM_MD4_BUSTED:
+ return MD4_DIGEST_LEN;
+ case CSUM_MD5:
+ return MD5_DIGEST_LEN;
+#ifdef SHA_DIGEST_LENGTH
+ case CSUM_SHA1:
+ return SHA_DIGEST_LENGTH;
+#endif
+#ifdef SHA256_DIGEST_LENGTH
+ case CSUM_SHA256:
+ return SHA256_DIGEST_LENGTH;
+#endif
+#ifdef SHA512_DIGEST_LENGTH
+ case CSUM_SHA512:
+ return SHA512_DIGEST_LENGTH;
+#endif
+ case CSUM_XXH64:
+ case CSUM_XXH3_64:
+ return 64/8;
+ case CSUM_XXH3_128:
+ return 128/8;
+ default: /* paranoia to prevent missing case values */
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ return 0;
+}
+
+/* Returns 0 if the checksum is not canonical (i.e. it includes a seed value).
+ * Returns 1 if the public sum order matches our internal sum order.
+ * Returns -1 if the public sum order is the reverse of our internal sum order.
+ */
+int canonical_checksum(int csum_type)
+{
+ switch (csum_type) {
+ case CSUM_NONE:
+ case CSUM_MD4_ARCHAIC:
+ case CSUM_MD4_OLD:
+ case CSUM_MD4_BUSTED:
+ break;
+ case CSUM_MD4:
+ case CSUM_MD5:
+ case CSUM_SHA1:
+ case CSUM_SHA256:
+ case CSUM_SHA512:
+ return -1;
+ case CSUM_XXH64:
+ case CSUM_XXH3_64:
+ case CSUM_XXH3_128:
+ return 1;
+ default: /* paranoia to prevent missing case values */
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ return 0;
+}
+
+#ifndef USE_ROLL_SIMD /* See simd-checksum-*.cpp. */
+/*
+ a simple 32 bit checksum that can be updated from either end
+ (inspired by Mark Adler's Adler-32 checksum)
+ */
+uint32 get_checksum1(char *buf1, int32 len)
+{
+ int32 i;
+ uint32 s1, s2;
+ schar *buf = (schar *)buf1;
+
+ s1 = s2 = 0;
+ for (i = 0; i < (len-4); i+=4) {
+ s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + 10*CHAR_OFFSET;
+ s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
+ }
+ for (; i < len; i++) {
+ s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
+ }
+ return (s1 & 0xffff) + (s2 << 16);
+}
+#endif
+
+void get_checksum2(char *buf, int32 len, char *sum)
+{
+#ifdef USE_OPENSSL
+ if (xfer_sum_evp_md) {
+ static EVP_MD_CTX *evp = NULL;
+ uchar seedbuf[4];
+ if (!evp && !(evp = EVP_MD_CTX_create()))
+ out_of_memory("get_checksum2");
+ EVP_DigestInit_ex(evp, xfer_sum_evp_md, NULL);
+ if (checksum_seed) {
+ SIVALu(seedbuf, 0, checksum_seed);
+ EVP_DigestUpdate(evp, seedbuf, 4);
+ }
+ EVP_DigestUpdate(evp, (uchar *)buf, len);
+ EVP_DigestFinal_ex(evp, (uchar *)sum, NULL);
+ } else
+#endif
+ switch (xfer_sum_nni->num) {
+#ifdef SUPPORT_XXHASH
+ case CSUM_XXH64:
+ SIVAL64(sum, 0, XXH64(buf, len, checksum_seed));
+ break;
+#endif
+#ifdef SUPPORT_XXH3
+ case CSUM_XXH3_64:
+ SIVAL64(sum, 0, XXH3_64bits_withSeed(buf, len, checksum_seed));
+ break;
+ case CSUM_XXH3_128: {
+ XXH128_hash_t digest = XXH3_128bits_withSeed(buf, len, checksum_seed);
+ SIVAL64(sum, 0, digest.low64);
+ SIVAL64(sum, 8, digest.high64);
+ break;
+ }
+#endif
+ case CSUM_MD5: {
+ md_context m5;
+ uchar seedbuf[4];
+ md5_begin(&m5);
+ if (proper_seed_order) {
+ if (checksum_seed) {
+ SIVALu(seedbuf, 0, checksum_seed);
+ md5_update(&m5, seedbuf, 4);
+ }
+ md5_update(&m5, (uchar *)buf, len);
+ } else {
+ md5_update(&m5, (uchar *)buf, len);
+ if (checksum_seed) {
+ SIVALu(seedbuf, 0, checksum_seed);
+ md5_update(&m5, seedbuf, 4);
+ }
+ }
+ md5_result(&m5, (uchar *)sum);
+ break;
+ }
+ case CSUM_MD4:
+ case CSUM_MD4_OLD:
+ case CSUM_MD4_BUSTED:
+ case CSUM_MD4_ARCHAIC: {
+ md_context m;
+ int32 i;
+ static char *buf1;
+ static int32 len1;
+
+ mdfour_begin(&m);
+
+ if (len > len1) {
+ if (buf1)
+ free(buf1);
+ buf1 = new_array(char, len+4);
+ len1 = len;
+ }
+
+ memcpy(buf1, buf, len);
+ if (checksum_seed) {
+ SIVAL(buf1,len,checksum_seed);
+ len += 4;
+ }
+
+ for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
+ mdfour_update(&m, (uchar *)(buf1+i), CSUM_CHUNK);
+
+ /*
+ * Prior to version 27 an incorrect MD4 checksum was computed
+ * by failing to call mdfour_tail() for block sizes that
+ * are multiples of 64. This is fixed by calling mdfour_update()
+ * even when there are no more bytes.
+ */
+ if (len - i > 0 || xfer_sum_nni->num > CSUM_MD4_BUSTED)
+ mdfour_update(&m, (uchar *)(buf1+i), len-i);
+
+ mdfour_result(&m, (uchar *)sum);
+ break;
+ }
+ default: /* paranoia to prevent missing case values */
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+}
+
+void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
+{
+ struct map_struct *buf;
+ OFF_T i, len = st_p->st_size;
+ int32 remainder;
+ int fd;
+
+ fd = do_open(fname, O_RDONLY, 0);
+ if (fd == -1) {
+ memset(sum, 0, file_sum_len);
+ return;
+ }
+
+ buf = map_file(fd, len, MAX_MAP_SIZE, CHUNK_SIZE);
+
+#ifdef USE_OPENSSL
+ if (file_sum_evp_md) {
+ static EVP_MD_CTX *evp = NULL;
+ if (!evp && !(evp = EVP_MD_CTX_create()))
+ out_of_memory("file_checksum");
+
+ EVP_DigestInit_ex(evp, file_sum_evp_md, NULL);
+
+ for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
+ EVP_DigestUpdate(evp, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
+
+ remainder = (int32)(len - i);
+ if (remainder > 0)
+ EVP_DigestUpdate(evp, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ EVP_DigestFinal_ex(evp, (uchar *)sum, NULL);
+ } else
+#endif
+ switch (file_sum_nni->num) {
+#ifdef SUPPORT_XXHASH
+ case CSUM_XXH64: {
+ static XXH64_state_t* state = NULL;
+ if (!state && !(state = XXH64_createState()))
+ out_of_memory("file_checksum");
+
+ XXH64_reset(state, 0);
+
+ for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
+ XXH64_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
+
+ remainder = (int32)(len - i);
+ if (remainder > 0)
+ XXH64_update(state, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ SIVAL64(sum, 0, XXH64_digest(state));
+ break;
+ }
+#endif
+#ifdef SUPPORT_XXH3
+ case CSUM_XXH3_64: {
+ static XXH3_state_t* state = NULL;
+ if (!state && !(state = XXH3_createState()))
+ out_of_memory("file_checksum");
+
+ XXH3_64bits_reset(state);
+
+ for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
+ XXH3_64bits_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
+
+ remainder = (int32)(len - i);
+ if (remainder > 0)
+ XXH3_64bits_update(state, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ SIVAL64(sum, 0, XXH3_64bits_digest(state));
+ break;
+ }
+ case CSUM_XXH3_128: {
+ XXH128_hash_t digest;
+ static XXH3_state_t* state = NULL;
+ if (!state && !(state = XXH3_createState()))
+ out_of_memory("file_checksum");
+
+ XXH3_128bits_reset(state);
+
+ for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
+ XXH3_128bits_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
+
+ remainder = (int32)(len - i);
+ if (remainder > 0)
+ XXH3_128bits_update(state, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ digest = XXH3_128bits_digest(state);
+ SIVAL64(sum, 0, digest.low64);
+ SIVAL64(sum, 8, digest.high64);
+ break;
+ }
+#endif
+ case CSUM_MD5: {
+ md_context m5;
+
+ md5_begin(&m5);
+
+ for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
+ md5_update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
+
+ remainder = (int32)(len - i);
+ if (remainder > 0)
+ md5_update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ md5_result(&m5, (uchar *)sum);
+ break;
+ }
+ case CSUM_MD4:
+ case CSUM_MD4_OLD:
+ case CSUM_MD4_BUSTED:
+ case CSUM_MD4_ARCHAIC: {
+ md_context m;
+
+ mdfour_begin(&m);
+
+ for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
+ mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK), CSUM_CHUNK);
+
+ /* Prior to version 27 an incorrect MD4 checksum was computed
+ * by failing to call mdfour_tail() for block sizes that
+ * are multiples of 64. This is fixed by calling mdfour_update()
+ * even when there are no more bytes. */
+ remainder = (int32)(len - i);
+ if (remainder > 0 || file_sum_nni->num > CSUM_MD4_BUSTED)
+ mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
+
+ mdfour_result(&m, (uchar *)sum);
+ break;
+ }
+ default:
+ rprintf(FERROR, "Invalid checksum-choice for --checksum: %s (%d)\n",
+ file_sum_nni->name, file_sum_nni->num);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ close(fd);
+ unmap_file(buf);
+}
+
+static int32 sumresidue;
+static md_context ctx_md;
+#ifdef SUPPORT_XXHASH
+static XXH64_state_t* xxh64_state;
+#endif
+#ifdef SUPPORT_XXH3
+static XXH3_state_t* xxh3_state;
+#endif
+static struct name_num_item *cur_sum_nni;
+int cur_sum_len;
+
+#ifdef USE_OPENSSL
+static const EVP_MD *cur_sum_evp_md;
+#endif
+
+/* Initialize a hash digest accumulator. Data is supplied via
+ * sum_update() and the resulting binary digest is retrieved via
+ * sum_end(). This only supports one active sum at a time. */
+int sum_init(struct name_num_item *nni, int seed)
+{
+ char s[4];
+
+ if (!nni)
+ nni = parse_csum_name(NULL, 0);
+ cur_sum_nni = nni;
+ cur_sum_len = csum_len_for_type(nni->num, 0);
+#ifdef USE_OPENSSL
+ cur_sum_evp_md = csum_evp_md(nni);
+#endif
+
+#ifdef USE_OPENSSL
+ if (cur_sum_evp_md) {
+ if (!ctx_evp && !(ctx_evp = EVP_MD_CTX_create()))
+ out_of_memory("file_checksum");
+ EVP_DigestInit_ex(ctx_evp, cur_sum_evp_md, NULL);
+ } else
+#endif
+ switch (cur_sum_nni->num) {
+#ifdef SUPPORT_XXHASH
+ case CSUM_XXH64:
+ if (!xxh64_state && !(xxh64_state = XXH64_createState()))
+ out_of_memory("sum_init");
+ XXH64_reset(xxh64_state, 0);
+ break;
+#endif
+#ifdef SUPPORT_XXH3
+ case CSUM_XXH3_64:
+ if (!xxh3_state && !(xxh3_state = XXH3_createState()))
+ out_of_memory("sum_init");
+ XXH3_64bits_reset(xxh3_state);
+ break;
+ case CSUM_XXH3_128:
+ if (!xxh3_state && !(xxh3_state = XXH3_createState()))
+ out_of_memory("sum_init");
+ XXH3_128bits_reset(xxh3_state);
+ break;
+#endif
+ case CSUM_MD5:
+ md5_begin(&ctx_md);
+ break;
+ case CSUM_MD4:
+ mdfour_begin(&ctx_md);
+ sumresidue = 0;
+ break;
+ case CSUM_MD4_OLD:
+ case CSUM_MD4_BUSTED:
+ case CSUM_MD4_ARCHAIC:
+ mdfour_begin(&ctx_md);
+ sumresidue = 0;
+ SIVAL(s, 0, seed);
+ sum_update(s, 4);
+ break;
+ case CSUM_NONE:
+ break;
+ default: /* paranoia to prevent missing case values */
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ return cur_sum_len;
+}
+
+/* Feed data into a hash digest accumulator. */
+void sum_update(const char *p, int32 len)
+{
+#ifdef USE_OPENSSL
+ if (cur_sum_evp_md) {
+ EVP_DigestUpdate(ctx_evp, (uchar *)p, len);
+ } else
+#endif
+ switch (cur_sum_nni->num) {
+#ifdef SUPPORT_XXHASH
+ case CSUM_XXH64:
+ XXH64_update(xxh64_state, p, len);
+ break;
+#endif
+#ifdef SUPPORT_XXH3
+ case CSUM_XXH3_64:
+ XXH3_64bits_update(xxh3_state, p, len);
+ break;
+ case CSUM_XXH3_128:
+ XXH3_128bits_update(xxh3_state, p, len);
+ break;
+#endif
+ case CSUM_MD5:
+ md5_update(&ctx_md, (uchar *)p, len);
+ break;
+ case CSUM_MD4:
+ case CSUM_MD4_OLD:
+ case CSUM_MD4_BUSTED:
+ case CSUM_MD4_ARCHAIC:
+ if (len + sumresidue < CSUM_CHUNK) {
+ memcpy(ctx_md.buffer + sumresidue, p, len);
+ sumresidue += len;
+ break;
+ }
+
+ if (sumresidue) {
+ int32 i = CSUM_CHUNK - sumresidue;
+ memcpy(ctx_md.buffer + sumresidue, p, i);
+ mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, CSUM_CHUNK);
+ len -= i;
+ p += i;
+ }
+
+ while (len >= CSUM_CHUNK) {
+ mdfour_update(&ctx_md, (uchar *)p, CSUM_CHUNK);
+ len -= CSUM_CHUNK;
+ p += CSUM_CHUNK;
+ }
+
+ sumresidue = len;
+ if (sumresidue)
+ memcpy(ctx_md.buffer, p, sumresidue);
+ break;
+ case CSUM_NONE:
+ break;
+ default: /* paranoia to prevent missing case values */
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+}
+
+/* The sum buffer only needs to be as long as the current checksum's digest
+ * len, not MAX_DIGEST_LEN. Note that for CSUM_MD4_ARCHAIC that is the full
+ * MD4_DIGEST_LEN even if the file-list code is going to ignore all but the
+ * first 2 bytes of it. */
+void sum_end(char *sum)
+{
+#ifdef USE_OPENSSL
+ if (cur_sum_evp_md) {
+ EVP_DigestFinal_ex(ctx_evp, (uchar *)sum, NULL);
+ } else
+#endif
+ switch (cur_sum_nni->num) {
+#ifdef SUPPORT_XXHASH
+ case CSUM_XXH64:
+ SIVAL64(sum, 0, XXH64_digest(xxh64_state));
+ break;
+#endif
+#ifdef SUPPORT_XXH3
+ case CSUM_XXH3_64:
+ SIVAL64(sum, 0, XXH3_64bits_digest(xxh3_state));
+ break;
+ case CSUM_XXH3_128: {
+ XXH128_hash_t digest = XXH3_128bits_digest(xxh3_state);
+ SIVAL64(sum, 0, digest.low64);
+ SIVAL64(sum, 8, digest.high64);
+ break;
+ }
+#endif
+ case CSUM_MD5:
+ md5_result(&ctx_md, (uchar *)sum);
+ break;
+ case CSUM_MD4:
+ case CSUM_MD4_OLD:
+ mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, sumresidue);
+ mdfour_result(&ctx_md, (uchar *)sum);
+ break;
+ case CSUM_MD4_BUSTED:
+ case CSUM_MD4_ARCHAIC:
+ if (sumresidue)
+ mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, sumresidue);
+ mdfour_result(&ctx_md, (uchar *)sum);
+ break;
+ case CSUM_NONE:
+ *sum = '\0';
+ break;
+ default: /* paranoia to prevent missing case values */
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+}
+
+#if defined SUPPORT_XXH3 || defined USE_OPENSSL
+static void verify_digest(struct name_num_item *nni, BOOL check_auth_list)
+{
+#ifdef SUPPORT_XXH3
+ static int xxh3_result = 0;
+#endif
+#ifdef USE_OPENSSL
+ static int prior_num = 0, prior_flags = 0, prior_result = 0;
+#endif
+
+#ifdef SUPPORT_XXH3
+ if (nni->num == CSUM_XXH3_64 || nni->num == CSUM_XXH3_128) {
+ if (!xxh3_result) {
+ char buf[32816];
+ int j;
+ for (j = 0; j < (int)sizeof buf; j++)
+ buf[j] = ' ' + (j % 96);
+ sum_init(nni, 0);
+ sum_update(buf, 32816);
+ sum_update(buf, 31152);
+ sum_update(buf, 32474);
+ sum_update(buf, 9322);
+ xxh3_result = XXH3_64bits_digest(xxh3_state) != 0xadbcf16d4678d1de ? -1 : 1;
+ }
+ if (xxh3_result < 0)
+ nni->num = CSUM_gone;
+ return;
+ }
+#endif
+
+#ifdef USE_OPENSSL
+ if (BITS_SETnUNSET(nni->flags, NNI_EVP, NNI_BUILTIN|NNI_EVP_OK)) {
+ if (nni->num == prior_num && nni->flags == prior_flags) {
+ nni->flags = prior_result;
+ if (!(nni->flags & NNI_EVP))
+ nni->num = CSUM_gone;
+ } else {
+ prior_num = nni->num;
+ prior_flags = nni->flags;
+ if (!csum_evp_md(nni))
+ nni->num = CSUM_gone;
+ prior_result = nni->flags;
+ if (check_auth_list && (nni = get_nni_by_num(&valid_auth_checksums, prior_num)) != NULL)
+ verify_digest(nni, False);
+ }
+ }
+#endif
+}
+#endif
+
+void init_checksum_choices()
+{
+#if defined SUPPORT_XXH3 || defined USE_OPENSSL
+ struct name_num_item *nni;
+#endif
+
+ if (initialized_choices)
+ return;
+
+#if defined SUPPORT_XXH3 || defined USE_OPENSSL
+ for (nni = valid_checksums.list; nni->name; nni++)
+ verify_digest(nni, True);
+
+ for (nni = valid_auth_checksums.list; nni->name; nni++)
+ verify_digest(nni, False);
+#endif
+
+ initialized_choices = 1;
+}
diff --git a/chmod.c b/chmod.c
new file mode 100644
index 0000000..8bbf791
--- /dev/null
+++ b/chmod.c
@@ -0,0 +1,249 @@
+/*
+ * Implement the core of the --chmod option.
+ *
+ * Copyright (C) 2002 Scott Howard
+ * Copyright (C) 2005-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+extern mode_t orig_umask;
+
+#define FLAG_X_KEEP (1<<0)
+#define FLAG_DIRS_ONLY (1<<1)
+#define FLAG_FILES_ONLY (1<<2)
+
+struct chmod_mode_struct {
+ struct chmod_mode_struct *next;
+ int ModeAND, ModeOR;
+ char flags;
+};
+
+#define CHMOD_ADD 1
+#define CHMOD_SUB 2
+#define CHMOD_EQ 3
+#define CHMOD_SET 4
+
+#define STATE_ERROR 0
+#define STATE_1ST_HALF 1
+#define STATE_2ND_HALF 2
+#define STATE_OCTAL_NUM 3
+
+/* Parse a chmod-style argument, and break it down into one or more AND/OR
+ * pairs in a linked list. We return a pointer to new items on success
+ * (appending the items to the specified list), or NULL on error. */
+struct chmod_mode_struct *parse_chmod(const char *modestr,
+ struct chmod_mode_struct **root_mode_ptr)
+{
+ int state = STATE_1ST_HALF;
+ int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
+ struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
+ *prev_mode = NULL;
+
+ while (state != STATE_ERROR) {
+ if (!*modestr || *modestr == ',') {
+ int bits;
+
+ if (!op) {
+ state = STATE_ERROR;
+ break;
+ }
+ prev_mode = curr_mode;
+ curr_mode = new_array(struct chmod_mode_struct, 1);
+ if (prev_mode)
+ prev_mode->next = curr_mode;
+ else
+ first_mode = curr_mode;
+ curr_mode->next = NULL;
+
+ if (where)
+ bits = where * what;
+ else {
+ where = 0111;
+ bits = (where * what) & ~orig_umask;
+ }
+
+ switch (op) {
+ case CHMOD_ADD:
+ curr_mode->ModeAND = CHMOD_BITS;
+ curr_mode->ModeOR = bits + topoct;
+ break;
+ case CHMOD_SUB:
+ curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
+ curr_mode->ModeOR = 0;
+ break;
+ case CHMOD_EQ:
+ curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
+ curr_mode->ModeOR = bits + topoct;
+ break;
+ case CHMOD_SET:
+ curr_mode->ModeAND = 0;
+ curr_mode->ModeOR = bits;
+ break;
+ }
+
+ curr_mode->flags = flags;
+
+ if (!*modestr)
+ break;
+ modestr++;
+
+ state = STATE_1ST_HALF;
+ where = what = op = topoct = topbits = flags = 0;
+ }
+
+ switch (state) {
+ case STATE_1ST_HALF:
+ switch (*modestr) {
+ case 'D':
+ if (flags & FLAG_FILES_ONLY)
+ state = STATE_ERROR;
+ flags |= FLAG_DIRS_ONLY;
+ break;
+ case 'F':
+ if (flags & FLAG_DIRS_ONLY)
+ state = STATE_ERROR;
+ flags |= FLAG_FILES_ONLY;
+ break;
+ case 'u':
+ where |= 0100;
+ topbits |= 04000;
+ break;
+ case 'g':
+ where |= 0010;
+ topbits |= 02000;
+ break;
+ case 'o':
+ where |= 0001;
+ break;
+ case 'a':
+ where |= 0111;
+ break;
+ case '+':
+ op = CHMOD_ADD;
+ state = STATE_2ND_HALF;
+ break;
+ case '-':
+ op = CHMOD_SUB;
+ state = STATE_2ND_HALF;
+ break;
+ case '=':
+ op = CHMOD_EQ;
+ state = STATE_2ND_HALF;
+ break;
+ default:
+ if (isDigit(modestr) && *modestr < '8' && !where) {
+ op = CHMOD_SET;
+ state = STATE_OCTAL_NUM;
+ where = 1;
+ what = *modestr - '0';
+ } else
+ state = STATE_ERROR;
+ break;
+ }
+ break;
+ case STATE_2ND_HALF:
+ switch (*modestr) {
+ case 'r':
+ what |= 4;
+ break;
+ case 'w':
+ what |= 2;
+ break;
+ case 'X':
+ flags |= FLAG_X_KEEP;
+ /* FALL THROUGH */
+ case 'x':
+ what |= 1;
+ break;
+ case 's':
+ if (topbits)
+ topoct |= topbits;
+ else
+ topoct = 04000;
+ break;
+ case 't':
+ topoct |= 01000;
+ break;
+ default:
+ state = STATE_ERROR;
+ break;
+ }
+ break;
+ case STATE_OCTAL_NUM:
+ if (isDigit(modestr) && *modestr < '8') {
+ what = what*8 + *modestr - '0';
+ if (what > CHMOD_BITS)
+ state = STATE_ERROR;
+ } else
+ state = STATE_ERROR;
+ break;
+ }
+ modestr++;
+ }
+
+ if (state == STATE_ERROR) {
+ free_chmod_mode(first_mode);
+ return NULL;
+ }
+
+ if (!(curr_mode = *root_mode_ptr))
+ *root_mode_ptr = first_mode;
+ else {
+ while (curr_mode->next)
+ curr_mode = curr_mode->next;
+ curr_mode->next = first_mode;
+ }
+
+ return first_mode;
+}
+
+
+/* Takes an existing file permission and a list of AND/OR changes, and
+ * create a new permissions. */
+int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
+{
+ int IsX = mode & 0111;
+ int NonPerm = mode & ~CHMOD_BITS;
+
+ for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
+ if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
+ continue;
+ if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
+ continue;
+ mode &= chmod_modes->ModeAND;
+ if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
+ mode |= chmod_modes->ModeOR & ~0111;
+ else
+ mode |= chmod_modes->ModeOR;
+ }
+
+ return mode | NonPerm;
+}
+
+/* Free the linked list created by parse_chmod. */
+int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
+{
+ struct chmod_mode_struct *next;
+
+ while (chmod_modes) {
+ next = chmod_modes->next;
+ free(chmod_modes);
+ chmod_modes = next;
+ }
+ return 0;
+}
diff --git a/cleanup.c b/cleanup.c
new file mode 100644
index 0000000..40d26ba
--- /dev/null
+++ b/cleanup.c
@@ -0,0 +1,298 @@
+/*
+ * End-of-run cleanup routines.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int dry_run;
+extern int am_server;
+extern int am_daemon;
+extern int am_receiver;
+extern int am_sender;
+extern int io_error;
+extern int keep_partial;
+extern int got_xfer_error;
+extern int protocol_version;
+extern int output_needs_newline;
+extern char *partial_dir;
+extern char *logfile_name;
+
+int called_from_signal_handler = 0;
+BOOL shutting_down = False;
+BOOL flush_ok_after_signal = False;
+
+#ifdef HAVE_SIGACTION
+static struct sigaction sigact;
+#endif
+
+/**
+ * Close all open sockets and files, allowing a (somewhat) graceful
+ * shutdown() of socket connections. This eliminates the abortive
+ * TCP RST sent by a Winsock-based system when the close() occurs.
+ **/
+void close_all(void)
+{
+#ifdef SHUTDOWN_ALL_SOCKETS
+ int max_fd;
+ int fd;
+ int ret;
+ STRUCT_STAT st;
+
+ max_fd = sysconf(_SC_OPEN_MAX) - 1;
+ for (fd = max_fd; fd >= 0; fd--) {
+ if ((ret = do_fstat(fd, &st)) == 0) {
+ if (is_a_socket(fd))
+ ret = shutdown(fd, 2);
+ ret = close(fd);
+ }
+ }
+#endif
+}
+
+/**
+ * @file cleanup.c
+ *
+ * Code for handling interrupted transfers. Depending on the @c
+ * --partial option, we may either delete the temporary file, or go
+ * ahead and overwrite the destination. This second behaviour only
+ * occurs if we've sent literal data and therefore hopefully made
+ * progress on the transfer.
+ **/
+
+/**
+ * Set to True once literal data has been sent across the link for the
+ * current file. (????)
+ *
+ * Handling the cleanup when a transfer is interrupted is tricky when
+ * --partial is selected. We need to ensure that the partial file is
+ * kept if any real data has been transferred.
+ **/
+int cleanup_got_literal = 0;
+
+static const char *cleanup_fname;
+static const char *cleanup_new_fname;
+static struct file_struct *cleanup_file;
+static int cleanup_fd_r = -1, cleanup_fd_w = -1;
+static pid_t cleanup_pid = 0;
+
+pid_t cleanup_child_pid = -1;
+
+/**
+ * Eventually calls exit(), passing @p code, therefore does not return.
+ *
+ * @param code one of the RERR_* codes from errcode.h.
+ **/
+NORETURN void _exit_cleanup(int code, const char *file, int line)
+{
+ static int switch_step = 0;
+ static int exit_code = 0, exit_line = 0;
+ static const char *exit_file = NULL;
+ static int first_code = 0;
+
+ SIGACTION(SIGUSR1, SIG_IGN);
+ SIGACTION(SIGUSR2, SIG_IGN);
+
+ if (!exit_code) { /* Preserve first error exit info when recursing. */
+ exit_code = code;
+ exit_file = file;
+ exit_line = line < 0 ? -line : line;
+ }
+
+ /* If this is the exit at the end of the run, the server side
+ * should not attempt to output a message (see log_exit()). */
+ if (am_server && code == 0)
+ am_server = 2;
+
+ /* Some of our actions might cause a recursive call back here, so we
+ * keep track of where we are in the cleanup and never repeat a step. */
+ switch (switch_step) {
+#include "case_N.h" /* case 0: */
+ switch_step++;
+
+ first_code = code;
+
+ if (output_needs_newline) {
+ fputc('\n', stdout);
+ output_needs_newline = 0;
+ }
+
+ if (DEBUG_GTE(EXIT, 2)) {
+ rprintf(FINFO,
+ "[%s] _exit_cleanup(code=%d, file=%s, line=%d): entered\n",
+ who_am_i(), code, src_file(file), line);
+ }
+
+#include "case_N.h"
+ switch_step++;
+
+ if (cleanup_child_pid != -1) {
+ int status;
+ int pid = wait_process(cleanup_child_pid, &status, WNOHANG);
+ if (pid == cleanup_child_pid) {
+ status = WEXITSTATUS(status);
+ if (status > exit_code)
+ exit_code = status;
+ }
+ }
+
+#include "case_N.h"
+ switch_step++;
+
+ if (cleanup_got_literal && (cleanup_fname || cleanup_fd_w != -1)) {
+ if (cleanup_fd_r != -1) {
+ close(cleanup_fd_r);
+ cleanup_fd_r = -1;
+ }
+ if (cleanup_fd_w != -1) {
+ flush_write_file(cleanup_fd_w);
+ close(cleanup_fd_w);
+ cleanup_fd_w = -1;
+ }
+ if (cleanup_fname && cleanup_new_fname && keep_partial
+ && handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) {
+ int tweak_modtime = 0;
+ const char *fname = cleanup_fname;
+ cleanup_fname = NULL;
+ if (!partial_dir) {
+ /* We don't want to leave a partial file with a modern time or it
+ * could be skipped via --update. Setting the time to something
+ * really old also helps it to stand out as unfinished in an ls. */
+ tweak_modtime = 1;
+ cleanup_file->modtime = 0;
+ }
+ finish_transfer(cleanup_new_fname, fname, NULL, NULL,
+ cleanup_file, tweak_modtime, !partial_dir);
+ }
+ }
+
+#include "case_N.h"
+ switch_step++;
+
+ if (flush_ok_after_signal) {
+ flush_ok_after_signal = False;
+ if (code == RERR_SIGNAL)
+ io_flush(FULL_FLUSH);
+ }
+ if (!exit_code && !code)
+ io_flush(FULL_FLUSH);
+
+#include "case_N.h"
+ switch_step++;
+
+ if (cleanup_fname)
+ do_unlink(cleanup_fname);
+ if (exit_code)
+ kill_all(SIGUSR1);
+ if (cleanup_pid && cleanup_pid == getpid()) {
+ char *pidf = lp_pid_file();
+ if (pidf && *pidf)
+ unlink(lp_pid_file());
+ }
+
+ if (exit_code == 0) {
+ if (code)
+ exit_code = code;
+ if (io_error & IOERR_DEL_LIMIT)
+ exit_code = RERR_DEL_LIMIT;
+ if (io_error & IOERR_VANISHED)
+ exit_code = RERR_VANISHED;
+ if (io_error & IOERR_GENERAL || got_xfer_error)
+ exit_code = RERR_PARTIAL;
+ }
+
+ /* If line < 0, this exit is after a MSG_ERROR_EXIT event, so
+ * we don't want to output a duplicate error. */
+ if ((exit_code && line > 0)
+ || am_daemon || (logfile_name && (am_server || !INFO_GTE(STATS, 1)))) {
+ log_exit(exit_code, exit_file, exit_line);
+ }
+
+#include "case_N.h"
+ switch_step++;
+
+ if (DEBUG_GTE(EXIT, 1)) {
+ rprintf(FINFO,
+ "[%s] _exit_cleanup(code=%d, file=%s, line=%d): "
+ "about to call exit(%d)%s\n",
+ who_am_i(), first_code, exit_file, exit_line, exit_code,
+ dry_run ? " (DRY RUN)" : "");
+ }
+
+#include "case_N.h"
+ switch_step++;
+
+ if (exit_code && exit_code != RERR_SOCKETIO && exit_code != RERR_STREAMIO && exit_code != RERR_SIGNAL1
+ && exit_code != RERR_TIMEOUT && !shutting_down) {
+ if (protocol_version >= 31 || am_receiver) {
+ if (line > 0) {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT with exit_code %d\n",
+ who_am_i(), exit_code);
+ }
+ send_msg_int(MSG_ERROR_EXIT, exit_code);
+ }
+ if (!am_sender)
+ io_flush(MSG_FLUSH); /* Be sure to send all messages */
+ noop_io_until_death();
+ }
+ else if (!am_sender)
+ io_flush(MSG_FLUSH); /* Be sure to send all messages */
+ }
+
+#include "case_N.h"
+ switch_step++;
+
+ if (am_server && exit_code)
+ msleep(100);
+ close_all();
+
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+
+ if (called_from_signal_handler)
+ _exit(exit_code);
+ exit(exit_code);
+}
+
+void cleanup_disable(void)
+{
+ cleanup_fname = cleanup_new_fname = NULL;
+ cleanup_fd_r = cleanup_fd_w = -1;
+ cleanup_got_literal = 0;
+}
+
+
+void cleanup_set(const char *fnametmp, const char *fname, struct file_struct *file,
+ int fd_r, int fd_w)
+{
+ cleanup_fname = fnametmp;
+ cleanup_new_fname = fname; /* can be NULL on a partial-dir failure */
+ cleanup_file = file;
+ cleanup_fd_r = fd_r;
+ cleanup_fd_w = fd_w;
+}
+
+void cleanup_set_pid(pid_t pid)
+{
+ cleanup_pid = pid;
+}
diff --git a/clientname.c b/clientname.c
new file mode 100644
index 0000000..ea94894
--- /dev/null
+++ b/clientname.c
@@ -0,0 +1,538 @@
+/*
+ * Functions for looking up the remote name or addr of a socket.
+ *
+ * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2002-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/*
+ * This file is now converted to use the new-style getaddrinfo()
+ * interface, which supports IPv6 but is also supported on recent
+ * IPv4-only machines. On systems that don't have that interface, we
+ * emulate it using the KAME implementation.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+extern int am_daemon;
+
+static const char default_name[] = "UNKNOWN";
+static const char proxyv2sig[] = "\r\n\r\n\0\r\nQUIT\n";
+
+static char ipaddr_buf[100];
+
+#define PROXY_V2_SIG_SIZE ((int)sizeof proxyv2sig - 1)
+#define PROXY_V2_HEADER_SIZE (PROXY_V2_SIG_SIZE + 1 + 1 + 2)
+
+#define CMD_LOCAL 0
+#define CMD_PROXY 1
+
+#define PROXY_FAM_TCPv4 0x11
+#define PROXY_FAM_TCPv6 0x21
+
+#define GET_SOCKADDR_FAMILY(ss) ((struct sockaddr*)ss)->sa_family
+
+static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len);
+static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size);
+static int valid_ipaddr(const char *s, int allow_scope);
+
+/* Return the IP addr of the client as a string. */
+char *client_addr(int fd)
+{
+ struct sockaddr_storage ss;
+ socklen_t length = sizeof ss;
+
+ if (*ipaddr_buf)
+ return ipaddr_buf;
+
+ if (am_daemon < 0) { /* daemon over --rsh mode */
+ char *env_str;
+ strlcpy(ipaddr_buf, "0.0.0.0", sizeof ipaddr_buf);
+ if ((env_str = getenv("REMOTE_HOST")) != NULL
+ || (env_str = getenv("SSH_CONNECTION")) != NULL
+ || (env_str = getenv("SSH_CLIENT")) != NULL
+ || (env_str = getenv("SSH2_CLIENT")) != NULL) {
+ char *p;
+ strlcpy(ipaddr_buf, env_str, sizeof ipaddr_buf);
+ /* Truncate the value to just the IP address. */
+ if ((p = strchr(ipaddr_buf, ' ')) != NULL)
+ *p = '\0';
+ }
+ if (valid_ipaddr(ipaddr_buf, True))
+ return ipaddr_buf;
+ }
+
+ client_sockaddr(fd, &ss, &length);
+ getnameinfo((struct sockaddr *)&ss, length, ipaddr_buf, sizeof ipaddr_buf, NULL, 0, NI_NUMERICHOST);
+
+ return ipaddr_buf;
+}
+
+
+/**
+ * Return the DNS name of the client.
+ *
+ * The name is statically cached so that repeated lookups are quick,
+ * so there is a limit of one lookup per customer.
+ *
+ * If anything goes wrong, including the name->addr->name check, then
+ * we just use "UNKNOWN", so you can use that value in hosts allow
+ * lines.
+ *
+ * After translation from sockaddr to name we do a forward lookup to
+ * make sure nobody is spoofing PTR records.
+ **/
+char *client_name(const char *ipaddr)
+{
+ static char name_buf[100];
+ char port_buf[100];
+ struct sockaddr_storage ss;
+ socklen_t ss_len;
+ struct addrinfo hint, *answer;
+ int err;
+
+ if (*name_buf)
+ return name_buf;
+
+ strlcpy(name_buf, default_name, sizeof name_buf);
+
+ if (strcmp(ipaddr, "0.0.0.0") == 0)
+ return name_buf;
+
+ memset(&ss, 0, sizeof ss);
+ memset(&hint, 0, sizeof hint);
+
+#ifdef AI_NUMERICHOST
+ hint.ai_flags = AI_NUMERICHOST;
+#endif
+ hint.ai_socktype = SOCK_STREAM;
+
+ if ((err = getaddrinfo(ipaddr, NULL, &hint, &answer)) != 0) {
+ rprintf(FLOG, "malformed address %s: %s\n", ipaddr, gai_strerror(err));
+ return name_buf;
+ }
+
+ switch (answer->ai_family) {
+ case AF_INET:
+ ss_len = sizeof (struct sockaddr_in);
+ memcpy(&ss, answer->ai_addr, ss_len);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ ss_len = sizeof (struct sockaddr_in6);
+ memcpy(&ss, answer->ai_addr, ss_len);
+ break;
+#endif
+ default:
+ NOISY_DEATH("Unknown ai_family value");
+ }
+ freeaddrinfo(answer);
+
+ /* reverse lookup */
+ err = getnameinfo((struct sockaddr*)&ss, ss_len, name_buf, sizeof name_buf,
+ port_buf, sizeof port_buf, NI_NAMEREQD | NI_NUMERICSERV);
+ if (err) {
+ strlcpy(name_buf, default_name, sizeof name_buf);
+ rprintf(FLOG, "name lookup failed for %s: %s\n", ipaddr, gai_strerror(err));
+ } else
+ check_name(ipaddr, &ss, name_buf, sizeof name_buf);
+
+ return name_buf;
+}
+
+
+/* Try to read a proxy protocol header (V1 or V2). Returns 1 on success or 0 on failure. */
+int read_proxy_protocol_header(int fd)
+{
+ union {
+ struct {
+ char line[108];
+ } v1;
+ struct {
+ char sig[PROXY_V2_SIG_SIZE];
+ char ver_cmd;
+ char fam;
+ char len[2];
+ union {
+ struct {
+ char src_addr[4];
+ char dst_addr[4];
+ char src_port[2];
+ char dst_port[2];
+ } ip4;
+ struct {
+ char src_addr[16];
+ char dst_addr[16];
+ char src_port[2];
+ char dst_port[2];
+ } ip6;
+ struct {
+ char src_addr[108];
+ char dst_addr[108];
+ } unx;
+ } addr;
+ } v2;
+ } hdr;
+
+ read_buf(fd, (char*)&hdr, PROXY_V2_SIG_SIZE);
+
+ if (memcmp(hdr.v2.sig, proxyv2sig, PROXY_V2_SIG_SIZE) == 0) { /* Proxy V2 */
+ int ver, cmd, size;
+
+ read_buf(fd, (char*)&hdr + PROXY_V2_SIG_SIZE, PROXY_V2_HEADER_SIZE - PROXY_V2_SIG_SIZE);
+
+ ver = (hdr.v2.ver_cmd & 0xf0) >> 4;
+ cmd = (hdr.v2.ver_cmd & 0x0f);
+ size = (hdr.v2.len[0] << 8) + hdr.v2.len[1];
+
+ if (ver != 2 || size + PROXY_V2_HEADER_SIZE > (int)sizeof hdr)
+ return 0;
+
+ /* Grab all the remaining data in the binary request. */
+ read_buf(fd, (char*)&hdr + PROXY_V2_HEADER_SIZE, size);
+
+ switch (cmd) {
+ case CMD_PROXY:
+ switch (hdr.v2.fam) {
+ case PROXY_FAM_TCPv4:
+ if (size != sizeof hdr.v2.addr.ip4)
+ return 0;
+ inet_ntop(AF_INET, hdr.v2.addr.ip4.src_addr, ipaddr_buf, sizeof ipaddr_buf);
+ return valid_ipaddr(ipaddr_buf, False);
+#ifdef INET6
+ case PROXY_FAM_TCPv6:
+ if (size != sizeof hdr.v2.addr.ip6)
+ return 0;
+ inet_ntop(AF_INET6, hdr.v2.addr.ip6.src_addr, ipaddr_buf, sizeof ipaddr_buf);
+ return valid_ipaddr(ipaddr_buf, False);
+#endif
+ default:
+ break;
+ }
+ /* For an unsupported protocol we'll ignore the proxy data (leaving ipaddr_buf unset)
+ * and accept the connection, which will get handled as a normal socket addr. */
+ return 1;
+ case CMD_LOCAL:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+ }
+
+ if (memcmp(hdr.v1.line, "PROXY", 5) == 0) { /* Proxy V1 */
+ char *endc, *sp, *p = hdr.v1.line + PROXY_V2_SIG_SIZE;
+ int port_chk;
+
+ *p = '\0';
+ if (!strchr(hdr.v1.line, '\n')) {
+ while (1) {
+ read_buf(fd, p, 1);
+ if (*p++ == '\n')
+ break;
+ if (p - hdr.v1.line >= (int)sizeof hdr.v1.line - 1)
+ return 0;
+ }
+ *p = '\0';
+ }
+
+ endc = strchr(hdr.v1.line, '\r');
+ if (!endc || endc[1] != '\n' || endc[2])
+ return 0;
+ *endc = '\0';
+
+ p = hdr.v1.line + 5;
+
+ if (!isSpace(p++))
+ return 0;
+ if (strncmp(p, "TCP4", 4) == 0)
+ p += 4;
+ else if (strncmp(p, "TCP6", 4) == 0)
+ p += 4;
+ else if (strncmp(p, "UNKNOWN", 7) == 0)
+ return 1;
+ else
+ return 0;
+
+ if (!isSpace(p++))
+ return 0;
+
+ if ((sp = strchr(p, ' ')) == NULL)
+ return 0;
+ *sp = '\0';
+ if (!valid_ipaddr(p, False))
+ return 0;
+ strlcpy(ipaddr_buf, p, sizeof ipaddr_buf); /* It will always fit when valid. */
+
+ p = sp + 1;
+ if ((sp = strchr(p, ' ')) == NULL)
+ return 0;
+ *sp = '\0';
+ if (!valid_ipaddr(p, False))
+ return 0;
+ /* Ignore destination address. */
+
+ p = sp + 1;
+ if ((sp = strchr(p, ' ')) == NULL)
+ return 0;
+ *sp = '\0';
+ port_chk = strtol(p, &endc, 10);
+ if (*endc || port_chk == 0)
+ return 0;
+ /* Ignore source port. */
+
+ p = sp + 1;
+ port_chk = strtol(p, &endc, 10);
+ if (*endc || port_chk == 0)
+ return 0;
+ /* Ignore destination port. */
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Get the sockaddr for the client.
+ *
+ * If it comes in as an ipv4 address mapped into IPv6 format then we
+ * convert it back to a regular IPv4.
+ **/
+static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len)
+{
+ memset(ss, 0, sizeof *ss);
+
+ if (getpeername(fd, (struct sockaddr *) ss, ss_len)) {
+ /* FIXME: Can we really not continue? */
+ rsyserr(FLOG, errno, "getpeername on fd%d failed", fd);
+ exit_cleanup(RERR_SOCKETIO);
+ }
+
+#ifdef INET6
+ if (GET_SOCKADDR_FAMILY(ss) == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
+ /* OK, so ss is in the IPv6 family, but it is really
+ * an IPv4 address: something like
+ * "::ffff:10.130.1.2". If we use it as-is, then the
+ * reverse lookup might fail or perhaps something else
+ * bad might happen. So instead we convert it to an
+ * equivalent address in the IPv4 address family. */
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in *sin;
+
+ memcpy(&sin6, ss, sizeof sin6);
+ sin = (struct sockaddr_in *)ss;
+ memset(sin, 0, sizeof *sin);
+ sin->sin_family = AF_INET;
+ *ss_len = sizeof (struct sockaddr_in);
+#ifdef HAVE_SOCKADDR_IN_LEN
+ sin->sin_len = *ss_len;
+#endif
+ sin->sin_port = sin6.sin6_port;
+
+ /* There is a macro to extract the mapped part
+ * (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem
+ * to be present in the Linux headers. */
+ memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12], sizeof sin->sin_addr);
+ }
+#endif
+}
+
+
+/**
+ * Compare an addrinfo from the resolver to a sockinfo.
+ *
+ * Like strcmp, returns 0 for identical.
+ **/
+static int compare_addrinfo_sockaddr(const struct addrinfo *ai, const struct sockaddr_storage *ss)
+{
+ int ss_family = GET_SOCKADDR_FAMILY(ss);
+ const char fn[] = "compare_addrinfo_sockaddr";
+
+ if (ai->ai_family != ss_family) {
+ rprintf(FLOG, "%s: response family %d != %d\n",
+ fn, ai->ai_family, ss_family);
+ return 1;
+ }
+
+ /* The comparison method depends on the particular AF. */
+ if (ss_family == AF_INET) {
+ const struct sockaddr_in *sin1, *sin2;
+
+ sin1 = (const struct sockaddr_in *) ss;
+ sin2 = (const struct sockaddr_in *) ai->ai_addr;
+
+ return memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof sin1->sin_addr);
+ }
+
+#ifdef INET6
+ if (ss_family == AF_INET6) {
+ const struct sockaddr_in6 *sin1, *sin2;
+
+ sin1 = (const struct sockaddr_in6 *) ss;
+ sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
+
+ if (ai->ai_addrlen < (int)sizeof (struct sockaddr_in6)) {
+ rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",
+ fn, (int)ai->ai_addrlen);
+ return 1;
+ }
+
+ if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof sin1->sin6_addr))
+ return 1;
+
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+ if (sin1->sin6_scope_id != sin2->sin6_scope_id)
+ return 1;
+#endif
+ return 0;
+ }
+#endif /* INET6 */
+
+ /* don't know */
+ return 1;
+}
+
+
+/**
+ * Do a forward lookup on @p name_buf and make sure it corresponds to
+ * @p ss -- otherwise we may be being spoofed. If we suspect we are,
+ * then we don't abort the connection but just emit a warning, and
+ * change @p name_buf to be "UNKNOWN".
+ *
+ * We don't do anything with the service when checking the name,
+ * because it doesn't seem that it could be spoofed in any way, and
+ * getaddrinfo on random service names seems to cause problems on AIX.
+ **/
+static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size)
+{
+ struct addrinfo hints, *res, *res0;
+ int error;
+ int ss_family = GET_SOCKADDR_FAMILY(ss);
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = ss_family;
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(name_buf, NULL, &hints, &res0);
+ if (error) {
+ rprintf(FLOG, "forward name lookup for %s failed: %s\n",
+ name_buf, gai_strerror(error));
+ strlcpy(name_buf, default_name, name_buf_size);
+ return error;
+ }
+
+ /* Given all these results, we expect that one of them will be
+ * the same as ss. The comparison is a bit complicated. */
+ for (res = res0; res; res = res->ai_next) {
+ if (!compare_addrinfo_sockaddr(res, ss))
+ break; /* OK, identical */
+ }
+
+ if (!res0) {
+ /* We hit the end of the list without finding an
+ * address that was the same as ss. */
+ rprintf(FLOG, "no known address for \"%s\": "
+ "spoofed address?\n", name_buf);
+ strlcpy(name_buf, default_name, name_buf_size);
+ } else if (res == NULL) {
+ /* We hit the end of the list without finding an
+ * address that was the same as ss. */
+ rprintf(FLOG, "%s is not a known address for \"%s\": "
+ "spoofed address?\n", ipaddr, name_buf);
+ strlcpy(name_buf, default_name, name_buf_size);
+ }
+
+ freeaddrinfo(res0);
+ return 0;
+}
+
+/* Returns 1 for a valid IPv4 or IPv6 addr, or 0 for a bad one. */
+static int valid_ipaddr(const char *s, int allow_scope)
+{
+ int i;
+
+ if (strchr(s, ':') != NULL) { /* Only IPv6 has a colon. */
+ int count, saw_double_colon = 0;
+ int ipv4_at_end = 0;
+
+ if (*s == ':') { /* A colon at the start must be a :: */
+ if (*++s != ':')
+ return 0;
+ saw_double_colon = 1;
+ s++;
+ }
+
+ for (count = 0; count < 8; count++) {
+ if (!*s)
+ return saw_double_colon;
+ if (allow_scope && *s == '%') {
+ if (saw_double_colon)
+ break;
+ return 0;
+ }
+
+ if (strchr(s, ':') == NULL && strchr(s, '.') != NULL) {
+ if ((!saw_double_colon && count != 6) || (saw_double_colon && count > 6))
+ return 0;
+ ipv4_at_end = 1;
+ break;
+ }
+
+ if (!isHexDigit(s++)) /* Need 1-4 hex digits */
+ return 0;
+ if (isHexDigit(s) && isHexDigit(++s) && isHexDigit(++s) && isHexDigit(++s))
+ return 0;
+
+ if (*s == ':') {
+ if (!*++s)
+ return 0;
+ if (*s == ':') {
+ if (saw_double_colon)
+ return 0;
+ saw_double_colon = 1;
+ s++;
+ }
+ }
+ }
+
+ if (!ipv4_at_end) {
+ if (allow_scope && *s == '%')
+ for (s++; isAlNum(s); s++) { }
+ return !*s && s[-1] != '%';
+ }
+ }
+
+ /* IPv4 */
+ for (i = 0; i < 4; i++) {
+ long n;
+ char *end;
+
+ if (i && *s++ != '.')
+ return 0;
+ n = strtol(s, &end, 10);
+ if (n > 255 || n < 0 || end <= s || end > s+3)
+ return 0;
+ s = end;
+ }
+
+ return !*s;
+}
diff --git a/clientserver.c b/clientserver.c
new file mode 100644
index 0000000..7c897ab
--- /dev/null
+++ b/clientserver.c
@@ -0,0 +1,1538 @@
+/*
+ * The socket based protocol for setting up a connection with rsyncd.
+ *
+ * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 2001-2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2002-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "ifuncs.h"
+
+extern int quiet;
+extern int dry_run;
+extern int output_motd;
+extern int list_only;
+extern int am_sender;
+extern int am_server;
+extern int am_daemon;
+extern int am_root;
+extern int msgs2stderr;
+extern int rsync_port;
+extern int protect_args;
+extern int ignore_errors;
+extern int preserve_xattrs;
+extern int kluge_around_eof;
+extern int munge_symlinks;
+extern int open_noatime;
+extern int sanitize_paths;
+extern int numeric_ids;
+extern int filesfrom_fd;
+extern int remote_protocol;
+extern int protocol_version;
+extern int io_timeout;
+extern int no_detach;
+extern int write_batch;
+extern int old_style_args;
+extern int default_af_hint;
+extern int logfile_format_has_i;
+extern int logfile_format_has_o_or_i;
+extern char *bind_address;
+extern char *config_file;
+extern char *logfile_format;
+extern char *files_from;
+extern char *tmpdir;
+extern char *early_input_file;
+extern struct chmod_mode_struct *chmod_modes;
+extern filter_rule_list daemon_filter_list;
+#ifdef ICONV_OPTION
+extern char *iconv_opt;
+extern iconv_t ic_send, ic_recv;
+#endif
+extern uid_t our_uid;
+extern gid_t our_gid;
+
+char *auth_user;
+char *daemon_auth_choices;
+int read_only = 0;
+int module_id = -1;
+int pid_file_fd = -1;
+int early_input_len = 0;
+char *early_input = NULL;
+pid_t namecvt_pid = 0;
+struct chmod_mode_struct *daemon_chmod_modes;
+
+#define EARLY_INPUT_CMD "#early_input="
+#define EARLY_INPUT_CMDLEN (sizeof EARLY_INPUT_CMD - 1)
+
+/* module_dirlen is the length of the module_dir string when in daemon
+ * mode and module_dir is not "/"; otherwise 0. (Note that a chroot-
+ * enabled module can have a non-"/" module_dir these days.) */
+char *module_dir = NULL;
+unsigned int module_dirlen = 0;
+
+char *full_module_path;
+
+static int rl_nulls = 0;
+static int namecvt_fd_req = -1, namecvt_fd_ans = -1;
+
+#ifdef HAVE_SIGACTION
+static struct sigaction sigact;
+#endif
+
+static item_list gid_list = EMPTY_ITEM_LIST;
+
+/* Used when "reverse lookup" is off. */
+const char undetermined_hostname[] = "UNDETERMINED";
+
+/**
+ * Run a client connected to an rsyncd. The alternative to this
+ * function for remote-shell connections is do_cmd().
+ *
+ * After negotiating which module to use and reading the server's
+ * motd, this hands over to client_run(). Telling the server the
+ * module will cause it to chroot/setuid/etc.
+ *
+ * Instead of doing a transfer, the client may at this stage instead
+ * get a listing of remote modules and exit.
+ *
+ * @return -1 for error in startup, or the result of client_run().
+ * Either way, it eventually gets passed to exit_cleanup().
+ **/
+int start_socket_client(char *host, int remote_argc, char *remote_argv[],
+ int argc, char *argv[])
+{
+ int fd, ret;
+ char *p, *user = NULL;
+
+ /* This is redundant with code in start_inband_exchange(), but this
+ * short-circuits a problem in the client before we open a socket,
+ * and the extra check won't hurt. */
+ if (**remote_argv == '/') {
+ rprintf(FERROR,
+ "ERROR: The remote path must start with a module name not a /\n");
+ return -1;
+ }
+
+ if ((p = strrchr(host, '@')) != NULL) {
+ user = host;
+ host = p+1;
+ *p = '\0';
+ }
+
+ fd = open_socket_out_wrapped(host, rsync_port, bind_address, default_af_hint);
+ if (fd == -1)
+ exit_cleanup(RERR_SOCKETIO);
+
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+
+ ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv);
+
+ return ret ? ret : client_run(fd, fd, -1, argc, argv);
+}
+
+static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client)
+{
+ int remote_sub = -1;
+ int our_sub = get_subprotocol_version();
+
+ output_daemon_greeting(f_out, am_client);
+ if (!am_client) {
+ char *motd = lp_motd_file();
+ if (motd && *motd) {
+ FILE *f = fopen(motd, "r");
+ while (f && !feof(f)) {
+ int len = fread(buf, 1, bufsiz - 1, f);
+ if (len > 0)
+ write_buf(f_out, buf, len);
+ }
+ if (f)
+ fclose(f);
+ write_sbuf(f_out, "\n");
+ }
+ }
+
+ /* This strips the \n. */
+ if (!read_line_old(f_in, buf, bufsiz, 0)) {
+ if (am_client)
+ rprintf(FERROR, "rsync: did not see server greeting\n");
+ return -1;
+ }
+
+ if (sscanf(buf, "@RSYNCD: %d.%d", &remote_protocol, &remote_sub) < 1) {
+ if (am_client)
+ rprintf(FERROR, "rsync: server sent \"%s\" rather than greeting\n", buf);
+ else
+ io_printf(f_out, "@ERROR: protocol startup error\n");
+ return -1;
+ }
+
+ if (remote_sub < 0) {
+ if (remote_protocol >= 30) {
+ if (am_client)
+ rprintf(FERROR, "rsync: the server omitted the subprotocol value: %s\n", buf);
+ else
+ io_printf(f_out, "@ERROR: your client omitted the subprotocol value: %s\n", buf);
+ return -1;
+ }
+ remote_sub = 0;
+ }
+
+ daemon_auth_choices = strchr(buf + 9, ' ');
+ if (daemon_auth_choices) {
+ char *cp;
+ daemon_auth_choices = strdup(daemon_auth_choices + 1);
+ if ((cp = strchr(daemon_auth_choices, '\n')) != NULL)
+ *cp = '\0';
+ } else if (remote_protocol > 31) {
+ if (am_client)
+ rprintf(FERROR, "rsync: the server omitted the digest name list: %s\n", buf);
+ else
+ io_printf(f_out, "@ERROR: your client omitted the digest name list: %s\n", buf);
+ return -1;
+ }
+
+ if (protocol_version > remote_protocol) {
+ protocol_version = remote_protocol;
+ if (remote_sub)
+ protocol_version--;
+ } else if (protocol_version == remote_protocol) {
+ if (remote_sub != our_sub)
+ protocol_version--;
+ }
+#if SUBPROTOCOL_VERSION != 0
+ else if (protocol_version < remote_protocol) {
+ if (our_sub)
+ protocol_version--;
+ }
+#endif
+
+ if (protocol_version >= 30)
+ rl_nulls = 1;
+
+ return 0;
+}
+
+int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char *argv[])
+{
+ int i, modlen;
+ char line[BIGPATHBUFLEN];
+ char *sargs[MAX_ARGS];
+ int sargc = 0;
+ char *p, *modname;
+
+ assert(argc > 0 && *argv != NULL);
+
+ if (**argv == '/') {
+ rprintf(FERROR,
+ "ERROR: The remote path must start with a module name\n");
+ return -1;
+ }
+
+ if (!(p = strchr(*argv, '/')))
+ modlen = strlen(*argv);
+ else
+ modlen = p - *argv;
+
+ modname = new_array(char, modlen+1+1); /* room for '/' & '\0' */
+ strlcpy(modname, *argv, modlen + 1);
+ modname[modlen] = '/';
+ modname[modlen+1] = '\0';
+
+ if (!user)
+ user = getenv("USER");
+ if (!user)
+ user = getenv("LOGNAME");
+
+ if (exchange_protocols(f_in, f_out, line, sizeof line, 1) < 0)
+ return -1;
+
+ if (early_input_file) {
+ STRUCT_STAT st;
+ FILE *f = fopen(early_input_file, "rb");
+ if (!f || do_fstat(fileno(f), &st) < 0) {
+ rsyserr(FERROR, errno, "failed to open %s", early_input_file);
+ return -1;
+ }
+ early_input_len = st.st_size;
+ if (early_input_len > (int)sizeof line) {
+ rprintf(FERROR, "%s is > %d bytes.\n", early_input_file, (int)sizeof line);
+ return -1;
+ }
+ if (early_input_len > 0) {
+ io_printf(f_out, EARLY_INPUT_CMD "%d\n", early_input_len);
+ while (early_input_len > 0) {
+ int len;
+ if (feof(f)) {
+ rprintf(FERROR, "Early EOF in %s\n", early_input_file);
+ return -1;
+ }
+ len = fread(line, 1, early_input_len, f);
+ if (len > 0) {
+ write_buf(f_out, line, len);
+ early_input_len -= len;
+ }
+ }
+ }
+ fclose(f);
+ }
+
+ server_options(sargs, &sargc);
+
+ if (sargc >= MAX_ARGS - 2)
+ goto arg_overflow;
+
+ sargs[sargc++] = ".";
+
+ if (!old_style_args)
+ snprintf(line, sizeof line, " %.*s/", modlen, modname);
+
+ while (argc > 0) {
+ if (sargc >= MAX_ARGS - 1) {
+ arg_overflow:
+ rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (strncmp(*argv, modname, modlen) == 0 && argv[0][modlen] == '\0')
+ sargs[sargc++] = modname; /* we send "modname/" */
+ else {
+ char *arg = *argv;
+ int extra_chars = *arg == '-' ? 2 : 0; /* a leading dash needs a "./" prefix. */
+ /* If --old-args was not specified, make sure that the arg won't split at a mod name! */
+ if (!old_style_args && (p = strstr(arg, line)) != NULL) {
+ do {
+ extra_chars += 2;
+ } while ((p = strstr(p+1, line)) != NULL);
+ }
+ if (extra_chars) {
+ char *f = arg;
+ char *t = arg = new_array(char, strlen(arg) + extra_chars + 1);
+ if (*f == '-') {
+ *t++ = '.';
+ *t++ = '/';
+ }
+ while (*f) {
+ if (*f == ' ' && strncmp(f, line, modlen+2) == 0) {
+ *t++ = '[';
+ *t++ = *f++;
+ *t++ = ']';
+ } else
+ *t++ = *f++;
+ }
+ *t = '\0';
+ }
+ sargs[sargc++] = arg;
+ }
+ argv++;
+ argc--;
+ }
+
+ sargs[sargc] = NULL;
+
+ if (DEBUG_GTE(CMD, 1))
+ print_child_argv("sending daemon args:", sargs);
+
+ io_printf(f_out, "%.*s\n", modlen, modname);
+
+ /* Old servers may just drop the connection here,
+ rather than sending a proper EXIT command. Yuck. */
+ kluge_around_eof = list_only && protocol_version < 25 ? 1 : 0;
+
+ while (1) {
+ if (!read_line_old(f_in, line, sizeof line, 0)) {
+ rprintf(FERROR, "rsync: didn't get server startup line\n");
+ return -1;
+ }
+
+ if (strncmp(line,"@RSYNCD: AUTHREQD ",18) == 0) {
+ auth_client(f_out, user, line+18);
+ continue;
+ }
+
+ if (strcmp(line,"@RSYNCD: OK") == 0)
+ break;
+
+ if (strcmp(line,"@RSYNCD: EXIT") == 0) {
+ /* This is sent by recent versions of the
+ * server to terminate the listing of modules.
+ * We don't want to go on and transfer
+ * anything; just exit. */
+ exit(0);
+ }
+
+ if (strncmp(line, "@ERROR", 6) == 0) {
+ rprintf(FERROR, "%s\n", line);
+ /* This is always fatal; the server will now
+ * close the socket. */
+ return -1;
+ }
+
+ /* This might be a MOTD line or a module listing, but there is
+ * no way to differentiate it. The manpage mentions this. */
+ if (output_motd)
+ rprintf(FINFO, "%s\n", line);
+ }
+ kluge_around_eof = 0;
+
+ if (rl_nulls) {
+ for (i = 0; i < sargc; i++) {
+ if (!sargs[i]) /* stop at --secluded-args NULL */
+ break;
+ write_sbuf(f_out, sargs[i]);
+ write_byte(f_out, 0);
+ }
+ write_byte(f_out, 0);
+ } else {
+ for (i = 0; i < sargc; i++)
+ io_printf(f_out, "%s\n", sargs[i]);
+ write_sbuf(f_out, "\n");
+ }
+
+ if (protect_args)
+ send_protected_args(f_out, sargs);
+
+ if (protocol_version < 23) {
+ if (protocol_version == 22 || !am_sender)
+ io_start_multiplex_in(f_in);
+ }
+
+ free(modname);
+
+ return 0;
+}
+
+#if defined HAVE_SETENV || defined HAVE_PUTENV
+static int read_arg_from_pipe(int fd, char *buf, int limit)
+{
+ char *bp = buf, *eob = buf + limit - 1;
+
+ while (1) {
+ int got = read(fd, bp, 1);
+ if (got != 1) {
+ if (got < 0 && errno == EINTR)
+ continue;
+ return -1;
+ }
+ if (*bp == '\0')
+ break;
+ if (bp < eob)
+ bp++;
+ }
+ *bp = '\0';
+
+ return bp - buf;
+}
+#endif
+
+void set_env_str(const char *var, const char *str)
+{
+#ifdef HAVE_SETENV
+ if (setenv(var, str, 1) < 0)
+ out_of_memory("set_env_str");
+#else
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s=%s", var, str) < 0)
+ out_of_memory("set_env_str");
+ putenv(mem);
+#else
+ (void)var;
+ (void)str;
+#endif
+#endif
+}
+
+#if defined HAVE_SETENV || defined HAVE_PUTENV
+
+static void set_envN_str(const char *var, int num, const char *str)
+{
+#ifdef HAVE_SETENV
+ char buf[128];
+ (void)snprintf(buf, sizeof buf, "%s%d", var, num);
+ if (setenv(buf, str, 1) < 0)
+ out_of_memory("set_env_str");
+#else
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s%d=%s", var, num, str) < 0)
+ out_of_memory("set_envN_str");
+ putenv(mem);
+#endif
+#endif
+}
+
+void set_env_num(const char *var, long num)
+{
+#ifdef HAVE_SETENV
+ char val[64];
+ (void)snprintf(val, sizeof val, "%ld", num);
+ if (setenv(var, val, 1) < 0)
+ out_of_memory("set_env_str");
+#else
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s=%ld", var, num) < 0)
+ out_of_memory("set_env_num");
+ putenv(mem);
+#endif
+#endif
+}
+
+/* Used for "early exec", "pre-xfer exec", and the "name converter" script. */
+static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
+{
+ int arg_fds[2], error_fds[2], arg_fd;
+ pid_t pid;
+
+ if ((error_fd_ptr && pipe(error_fds) < 0) || pipe(arg_fds) < 0 || (pid = fork()) < 0)
+ return (pid_t)-1;
+
+ if (pid == 0) {
+ char buf[BIGPATHBUFLEN];
+ int j, len, status;
+
+ if (error_fd_ptr) {
+ close(error_fds[0]);
+ set_blocking(error_fds[1]);
+ }
+
+ close(arg_fds[1]);
+ arg_fd = arg_fds[0];
+ set_blocking(arg_fd);
+
+ len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
+ if (len <= 0)
+ _exit(1);
+ set_env_str("RSYNC_REQUEST", buf);
+
+ for (j = 0; ; j++) {
+ len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
+ if (len <= 0) {
+ if (!len)
+ break;
+ _exit(1);
+ }
+ set_envN_str("RSYNC_ARG", j, buf);
+ }
+
+ dup2(arg_fd, STDIN_FILENO);
+ close(arg_fd);
+
+ if (error_fd_ptr) {
+ dup2(error_fds[1], STDOUT_FILENO);
+ close(error_fds[1]);
+ }
+
+ status = shell_exec(cmd);
+
+ if (!WIFEXITED(status))
+ _exit(1);
+ _exit(WEXITSTATUS(status));
+ }
+
+ if (error_fd_ptr) {
+ close(error_fds[1]);
+ *error_fd_ptr = error_fds[0];
+ set_blocking(error_fds[0]);
+ }
+
+ close(arg_fds[0]);
+ arg_fd = *arg_fd_ptr = arg_fds[1];
+ set_blocking(arg_fd);
+
+ return pid;
+}
+
+#endif
+
+static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int exec_type)
+{
+ int j = 0;
+
+ if (!request)
+ request = "(NONE)";
+
+ write_buf(write_fd, request, strlen(request)+1);
+ if (early_argv) {
+ for ( ; *early_argv; early_argv++)
+ write_buf(write_fd, *early_argv, strlen(*early_argv)+1);
+ j = 1; /* Skip arg0 name in argv. */
+ }
+ if (argv) {
+ for ( ; argv[j]; j++)
+ write_buf(write_fd, argv[j], strlen(argv[j])+1);
+ }
+ write_byte(write_fd, 0);
+
+ if (exec_type == 1 && early_input_len)
+ write_buf(write_fd, early_input, early_input_len);
+
+ if (exec_type != 2) /* the name converter needs this left open */
+ close(write_fd);
+}
+
+static char *finish_pre_exec(const char *desc, pid_t pid, int read_fd)
+{
+ char buf[BIGPATHBUFLEN], *bp, *cr;
+ int j, status = -1, msglen = sizeof buf - 1;
+
+ if (read_fd >= 0) {
+ /* Read the stdout from the program. This it is only displayed
+ * to the user if the script also returns an error status. */
+ for (bp = buf, cr = buf; msglen > 0; msglen -= j) {
+ if ((j = read(read_fd, bp, msglen)) <= 0) {
+ if (j == 0)
+ break;
+ if (errno == EINTR)
+ continue;
+ break; /* Just ignore the read error for now... */
+ }
+ bp[j] = '\0';
+ while (1) {
+ if ((cr = strchr(cr, '\r')) == NULL) {
+ cr = bp + j;
+ break;
+ }
+ if (!cr[1])
+ break; /* wait for more data before we decide what to do */
+ if (cr[1] == '\n') {
+ memmove(cr, cr+1, j - (cr - bp));
+ j--;
+ } else
+ cr++;
+ }
+ bp += j;
+ }
+ *bp = '\0';
+
+ close(read_fd);
+ } else
+ *buf = '\0';
+
+ if (wait_process(pid, &status, 0) < 0
+ || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ char *e;
+ if (asprintf(&e, "%s returned failure (%d)%s%s%s\n%s",
+ desc, status, status < 0 ? ": " : "",
+ status < 0 ? strerror(errno) : "",
+ *buf ? ":" : "", buf) < 0)
+ return "out_of_memory in finish_pre_exec\n";
+ return e;
+ }
+ return NULL;
+}
+
+static int path_failure(int f_out, const char *dir, BOOL was_chdir)
+{
+ if (was_chdir)
+ rsyserr(FLOG, errno, "chdir %s failed", dir);
+ else
+ rprintf(FLOG, "normalize_path(%s) failed\n", dir);
+ io_printf(f_out, "@ERROR: chdir failed\n");
+ return -1;
+}
+
+static int add_a_group(int f_out, const char *gname)
+{
+ gid_t gid, *gid_p;
+ if (!group_to_gid(gname, &gid, True)) {
+ rprintf(FLOG, "Invalid gid %s\n", gname);
+ io_printf(f_out, "@ERROR: invalid gid %s\n", gname);
+ return -1;
+ }
+ gid_p = EXPAND_ITEM_LIST(&gid_list, gid_t, -32);
+ *gid_p = gid;
+ return 0;
+}
+
+#ifdef HAVE_GETGROUPLIST
+static int want_all_groups(int f_out, uid_t uid)
+{
+ const char *err;
+ if ((err = getallgroups(uid, &gid_list)) != NULL) {
+ rsyserr(FLOG, errno, "%s", err);
+ io_printf(f_out, "@ERROR: %s\n", err);
+ return -1;
+ }
+ return 0;
+}
+#elif defined HAVE_INITGROUPS
+static struct passwd *want_all_groups(int f_out, uid_t uid)
+{
+ struct passwd *pw;
+ gid_t *gid_p;
+ if ((pw = getpwuid(uid)) == NULL) {
+ rsyserr(FLOG, errno, "getpwuid failed");
+ io_printf(f_out, "@ERROR: getpwuid failed\n");
+ return NULL;
+ }
+ /* Start with the default group and initgroups() will add the rest. */
+ gid_p = EXPAND_ITEM_LIST(&gid_list, gid_t, -32);
+ *gid_p = pw->pw_gid;
+ return pw;
+}
+#endif
+
+static int rsync_module(int f_in, int f_out, int i, const char *addr, const char *host)
+{
+ int argc;
+ char **argv, **orig_argv, **orig_early_argv, *module_chdir;
+ char line[BIGPATHBUFLEN];
+#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
+ struct passwd *pw = NULL;
+#endif
+ uid_t uid;
+ int set_uid;
+ char *p, *err_msg = NULL;
+ char *name = lp_name(i);
+ int use_chroot = lp_use_chroot(i); /* might be 1 (yes), 0 (no), or -1 (unset) */
+ int ret, pre_exec_arg_fd = -1, pre_exec_error_fd = -1;
+ int save_munge_symlinks;
+ pid_t pre_exec_pid = 0;
+ char *request = NULL;
+
+ set_env_str("RSYNC_MODULE_NAME", name);
+
+#ifdef ICONV_OPTION
+ iconv_opt = lp_charset(i);
+ if (*iconv_opt)
+ setup_iconv();
+ iconv_opt = NULL;
+#endif
+
+ /* If reverse lookup is disabled globally but enabled for this module,
+ * we need to do it now before the access check. */
+ if (host == undetermined_hostname && lp_reverse_lookup(i))
+ host = client_name(client_addr(f_in));
+ set_env_str("RSYNC_HOST_NAME", host);
+ set_env_str("RSYNC_HOST_ADDR", addr);
+
+ if (!allow_access(addr, &host, i)) {
+ rprintf(FLOG, "rsync denied on module %s from %s (%s)\n",
+ name, host, addr);
+ if (!lp_list(i))
+ io_printf(f_out, "@ERROR: Unknown module '%s'\n", name);
+ else {
+ io_printf(f_out,
+ "@ERROR: access denied to %s from %s (%s)\n",
+ name, host, addr);
+ }
+ return -1;
+ }
+
+ if (am_daemon > 0) {
+ rprintf(FLOG, "rsync allowed access on module %s from %s (%s)\n",
+ name, host, addr);
+ }
+
+ if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) {
+ if (errno) {
+ rsyserr(FLOG, errno, "failed to open lock file %s",
+ lp_lock_file(i));
+ io_printf(f_out, "@ERROR: failed to open lock file\n");
+ } else {
+ rprintf(FLOG, "max connections (%d) reached\n",
+ lp_max_connections(i));
+ io_printf(f_out, "@ERROR: max connections (%d) reached -- try again later\n",
+ lp_max_connections(i));
+ }
+ return -1;
+ }
+
+ read_only = lp_read_only(i); /* may also be overridden by auth_server() */
+ auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD ");
+
+ if (!auth_user) {
+ io_printf(f_out, "@ERROR: auth failed on module %s\n", name);
+ return -1;
+ }
+ set_env_str("RSYNC_USER_NAME", auth_user);
+
+ module_id = i;
+
+ if (lp_transfer_logging(module_id) && !logfile_format)
+ logfile_format = lp_log_format(module_id);
+ if (log_format_has(logfile_format, 'i'))
+ logfile_format_has_i = 1;
+ if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
+ logfile_format_has_o_or_i = 1;
+
+ uid = MY_UID();
+ am_root = (uid == ROOT_UID);
+
+ p = *lp_uid(module_id) ? lp_uid(module_id) : am_root ? NOBODY_USER : NULL;
+ if (p) {
+ if (!user_to_uid(p, &uid, True)) {
+ rprintf(FLOG, "Invalid uid %s\n", p);
+ io_printf(f_out, "@ERROR: invalid uid %s\n", p);
+ return -1;
+ }
+ set_uid = 1;
+ } else
+ set_uid = 0;
+
+ p = *lp_gid(module_id) ? conf_strtok(lp_gid(module_id)) : NULL;
+ if (p) {
+ /* The "*" gid must be the first item in the list. */
+ if (strcmp(p, "*") == 0) {
+#ifdef HAVE_GETGROUPLIST
+ if (want_all_groups(f_out, uid) < 0)
+ return -1;
+#elif defined HAVE_INITGROUPS
+ if ((pw = want_all_groups(f_out, uid)) == NULL)
+ return -1;
+#else
+ rprintf(FLOG, "This rsync does not support a gid of \"*\"\n");
+ io_printf(f_out, "@ERROR: invalid gid setting.\n");
+ return -1;
+#endif
+ } else if (add_a_group(f_out, p) < 0)
+ return -1;
+ while ((p = conf_strtok(NULL)) != NULL) {
+#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
+ if (pw) {
+ rprintf(FLOG, "This rsync cannot add groups after \"*\".\n");
+ io_printf(f_out, "@ERROR: invalid gid setting.\n");
+ return -1;
+ }
+#endif
+ if (add_a_group(f_out, p) < 0)
+ return -1;
+ }
+ } else if (am_root) {
+ if (add_a_group(f_out, NOBODY_GROUP) < 0)
+ return -1;
+ }
+
+ module_dir = lp_path(module_id);
+ if (*module_dir == '\0') {
+ rprintf(FLOG, "No path specified for module %s\n", name);
+ io_printf(f_out, "@ERROR: no path setting.\n");
+ return -1;
+ }
+ if (use_chroot < 0) {
+ if (strstr(module_dir, "/./") != NULL)
+ use_chroot = 1; /* The module is expecting a chroot inner & outer path. */
+ else if (chroot("/") < 0) {
+ rprintf(FLOG, "chroot test failed: %s. "
+ "Switching 'use chroot' from unset to false.\n",
+ strerror(errno));
+ use_chroot = 0;
+ } else {
+ if (chdir("/") < 0)
+ rsyserr(FLOG, errno, "chdir(\"/\") failed");
+ use_chroot = 1;
+ }
+ }
+ if (use_chroot) {
+ if ((p = strstr(module_dir, "/./")) != NULL) {
+ *p = '\0'; /* Temporary... */
+ if (!(module_chdir = normalize_path(module_dir, True, NULL)))
+ return path_failure(f_out, module_dir, False);
+ *p = '/';
+ if (!(p = normalize_path(p + 2, True, &module_dirlen)))
+ return path_failure(f_out, strstr(module_dir, "/./"), False);
+ if (!(full_module_path = normalize_path(module_dir, False, NULL)))
+ full_module_path = module_dir;
+ module_dir = p;
+ } else {
+ if (!(module_chdir = normalize_path(module_dir, False, NULL)))
+ return path_failure(f_out, module_dir, False);
+ full_module_path = module_chdir;
+ module_dir = "/";
+ module_dirlen = 1;
+ }
+ } else {
+ if (!(module_chdir = normalize_path(module_dir, False, &module_dirlen)))
+ return path_failure(f_out, module_dir, False);
+ full_module_path = module_dir = module_chdir;
+ }
+ set_env_str("RSYNC_MODULE_PATH", full_module_path);
+
+ if (module_dirlen == 1) {
+ module_dirlen = 0;
+ set_filter_dir("/", 1);
+ } else
+ set_filter_dir(module_dir, module_dirlen);
+
+ p = lp_filter(module_id);
+ parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3);
+
+ p = lp_include_from(module_id);
+ parse_filter_file(&daemon_filter_list, p, rule_template(FILTRULE_INCLUDE),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
+
+ p = lp_include(module_id);
+ parse_filter_str(&daemon_filter_list, p,
+ rule_template(FILTRULE_INCLUDE | FILTRULE_WORD_SPLIT),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
+
+ p = lp_exclude_from(module_id);
+ parse_filter_file(&daemon_filter_list, p, rule_template(0),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
+
+ p = lp_exclude(module_id);
+ parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
+ XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
+
+ log_init(1);
+
+#if defined HAVE_SETENV || defined HAVE_PUTENV
+ if ((*lp_early_exec(module_id) || *lp_prexfer_exec(module_id)
+ || *lp_postxfer_exec(module_id) || *lp_name_converter(module_id))
+ && !getenv("RSYNC_NO_XFER_EXEC")) {
+ set_env_num("RSYNC_PID", (long)getpid());
+
+ /* For post-xfer exec, fork a new process to run the rsync
+ * daemon while this process waits for the exit status and
+ * runs the indicated command at that point. */
+ if (*lp_postxfer_exec(module_id)) {
+ pid_t pid = fork();
+ if (pid < 0) {
+ rsyserr(FLOG, errno, "fork failed");
+ io_printf(f_out, "@ERROR: fork failed\n");
+ return -1;
+ }
+ if (pid) {
+ int status;
+ close(f_in);
+ if (f_out != f_in)
+ close(f_out);
+ if (wait_process(pid, &status, 0) < 0)
+ status = -1;
+ set_env_num("RSYNC_RAW_STATUS", status);
+ if (WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else
+ status = -1;
+ set_env_num("RSYNC_EXIT_STATUS", status);
+ if (shell_exec(lp_postxfer_exec(module_id)) < 0)
+ status = -1;
+ _exit(status);
+ }
+ }
+
+ /* For early exec, fork a child process to run the indicated
+ * command and wait for it to exit. */
+ if (*lp_early_exec(module_id)) {
+ int arg_fd;
+ pid_t pid = start_pre_exec(lp_early_exec(module_id), &arg_fd, NULL);
+ if (pid == (pid_t)-1) {
+ rsyserr(FLOG, errno, "early exec preparation failed");
+ io_printf(f_out, "@ERROR: early exec preparation failed\n");
+ return -1;
+ }
+ write_pre_exec_args(arg_fd, NULL, NULL, NULL, 1);
+ if (finish_pre_exec("early exec", pid, -1) != NULL) {
+ rsyserr(FLOG, errno, "early exec failed");
+ io_printf(f_out, "@ERROR: early exec failed\n");
+ return -1;
+ }
+ }
+
+ /* For pre-xfer exec, fork a child process to run the indicated
+ * command, though it first waits for the parent process to
+ * send us the user's request via a pipe. */
+ if (*lp_prexfer_exec(module_id)) {
+ pre_exec_pid = start_pre_exec(lp_prexfer_exec(module_id), &pre_exec_arg_fd, &pre_exec_error_fd);
+ if (pre_exec_pid == (pid_t)-1) {
+ rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
+ io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
+ return -1;
+ }
+ }
+
+ if (*lp_name_converter(module_id)) {
+ namecvt_pid = start_pre_exec(lp_name_converter(module_id), &namecvt_fd_req, &namecvt_fd_ans);
+ if (namecvt_pid == (pid_t)-1) {
+ rsyserr(FLOG, errno, "name-converter exec preparation failed");
+ io_printf(f_out, "@ERROR: name-converter exec preparation failed\n");
+ return -1;
+ }
+ }
+ }
+#endif
+
+ if (early_input) {
+ free(early_input);
+ early_input = NULL;
+ }
+
+ if (use_chroot) {
+ if (chroot(module_chdir)) {
+ rsyserr(FLOG, errno, "chroot(\"%s\") failed", module_chdir);
+ io_printf(f_out, "@ERROR: chroot failed\n");
+ return -1;
+ }
+ module_chdir = module_dir;
+ }
+
+ if (!change_dir(module_chdir, CD_NORMAL))
+ return path_failure(f_out, module_chdir, True);
+ if (module_dirlen)
+ sanitize_paths = 1;
+
+ if ((munge_symlinks = lp_munge_symlinks(module_id)) < 0)
+ munge_symlinks = !use_chroot || module_dirlen;
+ if (munge_symlinks) {
+ STRUCT_STAT st;
+ char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */
+ strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */
+ if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) {
+ rprintf(FLOG, "Symlink munging is unsafe when a %s directory exists.\n",
+ prefix);
+ io_printf(f_out, "@ERROR: daemon security issue -- contact admin\n", name);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ }
+
+ if (gid_list.count) {
+ gid_t *gid_array = gid_list.items;
+ if (setgid(gid_array[0])) {
+ rsyserr(FLOG, errno, "setgid %ld failed", (long)gid_array[0]);
+ io_printf(f_out, "@ERROR: setgid failed\n");
+ return -1;
+ }
+#ifdef HAVE_SETGROUPS
+ /* Set the group(s) we want to be active. */
+ if (setgroups(gid_list.count, gid_array)) {
+ rsyserr(FLOG, errno, "setgroups failed");
+ io_printf(f_out, "@ERROR: setgroups failed\n");
+ return -1;
+ }
+#endif
+#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
+ /* pw is set if the user wants all the user's groups. */
+ if (pw && initgroups(pw->pw_name, pw->pw_gid) < 0) {
+ rsyserr(FLOG, errno, "initgroups failed");
+ io_printf(f_out, "@ERROR: initgroups failed\n");
+ return -1;
+ }
+#endif
+ our_gid = MY_GID();
+ }
+
+ if (set_uid) {
+ if (setuid(uid) < 0
+#ifdef HAVE_SETEUID
+ || seteuid(uid) < 0
+#endif
+ ) {
+ rsyserr(FLOG, errno, "setuid %ld failed", (long)uid);
+ io_printf(f_out, "@ERROR: setuid failed\n");
+ return -1;
+ }
+
+ our_uid = MY_UID();
+ am_root = (our_uid == ROOT_UID);
+ }
+
+ if (lp_temp_dir(module_id) && *lp_temp_dir(module_id)) {
+ tmpdir = lp_temp_dir(module_id);
+ if (strlen(tmpdir) >= MAXPATHLEN - 10) {
+ rprintf(FLOG,
+ "the 'temp dir' value for %s is WAY too long -- ignoring.\n",
+ name);
+ tmpdir = NULL;
+ }
+ }
+
+ io_printf(f_out, "@RSYNCD: OK\n");
+
+ read_args(f_in, name, line, sizeof line, rl_nulls, &argv, &argc, &request);
+ orig_argv = argv;
+
+ save_munge_symlinks = munge_symlinks;
+
+ reset_output_levels(); /* future verbosity is controlled by client options */
+ ret = parse_arguments(&argc, (const char ***) &argv);
+ if (protect_args && ret) {
+ orig_early_argv = orig_argv;
+ protect_args = 2;
+ read_args(f_in, name, line, sizeof line, 1, &argv, &argc, &request);
+ orig_argv = argv;
+ ret = parse_arguments(&argc, (const char ***) &argv);
+ } else
+ orig_early_argv = NULL;
+
+ /* The default is to use the user's setting unless the module sets True or False. */
+ if (lp_open_noatime(module_id) >= 0)
+ open_noatime = lp_open_noatime(module_id);
+
+ munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */
+
+ if (am_daemon > 0)
+ msgs2stderr = 0; /* A non-rsh-run daemon doesn't have stderr for msgs. */
+
+ if (pre_exec_pid) {
+ write_pre_exec_args(pre_exec_arg_fd, request, orig_early_argv, orig_argv, 0);
+ err_msg = finish_pre_exec("pre-xfer exec", pre_exec_pid, pre_exec_error_fd);
+ }
+
+ if (namecvt_pid)
+ write_pre_exec_args(namecvt_fd_req, request, orig_early_argv, orig_argv, 2);
+
+ if (orig_early_argv)
+ free(orig_early_argv);
+
+ am_server = 1; /* Don't let someone try to be tricky. */
+ quiet = 0;
+ if (lp_ignore_errors(module_id))
+ ignore_errors = 1;
+ if (write_batch < 0)
+ dry_run = 1;
+
+ if (lp_fake_super(module_id)) {
+ if (preserve_xattrs > 1)
+ preserve_xattrs = 1;
+ am_root = -1;
+ } else if (am_root < 0) /* Treat --fake-super from client as --super. */
+ am_root = 2;
+
+ if (filesfrom_fd == 0)
+ filesfrom_fd = f_in;
+
+ if (request) {
+ if (*auth_user) {
+ rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n",
+ am_sender ? "on" : "to",
+ request, auth_user, host, addr);
+ } else {
+ rprintf(FLOG, "rsync %s %s from %s (%s)\n",
+ am_sender ? "on" : "to",
+ request, host, addr);
+ }
+ free(request);
+ }
+
+#ifndef DEBUG
+ /* don't allow the logs to be flooded too fast */
+ limit_output_verbosity(lp_max_verbosity(module_id));
+#endif
+
+ if (protocol_version < 23 && (protocol_version == 22 || am_sender))
+ io_start_multiplex_out(f_out);
+ else if (!ret || err_msg) {
+ /* We have to get I/O multiplexing started so that we can
+ * get the error back to the client. This means getting
+ * the protocol setup finished first in later versions. */
+ setup_protocol(f_out, f_in);
+ if (!am_sender) {
+ /* Since we failed in our option parsing, we may not
+ * have finished parsing that the client sent us a
+ * --files-from option, so look for it manually.
+ * Without this, the socket would be in the wrong
+ * state for the upcoming error message. */
+ if (!files_from) {
+ int i;
+ for (i = 0; i < argc; i++) {
+ if (strncmp(argv[i], "--files-from", 12) == 0) {
+ files_from = "";
+ break;
+ }
+ }
+ }
+ if (files_from)
+ write_byte(f_out, 0);
+ }
+ io_start_multiplex_out(f_out);
+ }
+
+ if (!ret || err_msg) {
+ if (err_msg) {
+ while ((p = strchr(err_msg, '\n')) != NULL) {
+ int len = p - err_msg + 1;
+ rwrite(FERROR, err_msg, len, 0);
+ err_msg += len;
+ }
+ if (*err_msg)
+ rprintf(FERROR, "%s\n", err_msg);
+ io_flush(MSG_FLUSH);
+ } else
+ option_error();
+ msleep(400);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+#ifdef ICONV_OPTION
+ if (!iconv_opt) {
+ if (ic_send != (iconv_t)-1) {
+ iconv_close(ic_send);
+ ic_send = (iconv_t)-1;
+ }
+ if (ic_recv != (iconv_t)-1) {
+ iconv_close(ic_recv);
+ ic_recv = (iconv_t)-1;
+ }
+ }
+#endif
+
+ if (!numeric_ids
+ && (use_chroot ? lp_numeric_ids(module_id) != False && !*lp_name_converter(module_id)
+ : lp_numeric_ids(module_id) == True))
+ numeric_ids = -1; /* Set --numeric-ids w/o breaking protocol. */
+
+ if (lp_timeout(module_id) && (!io_timeout || lp_timeout(module_id) < io_timeout))
+ set_io_timeout(lp_timeout(module_id));
+
+ /* If we have some incoming/outgoing chmod changes, append them to
+ * any user-specified changes (making our changes have priority).
+ * We also get a pointer to just our changes so that a receiver
+ * process can use them separately if --perms wasn't specified. */
+ if (am_sender)
+ p = lp_outgoing_chmod(module_id);
+ else
+ p = lp_incoming_chmod(module_id);
+ if (*p && !(daemon_chmod_modes = parse_chmod(p, &chmod_modes))) {
+ rprintf(FLOG, "Invalid \"%sing chmod\" directive: %s\n",
+ am_sender ? "outgo" : "incom", p);
+ }
+
+ start_server(f_in, f_out, argc, argv);
+
+ return 0;
+}
+
+BOOL namecvt_call(const char *cmd, const char **name_p, id_t *id_p)
+{
+ char buf[1024];
+ int got, len;
+
+ if (*name_p)
+ len = snprintf(buf, sizeof buf, "%s %s\n", cmd, *name_p);
+ else
+ len = snprintf(buf, sizeof buf, "%s %ld\n", cmd, (long)*id_p);
+ if (len >= (int)sizeof buf) {
+ rprintf(FERROR, "namecvt_call() request was too large.\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ while ((got = write(namecvt_fd_req, buf, len)) != len) {
+ if (got < 0 && errno == EINTR)
+ continue;
+ rprintf(FERROR, "Connection to name-converter failed.\n");
+ exit_cleanup(RERR_SOCKETIO);
+ }
+
+ if (!read_line_old(namecvt_fd_ans, buf, sizeof buf, 0))
+ return False;
+
+ if (*name_p)
+ *id_p = (id_t)atol(buf);
+ else
+ *name_p = strdup(buf);
+
+ return True;
+}
+
+/* send a list of available modules to the client. Don't list those
+ with "list = False". */
+static void send_listing(int fd)
+{
+ int n = lp_num_modules();
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (lp_list(i))
+ io_printf(fd, "%-15s\t%s\n", lp_name(i), lp_comment(i));
+ }
+
+ if (protocol_version >= 25)
+ io_printf(fd,"@RSYNCD: EXIT\n");
+}
+
+static int load_config(int globals_only)
+{
+ if (!config_file) {
+ if (am_daemon < 0 && am_root <= 0)
+ config_file = RSYNCD_USERCONF;
+ else
+ config_file = RSYNCD_SYSCONF;
+ }
+ return lp_load(config_file, globals_only);
+}
+
+/* this is called when a connection is established to a client
+ and we want to start talking. The setup of the system is done from
+ here */
+int start_daemon(int f_in, int f_out)
+{
+ char line[1024];
+ const char *addr, *host;
+ char *p;
+ int i;
+
+ /* At this point, am_server is only set for a daemon started via rsh.
+ * Because am_server gets forced on soon, we'll set am_daemon to -1 as
+ * a flag that can be checked later on to distinguish a normal daemon
+ * from an rsh-run daemon. */
+ if (am_server)
+ am_daemon = -1;
+
+ io_set_sock_fds(f_in, f_out);
+
+ /* We must load the config file before calling any function that
+ * might cause log-file output to occur. This ensures that the
+ * "log file" param gets honored for the 2 non-forked use-cases
+ * (when rsync is run by init and run by a remote shell). */
+ if (!load_config(0))
+ exit_cleanup(RERR_SYNTAX);
+
+ if (lp_proxy_protocol() && !read_proxy_protocol_header(f_in))
+ return -1;
+
+ p = lp_daemon_chroot();
+ if (*p) {
+ log_init(0); /* Make use we've initialized syslog before chrooting. */
+ if (chroot(p) < 0) {
+ rsyserr(FLOG, errno, "daemon chroot(\"%s\") failed", p);
+ return -1;
+ }
+ if (chdir("/") < 0) {
+ rsyserr(FLOG, errno, "daemon chdir(\"/\") failed");
+ return -1;
+ }
+ }
+ p = lp_daemon_gid();
+ if (*p) {
+ gid_t gid;
+ if (!group_to_gid(p, &gid, True)) {
+ rprintf(FLOG, "Invalid daemon gid: %s\n", p);
+ return -1;
+ }
+ if (setgid(gid) < 0) {
+ rsyserr(FLOG, errno, "Unable to set group to daemon gid %ld", (long)gid);
+ return -1;
+ }
+ our_gid = MY_GID();
+ }
+ p = lp_daemon_uid();
+ if (*p) {
+ uid_t uid;
+ if (!user_to_uid(p, &uid, True)) {
+ rprintf(FLOG, "Invalid daemon uid: %s\n", p);
+ return -1;
+ }
+ if (setuid(uid) < 0) {
+ rsyserr(FLOG, errno, "Unable to set user to daemon uid %ld", (long)uid);
+ return -1;
+ }
+ our_uid = MY_UID();
+ am_root = (our_uid == ROOT_UID);
+ }
+
+ addr = client_addr(f_in);
+ host = lp_reverse_lookup(-1) ? client_name(addr) : undetermined_hostname;
+ rprintf(FLOG, "connect from %s (%s)\n", host, addr);
+
+ if (am_daemon > 0) {
+ set_socket_options(f_in, "SO_KEEPALIVE");
+ set_nonblocking(f_in);
+ }
+
+ if (exchange_protocols(f_in, f_out, line, sizeof line, 0) < 0)
+ return -1;
+
+ line[0] = 0;
+ if (!read_line_old(f_in, line, sizeof line, 0))
+ return -1;
+
+ if (strncmp(line, EARLY_INPUT_CMD, EARLY_INPUT_CMDLEN) == 0) {
+ early_input_len = strtol(line + EARLY_INPUT_CMDLEN, NULL, 10);
+ if (early_input_len <= 0 || early_input_len > BIGPATHBUFLEN) {
+ io_printf(f_out, "@ERROR: invalid early_input length\n");
+ return -1;
+ }
+ early_input = new_array(char, early_input_len);
+ read_buf(f_in, early_input, early_input_len);
+
+ if (!read_line_old(f_in, line, sizeof line, 0))
+ return -1;
+ }
+
+ if (!*line || strcmp(line, "#list") == 0) {
+ rprintf(FLOG, "module-list request from %s (%s)\n",
+ host, addr);
+ send_listing(f_out);
+ return -1;
+ }
+
+ if (*line == '#') {
+ /* it's some sort of command that I don't understand */
+ io_printf(f_out, "@ERROR: Unknown command '%s'\n", line);
+ return -1;
+ }
+
+ if ((i = lp_number(line)) < 0) {
+ rprintf(FLOG, "unknown module '%s' tried from %s (%s)\n",
+ line, host, addr);
+ io_printf(f_out, "@ERROR: Unknown module '%s'\n", line);
+ return -1;
+ }
+
+#ifdef HAVE_SIGACTION
+ sigact.sa_flags = SA_NOCLDSTOP;
+#endif
+ SIGACTION(SIGCHLD, remember_children);
+
+ return rsync_module(f_in, f_out, i, addr, host);
+}
+
+static void create_pid_file(void)
+{
+ char *pid_file = lp_pid_file();
+ char pidbuf[32];
+ STRUCT_STAT st1, st2;
+ char *fail = NULL;
+
+ if (!pid_file || !*pid_file)
+ return;
+
+#ifdef O_NOFOLLOW
+#define SAFE_OPEN_FLAGS (O_CREAT|O_NOFOLLOW)
+#else
+#define SAFE_OPEN_FLAGS (O_CREAT)
+#endif
+
+ /* These tests make sure that a temp-style lock dir is handled safely. */
+ st1.st_mode = 0;
+ if (do_lstat(pid_file, &st1) == 0 && !S_ISREG(st1.st_mode) && unlink(pid_file) < 0)
+ fail = "unlink";
+ else if ((pid_file_fd = do_open(pid_file, O_RDWR|SAFE_OPEN_FLAGS, 0664)) < 0)
+ fail = S_ISREG(st1.st_mode) ? "open" : "create";
+ else if (!lock_range(pid_file_fd, 0, 4))
+ fail = "lock";
+ else if (do_fstat(pid_file_fd, &st1) < 0)
+ fail = "fstat opened";
+ else if (st1.st_size > (int)sizeof pidbuf)
+ fail = "find small";
+ else if (do_lstat(pid_file, &st2) < 0)
+ fail = "lstat";
+ else if (!S_ISREG(st1.st_mode))
+ fail = "avoid file overwrite race for";
+ else if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+ fail = "verify stat info for";
+#ifdef HAVE_FTRUNCATE
+ else if (do_ftruncate(pid_file_fd, 0) < 0)
+ fail = "truncate";
+#endif
+ else {
+ pid_t pid = getpid();
+ int len = snprintf(pidbuf, sizeof pidbuf, "%d\n", (int)pid);
+#ifndef HAVE_FTRUNCATE
+ /* What can we do with a too-long file and no truncate? I guess we'll add extra newlines. */
+ while (len < st1.st_size) /* We already verified that st_size chars fits in the buffer. */
+ pidbuf[len++] = '\n';
+ /* We don't need the buffer to end in a '\0' (and we may not have room to add it). */
+#endif
+ if (write(pid_file_fd, pidbuf, len) != len)
+ fail = "write";
+ cleanup_set_pid(pid); /* Mark the file for removal on exit, even if the write failed. */
+ }
+
+ if (fail) {
+ char msg[1024];
+ snprintf(msg, sizeof msg, "failed to %s pid file %s: %s\n",
+ fail, pid_file, strerror(errno));
+ fputs(msg, stderr);
+ rprintf(FLOG, "%s", msg);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ /* The file is left open so that the lock remains valid. It is closed in our forked child procs. */
+}
+
+/* Become a daemon, discarding the controlling terminal. */
+static void become_daemon(void)
+{
+ int i;
+ pid_t pid = fork();
+
+ if (pid) {
+ if (pid < 0) {
+ fprintf(stderr, "failed to fork: %s\n", strerror(errno));
+ exit_cleanup(RERR_FILEIO);
+ }
+ _exit(0);
+ }
+
+ create_pid_file();
+
+ /* detach from the terminal */
+#ifdef HAVE_SETSID
+ setsid();
+#elif defined TIOCNOTTY
+ i = open("/dev/tty", O_RDWR);
+ if (i >= 0) {
+ ioctl(i, (int)TIOCNOTTY, (char *)0);
+ close(i);
+ }
+#endif
+ /* make sure that stdin, stdout an stderr don't stuff things
+ * up (library functions, for example) */
+ for (i = 0; i < 3; i++) {
+ close(i);
+ open("/dev/null", O_RDWR);
+ }
+}
+
+int daemon_main(void)
+{
+ if (is_a_socket(STDIN_FILENO)) {
+ int i;
+
+ /* we are running via inetd - close off stdout and
+ * stderr so that library functions (and getopt) don't
+ * try to use them. Redirect them to /dev/null */
+ for (i = 1; i < 3; i++) {
+ close(i);
+ open("/dev/null", O_RDWR);
+ }
+
+ return start_daemon(STDIN_FILENO, STDIN_FILENO);
+ }
+
+ if (!load_config(1)) {
+ fprintf(stderr, "Failed to parse config file: %s\n", config_file);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ set_dparams(0);
+
+ if (no_detach)
+ create_pid_file();
+ else
+ become_daemon();
+
+ if (rsync_port == 0 && (rsync_port = lp_rsync_port()) == 0)
+ rsync_port = RSYNC_PORT;
+ if (bind_address == NULL && *lp_bind_address())
+ bind_address = lp_bind_address();
+
+ log_init(0);
+
+ rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n",
+ rsync_version(), rsync_port);
+ /* TODO: If listening on a particular address, then show that
+ * address too. In fact, why not just do getnameinfo on the
+ * local address??? */
+
+ start_accept_loop(rsync_port, start_daemon);
+ return -1;
+}
diff --git a/cmd-or-msg b/cmd-or-msg
new file mode 100755
index 0000000..ccf9527
--- /dev/null
+++ b/cmd-or-msg
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+srcdir=`dirname $0`
+opt="$1"
+shift
+
+echo "$*"
+if ! "${@}"; then
+ echo "If you can't fix the issue, re-run $srcdir/configure with --$opt."
+ exit 1
+fi
diff --git a/compat.c b/compat.c
new file mode 100644
index 0000000..a8a6afe
--- /dev/null
+++ b/compat.c
@@ -0,0 +1,887 @@
+/*
+ * Compatibility routines for older rsync protocol versions.
+ *
+ * Copyright (C) Andrew Tridgell 1996
+ * Copyright (C) Paul Mackerras 1996
+ * Copyright (C) 2004-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "ifuncs.h"
+
+extern int am_server;
+extern int am_sender;
+extern int local_server;
+extern int inplace;
+extern int recurse;
+extern int use_qsort;
+extern int allow_inc_recurse;
+extern int preallocate_files;
+extern int append_mode;
+extern int fuzzy_basis;
+extern int read_batch;
+extern int write_batch;
+extern int delay_updates;
+extern int checksum_seed;
+extern int basis_dir_cnt;
+extern int prune_empty_dirs;
+extern int protocol_version;
+extern int protect_args;
+extern int preserve_uid;
+extern int preserve_gid;
+extern int preserve_atimes;
+extern int preserve_crtimes;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int xfer_flags_as_varint;
+extern int need_messages_from_generator;
+extern int delete_mode, delete_before, delete_during, delete_after;
+extern int do_compression;
+extern int do_compression_level;
+extern int saw_stderr_opt;
+extern int msgs2stderr;
+extern char *shell_cmd;
+extern char *partial_dir;
+extern char *files_from;
+extern char *filesfrom_host;
+extern const char *checksum_choice;
+extern const char *compress_choice;
+extern char *daemon_auth_choices;
+extern filter_rule_list filter_list;
+extern int need_unsorted_flist;
+#ifdef ICONV_OPTION
+extern iconv_t ic_send, ic_recv;
+extern char *iconv_opt;
+#endif
+extern struct name_num_obj valid_checksums, valid_auth_checksums;
+
+extern struct name_num_item *xfer_sum_nni;
+
+int remote_protocol = 0;
+int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
+int inc_recurse = 0;
+int compat_flags = 0;
+int use_safe_inc_flist = 0;
+int want_xattr_optim = 0;
+int proper_seed_order = 0;
+int inplace_partial = 0;
+int do_negotiated_strings = 0;
+int xmit_id0_names = 0;
+
+struct name_num_item *xattr_sum_nni;
+int xattr_sum_len = 0;
+
+/* These index values are for the file-list's extra-attribute array. */
+int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
+
+int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
+int sender_symlink_iconv = 0; /* sender should convert symlink content */
+
+#ifdef ICONV_OPTION
+int filesfrom_convert = 0;
+#endif
+
+#define MAX_NSTR_STRLEN 256
+
+struct name_num_item valid_compressions_items[] = {
+#ifdef SUPPORT_ZSTD
+ { CPRES_ZSTD, 0, "zstd", NULL },
+#endif
+#ifdef SUPPORT_LZ4
+ { CPRES_LZ4, 0, "lz4", NULL },
+#endif
+ { CPRES_ZLIBX, 0, "zlibx", NULL },
+ { CPRES_ZLIB, 0, "zlib", NULL },
+ { CPRES_NONE, 0, "none", NULL },
+ { 0, 0, NULL, NULL }
+};
+
+struct name_num_obj valid_compressions = {
+ "compress", NULL, 0, 0, valid_compressions_items
+};
+
+#define CF_INC_RECURSE (1<<0)
+#define CF_SYMLINK_TIMES (1<<1)
+#define CF_SYMLINK_ICONV (1<<2)
+#define CF_SAFE_FLIST (1<<3)
+#define CF_AVOID_XATTR_OPTIM (1<<4)
+#define CF_CHKSUM_SEED_FIX (1<<5)
+#define CF_INPLACE_PARTIAL_DIR (1<<6)
+#define CF_VARINT_FLIST_FLAGS (1<<7)
+#define CF_ID0_NAMES (1<<8)
+
+static const char *client_info;
+
+/* The server makes sure that if either side only supports a pre-release
+ * version of a protocol, that both sides must speak a compatible version
+ * of that protocol for it to be advertised as available. */
+static void check_sub_protocol(void)
+{
+ char *dot;
+ int their_protocol, their_sub;
+ int our_sub = get_subprotocol_version();
+
+ /* client_info starts with VER.SUB string if client is a pre-release. */
+ if (!(their_protocol = atoi(client_info))
+ || !(dot = strchr(client_info, '.'))
+ || !(their_sub = atoi(dot+1))) {
+#if SUBPROTOCOL_VERSION != 0
+ if (our_sub)
+ protocol_version--;
+#endif
+ return;
+ }
+
+ if (their_protocol < protocol_version) {
+ if (their_sub)
+ protocol_version = their_protocol - 1;
+ return;
+ }
+
+ if (their_protocol > protocol_version)
+ their_sub = 0; /* 0 == final version of older protocol */
+ if (their_sub != our_sub)
+ protocol_version--;
+}
+
+void set_allow_inc_recurse(void)
+{
+ if (!local_server)
+ client_info = shell_cmd ? shell_cmd : "";
+ else if (am_server) {
+ char buf[64];
+ maybe_add_e_option(buf, sizeof buf);
+ client_info = *buf ? strdup(buf+1) : ""; /* The +1 skips the leading "e". */
+ }
+
+ if (!recurse || use_qsort)
+ allow_inc_recurse = 0;
+ else if (!am_sender
+ && (delete_before || delete_after
+ || delay_updates || prune_empty_dirs))
+ allow_inc_recurse = 0;
+ else if (am_server && strchr(client_info, 'i') == NULL)
+ allow_inc_recurse = 0;
+}
+
+void parse_compress_choice(int final_call)
+{
+ if (valid_compressions.negotiated_nni)
+ do_compression = valid_compressions.negotiated_nni->num;
+ else if (compress_choice) {
+ struct name_num_item *nni = get_nni_by_name(&valid_compressions, compress_choice, -1);
+ if (!nni) {
+ rprintf(FERROR, "unknown compress name: %s\n", compress_choice);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ do_compression = nni->num;
+ if (am_server)
+ validate_choice_vs_env(NSTR_COMPRESS, do_compression, -1);
+ } else if (do_compression)
+ do_compression = CPRES_ZLIB;
+ else
+ do_compression = CPRES_NONE;
+
+ if (do_compression != CPRES_NONE && final_call)
+ init_compression_level(); /* There's a chance this might turn compression off! */
+
+ if (do_compression == CPRES_NONE)
+ compress_choice = NULL;
+
+ /* Snag the compression name for both write_batch's option output & the following debug output. */
+ if (valid_compressions.negotiated_nni)
+ compress_choice = valid_compressions.negotiated_nni->name;
+ else if (compress_choice == NULL) {
+ struct name_num_item *nni = get_nni_by_num(&valid_compressions, do_compression);
+ compress_choice = nni ? nni->name : "UNKNOWN";
+ }
+
+ if (final_call && DEBUG_GTE(NSTR, am_server ? 3 : 1)
+ && (do_compression != CPRES_NONE || do_compression_level != CLVL_NOT_SPECIFIED)) {
+ rprintf(FINFO, "%s%s compress: %s (level %d)\n",
+ am_server ? "Server" : "Client",
+ valid_compressions.negotiated_nni ? " negotiated" : "",
+ compress_choice, do_compression_level);
+ }
+}
+
+struct name_num_item *get_nni_by_name(struct name_num_obj *nno, const char *name, int len)
+{
+ struct name_num_item *nni;
+
+ if (len < 0)
+ len = strlen(name);
+
+ for (nni = nno->list; nni->name; nni++) {
+ if (nni->num == CSUM_gone)
+ continue;
+ if (strncasecmp(name, nni->name, len) == 0 && nni->name[len] == '\0')
+ return nni;
+ }
+
+ return NULL;
+}
+
+struct name_num_item *get_nni_by_num(struct name_num_obj *nno, int num)
+{
+ struct name_num_item *nni;
+
+ for (nni = nno->list; nni->name; nni++) {
+ if (num == nni->num)
+ return nni;
+ }
+
+ return NULL;
+}
+
+static void init_nno_saw(struct name_num_obj *nno, int val)
+{
+ struct name_num_item *nni;
+ int cnt;
+
+ if (!nno->saw_len) {
+ for (nni = nno->list; nni->name; nni++) {
+ if (nni->num >= nno->saw_len)
+ nno->saw_len = nni->num + 1;
+ }
+ }
+
+ if (!nno->saw) {
+ nno->saw = new_array0(uchar, nno->saw_len);
+
+ /* We'll take this opportunity to set the main_nni values for duplicates. */
+ for (cnt = 1, nni = nno->list; nni->name; nni++, cnt++) {
+ if (nni->num == CSUM_gone)
+ continue;
+ if (nno->saw[nni->num])
+ nni->main_nni = &nno->list[nno->saw[nni->num]-1];
+ else
+ nno->saw[nni->num] = cnt;
+ }
+ }
+
+ memset(nno->saw, val, nno->saw_len);
+}
+
+/* Simplify the user-provided string so that it contains valid names without any duplicates.
+ * It also sets the "saw" flags to a 1-relative count of which name was seen first. */
+static int parse_nni_str(struct name_num_obj *nno, const char *from, char *tobuf, int tobuf_len)
+{
+ char *to = tobuf, *tok = NULL;
+ int saw_tok = 0, cnt = 0;
+
+ while (1) {
+ int at_space = isSpace(from);
+ char ch = *from++;
+ if (ch == '&')
+ ch = '\0';
+ if (!ch || at_space) {
+ if (tok) {
+ struct name_num_item *nni = get_nni_by_name(nno, tok, to - tok);
+ if (nni && !nno->saw[nni->num]) {
+ nno->saw[nni->num] = ++cnt;
+ if (nni->main_nni) {
+ to = tok + strlcpy(tok, nni->main_nni->name, tobuf_len - (tok - tobuf));
+ if (to - tobuf >= tobuf_len) {
+ to = tok - 1;
+ break;
+ }
+ }
+ } else
+ to = tok - (tok != tobuf);
+ saw_tok = 1;
+ tok = NULL;
+ }
+ if (!ch)
+ break;
+ continue;
+ }
+ if (!tok) {
+ if (to != tobuf)
+ *to++ = ' ';
+ tok = to;
+ }
+ if (to - tobuf >= tobuf_len - 1) {
+ to = tok - (tok != tobuf);
+ break;
+ }
+ *to++ = ch;
+ }
+ *to = '\0';
+
+ if (saw_tok && to == tobuf)
+ return strlcpy(tobuf, "INVALID", MAX_NSTR_STRLEN);
+
+ return to - tobuf;
+}
+
+static int parse_negotiate_str(struct name_num_obj *nno, char *tmpbuf)
+{
+ struct name_num_item *nni, *ret = NULL;
+ int best = nno->saw_len; /* We want best == 1 from the client list, so start with a big number. */
+ char *space, *tok = tmpbuf;
+ while (tok) {
+ while (*tok == ' ') tok++; /* Should be unneeded... */
+ if (!*tok)
+ break;
+ if ((space = strchr(tok, ' ')) != NULL)
+ *space = '\0';
+ nni = get_nni_by_name(nno, tok, -1);
+ if (space) {
+ *space = ' ';
+ tok = space + 1;
+ } else
+ tok = NULL;
+ if (!nni || !nno->saw[nni->num] || best <= nno->saw[nni->num])
+ continue;
+ ret = nni;
+ best = nno->saw[nni->num];
+ if (best == 1 || am_server) /* The server side stops at the first acceptable client choice */
+ break;
+ }
+ if (ret) {
+ free(nno->saw);
+ nno->saw = NULL;
+ nno->negotiated_nni = ret->main_nni ? ret->main_nni : ret;
+ return 1;
+ }
+ return 0;
+}
+
+/* This routine is always called with a tmpbuf of MAX_NSTR_STRLEN length, but the
+ * buffer may be pre-populated with a "len" length string to use OR a len of -1
+ * to tell us to read a string from the fd. */
+static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf, int len)
+{
+ if (len < 0)
+ len = read_vstring(f_in, tmpbuf, MAX_NSTR_STRLEN);
+
+ if (DEBUG_GTE(NSTR, am_server ? 3 : 2)) {
+ if (am_server)
+ rprintf(FINFO, "Client %s list (on server): %s\n", nno->type, tmpbuf);
+ else
+ rprintf(FINFO, "Server %s list (on client): %s\n", nno->type, tmpbuf);
+ }
+
+ if (len > 0 && parse_negotiate_str(nno, tmpbuf))
+ return;
+
+ if (!am_server || !do_negotiated_strings) {
+ char *cp = tmpbuf;
+ int j;
+ rprintf(FERROR, "Failed to negotiate a %s choice.\n", nno->type);
+ rprintf(FERROR, "%s list: %s\n", am_server ? "Client" : "Server", tmpbuf);
+ /* Recreate our original list from the saw values. This can't overflow our huge
+ * buffer because we don't have enough valid entries to get anywhere close. */
+ for (j = 1, *cp = '\0'; j <= nno->saw_len; j++) {
+ struct name_num_item *nni;
+ for (nni = nno->list; nni->name; nni++) {
+ if (nno->saw[nni->num] == j) {
+ *cp++ = ' ';
+ cp += strlcpy(cp, nni->name, MAX_NSTR_STRLEN - (cp - tmpbuf));
+ break;
+ }
+ }
+ }
+ if (!*tmpbuf)
+ strlcpy(cp, " INVALID", MAX_NSTR_STRLEN);
+ rprintf(FERROR, "%s list:%s\n", am_server ? "Server" : "Client", tmpbuf);
+ }
+
+ exit_cleanup(RERR_UNSUPPORTED);
+}
+
+static const char *getenv_nstr(int ntype)
+{
+ const char *env_str = getenv(ntype == NSTR_COMPRESS ? "RSYNC_COMPRESS_LIST" : "RSYNC_CHECKSUM_LIST");
+
+ /* When writing a batch file, we always negotiate an old-style choice. */
+ if (write_batch)
+ env_str = ntype == NSTR_COMPRESS ? "zlib" : protocol_version >= 30 ? "md5" : "md4";
+
+ if (am_server && env_str) {
+ char *cp = strchr(env_str, '&');
+ if (cp)
+ env_str = cp + 1;
+ }
+
+ return env_str;
+}
+
+void validate_choice_vs_env(int ntype, int num1, int num2)
+{
+ struct name_num_obj *nno = ntype == NSTR_COMPRESS ? &valid_compressions : &valid_checksums;
+ const char *list_str = getenv_nstr(ntype);
+ char tmpbuf[MAX_NSTR_STRLEN];
+
+ if (!list_str)
+ return;
+
+ while (isSpace(list_str)) list_str++;
+
+ if (!*list_str)
+ return;
+
+ init_nno_saw(nno, 0);
+ parse_nni_str(nno, list_str, tmpbuf, MAX_NSTR_STRLEN);
+
+ if (ntype == NSTR_CHECKSUM) /* If "md4" is in the env list, all the old MD4 choices are OK too. */
+ nno->saw[CSUM_MD4_ARCHAIC] = nno->saw[CSUM_MD4_BUSTED] = nno->saw[CSUM_MD4_OLD] = nno->saw[CSUM_MD4];
+
+ if (!nno->saw[num1] || (num2 >= 0 && !nno->saw[num2])) {
+ rprintf(FERROR, "Your --%s-choice value (%s) was refused by the server.\n",
+ ntype == NSTR_COMPRESS ? "compress" : "checksum",
+ ntype == NSTR_COMPRESS ? compress_choice : checksum_choice);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ free(nno->saw);
+ nno->saw = NULL;
+}
+
+/* The saw buffer is initialized and used to store ordinal values from 1 to N
+ * for the order of the args in the array. If dup_markup == '\0', duplicates
+ * are removed otherwise the char is prefixed to the duplicate term and, if it
+ * is an opening paren/bracket/brace, the matching closing char is suffixed.
+ * "none" is removed on the client side unless dup_markup != '\0'. */
+int get_default_nno_list(struct name_num_obj *nno, char *to_buf, int to_buf_len, char dup_markup)
+{
+ struct name_num_item *nni;
+ int len = 0, cnt = 0;
+ char delim = '\0', post_delim;
+
+ switch (dup_markup) {
+ case '(': post_delim = ')'; break;
+ case '[': post_delim = ']'; break;
+ case '{': post_delim = '}'; break;
+ default: post_delim = '\0'; break;
+ }
+
+ init_nno_saw(nno, 0);
+
+ for (nni = nno->list, len = 0; nni->name; nni++) {
+ if (nni->num == CSUM_gone)
+ continue;
+ if (nni->main_nni) {
+ if (!dup_markup || nni->main_nni->num == CSUM_gone)
+ continue;
+ delim = dup_markup;
+ }
+ if (nni->num == 0 && !am_server && !dup_markup)
+ continue;
+ if (len)
+ to_buf[len++]= ' ';
+ if (delim) {
+ to_buf[len++]= delim;
+ delim = post_delim;
+ }
+ len += strlcpy(to_buf+len, nni->name, to_buf_len - len);
+ if (len >= to_buf_len - 3)
+ exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE... */
+ if (delim) {
+ to_buf[len++]= delim;
+ delim = '\0';
+ }
+ nno->saw[nni->num] = ++cnt;
+ }
+
+ return len;
+}
+
+static void send_negotiate_str(int f_out, struct name_num_obj *nno, int ntype)
+{
+ char tmpbuf[MAX_NSTR_STRLEN];
+ const char *list_str = getenv_nstr(ntype);
+ int len;
+
+ if (list_str && *list_str) {
+ init_nno_saw(nno, 0);
+ len = parse_nni_str(nno, list_str, tmpbuf, MAX_NSTR_STRLEN);
+ list_str = tmpbuf;
+ } else
+ list_str = NULL;
+
+ if (!list_str || !*list_str)
+ len = get_default_nno_list(nno, tmpbuf, MAX_NSTR_STRLEN, '\0');
+
+ if (DEBUG_GTE(NSTR, am_server ? 3 : 2)) {
+ if (am_server)
+ rprintf(FINFO, "Server %s list (on server): %s\n", nno->type, tmpbuf);
+ else
+ rprintf(FINFO, "Client %s list (on client): %s\n", nno->type, tmpbuf);
+ }
+
+ /* Each side sends their list of valid names to the other side and then both sides
+ * pick the first name in the client's list that is also in the server's list. */
+ if (do_negotiated_strings)
+ write_vstring(f_out, tmpbuf, len);
+}
+
+static void negotiate_the_strings(int f_in, int f_out)
+{
+ /* We send all the negotiation strings before we start to read them to help avoid a slow startup. */
+
+ init_checksum_choices();
+
+ if (!checksum_choice)
+ send_negotiate_str(f_out, &valid_checksums, NSTR_CHECKSUM);
+
+ if (do_compression && !compress_choice)
+ send_negotiate_str(f_out, &valid_compressions, NSTR_COMPRESS);
+
+ if (valid_checksums.saw) {
+ char tmpbuf[MAX_NSTR_STRLEN];
+ int len;
+ if (do_negotiated_strings)
+ len = -1;
+ else
+ len = strlcpy(tmpbuf, protocol_version >= 30 ? "md5" : "md4", MAX_NSTR_STRLEN);
+ recv_negotiate_str(f_in, &valid_checksums, tmpbuf, len);
+ }
+
+ if (valid_compressions.saw) {
+ char tmpbuf[MAX_NSTR_STRLEN];
+ int len;
+ if (do_negotiated_strings)
+ len = -1;
+ else
+ len = strlcpy(tmpbuf, "zlib", MAX_NSTR_STRLEN);
+ recv_negotiate_str(f_in, &valid_compressions, tmpbuf, len);
+ }
+
+ /* If the other side is too old to negotiate, the above steps just made sure that
+ * the env didn't disallow the old algorithm. Mark things as non-negotiated. */
+ if (!do_negotiated_strings)
+ valid_checksums.negotiated_nni = valid_compressions.negotiated_nni = NULL;
+}
+
+void setup_protocol(int f_out,int f_in)
+{
+ assert(file_extra_cnt == 0);
+ assert(EXTRA64_CNT == 2 || EXTRA64_CNT == 1);
+
+ /* All int64 values must be set first so that they are guaranteed to be
+ * aligned for direct int64-pointer memory access. */
+ if (preserve_atimes)
+ atimes_ndx = (file_extra_cnt += EXTRA64_CNT);
+ if (preserve_crtimes)
+ crtimes_ndx = (file_extra_cnt += EXTRA64_CNT);
+ if (am_sender) /* This is most likely in the file_extras64 union as well. */
+ pathname_ndx = (file_extra_cnt += PTR_EXTRA_CNT);
+ else
+ depth_ndx = ++file_extra_cnt;
+ if (preserve_uid)
+ uid_ndx = ++file_extra_cnt;
+ if (preserve_gid)
+ gid_ndx = ++file_extra_cnt;
+ if (preserve_acls && !am_sender)
+ acls_ndx = ++file_extra_cnt;
+ if (preserve_xattrs)
+ xattrs_ndx = ++file_extra_cnt;
+
+ if (am_server)
+ set_allow_inc_recurse();
+
+ if (remote_protocol == 0) {
+ if (am_server && !local_server)
+ check_sub_protocol();
+ if (!read_batch)
+ write_int(f_out, protocol_version);
+ remote_protocol = read_int(f_in);
+ if (protocol_version > remote_protocol)
+ protocol_version = remote_protocol;
+ }
+ if (read_batch && remote_protocol > protocol_version) {
+ rprintf(FERROR, "The protocol version in the batch file is too new (%d > %d).\n",
+ remote_protocol, protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (DEBUG_GTE(PROTO, 1)) {
+ rprintf(FINFO, "(%s) Protocol versions: remote=%d, negotiated=%d\n",
+ am_server? "Server" : "Client", remote_protocol, protocol_version);
+ }
+ if (remote_protocol < MIN_PROTOCOL_VERSION
+ || remote_protocol > MAX_PROTOCOL_VERSION) {
+ rprintf(FERROR,"protocol version mismatch -- is your shell clean?\n");
+ rprintf(FERROR,"(see the rsync manpage for an explanation)\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (remote_protocol < OLD_PROTOCOL_VERSION) {
+ rprintf(FINFO,"%s is very old version of rsync, upgrade recommended.\n",
+ am_server? "Client" : "Server");
+ }
+ if (protocol_version < MIN_PROTOCOL_VERSION) {
+ rprintf(FERROR, "--protocol must be at least %d on the %s.\n",
+ MIN_PROTOCOL_VERSION, am_server? "Server" : "Client");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (protocol_version > PROTOCOL_VERSION) {
+ rprintf(FERROR, "--protocol must be no more than %d on the %s.\n",
+ PROTOCOL_VERSION, am_server? "Server" : "Client");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (read_batch)
+ check_batch_flags();
+
+ if (!saw_stderr_opt && protocol_version <= 28 && am_server)
+ msgs2stderr = 0; /* The client side may not have stderr setup for us. */
+
+#ifndef SUPPORT_PREALLOCATION
+ if (preallocate_files && !am_sender) {
+ rprintf(FERROR, "preallocation is not supported on this %s\n",
+ am_server ? "Server" : "Client");
+ exit_cleanup(RERR_SYNTAX);
+ }
+#endif
+
+ if (protocol_version < 30) {
+ if (append_mode == 1)
+ append_mode = 2;
+ if (preserve_acls && !local_server) {
+ rprintf(FERROR,
+ "--acls requires protocol 30 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (preserve_xattrs && !local_server) {
+ rprintf(FERROR,
+ "--xattrs requires protocol 30 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ }
+
+ if (delete_mode && !(delete_before+delete_during+delete_after)) {
+ if (protocol_version < 30)
+ delete_before = 1;
+ else
+ delete_during = 1;
+ }
+
+ if (protocol_version < 29) {
+ if (fuzzy_basis) {
+ rprintf(FERROR,
+ "--fuzzy requires protocol 29 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (basis_dir_cnt && inplace) {
+ rprintf(FERROR,
+ "%s with --inplace requires protocol 29 or higher"
+ " (negotiated %d).\n",
+ alt_dest_opt(0), protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (basis_dir_cnt > 1) {
+ rprintf(FERROR,
+ "Using more than one %s option requires protocol"
+ " 29 or higher (negotiated %d).\n",
+ alt_dest_opt(0), protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (prune_empty_dirs) {
+ rprintf(FERROR,
+ "--prune-empty-dirs requires protocol 29 or higher"
+ " (negotiated %d).\n",
+ protocol_version);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ } else if (protocol_version >= 30) {
+ if (am_server) {
+ compat_flags = allow_inc_recurse ? CF_INC_RECURSE : 0;
+#ifdef CAN_SET_SYMLINK_TIMES
+ compat_flags |= CF_SYMLINK_TIMES;
+#endif
+#ifdef ICONV_OPTION
+ compat_flags |= CF_SYMLINK_ICONV;
+#endif
+ if (strchr(client_info, 'f') != NULL)
+ compat_flags |= CF_SAFE_FLIST;
+ if (strchr(client_info, 'x') != NULL)
+ compat_flags |= CF_AVOID_XATTR_OPTIM;
+ if (strchr(client_info, 'C') != NULL)
+ compat_flags |= CF_CHKSUM_SEED_FIX;
+ if (strchr(client_info, 'I') != NULL)
+ compat_flags |= CF_INPLACE_PARTIAL_DIR;
+ if (strchr(client_info, 'u') != NULL)
+ compat_flags |= CF_ID0_NAMES;
+ if (strchr(client_info, 'v') != NULL) {
+ do_negotiated_strings = 1;
+ compat_flags |= CF_VARINT_FLIST_FLAGS;
+ }
+ if (strchr(client_info, 'V') != NULL) { /* Support a pre-release 'V' that got superseded */
+ if (!write_batch)
+ compat_flags |= CF_VARINT_FLIST_FLAGS;
+ write_byte(f_out, compat_flags);
+ } else
+ write_varint(f_out, compat_flags);
+ } else { /* read_varint() is compatible with the older write_byte() when the 0x80 bit isn't on. */
+ compat_flags = read_varint(f_in);
+ if (compat_flags & CF_VARINT_FLIST_FLAGS)
+ do_negotiated_strings = 1;
+ }
+ /* The inc_recurse var MUST be set to 0 or 1. */
+ inc_recurse = compat_flags & CF_INC_RECURSE ? 1 : 0;
+ want_xattr_optim = protocol_version >= 31 && !(compat_flags & CF_AVOID_XATTR_OPTIM);
+ proper_seed_order = compat_flags & CF_CHKSUM_SEED_FIX ? 1 : 0;
+ xfer_flags_as_varint = compat_flags & CF_VARINT_FLIST_FLAGS ? 1 : 0;
+ xmit_id0_names = compat_flags & CF_ID0_NAMES ? 1 : 0;
+ if (!xfer_flags_as_varint && preserve_crtimes) {
+ fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --crtimes.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (am_sender) {
+ receiver_symlink_times = am_server
+ ? strchr(client_info, 'L') != NULL
+ : !!(compat_flags & CF_SYMLINK_TIMES);
+ }
+#ifdef CAN_SET_SYMLINK_TIMES
+ else
+ receiver_symlink_times = 1;
+#endif
+#ifdef ICONV_OPTION
+ sender_symlink_iconv = iconv_opt && (am_server
+ ? strchr(client_info, 's') != NULL
+ : !!(compat_flags & CF_SYMLINK_ICONV));
+#endif
+ if (inc_recurse && !allow_inc_recurse) {
+ /* This should only be able to happen in a batch. */
+ fprintf(stderr,
+ "Incompatible options specified for inc-recursive %s.\n",
+ read_batch ? "batch file" : "connection");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ use_safe_inc_flist = (compat_flags & CF_SAFE_FLIST) || protocol_version >= 31;
+ need_messages_from_generator = 1;
+ if (compat_flags & CF_INPLACE_PARTIAL_DIR)
+ inplace_partial = 1;
+#ifdef CAN_SET_SYMLINK_TIMES
+ } else if (!am_sender) {
+ receiver_symlink_times = 1;
+#endif
+ }
+
+ if (read_batch)
+ do_negotiated_strings = 0;
+
+ if (need_unsorted_flist && (!am_sender || inc_recurse))
+ unsort_ndx = ++file_extra_cnt;
+
+ if (partial_dir && *partial_dir != '/' && (!am_server || local_server)) {
+ int rflags = FILTRULE_NO_PREFIXES | FILTRULE_DIRECTORY;
+ if (!am_sender || protocol_version >= 30)
+ rflags |= FILTRULE_PERISHABLE;
+ parse_filter_str(&filter_list, partial_dir, rule_template(rflags), 0);
+ }
+
+
+#ifdef ICONV_OPTION
+ if (protect_args && files_from) {
+ if (am_sender)
+ filesfrom_convert = filesfrom_host && ic_send != (iconv_t)-1;
+ else
+ filesfrom_convert = !filesfrom_host && ic_recv != (iconv_t)-1;
+ }
+#endif
+
+ negotiate_the_strings(f_in, f_out);
+
+ if (am_server) {
+ if (!checksum_seed)
+ checksum_seed = time(NULL) ^ (getpid() << 6);
+ write_int(f_out, checksum_seed);
+ } else {
+ checksum_seed = read_int(f_in);
+ }
+
+ parse_checksum_choice(1); /* Sets file_sum_nni & xfer_sum_nni */
+ parse_compress_choice(1); /* Sets do_compression */
+
+ /* TODO in the future allow this algorithm to be chosen somehow, but it can't get too
+ * long or the size starts to cause a problem in the xattr abbrev/non-abbrev code. */
+ xattr_sum_nni = parse_csum_name(NULL, 0);
+ xattr_sum_len = csum_len_for_type(xattr_sum_nni->num, 0);
+
+ if (write_batch && !am_server)
+ write_batch_shell_file();
+
+ init_flist();
+}
+
+void output_daemon_greeting(int f_out, int am_client)
+{
+ char tmpbuf[MAX_NSTR_STRLEN];
+ int our_sub = get_subprotocol_version();
+
+ get_default_nno_list(&valid_auth_checksums, tmpbuf, MAX_NSTR_STRLEN, '\0');
+
+ io_printf(f_out, "@RSYNCD: %d.%d %s\n", protocol_version, our_sub, tmpbuf);
+
+ if (am_client && DEBUG_GTE(NSTR, 2))
+ rprintf(FINFO, "Client %s list (on client): %s\n", valid_auth_checksums.type, tmpbuf);
+}
+
+void negotiate_daemon_auth(int f_out, int am_client)
+{
+ char tmpbuf[MAX_NSTR_STRLEN];
+ int save_am_server = am_server;
+ int md4_is_old = 0;
+
+ if (!am_client)
+ am_server = 1;
+
+ if (daemon_auth_choices)
+ strlcpy(tmpbuf, daemon_auth_choices, MAX_NSTR_STRLEN);
+ else {
+ strlcpy(tmpbuf, protocol_version >= 30 ? "md5" : "md4", MAX_NSTR_STRLEN);
+ md4_is_old = 1;
+ }
+
+ if (am_client) {
+ recv_negotiate_str(-1, &valid_auth_checksums, tmpbuf, strlen(tmpbuf));
+ if (DEBUG_GTE(NSTR, 1)) {
+ rprintf(FINFO, "Client negotiated %s: %s\n", valid_auth_checksums.type,
+ valid_auth_checksums.negotiated_nni->name);
+ }
+ } else {
+ if (!parse_negotiate_str(&valid_auth_checksums, tmpbuf)) {
+ get_default_nno_list(&valid_auth_checksums, tmpbuf, MAX_NSTR_STRLEN, '\0');
+ io_printf(f_out, "@ERROR: your client does not support one of our daemon-auth checksums: %s\n",
+ tmpbuf);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ }
+ am_server = save_am_server;
+ if (md4_is_old && valid_auth_checksums.negotiated_nni->num == CSUM_MD4)
+ valid_auth_checksums.negotiated_nni->num = CSUM_MD4_OLD;
+}
+
+int get_subprotocol_version()
+{
+#if SUBPROTOCOL_VERSION != 0
+ return protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
+#else
+ return 0;
+#endif
+}
diff --git a/config.guess b/config.guess
new file mode 100644
index 0000000..92bfc33
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1684 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright 1992-2020 Free Software Foundation, Inc.
+
+timestamp='2020-04-26'
+
+# This file 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 3 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, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2020 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+ # prevent multiple calls if $tmp is already set
+ test "$tmp" && return 0
+ : "${TMPDIR=/tmp}"
+ # shellcheck disable=SC2039
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+ dummy=$tmp/dummy
+ case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+ ,,) echo "int x;" > "$dummy.c"
+ for driver in cc gcc c89 c99 ; do
+ if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$driver"
+ break
+ fi
+ done
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+ esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "$UNAME_SYSTEM" in
+Linux|GNU|GNU/*)
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ LIBC=gnu
+
+ set_cc_for_build
+ cat <<-EOF > "$dummy.c"
+ #include <features.h>
+ #if defined(__UCLIBC__)
+ LIBC=uclibc
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
+ #else
+ LIBC=gnu
+ #endif
+ EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+
+ # If ldd exists, use it to detect musl libc.
+ if command -v ldd >/dev/null && \
+ ldd --version 2>&1 | grep -q ^musl
+ then
+ LIBC=musl
+ fi
+ ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ "/sbin/$sysctl" 2>/dev/null || \
+ "/usr/sbin/$sysctl" 2>/dev/null || \
+ echo unknown)`
+ case "$UNAME_MACHINE_ARCH" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine="${arch}${endian}"-unknown
+ ;;
+ *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently (or will in the future) and ABI.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ os=netbsdelf
+ ;;
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "$UNAME_VERSION" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "$machine-${os}${release}${abi-}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
+ exit ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
+ exit ;;
+ *:MidnightBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
+ exit ;;
+ *:SolidBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
+ exit ;;
+ *:OS108:*:*)
+ echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE"
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:MirBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:Sortix:*:*)
+ echo "$UNAME_MACHINE"-unknown-sortix
+ exit ;;
+ *:Twizzler:*:*)
+ echo "$UNAME_MACHINE"-unknown-twizzler
+ exit ;;
+ *:Redox:*:*)
+ echo "$UNAME_MACHINE"-unknown-redox
+ exit ;;
+ mips:OSF1:*.*)
+ echo mips-dec-osf1
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE=alpha ;;
+ "EV5 (21164)")
+ UNAME_MACHINE=alphaev5 ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE=alphaev56 ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE=alphapca56 ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE=alphapca57 ;;
+ "EV6 (21264)")
+ UNAME_MACHINE=alphaev6 ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE=alphaev67 ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE=alphaev69 ;;
+ "EV7 (21364)")
+ UNAME_MACHINE=alphaev7 ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE=alphaev79 ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix"$UNAME_RELEASE"
+ exit ;;
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux"$UNAME_RELEASE"
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ set_cc_for_build
+ SUN_ARCH=i386
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH=x86_64
+ fi
+ fi
+ echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ ;;
+ sun4)
+ echo sparc-sun-sunos"$UNAME_RELEASE"
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos"$UNAME_RELEASE"
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint"$UNAME_RELEASE"
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint"$UNAME_RELEASE"
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint"$UNAME_RELEASE"
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix"$UNAME_RELEASE"
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+ dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos"$UNAME_RELEASE"
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
+ then
+ if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
+ [ "$TARGET_BINARY_INTERFACE"x = x ]
+ then
+ echo m88k-dg-dgux"$UNAME_RELEASE"
+ else
+ echo m88k-dg-dguxbcs"$UNAME_RELEASE"
+ fi
+ else
+ echo i586-dg-dgux"$UNAME_RELEASE"
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ case "$UNAME_MACHINE" in
+ 9000/31?) HP_ARCH=m68000 ;;
+ 9000/[34]??) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "$sc_cpu_version" in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "$sc_kernel_bits" in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "$HP_ARCH" = "" ]; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ "$HP_ARCH" = hppa2.0w ]
+ then
+ set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH=hppa2.0w
+ else
+ HP_ARCH=hppa64
+ fi
+ fi
+ echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux"$HPUX_REV"
+ exit ;;
+ 3050*:HI-UX:*:*)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo "$UNAME_MACHINE"-unknown-osf1mk
+ else
+ echo "$UNAME_MACHINE"-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ *:BSD/OS:*:*)
+ echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ arm:FreeBSD:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ set_cc_for_build
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi
+ else
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf
+ fi
+ exit ;;
+ *:FreeBSD:*:*)
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case "$UNAME_PROCESSOR" in
+ amd64)
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
+ esac
+ echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ i*:CYGWIN*:*)
+ echo "$UNAME_MACHINE"-pc-cygwin
+ exit ;;
+ *:MINGW64*:*)
+ echo "$UNAME_MACHINE"-pc-mingw64
+ exit ;;
+ *:MINGW*:*)
+ echo "$UNAME_MACHINE"-pc-mingw32
+ exit ;;
+ *:MSYS*:*)
+ echo "$UNAME_MACHINE"-pc-msys
+ exit ;;
+ i*:PW*:*)
+ echo "$UNAME_MACHINE"-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case "$UNAME_MACHINE" in
+ x86)
+ echo i586-pc-interix"$UNAME_RELEASE"
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ esac ;;
+ i*:UWIN*:*)
+ echo "$UNAME_MACHINE"-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-pc-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
+ exit ;;
+ *:Minix:*:*)
+ echo "$UNAME_MACHINE"-unknown-minix
+ exit ;;
+ aarch64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arc:Linux:*:* | arceb:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arm*:Linux:*:*)
+ set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
+ else
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ cris:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ crisv32:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ e2k:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ frv:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ hexagon:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ exit ;;
+ ia64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ k1om:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m32r*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m68*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ set_cc_for_build
+ IS_GLIBC=0
+ test x"${LIBC}" = xgnu && IS_GLIBC=1
+ sed 's/^ //' << EOF > "$dummy.c"
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #undef mips64
+ #undef mips64el
+ #if ${IS_GLIBC} && defined(_ABI64)
+ LIBCABI=gnuabi64
+ #else
+ #if ${IS_GLIBC} && defined(_ABIN32)
+ LIBCABI=gnuabin32
+ #else
+ LIBCABI=${LIBC}
+ #endif
+ #endif
+
+ #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa64r6
+ #else
+ #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa32r6
+ #else
+ #if defined(__mips64)
+ CPU=mips64
+ #else
+ CPU=mips
+ #endif
+ #endif
+ #endif
+
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ MIPS_ENDIAN=el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ MIPS_ENDIAN=
+ #else
+ MIPS_ENDIAN=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`"
+ test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+ ;;
+ mips64el:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-"$LIBC"
+ exit ;;
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-"$LIBC"
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-"$LIBC"
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+ PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+ *) echo hppa-unknown-linux-"$LIBC" ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-"$LIBC"
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-"$LIBC"
+ exit ;;
+ ppc64le:Linux:*:*)
+ echo powerpc64le-unknown-linux-"$LIBC"
+ exit ;;
+ ppcle:Linux:*:*)
+ echo powerpcle-unknown-linux-"$LIBC"
+ exit ;;
+ riscv32:Linux:*:* | riscv64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
+ exit ;;
+ sh64*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sh*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ tile*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ vax:Linux:*:*)
+ echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
+ exit ;;
+ x86_64:Linux:*:*)
+ set_cc_for_build
+ LIBCABI=$LIBC
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_X32 >/dev/null
+ then
+ LIBCABI="$LIBC"x32
+ fi
+ fi
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI"
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo "$UNAME_MACHINE"-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo "$UNAME_MACHINE"-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo "$UNAME_MACHINE"-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo "$UNAME_MACHINE"-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ i*86:*DOS:*:*)
+ echo "$UNAME_MACHINE"-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:*)
+ UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}"
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configure will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv"$UNAME_RELEASE"
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo "$UNAME_MACHINE"-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo "$UNAME_MACHINE"-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux"$UNAME_RELEASE"
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv"$UNAME_RELEASE"
+ else
+ echo mips-unknown-sysv"$UNAME_RELEASE"
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-ACE:SUPER-UX:*:*)
+ echo sxace-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Rhapsody:*:*)
+ echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ if command -v xcode-select > /dev/null 2> /dev/null && \
+ ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+ # Avoid executing cc if there is no toolchain installed as
+ # cc will be a stub that puts up a graphical alert
+ # prompting the user to install developer tools.
+ CC_FOR_BUILD=no_compiler_found
+ else
+ set_cc_for_build
+ fi
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # uname -m returns i386 or x86_64
+ UNAME_PROCESSOR=$UNAME_MACHINE
+ fi
+ echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = x86; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSR-*:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSV-*:NONSTOP_KERNEL:*:*)
+ echo nsv-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ # shellcheck disable=SC2154
+ if test "$cputype" = 386; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo "$UNAME_MACHINE"-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux"$UNAME_RELEASE"
+ exit ;;
+ *:DragonFly:*:*)
+ echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "$UNAME_MACHINE" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
+ exit ;;
+ i*86:rdos:*:*)
+ echo "$UNAME_MACHINE"-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo "$UNAME_MACHINE"-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo "$UNAME_MACHINE"-unknown-esx
+ exit ;;
+ amd64:Isilon\ OneFS:*:*)
+ echo x86_64-unknown-onefs
+ exit ;;
+ *:Unleashed:*:*)
+ echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE"
+ exit ;;
+esac
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname un;
+ uname (&un);
+ printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname *un;
+ uname (&un);
+ printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+ mips:Linux | mips64:Linux)
+ # If we got here on MIPS GNU/Linux, output extra information.
+ cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+ ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+and
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+EOF
+
+year=`echo $timestamp | sed 's,-.*,,'`
+# shellcheck disable=SC2003
+if test "`expr "\`date +%Y\`" - "$year"`" -lt 3 ; then
+ cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..a5fea77
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,847 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if link() can hard-link special files. */
+#undef CAN_HARDLINK_SPECIAL
+
+/* Define to 1 if link() can hard-link symlinks. */
+#undef CAN_HARDLINK_SYMLINK
+
+/* Define to 1 if chown modifies symlinks. */
+#undef CHOWN_MODIFIES_SYMLINK
+
+/* Undefine if you do not want locale features. By default this is defined. */
+#undef CONFIG_LOCALE
+
+/* Define to 1 if using 'alloca.c'. */
+#undef C_ALLOCA
+
+/* Define to 1 if using external zlib */
+#undef EXTERNAL_ZLIB
+
+/* Used to make "checker" understand that FD_ZERO() clears memory. */
+#undef FORCE_FD_ZERO_MEMSET
+
+/* Define to the type of elements in the array set by `getgroups'. Usually
+ this is either `int' or `gid_t'. */
+#undef GETGROUPS_T
+
+/* Define to 1 if the `getpgrp' function requires zero arguments. */
+#undef GETPGRP_VOID
+
+/* Define to 1 if you have the `aclsort' function. */
+#undef HAVE_ACLSORT
+
+/* true if you have acl_get_perm_np */
+#undef HAVE_ACL_GET_PERM_NP
+
+/* Define to 1 if you have the <acl/libacl.h> header file. */
+#undef HAVE_ACL_LIBACL_H
+
+/* true if you have AIX ACLs */
+#undef HAVE_AIX_ACLS
+
+/* Define to 1 if you have 'alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define to 1 if <alloca.h> works. */
+#undef HAVE_ALLOCA_H
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+#undef HAVE_ARPA_NAMESER_H
+
+/* Define to 1 if you have the `asprintf' function. */
+#undef HAVE_ASPRINTF
+
+/* Define to 1 if you have the `attropen' function. */
+#undef HAVE_ATTROPEN
+
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+#undef HAVE_ATTR_XATTR_H
+
+/* Define to 1 if readdir() is broken */
+#undef HAVE_BROKEN_READDIR
+
+/* Define to 1 if you have the <bsd/string.h> header file. */
+#undef HAVE_BSD_STRING_H
+
+/* Define to 1 if vsprintf has a C99-compatible return value */
+#undef HAVE_C99_VSNPRINTF
+
+/* Define to 1 if you have the `chflags' function. */
+#undef HAVE_CHFLAGS
+
+/* Define to 1 if you have the `chmod' function. */
+#undef HAVE_CHMOD
+
+/* Define to 1 if you have the `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define to 1 if you have the <compat.h> header file. */
+#undef HAVE_COMPAT_H
+
+/* Define to 1 if you have the "connect" function */
+#undef HAVE_CONNECT
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have the <dl.h> header file. */
+#undef HAVE_DL_H
+
+/* Define if posix_fallocate is efficient (Cygwin) */
+#undef HAVE_EFFICIENT_POSIX_FALLOCATE
+
+/* Define to 1 if errno is declared in errno.h */
+#undef HAVE_ERRNO_DECL
+
+/* Define to 1 if you have the `extattr_get_link' function. */
+#undef HAVE_EXTATTR_GET_LINK
+
+/* Define to 1 if you have the fallocate function and it compiles and links
+ without error */
+#undef HAVE_FALLOCATE
+
+/* Define if FALLOC_FL_PUNCH_HOLE is available. */
+#undef HAVE_FALLOC_FL_PUNCH_HOLE
+
+/* Define if FALLOC_FL_ZERO_RANGE is available. */
+#undef HAVE_FALLOC_FL_ZERO_RANGE
+
+/* Define to 1 if you have the `fchmod' function. */
+#undef HAVE_FCHMOD
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the <float.h> header file. */
+#undef HAVE_FLOAT_H
+
+/* True if you have FreeBSD xattrs */
+#undef HAVE_FREEBSD_XATTRS
+
+/* Define to 1 if you have the `fstat' function. */
+#undef HAVE_FSTAT
+
+/* Define to 1 if you have the `ftruncate' function. */
+#undef HAVE_FTRUNCATE
+
+/* Define to 1 if you have the "getaddrinfo" function and required types. */
+#undef HAVE_GETADDRINFO
+
+/* Define to 1 if you have the `getattrlist' function. */
+#undef HAVE_GETATTRLIST
+
+/* Define to 1 if you have the `getcwd' function. */
+#undef HAVE_GETCWD
+
+/* Define to 1 if you have the `getegid' function. */
+#undef HAVE_GETEGID
+
+/* Define to 1 if you have the `geteuid' function. */
+#undef HAVE_GETEUID
+
+/* Define to 1 if you have the `getgrouplist' function. */
+#undef HAVE_GETGROUPLIST
+
+/* Define to 1 if you have the `getgroups' function. */
+#undef HAVE_GETGROUPS
+
+/* Define to 1 if you have the `getpass' function. */
+#undef HAVE_GETPASS
+
+/* Define to 1 if you have the `getpgrp' function. */
+#undef HAVE_GETPGRP
+
+/* Define to 1 if gettimeofday() takes a time-zone arg */
+#undef HAVE_GETTIMEOFDAY_TZ
+
+/* Define to 1 if you have the `getxattr' function. */
+#undef HAVE_GETXATTR
+
+/* Define to 1 if you have the <grp.h> header file. */
+#undef HAVE_GRP_H
+
+/* true if you have HPUX ACLs */
+#undef HAVE_HPUX_ACLS
+
+/* Define to 1 if you have the <iconv.h> header file. */
+#undef HAVE_ICONV_H
+
+/* Define to 1 if you have the `iconv_open' function. */
+#undef HAVE_ICONV_OPEN
+
+/* Define to 1 if the system has the type `id_t'. */
+#undef HAVE_ID_T
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#undef HAVE_INET_NTOP
+
+/* Define to 1 if you have the `inet_pton' function. */
+#undef HAVE_INET_PTON
+
+/* Define to 1 if you have the `initgroups' function. */
+#undef HAVE_INITGROUPS
+
+/* Define to 1 if you have the `innetgr' function. */
+#undef HAVE_INNETGR
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* true if you have IRIX ACLs */
+#undef HAVE_IRIX_ACLS
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#undef HAVE_LANGINFO_H
+
+/* Define to 1 if you have the `lchmod' function. */
+#undef HAVE_LCHMOD
+
+/* Define to 1 if you have the `lchown' function. */
+#undef HAVE_LCHOWN
+
+/* Define to 1 if you have the `acl' library (-lacl). */
+#undef HAVE_LIBACL
+
+/* Define to 1 if you have the `attr' library (-lattr). */
+#undef HAVE_LIBATTR
+
+/* Define to 1 if you have the <libcharset.h> header file. */
+#undef HAVE_LIBCHARSET_H
+
+/* Define to 1 if you have the `inet' library (-linet). */
+#undef HAVE_LIBINET
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define to 1 if you have the `nsl_s' library (-lnsl_s). */
+#undef HAVE_LIBNSL_S
+
+/* Define to 1 if you have the `popt' library (-lpopt). */
+#undef HAVE_LIBPOPT
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+#undef HAVE_LIBRESOLV
+
+/* Define to 1 if you have the `sec' library (-lsec). */
+#undef HAVE_LIBSEC
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define to 1 if you have the `z' library (-lz). */
+#undef HAVE_LIBZ
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the `link' function. */
+#undef HAVE_LINK
+
+/* Define to 1 if you have the `linkat' function. */
+#undef HAVE_LINKAT
+
+/* Define to 1 if you have the <linux/falloc.h> header file. */
+#undef HAVE_LINUX_FALLOC_H
+
+/* True if you have Linux xattrs (or equivalent) */
+#undef HAVE_LINUX_XATTRS
+
+/* Define to 1 if you have the `locale_charset' function. */
+#undef HAVE_LOCALE_CHARSET
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if the type `long double' works and has more range or precision
+ than `double'. */
+#undef HAVE_LONG_DOUBLE
+
+/* Define to 1 if the type `long double' works and has more range or precision
+ than `double'. */
+#undef HAVE_LONG_DOUBLE_WIDER
+
+/* Define to 1 if you have the `lseek64' function. */
+#undef HAVE_LSEEK64
+
+/* Define to 1 if you have the `lutimes' function. */
+#undef HAVE_LUTIMES
+
+/* Define to 1 if you have the <lz4.h> header file. */
+#undef HAVE_LZ4_H
+
+/* Define to 1 if you have the `mallinfo' function. */
+#undef HAVE_MALLINFO
+
+/* Define to 1 if you have the `mallinfo2' function. */
+#undef HAVE_MALLINFO2
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the <mcheck.h> header file. */
+#undef HAVE_MCHECK_H
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the `mkfifo' function. */
+#undef HAVE_MKFIFO
+
+/* Define to 1 if you have the `mknod' function. */
+#undef HAVE_MKNOD
+
+/* Define to 1 if you have the `mkstemp64' function. */
+#undef HAVE_MKSTEMP64
+
+/* Define to 1 if you have the `mktime' function. */
+#undef HAVE_MKTIME
+
+/* Define to 1 if the system has the type `mode_t'. */
+#undef HAVE_MODE_T
+
+/* Define to 1 if you have the `mtrace' function. */
+#undef HAVE_MTRACE
+
+/* Define to 1 if you have the `nanosleep' function. */
+#undef HAVE_NANOSLEEP
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netgroup.h> header file. */
+#undef HAVE_NETGROUP_H
+
+/* Define to 1 if you have the <netinet/in_systm.h> header file. */
+#undef HAVE_NETINET_IN_SYSTM_H
+
+/* Define to 1 if you have the <netinet/ip.h> header file. */
+#undef HAVE_NETINET_IP_H
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#undef HAVE_NL_LANGINFO
+
+/* Define to 1 if the system has the type `off_t'. */
+#undef HAVE_OFF_T
+
+/* Define to 1 if you have the `open64' function. */
+#undef HAVE_OPEN64
+
+/* Define to 1 if you have the <openssl/md4.h> header file. */
+#undef HAVE_OPENSSL_MD4_H
+
+/* Define to 1 if you have the <openssl/md5.h> header file. */
+#undef HAVE_OPENSSL_MD5_H
+
+/* true if you have Mac OS X ACLs */
+#undef HAVE_OSX_ACLS
+
+/* True if you have Mac OS X xattrs */
+#undef HAVE_OSX_XATTRS
+
+/* Define to 1 if the system has the type `pid_t'. */
+#undef HAVE_PID_T
+
+/* Define to 1 if you have the <popt.h> header file. */
+#undef HAVE_POPT_H
+
+/* Define to 1 if you have the <popt/popt.h> header file. */
+#undef HAVE_POPT_POPT_H
+
+/* true if you have posix ACLs */
+#undef HAVE_POSIX_ACLS
+
+/* Define to 1 if you have the `posix_fallocate' function. */
+#undef HAVE_POSIX_FALLOCATE
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the `readlink' function. */
+#undef HAVE_READLINK
+
+/* Define to 1 if remote shell is remsh, not rsh */
+#undef HAVE_REMSH
+
+/* Define to 1 if mkstemp() is available and works right */
+#undef HAVE_SECURE_MKSTEMP
+
+/* Define to 1 if you have the `setattrlist' function. */
+#undef HAVE_SETATTRLIST
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define to 1 if you have the `seteuid' function. */
+#undef HAVE_SETEUID
+
+/* Define to 1 if you have the `setgroups' function. */
+#undef HAVE_SETGROUPS
+
+/* Define to 1 if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
+/* Define to 1 if you have the `setmode' function. */
+#undef HAVE_SETMODE
+
+/* Define to 1 if you have the `setsid' function. */
+#undef HAVE_SETSID
+
+/* Define to 1 if you have the `setvbuf' function. */
+#undef HAVE_SETVBUF
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#undef HAVE_SIGPROCMASK
+
+/* Define to 1 if the system has the type `size_t'. */
+#undef HAVE_SIZE_T
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Do we have sockaddr_in6.sin6_scope_id? */
+#undef HAVE_SOCKADDR_IN6_SCOPE_ID
+
+/* Do we have sockaddr_in.sin_len? */
+#undef HAVE_SOCKADDR_IN_LEN
+
+/* Do we have sockaddr.sa_len? */
+#undef HAVE_SOCKADDR_LEN
+
+/* Do we have sockaddr_un.sun_len? */
+#undef HAVE_SOCKADDR_UN_LEN
+
+/* Define to 1 if you have the "socketpair" function */
+#undef HAVE_SOCKETPAIR
+
+/* true if you have solaris ACLs */
+#undef HAVE_SOLARIS_ACLS
+
+/* True if you have Solaris xattrs */
+#undef HAVE_SOLARIS_XATTRS
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have the `strpbrk' function. */
+#undef HAVE_STRPBRK
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if the system has the type `struct addrinfo'. */
+#undef HAVE_STRUCT_ADDRINFO
+
+/* Define to 1 if the system has the type `struct sockaddr_storage'. */
+#undef HAVE_STRUCT_SOCKADDR_STORAGE
+
+/* Define to 1 if the system has the type `struct stat64'. */
+#undef HAVE_STRUCT_STAT64
+
+/* Define to 1 if `st_mtimensec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_MTIMENSEC
+
+/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+
+/* Define to 1 if `st_rdev' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_RDEV
+
+/* Define to 1 if you have the "struct utimbuf" type */
+#undef HAVE_STRUCT_UTIMBUF
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+#undef HAVE_SYS_ACL_H
+
+/* Define to 1 if you have the <sys/attr.h> header file. */
+#undef HAVE_SYS_ATTR_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if you have the <sys/extattr.h> header file. */
+#undef HAVE_SYS_EXTATTR_H
+
+/* Define to 1 if you have the SYS_fallocate syscall number */
+#undef HAVE_SYS_FALLOCATE
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+#undef HAVE_SYS_FCNTL_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/filio.h> header file. */
+#undef HAVE_SYS_FILIO_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/mode.h> header file. */
+#undef HAVE_SYS_MODE_H
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+#undef HAVE_SYS_UNISTD_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#undef HAVE_SYS_XATTR_H
+
+/* Define to 1 if you have the `tcgetpgrp' function. */
+#undef HAVE_TCGETPGRP
+
+/* true if you have Tru64 ACLs */
+#undef HAVE_TRU64_ACLS
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* true if you have UnixWare ACLs */
+#undef HAVE_UNIXWARE_ACLS
+
+/* Define to 1 if you have the `unsetenv' function. */
+#undef HAVE_UNSETENV
+
+/* Define to 1 if you have the `usleep' function. */
+#undef HAVE_USLEEP
+
+/* Define to 1 if you have the `utime' function. */
+#undef HAVE_UTIME
+
+/* Define to 1 if you have the `utimensat' function. */
+#undef HAVE_UTIMENSAT
+
+/* Define to 1 if you have the `utimes' function. */
+#undef HAVE_UTIMES
+
+/* Define to 1 if you have the <utime.h> header file. */
+#undef HAVE_UTIME_H
+
+/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
+#undef HAVE_UTIME_NULL
+
+/* Define to 1 if you have the `vasprintf' function. */
+#undef HAVE_VASPRINTF
+
+/* Define to 1 if you have the `va_copy' function. */
+#undef HAVE_VA_COPY
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#undef HAVE_VSNPRINTF
+
+/* Define to 1 if you have the `wait4' function. */
+#undef HAVE_WAIT4
+
+/* Define to 1 if you have the `waitpid' function. */
+#undef HAVE_WAITPID
+
+/* Define to 1 if you have the <xxhash.h> header file. */
+#undef HAVE_XXHASH_H
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#undef HAVE_ZLIB_H
+
+/* Define to 1 if you have the <zstd.h> header file. */
+#undef HAVE_ZSTD_H
+
+/* Define to 1 if you have the `_acl' function. */
+#undef HAVE__ACL
+
+/* Define to 1 if you have the `_facl' function. */
+#undef HAVE__FACL
+
+/* Define to 1 if you have the `__acl' function. */
+#undef HAVE___ACL
+
+/* Define to 1 if you have the `__facl' function. */
+#undef HAVE___FACL
+
+/* Define to 1 if you have the `__va_copy' function. */
+#undef HAVE___VA_COPY
+
+/* Define as const if the declaration of iconv() needs const. */
+#undef ICONV_CONST
+
+/* Define if you want the --iconv option. Specifying a value will set the
+ default iconv setting (a NULL means no --iconv processing by default). */
+#undef ICONV_OPTION
+
+/* true if you have IPv6 */
+#undef INET6
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+ */
+#undef MAJOR_IN_MKDEV
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+ <sysmacros.h>. */
+#undef MAJOR_IN_SYSMACROS
+
+/* Define to 1 if makedev() takes 3 args */
+#undef MAKEDEV_TAKES_3_ARGS
+
+/* Define to 1 if mknod() can create FIFOs. */
+#undef MKNOD_CREATES_FIFOS
+
+/* Define to 1 if mknod() can create sockets. */
+#undef MKNOD_CREATES_SOCKETS
+
+/* unprivileged group for unprivileged user */
+#undef NOBODY_GROUP
+
+/* unprivileged user--e.g. nobody */
+#undef NOBODY_USER
+
+/* True if device files do not support xattrs */
+#undef NO_DEVICE_XATTRS
+
+/* True if special files do not support xattrs */
+#undef NO_SPECIAL_XATTRS
+
+/* True if symlinks do not support user xattrs */
+#undef NO_SYMLINK_USER_XATTRS
+
+/* True if symlinks do not support xattrs */
+#undef NO_SYMLINK_XATTRS
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* location of configuration file for rsync server */
+#undef RSYNCD_SYSCONF
+
+/* location of rsync on remote machine */
+#undef RSYNC_PATH
+
+/* default -e command */
+#undef RSYNC_RSH
+
+/* Define to 1 if --secluded-args should be the default */
+#undef RSYNC_USE_SECLUDED_ARGS
+
+/* Define to 1 if sockets need to be shutdown */
+#undef SHUTDOWN_ALL_SOCKETS
+
+/* Define to 1 if "signed char" is a valid type */
+#undef SIGNED_CHAR_OK
+
+/* The size of `char*', as computed by sizeof. */
+#undef SIZEOF_CHARP
+
+/* The size of `int', as computed by sizeof. */
+#undef SIZEOF_INT
+
+/* The size of `int16_t', as computed by sizeof. */
+#undef SIZEOF_INT16_T
+
+/* The size of `int32_t', as computed by sizeof. */
+#undef SIZEOF_INT32_T
+
+/* The size of `int64_t', as computed by sizeof. */
+#undef SIZEOF_INT64_T
+
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
+
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
+/* The size of `off64_t', as computed by sizeof. */
+#undef SIZEOF_OFF64_T
+
+/* The size of `off_t', as computed by sizeof. */
+#undef SIZEOF_OFF_T
+
+/* The size of `short', as computed by sizeof. */
+#undef SIZEOF_SHORT
+
+/* The size of `time_t', as computed by sizeof. */
+#undef SIZEOF_TIME_T
+
+/* The size of `uint16_t', as computed by sizeof. */
+#undef SIZEOF_UINT16_T
+
+/* The size of `uint32_t', as computed by sizeof. */
+#undef SIZEOF_UINT32_T
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+/* Define to 1 to add support for ACLs */
+#undef SUPPORT_ACLS
+
+/* Undefine if you do not want LZ4 compression. By default this is defined. */
+#undef SUPPORT_LZ4
+
+/* Define to 1 to add support for extended attributes */
+#undef SUPPORT_XATTRS
+
+/* Undefine if you do not want xxhash checksums. By default this is defined.
+ */
+#undef SUPPORT_XXHASH
+
+/* Undefine if you do not want zstd compression. By default this is defined.
+ */
+#undef SUPPORT_ZSTD
+
+/* Define to 1 if you want rsync to make use of iconv_open() */
+#undef USE_ICONV_OPEN
+
+/* Define to 1 to enable MD5 ASM optimizations */
+#undef USE_MD5_ASM
+
+/* Undefine if you do not want to use openssl crypto library. By default this
+ is defined. */
+#undef USE_OPENSSL
+
+/* Define to 1 to enable rolling-checksum ASM optimizations (requires
+ --enable-roll-simd) */
+#undef USE_ROLL_ASM
+
+/* Define to 1 to enable rolling-checksum SIMD optimizations */
+#undef USE_ROLL_SIMD
+
+/* String to pass to iconv() for the UTF-8 charset. */
+#undef UTF8_CHARSET
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define _GNU_SOURCE so that we get all necessary prototypes */
+#undef _GNU_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* type to use in place of socklen_t if not defined */
+#undef socklen_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
diff --git a/config.sub b/config.sub
new file mode 100644
index 0000000..973a298
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1793 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright 1992-2020 Free Software Foundation, Inc.
+
+timestamp='2020-05-04'
+
+# This file 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 3 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, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2020 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo "$1"
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+
+# Separate into logical components for further validation
+case $1 in
+ *-*-*-*-*)
+ echo Invalid configuration \`"$1"\': more than four components >&2
+ exit 1
+ ;;
+ *-*-*-*)
+ basic_machine=$field1-$field2
+ os=$field3-$field4
+ ;;
+ *-*-*)
+ # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+ # parts
+ maybe_os=$field2-$field3
+ case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
+ | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
+ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+ | storm-chaos* | os2-emx* | rtmk-nova*)
+ basic_machine=$field1
+ os=$maybe_os
+ ;;
+ android-linux)
+ basic_machine=$field1-unknown
+ os=linux-android
+ ;;
+ *)
+ basic_machine=$field1-$field2
+ os=$field3
+ ;;
+ esac
+ ;;
+ *-*)
+ # A lone config we happen to match not fitting any pattern
+ case $field1-$field2 in
+ decstation-3100)
+ basic_machine=mips-dec
+ os=
+ ;;
+ *-*)
+ # Second component is usually, but not always the OS
+ case $field2 in
+ # Prevent following clause from handling this valid os
+ sun*os*)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ # Manufacturers
+ dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+ | unicom* | ibm* | next | hp | isi* | apollo | altos* \
+ | convergent* | ncr* | news | 32* | 3600* | 3100* \
+ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+ | ultra | tti* | harris | dolphin | highlevel | gould \
+ | cbm | ns | masscomp | apple | axis | knuth | cray \
+ | microblaze* | sim | cisco \
+ | oki | wec | wrs | winbond)
+ basic_machine=$field1-$field2
+ os=
+ ;;
+ *)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ *)
+ # Convert single-component short-hands not valid as part of
+ # multi-component configurations.
+ case $field1 in
+ 386bsd)
+ basic_machine=i386-pc
+ os=bsd
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=scout
+ ;;
+ alliant)
+ basic_machine=fx80-alliant
+ os=
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ os=
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=sysv
+ ;;
+ amiga)
+ basic_machine=m68k-unknown
+ os=
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=linux
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=bsd
+ ;;
+ cray)
+ basic_machine=j90-cray
+ os=unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ os=
+ ;;
+ da30)
+ basic_machine=m68k-da30
+ os=
+ ;;
+ decstation | pmax | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ os=
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=msdosdjgpp
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=ebmon
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=ose
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=go32
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=sysv3
+ ;;
+ hp300 | hp300hpux)
+ basic_machine=m68k-hp
+ os=hpux
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=bsd
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=proelf
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=mach
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=linux
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=sysv
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=mingw64
+ ;;
+ mingw32)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=mingw32ce
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=morphos
+ ;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=moxiebox
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=msdos
+ ;;
+ msys)
+ basic_machine=i686-pc
+ os=msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ os=nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-pc
+ os=netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=sysv
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=nonstopux
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=os68k
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=linux
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ os=rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ os=
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=sysv2
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ os=
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ os=
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=sunos4
+ ;;
+ sun3)
+ basic_machine=m68k-sun
+ os=
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=sunos4
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ os=
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=solaris2
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ os=
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=unicos
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=tops20
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=vms
+ ;;
+ vsta)
+ basic_machine=i386-pc
+ os=vsta
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=vxworks
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=unicos
+ ;;
+ *)
+ basic_machine=$1
+ os=
+ ;;
+ esac
+ ;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+ # Here we handle the default manufacturer of certain CPU types. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ cpu=hppa1.1
+ vendor=winbond
+ ;;
+ op50n)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ op60c)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ ibm*)
+ cpu=i370
+ vendor=ibm
+ ;;
+ orion105)
+ cpu=clipper
+ vendor=highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ cpu=m68k
+ vendor=apple
+ ;;
+ pmac | pmac-mpw)
+ cpu=powerpc
+ vendor=apple
+ ;;
+
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ cpu=m68000
+ vendor=att
+ ;;
+ 3b*)
+ cpu=we32k
+ vendor=att
+ ;;
+ bluegene*)
+ cpu=powerpc
+ vendor=ibm
+ os=cnk
+ ;;
+ decsystem10* | dec10*)
+ cpu=pdp10
+ vendor=dec
+ os=tops10
+ ;;
+ decsystem20* | dec20*)
+ cpu=pdp10
+ vendor=dec
+ os=tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ cpu=m68k
+ vendor=motorola
+ ;;
+ dpx2*)
+ cpu=m68k
+ vendor=bull
+ os=sysv3
+ ;;
+ encore | umax | mmax)
+ cpu=ns32k
+ vendor=encore
+ ;;
+ elxsi)
+ cpu=elxsi
+ vendor=elxsi
+ os=${os:-bsd}
+ ;;
+ fx2800)
+ cpu=i860
+ vendor=alliant
+ ;;
+ genix)
+ cpu=ns32k
+ vendor=ns
+ ;;
+ h3050r* | hiux*)
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ cpu=m68000
+ vendor=hp
+ ;;
+ hp9k3[2-9][0-9])
+ cpu=m68k
+ vendor=hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ i*86v32)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv32
+ ;;
+ i*86v4*)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv4
+ ;;
+ i*86v)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv
+ ;;
+ i*86sol2)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=solaris2
+ ;;
+ j90 | j90-cray)
+ cpu=j90
+ vendor=cray
+ os=${os:-unicos}
+ ;;
+ iris | iris4d)
+ cpu=mips
+ vendor=sgi
+ case $os in
+ irix*)
+ ;;
+ *)
+ os=irix4
+ ;;
+ esac
+ ;;
+ miniframe)
+ cpu=m68000
+ vendor=convergent
+ ;;
+ *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ cpu=m68k
+ vendor=atari
+ os=mint
+ ;;
+ news-3600 | risc-news)
+ cpu=mips
+ vendor=sony
+ os=newsos
+ ;;
+ next | m*-next)
+ cpu=m68k
+ vendor=next
+ case $os in
+ openstep*)
+ ;;
+ nextstep*)
+ ;;
+ ns2*)
+ os=nextstep2
+ ;;
+ *)
+ os=nextstep3
+ ;;
+ esac
+ ;;
+ np1)
+ cpu=np1
+ vendor=gould
+ ;;
+ op50n-* | op60c-*)
+ cpu=hppa1.1
+ vendor=oki
+ os=proelf
+ ;;
+ pa-hitachi)
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
+ ;;
+ pbd)
+ cpu=sparc
+ vendor=tti
+ ;;
+ pbb)
+ cpu=m68k
+ vendor=tti
+ ;;
+ pc532)
+ cpu=ns32k
+ vendor=pc532
+ ;;
+ pn)
+ cpu=pn
+ vendor=gould
+ ;;
+ power)
+ cpu=power
+ vendor=ibm
+ ;;
+ ps2)
+ cpu=i386
+ vendor=ibm
+ ;;
+ rm[46]00)
+ cpu=mips
+ vendor=siemens
+ ;;
+ rtpc | rtpc-*)
+ cpu=romp
+ vendor=ibm
+ ;;
+ sde)
+ cpu=mipsisa32
+ vendor=sde
+ os=${os:-elf}
+ ;;
+ simso-wrs)
+ cpu=sparclite
+ vendor=wrs
+ os=vxworks
+ ;;
+ tower | tower-32)
+ cpu=m68k
+ vendor=ncr
+ ;;
+ vpp*|vx|vx-*)
+ cpu=f301
+ vendor=fujitsu
+ ;;
+ w65)
+ cpu=w65
+ vendor=wdc
+ ;;
+ w89k-*)
+ cpu=hppa1.1
+ vendor=winbond
+ os=proelf
+ ;;
+ none)
+ cpu=none
+ vendor=none
+ ;;
+ leon|leon[3-9])
+ cpu=sparc
+ vendor=$basic_machine
+ ;;
+ leon-*|leon[3-9]-*)
+ cpu=sparc
+ vendor=`echo "$basic_machine" | sed 's/-.*//'`
+ ;;
+
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ cpu=$basic_machine
+ vendor=pc
+ ;;
+ # These rules are duplicated from below for sake of the special case above;
+ # i.e. things that normalized to x86 arches should also default to "pc"
+ pc98)
+ cpu=i386
+ vendor=pc
+ ;;
+ x64 | amd64)
+ cpu=x86_64
+ vendor=pc
+ ;;
+ # Recognize the basic CPU types without company name.
+ *)
+ cpu=$basic_machine
+ vendor=unknown
+ ;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+ # Here we handle the default manufacturer of certain CPU types in canonical form. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ craynv-unknown)
+ vendor=cray
+ os=${os:-unicosmp}
+ ;;
+ c90-unknown | c90-cray)
+ vendor=cray
+ os=${os:-unicos}
+ ;;
+ fx80-unknown)
+ vendor=alliant
+ ;;
+ romp-unknown)
+ vendor=ibm
+ ;;
+ mmix-unknown)
+ vendor=knuth
+ ;;
+ microblaze-unknown | microblazeel-unknown)
+ vendor=xilinx
+ ;;
+ rs6000-unknown)
+ vendor=ibm
+ ;;
+ vax-unknown)
+ vendor=dec
+ ;;
+ pdp11-unknown)
+ vendor=dec
+ ;;
+ we32k-unknown)
+ vendor=att
+ ;;
+ cydra-unknown)
+ vendor=cydrome
+ ;;
+ i370-ibm*)
+ vendor=ibm
+ ;;
+ orion-unknown)
+ vendor=highlevel
+ ;;
+ xps-unknown | xps100-unknown)
+ cpu=xps100
+ vendor=honeywell
+ ;;
+
+ # Here we normalize CPU types with a missing or matching vendor
+ dpx20-unknown | dpx20-bull)
+ cpu=rs6000
+ vendor=bull
+ os=${os:-bosx}
+ ;;
+
+ # Here we normalize CPU types irrespective of the vendor
+ amd64-*)
+ cpu=x86_64
+ ;;
+ blackfin-*)
+ cpu=bfin
+ os=linux
+ ;;
+ c54x-*)
+ cpu=tic54x
+ ;;
+ c55x-*)
+ cpu=tic55x
+ ;;
+ c6x-*)
+ cpu=tic6x
+ ;;
+ e500v[12]-*)
+ cpu=powerpc
+ os=$os"spe"
+ ;;
+ mips3*-*)
+ cpu=mips64
+ ;;
+ ms1-*)
+ cpu=mt
+ ;;
+ m68knommu-*)
+ cpu=m68k
+ os=linux
+ ;;
+ m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+ cpu=s12z
+ ;;
+ openrisc-*)
+ cpu=or32
+ ;;
+ parisc-*)
+ cpu=hppa
+ os=linux
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ cpu=i586
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+ cpu=i686
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ cpu=i686
+ ;;
+ pentium4-*)
+ cpu=i786
+ ;;
+ pc98-*)
+ cpu=i386
+ ;;
+ ppc-* | ppcbe-*)
+ cpu=powerpc
+ ;;
+ ppcle-* | powerpclittle-*)
+ cpu=powerpcle
+ ;;
+ ppc64-*)
+ cpu=powerpc64
+ ;;
+ ppc64le-* | powerpc64little-*)
+ cpu=powerpc64le
+ ;;
+ sb1-*)
+ cpu=mipsisa64sb1
+ ;;
+ sb1el-*)
+ cpu=mipsisa64sb1el
+ ;;
+ sh5e[lb]-*)
+ cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+ ;;
+ spur-*)
+ cpu=spur
+ ;;
+ strongarm-* | thumb-*)
+ cpu=arm
+ ;;
+ tx39-*)
+ cpu=mipstx39
+ ;;
+ tx39el-*)
+ cpu=mipstx39el
+ ;;
+ x64-*)
+ cpu=x86_64
+ ;;
+ xscale-* | xscalee[bl]-*)
+ cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+ ;;
+
+ # Recognize the canonical CPU Types that limit and/or modify the
+ # company names they are paired with.
+ cr16-*)
+ os=${os:-elf}
+ ;;
+ crisv32-* | etraxfs*-*)
+ cpu=crisv32
+ vendor=axis
+ ;;
+ cris-* | etrax*-*)
+ cpu=cris
+ vendor=axis
+ ;;
+ crx-*)
+ os=${os:-elf}
+ ;;
+ neo-tandem)
+ cpu=neo
+ vendor=tandem
+ ;;
+ nse-tandem)
+ cpu=nse
+ vendor=tandem
+ ;;
+ nsr-tandem)
+ cpu=nsr
+ vendor=tandem
+ ;;
+ nsv-tandem)
+ cpu=nsv
+ vendor=tandem
+ ;;
+ nsx-tandem)
+ cpu=nsx
+ vendor=tandem
+ ;;
+ s390-*)
+ cpu=s390
+ vendor=ibm
+ ;;
+ s390x-*)
+ cpu=s390x
+ vendor=ibm
+ ;;
+ tile*-*)
+ os=${os:-linux-gnu}
+ ;;
+
+ *)
+ # Recognize the canonical CPU types that are allowed with any
+ # company name.
+ case $cpu in
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | abacus \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+ | alphapca5[67] | alpha64pca5[67] \
+ | am33_2.0 \
+ | amdgcn \
+ | arc | arceb \
+ | arm | arm[lb]e | arme[lb] | armv* \
+ | avr | avr32 \
+ | asmjs \
+ | ba \
+ | be32 | be64 \
+ | bfin | bpf | bs2000 \
+ | c[123]* | c30 | [cjt]90 | c4x \
+ | c8051 | clipper | craynv | csky | cydra \
+ | d10v | d30v | dlx | dsp16xx \
+ | e2k | elxsi | epiphany \
+ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+ | h8300 | h8500 \
+ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i*86 | i860 | i960 | ia16 | ia64 \
+ | ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle \
+ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+ | m88110 | m88k | maxq | mb | mcore | mep | metag \
+ | microblaze | microblazeel \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64eb | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mmix \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nfp \
+ | nios | nios2 | nios2eb | nios2el \
+ | none | np1 | ns16k | ns32k | nvptx \
+ | open8 \
+ | or1k* \
+ | or32 \
+ | orion \
+ | picochip \
+ | pdp10 | pdp11 | pj | pjl | pn | power \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+ | pru \
+ | pyramid \
+ | riscv | riscv32 | riscv64 \
+ | rl78 | romp | rs6000 | rx \
+ | score \
+ | sh | shl \
+ | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+ | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+ | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+ | spu \
+ | tahoe \
+ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+ | tron \
+ | ubicom32 \
+ | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+ | vax \
+ | visium \
+ | w65 \
+ | wasm32 | wasm64 \
+ | we32k \
+ | x86 | x86_64 | xc16x | xgate | xps100 \
+ | xstormy16 | xtensa* \
+ | ymp \
+ | z8k | z80)
+ ;;
+
+ *)
+ echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+ digital*)
+ vendor=dec
+ ;;
+ commodore*)
+ vendor=cbm
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x$os != x ]
+then
+case $os in
+ # First match some system type aliases that might get confused
+ # with valid system types.
+ # solaris* is a basic system type, with this one exception.
+ auroraux)
+ os=auroraux
+ ;;
+ bluegene*)
+ os=cnk
+ ;;
+ solaris1 | solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ solaris)
+ os=solaris2
+ ;;
+ unixware*)
+ os=sysv4.2uw
+ ;;
+ gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # es1800 is here to avoid being matched by es* (a different OS)
+ es1800*)
+ os=ose
+ ;;
+ # Some version numbers need modification
+ chorusos*)
+ os=chorusos
+ ;;
+ isc)
+ os=isc2.2
+ ;;
+ sco6)
+ os=sco5v6
+ ;;
+ sco5)
+ os=sco3.2v5
+ ;;
+ sco4)
+ os=sco3.2v4
+ ;;
+ sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ ;;
+ sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ scout)
+ # Don't match below
+ ;;
+ sco*)
+ os=sco3.2v2
+ ;;
+ psos*)
+ os=psos
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ # sysv* is not here because it comes later, after sysvr4.
+ gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | kopensolaris* | plan9* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* | twizzler* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | knetbsd* | mirbsd* | netbsd* \
+ | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
+ | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
+ | linux-newlib* | linux-musl* | linux-uclibc* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* \
+ | morphos* | superux* | rtmk* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+ | nsk* | powerunix* | genode*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ qnx*)
+ case $cpu in
+ x86 | i*86)
+ ;;
+ *)
+ os=nto-$os
+ ;;
+ esac
+ ;;
+ hiux*)
+ os=hiuxwe2
+ ;;
+ nto-qnx*)
+ ;;
+ nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ sim | xray | os68k* | v88r* \
+ | windows* | osx | abug | netware* | os9* \
+ | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
+ ;;
+ linux-dietlibc)
+ os=linux-dietlibc
+ ;;
+ linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ lynx*178)
+ os=lynxos178
+ ;;
+ lynx*5)
+ os=lynxos5
+ ;;
+ lynx*)
+ os=lynxos
+ ;;
+ mac*)
+ os=`echo "$os" | sed -e 's|mac|macos|'`
+ ;;
+ opened*)
+ os=openedition
+ ;;
+ os400*)
+ os=os400
+ ;;
+ sunos5*)
+ os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+ ;;
+ sunos6*)
+ os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+ ;;
+ wince*)
+ os=wince
+ ;;
+ utek*)
+ os=bsd
+ ;;
+ dynix*)
+ os=bsd
+ ;;
+ acis*)
+ os=aos
+ ;;
+ atheos*)
+ os=atheos
+ ;;
+ syllable*)
+ os=syllable
+ ;;
+ 386bsd)
+ os=bsd
+ ;;
+ ctix* | uts*)
+ os=sysv
+ ;;
+ nova*)
+ os=rtmk-nova
+ ;;
+ ns2)
+ os=nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ sinix*)
+ os=sysv4
+ ;;
+ tpf*)
+ os=tpf
+ ;;
+ triton*)
+ os=sysv3
+ ;;
+ oss*)
+ os=sysv3
+ ;;
+ svr4*)
+ os=sysv4
+ ;;
+ svr3)
+ os=sysv3
+ ;;
+ sysvr4)
+ os=sysv4
+ ;;
+ # This must come after sysvr4.
+ sysv*)
+ ;;
+ ose*)
+ os=ose
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ os=mint
+ ;;
+ zvmoe)
+ os=zvmoe
+ ;;
+ dicos*)
+ os=dicos
+ ;;
+ pikeos*)
+ # Until real need of OS specific support for
+ # particular features comes up, bare metal
+ # configurations are quite functional.
+ case $cpu in
+ arm*)
+ os=eabi
+ ;;
+ *)
+ os=elf
+ ;;
+ esac
+ ;;
+ nacl*)
+ ;;
+ ios)
+ ;;
+ none)
+ ;;
+ *-eabi)
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $cpu-$vendor in
+ score-*)
+ os=elf
+ ;;
+ spu-*)
+ os=elf
+ ;;
+ *-acorn)
+ os=riscix1.2
+ ;;
+ arm*-rebel)
+ os=linux
+ ;;
+ arm*-semi)
+ os=aout
+ ;;
+ c4x-* | tic4x-*)
+ os=coff
+ ;;
+ c8051-*)
+ os=elf
+ ;;
+ clipper-intergraph)
+ os=clix
+ ;;
+ hexagon-*)
+ os=elf
+ ;;
+ tic54x-*)
+ os=coff
+ ;;
+ tic55x-*)
+ os=coff
+ ;;
+ tic6x-*)
+ os=coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=tops20
+ ;;
+ pdp11-*)
+ os=none
+ ;;
+ *-dec | vax-*)
+ os=ultrix4.2
+ ;;
+ m68*-apollo)
+ os=domain
+ ;;
+ i386-sun)
+ os=sunos4.0.2
+ ;;
+ m68000-sun)
+ os=sunos3
+ ;;
+ m68*-cisco)
+ os=aout
+ ;;
+ mep-*)
+ os=elf
+ ;;
+ mips*-cisco)
+ os=elf
+ ;;
+ mips*-*)
+ os=elf
+ ;;
+ or32-*)
+ os=coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=sysv3
+ ;;
+ sparc-* | *-sun)
+ os=sunos4.1.1
+ ;;
+ pru-*)
+ os=elf
+ ;;
+ *-be)
+ os=beos
+ ;;
+ *-ibm)
+ os=aix
+ ;;
+ *-knuth)
+ os=mmixware
+ ;;
+ *-wec)
+ os=proelf
+ ;;
+ *-winbond)
+ os=proelf
+ ;;
+ *-oki)
+ os=proelf
+ ;;
+ *-hp)
+ os=hpux
+ ;;
+ *-hitachi)
+ os=hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=sysv
+ ;;
+ *-cbm)
+ os=amigaos
+ ;;
+ *-dg)
+ os=dgux
+ ;;
+ *-dolphin)
+ os=sysv3
+ ;;
+ m68k-ccur)
+ os=rtu
+ ;;
+ m88k-omron*)
+ os=luna
+ ;;
+ *-next)
+ os=nextstep
+ ;;
+ *-sequent)
+ os=ptx
+ ;;
+ *-crds)
+ os=unos
+ ;;
+ *-ns)
+ os=genix
+ ;;
+ i370-*)
+ os=mvs
+ ;;
+ *-gould)
+ os=sysv
+ ;;
+ *-highlevel)
+ os=bsd
+ ;;
+ *-encore)
+ os=bsd
+ ;;
+ *-sgi)
+ os=irix
+ ;;
+ *-siemens)
+ os=sysv4
+ ;;
+ *-masscomp)
+ os=rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=uxpv
+ ;;
+ *-rom68k)
+ os=coff
+ ;;
+ *-*bug)
+ os=coff
+ ;;
+ *-apple)
+ os=macos
+ ;;
+ *-atari*)
+ os=mint
+ ;;
+ *-wrs)
+ os=vxworks
+ ;;
+ *)
+ os=none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+case $vendor in
+ unknown)
+ case $os in
+ riscix*)
+ vendor=acorn
+ ;;
+ sunos*)
+ vendor=sun
+ ;;
+ cnk*|-aix*)
+ vendor=ibm
+ ;;
+ beos*)
+ vendor=be
+ ;;
+ hpux*)
+ vendor=hp
+ ;;
+ mpeix*)
+ vendor=hp
+ ;;
+ hiux*)
+ vendor=hitachi
+ ;;
+ unos*)
+ vendor=crds
+ ;;
+ dgux*)
+ vendor=dg
+ ;;
+ luna*)
+ vendor=omron
+ ;;
+ genix*)
+ vendor=ns
+ ;;
+ clix*)
+ vendor=intergraph
+ ;;
+ mvs* | opened*)
+ vendor=ibm
+ ;;
+ os400*)
+ vendor=ibm
+ ;;
+ ptx*)
+ vendor=sequent
+ ;;
+ tpf*)
+ vendor=ibm
+ ;;
+ vxsim* | vxworks* | windiss*)
+ vendor=wrs
+ ;;
+ aux*)
+ vendor=apple
+ ;;
+ hms*)
+ vendor=hitachi
+ ;;
+ mpw* | macos*)
+ vendor=apple
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ vendor=atari
+ ;;
+ vos*)
+ vendor=stratus
+ ;;
+ esac
+ ;;
+esac
+
+echo "$cpu-$vendor-$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755
index 0000000..51c3fee
--- /dev/null
+++ b/configure
@@ -0,0 +1,27 @@
+#!/bin/sh -e
+# This configure script ensures that the configure.sh script exists, and
+# if not, it tries to fetch rsync's generated files or build them. We
+# then transfer control to the configure.sh script to do the real work.
+
+dir=`dirname $0`
+if test x"$dir" = x; then
+ dir=.
+fi
+
+if test "$dir" = '.'; then
+ branch=`packaging/prep-auto-dir` || exit 1
+ if test x"$branch" != x; then
+ cd build || exit 1
+ dir=..
+ fi
+fi
+
+if test ! -f configure.sh; then
+ if ! "$dir/prepare-source" build; then
+ echo 'Failed to build configure.sh and/or config.h.in -- giving up.' >&2
+ rm -f configure.sh
+ exit 1
+ fi
+fi
+
+exec ./configure.sh --srcdir="$dir" "${@}"
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a2c9955
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,1445 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT([rsync],[ ],[https://rsync.samba.org/bug-tracking.html])
+
+AC_C_BIGENDIAN
+AC_HEADER_DIRENT
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \
+ unistd.h utime.h compat.h sys/param.h ctype.h sys/wait.h sys/stat.h \
+ sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h grp.h \
+ sys/un.h sys/attr.h arpa/inet.h arpa/nameser.h locale.h sys/types.h \
+ netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h mcheck.h \
+ sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \
+ popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netgroup.h \
+ zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h \
+ bsd/string.h)
+AC_CHECK_HEADERS([netinet/ip.h], [], [], [[#include <netinet/in.h>]])
+AC_HEADER_MAJOR_FIXED
+
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_SRCDIR([byteorder.h])
+AC_CONFIG_HEADERS([config.h])
+AC_PREREQ([2.69])
+
+PACKAGE_VERSION=`sed -n 's/.*RSYNC_VERSION.*"\(.*\)".*/\1/p' <$srcdir/version.h`
+
+AC_MSG_NOTICE([Configuring rsync $PACKAGE_VERSION])
+
+LDFLAGS=${LDFLAGS-""}
+
+AC_CANONICAL_HOST
+
+dnl define the directory for replacement function since AC_LIBOBJ does not
+dnl officially support subdirs and fails with automake
+AC_CONFIG_LIBOBJ_DIR([lib])
+
+# We must decide this before testing the compiler.
+
+# Please allow this to default to yes, so that your users have more
+# chance of getting a useful stack trace if problems occur.
+
+AC_MSG_CHECKING([whether to include debugging symbols])
+AC_ARG_ENABLE(debug,
+ AS_HELP_STRING([--disable-debug],[disable to omit debugging symbols and features]))
+
+if test x"$enable_debug" = x"no"; then
+ AC_MSG_RESULT(no)
+ ac_cv_prog_cc_g=no
+else
+ AC_MSG_RESULT([yes])
+ dnl AC_DEFINE(DEBUG, 1, [Define to turn on debugging code that may slow normal operation])
+ # leave ac_cv_prog_cc_g alone; AC_PROG_CC will try to include -g if it can
+fi
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_CXX
+AC_PROG_AWK
+AC_PROG_EGREP
+AC_PROG_INSTALL
+AC_PROG_MKDIR_P
+AC_SUBST(SHELL)
+AC_PATH_PROG([PERL], [perl])
+AC_PATH_PROG([PYTHON3], [python3])
+
+AC_DEFINE([_GNU_SOURCE], 1,
+ [Define _GNU_SOURCE so that we get all necessary prototypes])
+
+if test x"$ac_cv_prog_cc_stdc" = x"no"; then
+ AC_MSG_WARN([rsync requires an ANSI C compiler and you do not seem to have one])
+fi
+
+no_lib=''
+err_msg=''
+nl='
+'
+
+AC_ARG_ENABLE(profile,
+ AS_HELP_STRING([--enable-profile],[enable to turn on CPU profiling]))
+if test x"$enable_profile" = x"yes"; then
+ CFLAGS="$CFLAGS -pg"
+fi
+
+AC_MSG_CHECKING([if md2man can create manpages])
+if test x"$ac_cv_path_PYTHON3" = x; then
+ AC_MSG_RESULT(no - python3 not found)
+ md2man_works=no
+else
+ md2man_out=`"$srcdir/md2man" --test "$srcdir/rsync-ssl.1.md" 2>&1`
+ if test $? = 0; then
+ AC_MSG_RESULT(yes)
+ md2man_works=yes
+ else
+ AC_MSG_RESULT(no)
+ md2man_works=no
+ echo "$md2man_out"
+ fi
+fi
+
+AC_MSG_CHECKING([if we require man-page building])
+AC_ARG_ENABLE([md2man],
+ AS_HELP_STRING([--disable-md2man],[disable to omit manpage creation]))
+if test x"$enable_md2man" != x"no"; then
+ if test -f "$srcdir/rsync.1"; then
+ AC_MSG_RESULT(optional)
+ else
+ AC_MSG_RESULT(required)
+ if test x"$md2man_works" = x"no"; then
+ err_msg="$err_msg$nl- You need python3 and either the cmarkgfm OR commonmark python3 lib in order"
+ err_msg="$err_msg$nl to build manpages based on the git source (manpages are included in the"
+ err_msg="$err_msg$nl official release tar files)."
+ no_lib="$no_lib md2man"
+ fi
+ fi
+ MAKE_MAN=man
+else
+ AC_MSG_RESULT(no)
+fi
+
+# Specifically, this turns on panic_action handling.
+AC_ARG_ENABLE(maintainer-mode,
+ AS_HELP_STRING([--enable-maintainer-mode],[enable to turn on extra debug features]))
+if test x"$enable_maintainer_mode" = x"yes"; then
+ CFLAGS="$CFLAGS -DMAINTAINER_MODE"
+fi
+
+# This is needed for our included version of popt. Kind of silly, but
+# I don't want our version too far out of sync.
+CFLAGS="$CFLAGS -DHAVE_CONFIG_H"
+
+# If GCC, turn on warnings.
+if test x"$GCC" = x"yes"; then
+ CFLAGS="$CFLAGS -Wall -W"
+fi
+
+AC_ARG_WITH(openssl-conf,
+ AS_HELP_STRING([--with-openssl-conf=PATH],[set default OPENSSL_CONF path for rsync]))
+case "$with_openssl_conf" in
+ *[^-/a-zA-Z0-9.,=@+_]*) AC_MSG_ERROR([Invalid path given to --with-openssl-conf]) ;;
+ /*) CFLAGS="$CFLAGS -DSET_OPENSSL_CONF=$with_openssl_conf" ;;
+ no|'') ;;
+ yes) AC_MSG_ERROR([No path given to --with-openssl-conf]) ;;
+ *) AC_MSG_ERROR([Non absolute path given to --with-openssl-conf]) ;;
+esac
+
+AC_ARG_WITH(rrsync,
+ AS_HELP_STRING([--with-rrsync],[also install the rrsync script and its manpage]))
+if test x"$with_rrsync" != x"yes"; then
+ with_rrsync=no
+else
+ MAKE_RRSYNC='rrsync'
+ MAKE_RRSYNC_1='rrsync.1'
+ GEN_RRSYNC='rrsync.1 rrsync.1.html'
+fi
+AC_SUBST(with_rrsync)
+
+AC_ARG_WITH(included-popt,
+ AS_HELP_STRING([--with-included-popt],[use bundled popt library, not from system]))
+
+AC_ARG_WITH(included-zlib,
+ AS_HELP_STRING([--with-included-zlib],[use bundled zlib library, not from system]))
+
+AC_ARG_WITH(secluded-args,
+ AS_HELP_STRING([--with-secluded-args],[make --secluded-args option the default]))
+if test x"$with_secluded_args" = x"yes"; then
+ AC_DEFINE_UNQUOTED(RSYNC_USE_SECLUDED_ARGS, 1, [Define to 1 if --secluded-args should be the default])
+fi
+
+AC_ARG_WITH(rsync-path,
+ AS_HELP_STRING([--with-rsync-path=PATH],[set default --rsync-path to PATH (default: rsync)]),
+ [ RSYNC_PATH="$with_rsync_path" ],
+ [ RSYNC_PATH="rsync" ])
+
+AC_DEFINE_UNQUOTED(RSYNC_PATH, "$RSYNC_PATH", [location of rsync on remote machine])
+
+AC_ARG_WITH(rsyncd-conf,
+ AS_HELP_STRING([--with-rsyncd-conf=PATH],[set configuration file for rsync server to PATH (default: /etc/rsyncd.conf)]),
+ [ if test ! -z "$with_rsyncd_conf" ; then
+ case $with_rsyncd_conf in
+ yes|no)
+ RSYNCD_SYSCONF="/etc/rsyncd.conf"
+ ;;
+ /*)
+ RSYNCD_SYSCONF="$with_rsyncd_conf"
+ ;;
+ *)
+ AC_MSG_ERROR(You must specify an absolute path to --with-rsyncd-conf=PATH)
+ ;;
+ esac
+ else
+ RSYNCD_SYSCONF="/etc/rsyncd.conf"
+ fi ],
+ [ RSYNCD_SYSCONF="/etc/rsyncd.conf" ])
+
+AC_DEFINE_UNQUOTED(RSYNCD_SYSCONF, "$RSYNCD_SYSCONF", [location of configuration file for rsync server])
+
+AC_ARG_WITH(rsh,
+ AS_HELP_STRING([--with-rsh=CMD],[set remote shell command to CMD (default: ssh)]))
+
+AC_CHECK_PROG(HAVE_REMSH, remsh, 1, 0)
+if test x$HAVE_REMSH = x1; then
+ AC_DEFINE(HAVE_REMSH, 1, [Define to 1 if remote shell is remsh, not rsh])
+fi
+
+if test x"$with_rsh" != x; then
+ RSYNC_RSH="$with_rsh"
+else
+ RSYNC_RSH="ssh"
+fi
+AC_DEFINE_UNQUOTED(RSYNC_RSH, "$RSYNC_RSH", [default -e command])
+
+# Some programs on solaris are only found in /usr/xpg4/bin (or work better than others versions).
+AC_PATH_PROG(SHELL_PATH, sh, /bin/sh, [/usr/xpg4/bin$PATH_SEPARATOR$PATH])
+AC_PATH_PROG(FAKEROOT_PATH, fakeroot, /usr/bin/fakeroot, [/usr/xpg4/bin$PATH_SEPARATOR$PATH])
+
+AC_ARG_WITH(nobody-user,
+ AS_HELP_STRING([--with-nobody-user=USER],[set the default unprivileged user (default nobody)]),
+ [ NOBODY_USER="$with_nobody_user" ],
+ [ NOBODY_USER="nobody" ])
+
+AC_ARG_WITH(nobody-group,
+ AS_HELP_STRING([--with-nobody-group=GROUP],[set the default unprivileged group (default nobody or nogroup)]),
+ [ NOBODY_GROUP="$with_nobody_group" ])
+
+if test x"$with_nobody_group" = x; then
+ AC_MSG_CHECKING([the group for user "nobody"])
+ if grep '^nobody:' /etc/group >/dev/null 2>&1; then
+ NOBODY_GROUP=nobody
+ elif grep '^nogroup:' /etc/group >/dev/null 2>&1; then
+ NOBODY_GROUP=nogroup
+ else
+ NOBODY_GROUP=nobody # test for others?
+ fi
+ AC_MSG_RESULT($NOBODY_GROUP)
+fi
+
+AC_DEFINE_UNQUOTED(NOBODY_USER, "$NOBODY_USER", [unprivileged user--e.g. nobody])
+AC_DEFINE_UNQUOTED(NOBODY_GROUP, "$NOBODY_GROUP", [unprivileged group for unprivileged user])
+
+# rolling-checksum SIMD optimizations
+ROLL_SIMD=
+
+AC_MSG_CHECKING([whether to enable rolling-checksum SIMD optimizations])
+AC_ARG_ENABLE(roll-simd,
+ AS_HELP_STRING([--enable-roll-simd],[enable/disable to control rolling-checksum SIMD optimizations (requires c++)]))
+
+# Clag is crashing with -g -O2, so we'll get rid of -g for now.
+CXXFLAGS=`echo "$CXXFLAGS" | sed 's/-g //'`
+m4_define(SIMD_X86_64_TEST, [[#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <immintrin.h>
+__attribute__ ((target("default"))) int test_ssse3(int x) { return x; }
+__attribute__ ((target("default"))) int test_sse2(int x) { return x; }
+__attribute__ ((target("default"))) int test_avx2(int x) { return x; }
+__attribute__ ((target("ssse3"))) int test_ssse3(int x) { return x; }
+__attribute__ ((target("sse2"))) int test_sse2(int x) { return x; }
+__attribute__ ((target("avx2"))) int test_avx2(int x) { return x; }
+typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(1)));
+typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(1)));
+__attribute__ ((target("default"))) void more_testing(char* buf, int len) { }
+__attribute__ ((target("ssse3"))) void more_testing(char* buf, int len)
+{
+ int i;
+ for (i = 0; i < (len-32); i+=32) {
+ __m128i in8_1, in8_2;
+ in8_1 = _mm_lddqu_si128((__m128i_u*)&buf[i]);
+ in8_2 = _mm_lddqu_si128((__m128i_u*)&buf[i + 16]);
+ }
+}
+]])
+
+if test x"$enable_roll_simd" = x""; then
+ case "$host_os" in
+ *linux*) ;;
+ *) enable_roll_simd=no ;;
+ esac
+fi
+
+if test x"$enable_roll_simd" != x"no"; then
+ # For x86-64 SIMD, g++ >=5 or clang++ >=7 is required
+ if test x"$host_cpu" = x"x86_64" || test x"$host_cpu" = x"amd64"; then
+ AC_LANG(C++)
+ if test x"$host" = x"$build"; then
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([SIMD_X86_64_TEST],[[if (test_ssse3(42) != 42 || test_sse2(42) != 42 || test_avx2(42) != 42) exit(1);]])],
+ [CXX_OK=yes],[CXX_OK=no])
+ else
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([SIMD_X86_64_TEST])],[CXX_OK=yes],[CXX_OK=no])
+ fi
+ AC_LANG(C)
+ if test x"$CXX_OK" = x"yes"; then
+ # AC_MSG_RESULT() is called below.
+ ROLL_SIMD="$host_cpu"
+ elif test x"$enable_roll_simd" = x"yes"; then
+ AC_MSG_RESULT(error)
+ AC_MSG_ERROR(The rolling-checksum SIMD compilation test failed.
+Omit --enable-roll-simd to continue without it.)
+ fi
+ elif test x"$enable_roll_simd" = x"yes"; then
+ AC_MSG_RESULT(unavailable)
+ AC_MSG_ERROR(The rolling-checksum SIMD optimizations are currently x86_64|amd64 only.
+Omit --enable-roll-simd to continue without it.)
+ fi
+fi
+
+if test x"$ROLL_SIMD" != x""; then
+ AC_MSG_RESULT([yes ($ROLL_SIMD)])
+ AC_DEFINE(USE_ROLL_SIMD, 1, [Define to 1 to enable rolling-checksum SIMD optimizations])
+ ROLL_SIMD='$(ROLL_SIMD_'"$ROLL_SIMD)"
+ # We only use c++ for its target attribute dispatching, disable unneeded bulky features
+ CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-rtti"
+ # Apple often has "g++" as a symlink for clang. Try to find out the truth.
+ CXX_VERSION=`$CXX --version 2>/dev/null | head -n 2`
+ case "$CXX_VERSION" in
+ *clang*) CXXFLAGS="$CXXFLAGS -fno-slp-vectorize" ;; # avoid a performance hit
+ esac
+else
+ AC_MSG_RESULT(no)
+fi
+
+AC_SUBST(ROLL_SIMD)
+
+AC_MSG_CHECKING([if assembler accepts noexecstack])
+OLD_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -Wa,--noexecstack"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[return 0;]])],
+[ NOEXECSTACK='-Wa,--noexecstack' ; AC_MSG_RESULT(yes) ],
+[ NOEXECSTACK='' ; AC_MSG_RESULT(no) ])
+CFLAGS="$OLD_CFLAGS"
+AC_SUBST(NOEXECSTACK)
+
+# arrgh. libc in some old debian version screwed up the largefile
+# stuff, getting byte range locking wrong
+AC_CACHE_CHECK([for broken largefile support],rsync_cv_HAVE_BROKEN_LARGEFILE,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#define _FILE_OFFSET_BITS 64
+$ac_includes_default
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#elif defined HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+int main(void)
+{
+ struct flock lock;
+ int status;
+ char tpl[32] = "/tmp/locktest.XXXXXX";
+ int fd = mkstemp(tpl);
+ if (fd < 0) {
+ strcpy(tpl, "conftest.dat");
+ fd = open(tpl, O_CREAT|O_RDWR, 0600);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+ lock.l_pid = 0;
+ fcntl(fd,F_SETLK,&lock);
+ if (fork() == 0) {
+ lock.l_start = 1;
+ _exit(fcntl(fd,F_SETLK,&lock) == 0);
+ }
+ wait(&status);
+ unlink(tpl);
+ return WEXITSTATUS(status);
+}
+]])],[rsync_cv_HAVE_BROKEN_LARGEFILE=yes],[rsync_cv_HAVE_BROKEN_LARGEFILE=no],[rsync_cv_HAVE_BROKEN_LARGEFILE=cross])])
+if test x"$rsync_cv_HAVE_BROKEN_LARGEFILE" != x"yes"; then
+ AC_SYS_LARGEFILE
+fi
+
+AC_MSG_CHECKING([whether to enable ipv6])
+AC_ARG_ENABLE(ipv6,
+AS_HELP_STRING([--disable-ipv6],[disable to omit ipv6 support]),
+[ case "$enableval" in
+ no)
+ AC_MSG_RESULT(no)
+ ;;
+ *) AC_MSG_RESULT(yes)
+ AC_DEFINE(INET6, 1, [true if you have IPv6])
+ ;;
+ esac ],
+
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[ /* AF_INET6 availability check */
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+main()
+{
+ if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
+ exit(1);
+ else
+ exit(0);
+}
+]])],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(INET6, 1, true if you have IPv6)],
+ [AC_MSG_RESULT(no)],
+ [AC_MSG_RESULT(no)]
+))
+
+dnl Do you want to disable use of locale functions
+AC_ARG_ENABLE([locale],
+ AS_HELP_STRING([--disable-locale],[disable to omit locale features]))
+AH_TEMPLATE([CONFIG_LOCALE],
+[Undefine if you do not want locale features. By default this is defined.])
+if test x"$enable_locale" != x"no"; then
+ AC_DEFINE(CONFIG_LOCALE)
+fi
+
+AC_MSG_CHECKING([whether to call shutdown on all sockets])
+case $host_os in
+ *cygwin* ) AC_MSG_RESULT(yes)
+ AC_DEFINE(SHUTDOWN_ALL_SOCKETS, 1,
+ [Define to 1 if sockets need to be shutdown])
+ ;;
+ * ) AC_MSG_RESULT(no);;
+esac
+
+AC_MSG_CHECKING([whether to enable use of openssl crypto library])
+AC_ARG_ENABLE([openssl],
+ AS_HELP_STRING([--disable-openssl],[disable to omit openssl crypto library]))
+AH_TEMPLATE([USE_OPENSSL],
+[Undefine if you do not want to use openssl crypto library. By default this is defined.])
+if test x"$enable_openssl" != x"no"; then
+ if test x"$ac_cv_header_openssl_md4_h" = x"yes" && test x"$ac_cv_header_openssl_md5_h" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_SEARCH_LIBS(MD5_Init, crypto,
+ [AC_DEFINE(USE_OPENSSL)
+ enable_openssl=yes],
+ [err_msg="$err_msg$nl- Failed to find MD5_Init function in openssl crypto lib.";
+ no_lib="$no_lib openssl"])
+ else
+ AC_MSG_RESULT(no)
+ err_msg="$err_msg$nl- Failed to find openssl/md4.h and openssl/md5.h for openssl crypto lib support."
+ no_lib="$no_lib openssl"
+ fi
+ if test x"$enable_md5_asm" != x"yes"; then
+ enable_md5_asm=no
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+
+MD5_ASM=
+
+AC_MSG_CHECKING([whether to enable MD5 ASM optimizations])
+AC_ARG_ENABLE(md5-asm,
+ AS_HELP_STRING([--enable-md5-asm],[enable/disable to control MD5 ASM optimizations]))
+
+if test x"$enable_md5_asm" = x""; then
+ case "$host_os" in
+ *linux*) ;;
+ *) enable_md5_asm=no ;;
+ esac
+fi
+
+if test x"$enable_md5_asm" != x"no"; then
+ if test x"$host_cpu" = x"x86_64" || test x"$host_cpu" = x"amd64"; then
+ MD5_ASM="$host_cpu"
+ elif test x"$enable_md5_asm" = x"yes"; then
+ AC_MSG_RESULT(unavailable)
+ AC_MSG_ERROR(The ASM optimizations are currently x86_64|amd64 only.
+Omit --enable-md5-asm to continue without it.)
+ fi
+fi
+
+if test x"$MD5_ASM" != x""; then
+ AC_MSG_RESULT([yes ($MD5_ASM)])
+ AC_DEFINE(USE_MD5_ASM, 1, [Define to 1 to enable MD5 ASM optimizations])
+ MD5_ASM='$(MD5_ASM_'"$MD5_ASM)"
+else
+ AC_MSG_RESULT(no)
+fi
+
+AC_SUBST(MD5_ASM)
+
+ROLL_ASM=
+
+AC_MSG_CHECKING([whether to enable rolling-checksum ASM optimizations])
+AC_ARG_ENABLE(roll-asm,
+ AS_HELP_STRING([--enable-roll-asm],[enable/disable to control rolling-checksum ASM optimizations (requires --enable-roll-simd)]))
+
+if test x"$ROLL_SIMD" = x""; then
+ enable_roll_asm=no
+fi
+
+if test x"$enable_roll_asm" = x"yes"; then
+ ROLL_ASM="$host_cpu"
+ AC_MSG_RESULT([yes ($ROLL_ASM)])
+ AC_DEFINE(USE_ROLL_ASM, 1, [Define to 1 to enable rolling-checksum ASM optimizations (requires --enable-roll-simd)])
+ ROLL_ASM='$(ROLL_ASM_'"$ROLL_ASM)"
+else
+ AC_MSG_RESULT(no)
+fi
+
+AC_SUBST(ROLL_ASM)
+
+AC_MSG_CHECKING([whether to enable xxhash checksum support])
+AC_ARG_ENABLE([xxhash],
+ AS_HELP_STRING([--disable-xxhash],[disable to omit xxhash checksums]))
+AH_TEMPLATE([SUPPORT_XXHASH],
+[Undefine if you do not want xxhash checksums. By default this is defined.])
+if test x"$enable_xxhash" != x"no"; then
+ if test x"$ac_cv_header_xxhash_h" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_SEARCH_LIBS(XXH64_createState, xxhash,
+ [AC_DEFINE(SUPPORT_XXHASH)],
+ [err_msg="$err_msg$nl- Failed to find XXH64_createState function in xxhash lib.";
+ no_lib="$no_lib xxhash"])
+ else
+ AC_MSG_RESULT(no)
+ err_msg="$err_msg$nl- Failed to find xxhash.h for xxhash checksum support.";
+ no_lib="$no_lib xxhash"
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+
+AC_MSG_CHECKING([whether to enable zstd compression])
+AC_ARG_ENABLE([zstd],
+ AS_HELP_STRING([--disable-zstd], [disable to omit zstd compression]))
+AH_TEMPLATE([SUPPORT_ZSTD],
+[Undefine if you do not want zstd compression. By default this is defined.])
+if test x"$enable_zstd" != x"no"; then
+ if test x"$ac_cv_header_zstd_h" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_SEARCH_LIBS(ZSTD_minCLevel, zstd,
+ [AC_DEFINE(SUPPORT_ZSTD)],
+ [err_msg="$err_msg$nl- Failed to find ZSTD_minCLevel function in zstd lib.";
+ no_lib="$no_lib zstd"])
+ else
+ AC_MSG_RESULT(no)
+ err_msg="$err_msg$nl- Failed to find zstd.h for zstd compression support.";
+ no_lib="$no_lib zstd"
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+
+AC_MSG_CHECKING([whether to enable LZ4 compression])
+AC_ARG_ENABLE([lz4],
+ AS_HELP_STRING([--disable-lz4], [disable to omit LZ4 compression]))
+AH_TEMPLATE([SUPPORT_LZ4],
+[Undefine if you do not want LZ4 compression. By default this is defined.])
+if test x"$enable_lz4" != x"no"; then
+ if test x"$ac_cv_header_lz4_h" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_SEARCH_LIBS(LZ4_compress_default, lz4,
+ [AC_DEFINE(SUPPORT_LZ4)],
+ [err_msg="$err_msg$nl- Failed to find LZ4_compress_default function in lz4 lib.";
+ no_lib="$no_lib lz4"])
+ else
+ AC_MSG_RESULT(no)
+ err_msg="$err_msg$nl- Failed to find lz4.h for lz4 compression support."
+ no_lib="$no_lib lz4"
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+
+if test x"$no_lib" != x; then
+ echo ""
+ echo "Configure found the following issues:"
+ echo "$err_msg"
+ echo ""
+ echo "See the INSTALL file for hints on how to install the missing libraries and/or"
+ echo "how to generate (or fetch) manpages:"
+ echo " https://github.com/WayneD/rsync/blob/master/INSTALL.md"
+ echo ""
+ echo "To disable one or more features, the relevant configure options are:"
+ for lib in $no_lib; do
+ echo " --disable-$lib"
+ done
+ echo ""
+ AC_MSG_ERROR(Aborting configure run)
+fi
+
+AC_CACHE_CHECK([if makedev takes 3 args],rsync_cv_MAKEDEV_TAKES_3_ARGS,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
+# define makedev mkdev
+# endif
+#elif defined MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif
+
+int main(void)
+{
+ dev_t dev = makedev(0, 5, 7);
+ if (major(dev) != 5 || minor(dev) != 7)
+ return 1;
+ return 0;
+}
+]])],[rsync_cv_MAKEDEV_TAKES_3_ARGS=yes],[rsync_cv_MAKEDEV_TAKES_3_ARGS=no],[rsync_cv_MAKEDEV_TAKES_3_ARGS=no])])
+if test x"$rsync_cv_MAKEDEV_TAKES_3_ARGS" = x"yes"; then
+ AC_DEFINE(MAKEDEV_TAKES_3_ARGS, 1, [Define to 1 if makedev() takes 3 args])
+fi
+
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_CHECK_SIZEOF(long long)
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int16_t)
+AC_CHECK_SIZEOF(uint16_t)
+AC_CHECK_SIZEOF(int32_t)
+AC_CHECK_SIZEOF(uint32_t)
+AC_CHECK_SIZEOF(int64_t)
+AC_CHECK_SIZEOF(off_t)
+AC_CHECK_SIZEOF(off64_t)
+AC_CHECK_SIZEOF(time_t)
+AC_CHECK_SIZEOF(char*)
+
+AC_C_INLINE
+
+AC_TYPE_LONG_DOUBLE_WIDER
+ac_cv_c_long_double=$ac_cv_type_long_double_wider
+if test $ac_cv_c_long_double = yes; then
+ AC_DEFINE([HAVE_LONG_DOUBLE],[1],[Define to 1 if the type `long double' works and has more range or precision than `double'.])
+fi
+
+AC_TYPE_UID_T
+AC_CHECK_TYPES([mode_t,off_t,size_t,pid_t,id_t])
+if test "$cross_compiling" = no; then
+ AC_TYPE_GETGROUPS
+else
+ AC_DEFINE([GETGROUPS_T],[gid_t],[Define to the type of elements in the array set by `getgroups'. Usually this is either `int' or `gid_t'.])
+fi
+AC_CHECK_MEMBERS([struct stat.st_rdev,
+ struct stat.st_mtimensec,
+ struct stat.st_mtimespec.tv_nsec,
+ struct stat.st_mtim.tv_nsec],,,[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif])
+
+TYPE_SOCKLEN_T
+
+AC_CACHE_CHECK([for errno in errno.h],rsync_cv_errno, [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <errno.h>]], [[int i = errno]])],[rsync_cv_errno=yes],[rsync_cv_have_errno_decl=no])])
+if test x"$rsync_cv_errno" = x"yes"; then
+ AC_DEFINE(HAVE_ERRNO_DECL, 1, [Define to 1 if errno is declared in errno.h])
+fi
+
+# The following test taken from the cvs sources
+# If we can't find connect, try looking in -lsocket, -lnsl, and -linet.
+# These need checks to be before checks for any other functions that
+# might be in the same libraries.
+# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has
+# libsocket.so which has a bad implementation of gethostbyname (it
+# only looks in /etc/hosts), so we only look for -lsocket if we need
+# it.
+AC_CHECK_FUNCS(connect)
+if test x"$ac_cv_func_connect" = x"no"; then
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) AC_CHECK_LIB(nsl_s, printf) ;;
+ esac
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) AC_CHECK_LIB(nsl, printf) ;;
+ esac
+ case "$LIBS" in
+ *-lsocket*) ;;
+ *) AC_CHECK_LIB(socket, connect) ;;
+ esac
+ case "$LIBS" in
+ *-linet*) ;;
+ *) AC_CHECK_LIB(inet, connect) ;;
+ esac
+ dnl We can't just call AC_CHECK_FUNCS(connect) here, because the value
+ dnl has been cached.
+ if test x"$ac_cv_lib_socket_connect" = x"yes" ||
+ test x"$ac_cv_lib_inet_connect" = x"yes"; then
+ # ac_cv_func_connect=yes
+ # don't! it would cause AC_CHECK_FUNC to succeed next time configure is run
+ AC_DEFINE(HAVE_CONNECT, 1, [Define to 1 if you have the "connect" function])
+ fi
+fi
+
+AC_SEARCH_LIBS(inet_ntop, resolv)
+
+# For OS X, Solaris, HP-UX, etc.: figure out if -liconv is needed. We'll
+# accept either iconv_open or libiconv_open, since some include files map
+# the former to the latter.
+AC_SEARCH_LIBS(iconv_open, iconv)
+AC_SEARCH_LIBS(libiconv_open, iconv)
+
+AC_MSG_CHECKING([for iconv declaration])
+AC_CACHE_VAL(am_cv_proto_iconv, [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+]], [[]])],[am_cv_proto_iconv_arg1=""],[am_cv_proto_iconv_arg1="const"])
+ am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
+ am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed 's/( /(/'`
+AC_MSG_RESULT([$]{ac_t:-
+ }[$]am_cv_proto_iconv)
+AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
+ [Define as const if the declaration of iconv() needs const.])
+
+dnl AC_MSG_NOTICE([Looking in libraries: $LIBS])
+
+AC_REPLACE_FUNCS([inet_ntop inet_pton])
+
+AC_HAVE_TYPE([struct addrinfo], [#include <netdb.h>])
+AC_HAVE_TYPE([struct sockaddr_storage], [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif])
+
+# Irix 6.5 has getaddrinfo but not the corresponding defines, so use
+# builtin getaddrinfo if one of the defines don't exist
+AC_CACHE_CHECK([whether defines needed by getaddrinfo exist],
+ rsync_cv_HAVE_GETADDR_DEFINES,[
+ AC_EGREP_CPP(yes, [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef AI_PASSIVE
+yes
+#endif],
+ rsync_cv_HAVE_GETADDR_DEFINES=yes,
+ rsync_cv_HAVE_GETADDR_DEFINES=no)])
+AS_IF([test x"$rsync_cv_HAVE_GETADDR_DEFINES" = x"yes" && test x"$ac_cv_type_struct_addrinfo" = x"yes"],[
+ # Tru64 UNIX has getaddrinfo() but has it renamed in libc as
+ # something else so we must include <netdb.h> to get the
+ # redefinition.
+ AC_CHECK_FUNCS(getaddrinfo, ,
+ [AC_MSG_CHECKING([for getaddrinfo by including <netdb.h>])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netdb.h>]], [[getaddrinfo(NULL, NULL, NULL, NULL);]])],[AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_GETADDRINFO, 1,
+ [Define to 1 if you have the "getaddrinfo" function and required types.])],[AC_MSG_RESULT([no])
+ AC_LIBOBJ([getaddrinfo])])])
+ ],[AC_LIBOBJ([getaddrinfo])])
+
+AC_CHECK_MEMBER([struct sockaddr.sa_len],
+ [ AC_DEFINE(HAVE_SOCKADDR_LEN, 1, [Do we have sockaddr.sa_len?]) ],
+ [],
+ [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+])
+
+AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
+ [ AC_DEFINE(HAVE_SOCKADDR_IN_LEN, 1, [Do we have sockaddr_in.sin_len?]) ],
+ [],
+ [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+])
+
+AC_CHECK_MEMBER([struct sockaddr_un.sun_len],
+ [ AC_DEFINE(HAVE_SOCKADDR_UN_LEN, 1, [Do we have sockaddr_un.sun_len?]) ],
+ [],
+ [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+])
+
+AC_CHECK_MEMBER([struct sockaddr_in6.sin6_scope_id],
+ [ AC_DEFINE(HAVE_SOCKADDR_IN6_SCOPE_ID, 1, [Do we have sockaddr_in6.sin6_scope_id?]) ],
+ [],
+ [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+])
+
+AC_HAVE_TYPE([struct stat64], [#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+])
+
+# if we can't find strcasecmp, look in -lresolv (for Unixware at least)
+#
+AC_CHECK_FUNCS(strcasecmp)
+if test x"$ac_cv_func_strcasecmp" = x"no"; then
+ AC_CHECK_LIB(resolv, strcasecmp)
+fi
+
+AC_CHECK_FUNCS(aclsort)
+if test x"$ac_cv_func_aclsort" = x"no"; then
+ AC_CHECK_LIB(sec, aclsort)
+fi
+
+dnl At the moment we don't test for a broken memcmp(), because all we
+dnl need to do is test for equality, not comparison, and it seems that
+dnl every platform has a memcmp that can do at least that.
+dnl AC_FUNC_MEMCMP
+
+AC_FUNC_UTIME_NULL
+AC_FUNC_ALLOCA
+AC_CHECK_FUNCS(waitpid wait4 getcwd chown chmod lchmod mknod mkfifo \
+ fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
+ chflags getattrlist mktime innetgr linkat \
+ memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
+ strlcat strlcpy strtol mallinfo mallinfo2 getgroups setgroups geteuid getegid \
+ setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
+ seteuid strerror putenv iconv_open locale_charset nl_langinfo getxattr \
+ extattr_get_link sigaction sigprocmask setattrlist getgrouplist \
+ initgroups utimensat posix_fallocate attropen setvbuf nanosleep usleep \
+ setenv unsetenv)
+
+dnl cygwin iconv.h defines iconv_open as libiconv_open
+if test x"$ac_cv_func_iconv_open" != x"yes"; then
+ AC_CHECK_FUNC(libiconv_open, [ac_cv_func_iconv_open=yes; AC_DEFINE(HAVE_ICONV_OPEN, 1)])
+fi
+
+dnl Preallocation stuff (also fallocate, posix_fallocate function tests above):
+
+AC_CACHE_CHECK([for useable fallocate],rsync_cv_have_fallocate,[
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif]], [[fallocate(0, 0, 0, 0);]])],[rsync_cv_have_fallocate=yes],[rsync_cv_have_fallocate=no])])
+if test x"$rsync_cv_have_fallocate" = x"yes"; then
+ AC_DEFINE(HAVE_FALLOCATE, 1, [Define to 1 if you have the fallocate function and it compiles and links without error])
+fi
+
+AC_MSG_CHECKING([for FALLOC_FL_PUNCH_HOLE])
+AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+ #define _GNU_SOURCE 1
+ #include <linux/falloc.h>
+ #ifndef FALLOC_FL_PUNCH_HOLE
+ #error FALLOC_FL_PUNCH_HOLE is missing
+ #endif
+ ]])], [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_FALLOC_FL_PUNCH_HOLE], [1], [Define if FALLOC_FL_PUNCH_HOLE is available.])
+ ], [
+ AC_MSG_RESULT([no])
+ ]
+)
+
+AC_MSG_CHECKING([for FALLOC_FL_ZERO_RANGE])
+AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+ #define _GNU_SOURCE 1
+ #include <linux/falloc.h>
+ #ifndef FALLOC_FL_ZERO_RANGE
+ #error FALLOC_FL_ZERO_RANGE is missing
+ #endif
+ ]])], [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_FALLOC_FL_ZERO_RANGE], [1], [Define if FALLOC_FL_ZERO_RANGE is available.])
+ ], [
+ AC_MSG_RESULT([no])
+ ]
+)
+
+AC_CACHE_CHECK([for SYS_fallocate],rsync_cv_have_sys_fallocate,[
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/syscall.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif]], [[syscall(SYS_fallocate, 0, 0, (loff_t)0, (loff_t)0);]])],[rsync_cv_have_sys_fallocate=yes],[rsync_cv_have_sys_fallocate=no])])
+if test x"$rsync_cv_have_sys_fallocate" = x"yes"; then
+ AC_DEFINE(HAVE_SYS_FALLOCATE, 1, [Define to 1 if you have the SYS_fallocate syscall number])
+fi
+
+if test x"$ac_cv_func_posix_fallocate" = x"yes"; then
+ AC_MSG_CHECKING([whether posix_fallocate is efficient])
+ case $host_os in
+ *cygwin*)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_EFFICIENT_POSIX_FALLOCATE, 1,
+ [Define if posix_fallocate is efficient (Cygwin)])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac
+fi
+
+dnl End of preallocation stuff
+
+AC_CHECK_FUNCS(getpgrp tcgetpgrp)
+if test $ac_cv_func_getpgrp = yes; then
+ AC_FUNC_GETPGRP
+fi
+
+AC_ARG_ENABLE(iconv-open,
+ AS_HELP_STRING([--disable-iconv-open],[disable to avoid all use of iconv_open()]),
+ [], [enable_iconv_open=$ac_cv_func_iconv_open])
+
+if test x"$enable_iconv_open" != x"no"; then
+ AC_DEFINE(USE_ICONV_OPEN, 1, [Define to 1 if you want rsync to make use of iconv_open()])
+fi
+
+AC_ARG_ENABLE(iconv,
+ AS_HELP_STRING([--disable-iconv],[disable to omit the --iconv option]),
+ [], [enable_iconv=$enable_iconv_open])
+AH_TEMPLATE([ICONV_OPTION],
+[Define if you want the --iconv option. Specifying a value will set the
+default iconv setting (a NULL means no --iconv processing by default).])
+if test x"$enable_iconv" != x"no"; then
+ if test x"$enable_iconv" = x"yes"; then
+ AC_DEFINE(ICONV_OPTION, NULL)
+ else
+ AC_DEFINE_UNQUOTED(ICONV_OPTION, "$enable_iconv")
+ fi
+ AC_DEFINE(UTF8_CHARSET, "UTF-8", [String to pass to iconv() for the UTF-8 charset.])
+fi
+
+AC_CACHE_CHECK([whether chown() modifies symlinks],rsync_cv_chown_modifies_symlink,[
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+int main(void) {
+ char const *dangling_symlink = "conftest.dangle";
+ unlink(dangling_symlink);
+ if (symlink("conftest.no-such", dangling_symlink) < 0) abort();
+ if (chown(dangling_symlink, getuid(), getgid()) < 0 && errno == ENOENT) return 1;
+ return 0;
+ }]])],[rsync_cv_chown_modifies_symlink=yes],[rsync_cv_chown_modifies_symlink=no],[rsync_cv_chown_modifies_symlink=no])])
+if test $rsync_cv_chown_modifies_symlink = yes; then
+ AC_DEFINE(CHOWN_MODIFIES_SYMLINK, 1, [Define to 1 if chown modifies symlinks.])
+fi
+
+AC_CACHE_CHECK([whether link() can hard-link symlinks],rsync_cv_can_hardlink_symlink,[
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#elif defined HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#define FILENAME "conftest.dangle"
+int main(void) {
+ unlink(FILENAME);
+ if (symlink("conftest.no-such", FILENAME) < 0) abort();
+ unlink(FILENAME "2");
+#ifdef HAVE_LINKAT
+ if (linkat(AT_FDCWD, FILENAME, AT_FDCWD, FILENAME "2", 0) < 0) return 1;
+#else
+ if (link(FILENAME, FILENAME "2") < 0) return 1;
+#endif
+ return 0;
+ }]])],[rsync_cv_can_hardlink_symlink=yes],[rsync_cv_can_hardlink_symlink=no],[rsync_cv_can_hardlink_symlink=no])])
+if test $rsync_cv_can_hardlink_symlink = yes; then
+ AC_DEFINE(CAN_HARDLINK_SYMLINK, 1, [Define to 1 if link() can hard-link symlinks.])
+fi
+
+AC_CACHE_CHECK([whether link() can hard-link special files],rsync_cv_can_hardlink_special,[
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#define FILENAME "conftest.fifi"
+int main(void) {
+ unlink(FILENAME);
+ if (mkfifo(FILENAME, 0777) < 0) abort();
+ unlink(FILENAME "2");
+ if (link(FILENAME, FILENAME "2") < 0) return 1;
+ return 0;
+ }]])],[rsync_cv_can_hardlink_special=yes],[rsync_cv_can_hardlink_special=no],[rsync_cv_can_hardlink_special=no])])
+if test $rsync_cv_can_hardlink_special = yes; then
+ AC_DEFINE(CAN_HARDLINK_SPECIAL, 1, [Define to 1 if link() can hard-link special files.])
+fi
+
+AC_CACHE_CHECK([for working socketpair],rsync_cv_HAVE_SOCKETPAIR,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+int main(void) {
+ int fd[2];
+ return (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != -1) ? 0 : 1;
+}]])],[rsync_cv_HAVE_SOCKETPAIR=yes],[rsync_cv_HAVE_SOCKETPAIR=no],[rsync_cv_HAVE_SOCKETPAIR=cross])])
+if test x"$rsync_cv_HAVE_SOCKETPAIR" = x"yes"; then
+ AC_DEFINE(HAVE_SOCKETPAIR, 1, [Define to 1 if you have the "socketpair" function])
+fi
+
+AC_REPLACE_FUNCS([getpass])
+
+if test x"$with_included_popt" != x"yes"; then
+ AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes])
+fi
+if test x"$ac_cv_header_popt_popt_h" = x"yes"; then
+ # If the system has /usr/include/popt/popt.h, we enable the
+ # included popt because an attempt to "#include <popt/popt.h>"
+ # would use our included header file anyway (due to -I.), and
+ # might conflict with the system popt.
+ with_included_popt=yes
+elif test x"$ac_cv_header_popt_h" != x"yes"; then
+ with_included_popt=yes
+fi
+
+AC_MSG_CHECKING([whether to use included libpopt])
+if test x"$with_included_popt" = x"yes"; then
+ AC_MSG_RESULT($srcdir/popt)
+ BUILD_POPT='$(popt_OBJS)'
+ CFLAGS="-I$srcdir/popt $CFLAGS"
+ if test x"$ALLOCA" != x
+ then
+ # this can be removed when/if we add an included alloca.c;
+ # see autoconf documentation on AC_FUNC_ALLOCA
+ AC_MSG_WARN([included libpopt will use malloc, not alloca (which wastes a small amount of memory)])
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+
+# We default to using our zlib unless --with-included-zlib=no is given.
+if test x"$with_included_zlib" != x"no"; then
+ with_included_zlib=yes
+elif test x"$ac_cv_header_zlib_h" != x"yes"; then
+ with_included_zlib=yes
+fi
+if test x"$with_included_zlib" != x"yes"; then
+ AC_CHECK_LIB(z, deflateParams, , [with_included_zlib=yes])
+fi
+
+AC_MSG_CHECKING([whether to use included zlib])
+if test x"$with_included_zlib" = x"yes"; then
+ AC_MSG_RESULT($srcdir/zlib)
+ BUILD_ZLIB='$(zlib_OBJS)'
+ CFLAGS="-I$srcdir/zlib $CFLAGS"
+else
+ AC_DEFINE(EXTERNAL_ZLIB, 1, [Define to 1 if using external zlib])
+ AC_MSG_RESULT(no)
+fi
+
+AC_CACHE_CHECK([for unsigned char],rsync_cv_SIGNED_CHAR_OK,[
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[signed char *s = (signed char *)""]])],[rsync_cv_SIGNED_CHAR_OK=yes],[rsync_cv_SIGNED_CHAR_OK=no])])
+if test x"$rsync_cv_SIGNED_CHAR_OK" = x"yes"; then
+ AC_DEFINE(SIGNED_CHAR_OK, 1, [Define to 1 if "signed char" is a valid type])
+fi
+
+AC_CACHE_CHECK([for broken readdir],rsync_cv_HAVE_BROKEN_READDIR,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <dirent.h>
+int main(void) { struct dirent *di; DIR *d = opendir("."); di = readdir(d);
+if (di && di->d_name[-2] == '.' && di->d_name[-1] == 0 &&
+di->d_name[0] == 0) return 0; return 1;} ]])],[rsync_cv_HAVE_BROKEN_READDIR=yes],[rsync_cv_HAVE_BROKEN_READDIR=no],[rsync_cv_HAVE_BROKEN_READDIR=cross])])
+if test x"$rsync_cv_HAVE_BROKEN_READDIR" = x"yes"; then
+ AC_DEFINE(HAVE_BROKEN_READDIR, 1, [Define to 1 if readdir() is broken])
+fi
+
+AC_CACHE_CHECK([for utimbuf],rsync_cv_HAVE_STRUCT_UTIMBUF,[
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <utime.h>]], [[struct utimbuf tbuf; tbuf.actime = 0; tbuf.modtime = 1; return utime("foo.c",&tbuf);]])],[rsync_cv_HAVE_STRUCT_UTIMBUF=yes],[rsync_cv_HAVE_STRUCT_UTIMBUF=no])])
+if test x"$rsync_cv_HAVE_STRUCT_UTIMBUF" = x"yes"; then
+ AC_DEFINE(HAVE_STRUCT_UTIMBUF, 1, [Define to 1 if you have the "struct utimbuf" type])
+fi
+
+AC_CACHE_CHECK([if gettimeofday takes tz argument],rsync_cv_HAVE_GETTIMEOFDAY_TZ,[
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif]], [[struct timeval tv; return gettimeofday(&tv, NULL);]])],[rsync_cv_HAVE_GETTIMEOFDAY_TZ=yes],[rsync_cv_HAVE_GETTIMEOFDAY_TZ=no])])
+if test x"$rsync_cv_HAVE_GETTIMEOFDAY_TZ" != x"no"; then
+ AC_DEFINE(HAVE_GETTIMEOFDAY_TZ, 1, [Define to 1 if gettimeofday() takes a time-zone arg])
+fi
+
+AC_CACHE_CHECK([for C99 vsnprintf],rsync_cv_HAVE_C99_VSNPRINTF,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+void foo(const char *format, ...) {
+ va_list ap;
+ int len;
+ static char buf[] = "12345678901234567890";
+
+ va_start(ap, format);
+ len = vsnprintf(0, 0, format, ap);
+ va_end(ap);
+ if (len != 5) exit(1);
+
+ if (snprintf(buf, 3, "hello") != 5 || strcmp(buf, "he") != 0) exit(1);
+}
+int main(void) { foo("hello"); return 0; }
+]])],[rsync_cv_HAVE_C99_VSNPRINTF=yes],[rsync_cv_HAVE_C99_VSNPRINTF=no],[rsync_cv_HAVE_C99_VSNPRINTF=cross])])
+if test x"$rsync_cv_HAVE_C99_VSNPRINTF" = x"yes"; then
+ AC_DEFINE(HAVE_C99_VSNPRINTF, 1, [Define to 1 if vsprintf has a C99-compatible return value])
+fi
+
+
+AC_CACHE_CHECK([for secure mkstemp],rsync_cv_HAVE_SECURE_MKSTEMP,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdlib.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+int main(void) {
+ struct stat st;
+ char tpl[20]="/tmp/test.XXXXXX";
+ int fd = mkstemp(tpl);
+ if (fd == -1) return 1;
+ unlink(tpl);
+ if (fstat(fd, &st) != 0) return 1;
+ if ((st.st_mode & 0777) != 0600) return 1;
+ return 0;
+}]])],[rsync_cv_HAVE_SECURE_MKSTEMP=yes],[rsync_cv_HAVE_SECURE_MKSTEMP=no],[rsync_cv_HAVE_SECURE_MKSTEMP=cross])])
+if test x"$rsync_cv_HAVE_SECURE_MKSTEMP" = x"yes"; then
+ case $host_os in
+ hpux*)
+ dnl HP-UX has a broken mkstemp() implementation they refuse to fix,
+ dnl so we noisily skip using it. See HP change request JAGaf34426
+ dnl for details. (sbonds)
+ AC_MSG_WARN(Skipping broken HP-UX mkstemp() -- using mktemp() instead)
+ ;;
+ *)
+ AC_DEFINE(HAVE_SECURE_MKSTEMP, 1, [Define to 1 if mkstemp() is available and works right])
+ ;;
+ esac
+fi
+
+
+AC_CACHE_CHECK([if mknod creates FIFOs],rsync_cv_MKNOD_CREATES_FIFOS,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+int main(void) { int rc, ec; char *fn = "fifo-test";
+unlink(fn); rc = mknod(fn,S_IFIFO,0600); ec = errno; unlink(fn);
+if (rc) {printf("(%d %d) ",rc,ec); return ec;}
+return 0;}]])],[rsync_cv_MKNOD_CREATES_FIFOS=yes],[rsync_cv_MKNOD_CREATES_FIFOS=no],[rsync_cv_MKNOD_CREATES_FIFOS=cross])])
+if test x"$rsync_cv_MKNOD_CREATES_FIFOS" = x"yes"; then
+ AC_DEFINE(MKNOD_CREATES_FIFOS, 1, [Define to 1 if mknod() can create FIFOs.])
+fi
+
+AC_CACHE_CHECK([if mknod creates sockets],rsync_cv_MKNOD_CREATES_SOCKETS,[
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+int main(void) { int rc, ec; char *fn = "sock-test";
+unlink(fn); rc = mknod(fn,S_IFSOCK,0600); ec = errno; unlink(fn);
+if (rc) {printf("(%d %d) ",rc,ec); return ec;}
+return 0;}]])],[rsync_cv_MKNOD_CREATES_SOCKETS=yes],[rsync_cv_MKNOD_CREATES_SOCKETS=no],[rsync_cv_MKNOD_CREATES_SOCKETS=cross])])
+if test x"$rsync_cv_MKNOD_CREATES_SOCKETS" = x"yes"; then
+ AC_DEFINE(MKNOD_CREATES_SOCKETS, 1, [Define to 1 if mknod() can create sockets.])
+fi
+
+#
+# The following test was mostly taken from the tcl/tk plus patches
+#
+AC_CACHE_CHECK([whether -c -o works],rsync_cv_DASHC_WORKS_WITH_DASHO,[
+rm -rf conftest*
+cat > conftest.$ac_ext <<EOF
+int main(void) { return 0; }
+EOF
+${CC-cc} -c -o conftest..o conftest.$ac_ext
+if test -f conftest..o; then
+ rsync_cv_DASHC_WORKS_WITH_DASHO=yes
+else
+ rsync_cv_DASHC_WORKS_WITH_DASHO=no
+fi
+rm -rf conftest*
+])
+if test x"$rsync_cv_DASHC_WORKS_WITH_DASHO" = x"yes"; then
+ OBJ_SAVE="#"
+ OBJ_RESTORE="#"
+ CC_SHOBJ_FLAG='-o $@'
+else
+ OBJ_SAVE=' @b=`basename $@ .o`;rm -f $$b.o.sav;if test -f $$b.o; then mv $$b.o $$b.o.sav;fi;'
+ OBJ_RESTORE=' @b=`basename $@ .o`;if test "$$b.o" != "$@"; then mv $$b.o $@; if test -f $$b.o.sav; then mv $$b.o.sav $$b.o; fi; fi'
+ CC_SHOBJ_FLAG=""
+fi
+
+AC_SUBST(OBJ_SAVE)
+AC_SUBST(OBJ_RESTORE)
+AC_SUBST(CC_SHOBJ_FLAG)
+AC_SUBST(BUILD_POPT)
+AC_SUBST(BUILD_ZLIB)
+AC_SUBST(MAKE_RRSYNC)
+AC_SUBST(MAKE_RRSYNC_1)
+AC_SUBST(GEN_RRSYNC)
+AC_SUBST(MAKE_MAN)
+
+AC_CHECK_FUNCS(_acl __acl _facl __facl)
+#################################################
+# check for ACL support
+
+AC_MSG_CHECKING([whether to support ACLs])
+AC_ARG_ENABLE(acl-support,
+ AS_HELP_STRING([--disable-acl-support],[disable to omit ACL support]))
+
+if test x"$enable_acl_support" = x"no"; then
+ AC_MSG_RESULT(no)
+else
+ case "$host_os" in
+ *sysv5*)
+ AC_MSG_RESULT(Using UnixWare ACLs)
+ AC_DEFINE(HAVE_UNIXWARE_ACLS, 1, [true if you have UnixWare ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1, [Define to 1 to add support for ACLs])
+ ;;
+ solaris*)
+ AC_MSG_RESULT(Using solaris ACLs)
+ AC_DEFINE(HAVE_SOLARIS_ACLS, 1, [true if you have solaris ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1)
+ ;;
+ *irix*)
+ AC_MSG_RESULT(Using IRIX ACLs)
+ AC_DEFINE(HAVE_IRIX_ACLS, 1, [true if you have IRIX ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1)
+ ;;
+ *aix*)
+ AC_MSG_RESULT(Using AIX ACLs)
+ AC_DEFINE(HAVE_AIX_ACLS, 1, [true if you have AIX ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1)
+ ;;
+ *osf*)
+ AC_MSG_RESULT(Using Tru64 ACLs)
+ AC_DEFINE(HAVE_TRU64_ACLS, 1, [true if you have Tru64 ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1)
+ LIBS="$LIBS -lpacl"
+ ;;
+ darwin*)
+ AC_MSG_RESULT(Using OS X ACLs)
+ AC_DEFINE(HAVE_OSX_ACLS, 1, [true if you have Mac OS X ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1)
+ ;;
+ *hpux*|*nsk*)
+ AC_MSG_RESULT(Using HPUX ACLs)
+ AC_DEFINE(HAVE_HPUX_ACLS, 1, [true if you have HPUX ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1)
+ ;;
+ *)
+ AC_MSG_RESULT(running tests:)
+ AC_CHECK_LIB(acl,acl_get_file)
+ AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif]], [[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);]])],[samba_cv_HAVE_POSIX_ACLS=yes],[samba_cv_HAVE_POSIX_ACLS=no])])
+ AC_MSG_CHECKING(ACL test results)
+ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
+ AC_MSG_RESULT(Using posix ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS, 1, [true if you have posix ACLs])
+ AC_DEFINE(SUPPORT_ACLS, 1)
+ AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif]], [[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);]])],[samba_cv_HAVE_ACL_GET_PERM_NP=yes],[samba_cv_HAVE_ACL_GET_PERM_NP=no])])
+ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+ AC_DEFINE(HAVE_ACL_GET_PERM_NP, 1, [true if you have acl_get_perm_np])
+ fi
+ else
+ if test x"$enable_acl_support" = x"yes"; then
+ AC_MSG_ERROR(Failed to find ACL support)
+ else
+ AC_MSG_RESULT(No ACL support found)
+ fi
+ fi
+ ;;
+ esac
+fi
+
+#################################################
+# check for extended attribute support
+AC_MSG_CHECKING(whether to support extended attributes)
+AC_ARG_ENABLE(xattr-support,
+ AS_HELP_STRING([--disable-xattr-support],[disable to omit extended attributes]),
+ [], [case "$ac_cv_func_getxattr$ac_cv_func_extattr_get_link$ac_cv_func_attropen" in
+ *yes*) enable_xattr_support=maybe ;;
+ *) enable_xattr_support=no ;;
+ esac])
+AH_TEMPLATE([SUPPORT_XATTRS],
+[Define to 1 to add support for extended attributes])
+if test x"$enable_xattr_support" = x"no"; then
+ AC_MSG_RESULT(no)
+else
+ case "$host_os" in
+ *linux*|*netbsd*|*cygwin*)
+ AC_MSG_RESULT(Using Linux xattrs)
+ AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs (or equivalent)])
+ AC_DEFINE(SUPPORT_XATTRS, 1)
+ AC_DEFINE(NO_SYMLINK_USER_XATTRS, 1, [True if symlinks do not support user xattrs])
+ AC_CHECK_LIB(attr,getxattr)
+ ;;
+ darwin*)
+ AC_MSG_RESULT(Using OS X xattrs)
+ AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs])
+ AC_DEFINE(SUPPORT_XATTRS, 1)
+ AC_DEFINE(NO_DEVICE_XATTRS, 1, [True if device files do not support xattrs])
+ AC_DEFINE(NO_SPECIAL_XATTRS, 1, [True if special files do not support xattrs])
+ ;;
+ freebsd*)
+ AC_MSG_RESULT(Using FreeBSD extattrs)
+ AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
+ AC_DEFINE(SUPPORT_XATTRS, 1)
+ ;;
+ solaris*)
+ AC_MSG_RESULT(Using Solaris xattrs)
+ AC_DEFINE(HAVE_SOLARIS_XATTRS, 1, [True if you have Solaris xattrs])
+ AC_DEFINE(SUPPORT_XATTRS, 1)
+ AC_DEFINE(NO_SYMLINK_XATTRS, 1, [True if symlinks do not support xattrs])
+ ;;
+ *)
+ if test x"$enable_xattr_support" = x"yes"; then
+ AC_MSG_ERROR(Failed to find extended attribute support)
+ else
+ AC_MSG_RESULT(No extended attribute support found)
+ fi
+ ;;
+ esac
+fi
+
+if test x"$enable_acl_support" = x"no" || test x"$enable_xattr_support" = x"no" || test x"$enable_iconv" = x"no"; then
+ AC_MSG_CHECKING([whether $CC supports -Wno-unused-parameter])
+ OLD_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -Wno-unused-parameter"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdio.h>]], [[printf("hello\n");]])],[rsync_warn_flag=yes],[rsync_warn_flag=no])
+ AC_MSG_RESULT([$rsync_warn_flag])
+ if test x"$rsync_warn_flag" = x"no"; then
+ CFLAGS="$OLD_CFLAGS"
+ fi
+fi
+
+case "$CC" in
+' checker'*|checker*)
+ AC_DEFINE(FORCE_FD_ZERO_MEMSET, 1, [Used to make "checker" understand that FD_ZERO() clears memory.])
+ ;;
+esac
+
+AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
+AC_OUTPUT
+
+AC_MSG_RESULT()
+AC_MSG_RESULT([ rsync $PACKAGE_VERSION configuration successful])
+AC_MSG_RESULT()
diff --git a/configure.sh b/configure.sh
new file mode 100755
index 0000000..00da71f
--- /dev/null
+++ b/configure.sh
@@ -0,0 +1,13395 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.71 for rsync.
+#
+# Report bugs to <https://rsync.samba.org/bug-tracking.html>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else $as_nop
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" "" $as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2) ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="as_nop=:
+if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else \$as_nop
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" )
+then :
+
+else \$as_nop
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+blah=\$(echo \$(echo blah))
+test x\"\$blah\" = xblah || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null
+then :
+ as_have_required=yes
+else $as_nop
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
+then :
+
+else $as_nop
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null
+then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+ if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi
+fi
+
+
+ if test "x$CONFIG_SHELL" != x
+then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno
+then :
+ printf "%s\n" "$0: This script requires a shell more modern than all"
+ printf "%s\n" "$0: the shells that I found on your system."
+ if test ${ZSH_VERSION+y} ; then
+ printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
+$0: https://rsync.samba.org/bug-tracking.html about your
+$0: system, including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+ return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else $as_nop
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else $as_nop
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+ return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ printf "%s\n" "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n. New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='rsync'
+PACKAGE_TARNAME='rsync'
+PACKAGE_VERSION=''
+PACKAGE_STRING='rsync'
+PACKAGE_BUGREPORT='https://rsync.samba.org/bug-tracking.html'
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stddef.h>
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_header_c_list=
+ac_unique_file="byteorder.h"
+ac_config_libobj_dir=lib
+ac_subst_vars='LTLIBOBJS
+MAKE_MAN
+GEN_RRSYNC
+MAKE_RRSYNC_1
+MAKE_RRSYNC
+BUILD_ZLIB
+BUILD_POPT
+CC_SHOBJ_FLAG
+OBJ_RESTORE
+OBJ_SAVE
+ALLOCA
+LIBOBJS
+ROLL_ASM
+MD5_ASM
+NOEXECSTACK
+ROLL_SIMD
+FAKEROOT_PATH
+SHELL_PATH
+HAVE_REMSH
+with_rrsync
+PYTHON3
+PERL
+MKDIR_P
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+EGREP
+GREP
+AWK
+ac_ct_CXX
+CXXFLAGS
+CXX
+CPP
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_debug
+enable_profile
+enable_md2man
+enable_maintainer_mode
+with_openssl_conf
+with_rrsync
+with_included_popt
+with_included_zlib
+with_secluded_args
+with_rsync_path
+with_rsyncd_conf
+with_rsh
+with_nobody_user
+with_nobody_group
+enable_roll_simd
+enable_largefile
+enable_ipv6
+enable_locale
+enable_openssl
+enable_md5_asm
+enable_roll_asm
+enable_xxhash
+enable_zstd
+enable_lz4
+enable_iconv_open
+enable_iconv
+enable_acl_support
+enable_xattr_support
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+CXX
+CXXFLAGS
+CCC'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: \`$ac_useropt'"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: \`$ac_useropt'"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: \`$ac_useropt'"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: \`$ac_useropt'"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir runstatedir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures rsync to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/rsync]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of rsync:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --disable-debug disable to omit debugging symbols and features
+ --enable-profile enable to turn on CPU profiling
+ --disable-md2man disable to omit manpage creation
+ --enable-maintainer-mode
+ enable to turn on extra debug features
+ --enable-roll-simd enable/disable to control rolling-checksum SIMD
+ optimizations (requires c++)
+ --disable-largefile omit support for large files
+ --disable-ipv6 disable to omit ipv6 support
+ --disable-locale disable to omit locale features
+ --disable-openssl disable to omit openssl crypto library
+ --enable-md5-asm enable/disable to control MD5 ASM optimizations
+ --enable-roll-asm enable/disable to control rolling-checksum ASM
+ optimizations (requires --enable-roll-simd)
+ --disable-xxhash disable to omit xxhash checksums
+ --disable-zstd disable to omit zstd compression
+ --disable-lz4 disable to omit LZ4 compression
+ --disable-iconv-open disable to avoid all use of iconv_open()
+ --disable-iconv disable to omit the --iconv option
+ --disable-acl-support disable to omit ACL support
+ --disable-xattr-support disable to omit extended attributes
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-openssl-conf=PATH
+ set default OPENSSL_CONF path for rsync
+ --with-rrsync also install the rrsync script and its manpage
+ --with-included-popt use bundled popt library, not from system
+ --with-included-zlib use bundled zlib library, not from system
+ --with-secluded-args make --secluded-args option the default
+ --with-rsync-path=PATH set default --rsync-path to PATH (default: rsync)
+ --with-rsyncd-conf=PATH set configuration file for rsync server to PATH
+ (default: /etc/rsyncd.conf)
+ --with-rsh=CMD set remote shell command to CMD (default: ssh)
+ --with-nobody-user=USER set the default unprivileged user (default nobody)
+ --with-nobody-group=GROUP
+ set the default unprivileged group (default nobody
+ or nogroup)
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+ CXX C++ compiler command
+ CXXFLAGS C++ compiler flags
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <https://rsync.samba.org/bug-tracking.html>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for configure.gnu first; this name is used for a wrapper for
+ # Metaconfig's "Configure" on case-insensitive file systems.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+rsync configure
+generated by GNU Autoconf 2.71
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest.beam
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext
+then :
+ ac_retval=0
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that
+# executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }
+then :
+ ac_retval=0
+else $as_nop
+ printf "%s\n" "$as_me: program exited with status $ac_status" >&5
+ printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "$3=yes"
+else $as_nop
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }
+then :
+ ac_retval=0
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }
+then :
+ ac_retval=0
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest.beam
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext
+then :
+ ac_retval=0
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+
+# ac_fn_cxx_try_run LINENO
+# ------------------------
+# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that
+# executables *can* be run.
+ac_fn_cxx_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }
+then :
+ ac_retval=0
+else $as_nop
+ printf "%s\n" "$as_me: program exited with status $ac_status" >&5
+ printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_run
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_hi=$ac_mid; break
+else $as_nop
+ as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ done
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_lo=$ac_mid; break
+else $as_nop
+ as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ done
+else $as_nop
+ ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_hi=$ac_mid
+else $as_nop
+ as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+static long int longval (void) { return $2; }
+static unsigned long int ulongval (void) { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main (void)
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (($2) < 0)
+ {
+ long int i = longval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%ld", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%lu", i);
+ }
+ /* Do not output a trailing newline, as this causes \r\n confusion
+ on some platforms. */
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else $as_nop
+ ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+ fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ eval "$3=no"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main (void)
+{
+if (sizeof ($2))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main (void)
+{
+if (sizeof (($2)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+ eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
+# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
+# ----------------------------------------------------
+# Tries to find if the field MEMBER exists in type AGGR, after including
+# INCLUDES, setting cache variable VAR accordingly.
+ac_fn_c_check_member ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+printf %s "checking for $2.$3... " >&6; }
+if eval test \${$4+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main (void)
+{
+static $2 ac_aggr;
+if (ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "$4=yes"
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main (void)
+{
+static $2 ac_aggr;
+if (sizeof ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "$4=yes"
+else $as_nop
+ eval "$4=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$4
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_member
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below. */
+
+#include <limits.h>
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main (void)
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ eval "$3=yes"
+else $as_nop
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+ac_configure_args_raw=
+for ac_arg
+do
+ case $ac_arg in
+ *\'*)
+ ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append ac_configure_args_raw " '$ac_arg'"
+done
+
+case $ac_configure_args_raw in
+ *$as_nl*)
+ ac_safe_unquote= ;;
+ *)
+ ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab.
+ ac_unsafe_a="$ac_unsafe_z#~"
+ ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
+ ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+esac
+
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by rsync $as_me, which was
+generated by GNU Autoconf 2.71. Invocation command line was
+
+ $ $0$ac_configure_args_raw
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ printf "%s\n" "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Sanitize IFS.
+ IFS=" "" $as_nl"
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ printf "%s\n" "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ printf "%s\n" "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ printf "%s\n" "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ printf "%s\n" "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ printf "%s\n" "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ printf "%s\n" "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ printf "%s\n" "$as_me: caught signal $ac_signal"
+ printf "%s\n" "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+printf "%s\n" "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+ ac_site_files="$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+ ac_site_files="$prefix/share/config.site $prefix/etc/config.site"
+else
+ ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+fi
+
+for ac_site_file in $ac_site_files
+do
+ case $ac_site_file in #(
+ */*) :
+ ;; #(
+ *) :
+ ac_site_file=./$ac_site_file ;;
+esac
+ if test -f "$ac_site_file" && test -r "$ac_site_file"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Test code for whether the C compiler supports C89 (global declarations)
+ac_c_conftest_c89_globals='
+/* Does the compiler advertise C89 conformance?
+ Do not test the value of __STDC__, because some compilers set it to 0
+ while being otherwise adequately conformant. */
+#if !defined __STDC__
+# error "Compiler does not advertise C89 conformance"
+#endif
+
+#include <stddef.h>
+#include <stdarg.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */
+struct buf { int x; };
+struct buf * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not \xHH hex character constants.
+ These do not provoke an error unfortunately, instead are silently treated
+ as an "x". The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously \x00 != x always comes out true, for an
+ array size at least. It is necessary to write \x00 == 0 to get something
+ that is true only with -std. */
+int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) '\''x'\''
+int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int),
+ int, int);'
+
+# Test code for whether the C compiler supports C89 (body of main).
+ac_c_conftest_c89_main='
+ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]);
+'
+
+# Test code for whether the C compiler supports C99 (global declarations)
+ac_c_conftest_c99_globals='
+// Does the compiler advertise C99 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# error "Compiler does not advertise C99 conformance"
+#endif
+
+#include <stdbool.h>
+extern int puts (const char *);
+extern int printf (const char *, ...);
+extern int dprintf (int, const char *, ...);
+extern void *malloc (size_t);
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+// dprintf is used instead of fprintf to avoid needing to declare
+// FILE and stderr.
+#define debug(...) dprintf (2, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ #error "your preprocessor is broken"
+#endif
+#if BIG_OK
+#else
+ #error "your preprocessor is broken"
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static bool
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str = "";
+ int number = 0;
+ float fnumber = 0;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case '\''s'\'': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case '\''d'\'': // int
+ number = va_arg (args_copy, int);
+ break;
+ case '\''f'\'': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+
+ return *str && number && fnumber;
+}
+'
+
+# Test code for whether the C compiler supports C99 (body of main).
+ac_c_conftest_c99_main='
+ // Check bool.
+ _Bool success = false;
+ success |= (argc != 0);
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[0] = argv[0][0];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\''
+ || dynamic_array[ni.number - 1] != 543);
+'
+
+# Test code for whether the C compiler supports C11 (global declarations)
+ac_c_conftest_c11_globals='
+// Does the compiler advertise C11 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
+# error "Compiler does not advertise C11 conformance"
+#endif
+
+// Check _Alignas.
+char _Alignas (double) aligned_as_double;
+char _Alignas (0) no_special_alignment;
+extern char aligned_as_int;
+char _Alignas (0) _Alignas (int) aligned_as_int;
+
+// Check _Alignof.
+enum
+{
+ int_alignment = _Alignof (int),
+ int_array_alignment = _Alignof (int[100]),
+ char_alignment = _Alignof (char)
+};
+_Static_assert (0 < -_Alignof (int), "_Alignof is signed");
+
+// Check _Noreturn.
+int _Noreturn does_not_return (void) { for (;;) continue; }
+
+// Check _Static_assert.
+struct test_static_assert
+{
+ int x;
+ _Static_assert (sizeof (int) <= sizeof (long int),
+ "_Static_assert does not work in struct");
+ long int y;
+};
+
+// Check UTF-8 literals.
+#define u8 syntax error!
+char const utf8_literal[] = u8"happens to be ASCII" "another string";
+
+// Check duplicate typedefs.
+typedef long *long_ptr;
+typedef long int *long_ptr;
+typedef long_ptr long_ptr;
+
+// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1.
+struct anonymous
+{
+ union {
+ struct { int i; int j; };
+ struct { int k; long int l; } w;
+ };
+ int m;
+} v1;
+'
+
+# Test code for whether the C compiler supports C11 (body of main).
+ac_c_conftest_c11_main='
+ _Static_assert ((offsetof (struct anonymous, i)
+ == offsetof (struct anonymous, w.k)),
+ "Anonymous union alignment botch");
+ v1.i = 2;
+ v1.w.k = 5;
+ ok |= v1.i != 5;
+'
+
+# Test code for whether the C compiler supports C11 (complete).
+ac_c_conftest_c11_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+${ac_c_conftest_c11_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ ${ac_c_conftest_c99_main}
+ ${ac_c_conftest_c11_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C99 (complete).
+ac_c_conftest_c99_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ ${ac_c_conftest_c99_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C89 (complete).
+ac_c_conftest_c89_program="${ac_c_conftest_c89_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ return ok;
+}
+"
+
+as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H"
+as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H"
+as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H"
+as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H"
+as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H"
+as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H"
+as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
+as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
+as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
+# Test code for whether the C++ compiler supports C++98 (global declarations)
+ac_cxx_conftest_cxx98_globals='
+// Does the compiler advertise C++98 conformance?
+#if !defined __cplusplus || __cplusplus < 199711L
+# error "Compiler does not advertise C++98 conformance"
+#endif
+
+// These inclusions are to reject old compilers that
+// lack the unsuffixed header files.
+#include <cstdlib>
+#include <exception>
+
+// <cassert> and <cstring> are *not* freestanding headers in C++98.
+extern void assert (int);
+namespace std {
+ extern int strcmp (const char *, const char *);
+}
+
+// Namespaces, exceptions, and templates were all added after "C++ 2.0".
+using std::exception;
+using std::strcmp;
+
+namespace {
+
+void test_exception_syntax()
+{
+ try {
+ throw "test";
+ } catch (const char *s) {
+ // Extra parentheses suppress a warning when building autoconf itself,
+ // due to lint rules shared with more typical C programs.
+ assert (!(strcmp) (s, "test"));
+ }
+}
+
+template <typename T> struct test_template
+{
+ T const val;
+ explicit test_template(T t) : val(t) {}
+ template <typename U> T add(U u) { return static_cast<T>(u) + val; }
+};
+
+} // anonymous namespace
+'
+
+# Test code for whether the C++ compiler supports C++98 (body of main)
+ac_cxx_conftest_cxx98_main='
+ assert (argc);
+ assert (! argv[0]);
+{
+ test_exception_syntax ();
+ test_template<double> tt (2.0);
+ assert (tt.add (4) == 6.0);
+ assert (true && !false);
+}
+'
+
+# Test code for whether the C++ compiler supports C++11 (global declarations)
+ac_cxx_conftest_cxx11_globals='
+// Does the compiler advertise C++ 2011 conformance?
+#if !defined __cplusplus || __cplusplus < 201103L
+# error "Compiler does not advertise C++11 conformance"
+#endif
+
+namespace cxx11test
+{
+ constexpr int get_val() { return 20; }
+
+ struct testinit
+ {
+ int i;
+ double d;
+ };
+
+ class delegate
+ {
+ public:
+ delegate(int n) : n(n) {}
+ delegate(): delegate(2354) {}
+
+ virtual int getval() { return this->n; };
+ protected:
+ int n;
+ };
+
+ class overridden : public delegate
+ {
+ public:
+ overridden(int n): delegate(n) {}
+ virtual int getval() override final { return this->n * 2; }
+ };
+
+ class nocopy
+ {
+ public:
+ nocopy(int i): i(i) {}
+ nocopy() = default;
+ nocopy(const nocopy&) = delete;
+ nocopy & operator=(const nocopy&) = delete;
+ private:
+ int i;
+ };
+
+ // for testing lambda expressions
+ template <typename Ret, typename Fn> Ret eval(Fn f, Ret v)
+ {
+ return f(v);
+ }
+
+ // for testing variadic templates and trailing return types
+ template <typename V> auto sum(V first) -> V
+ {
+ return first;
+ }
+ template <typename V, typename... Args> auto sum(V first, Args... rest) -> V
+ {
+ return first + sum(rest...);
+ }
+}
+'
+
+# Test code for whether the C++ compiler supports C++11 (body of main)
+ac_cxx_conftest_cxx11_main='
+{
+ // Test auto and decltype
+ auto a1 = 6538;
+ auto a2 = 48573953.4;
+ auto a3 = "String literal";
+
+ int total = 0;
+ for (auto i = a3; *i; ++i) { total += *i; }
+
+ decltype(a2) a4 = 34895.034;
+}
+{
+ // Test constexpr
+ short sa[cxx11test::get_val()] = { 0 };
+}
+{
+ // Test initializer lists
+ cxx11test::testinit il = { 4323, 435234.23544 };
+}
+{
+ // Test range-based for
+ int array[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3,
+ 14, 19, 17, 8, 6, 20, 16, 2, 11, 1};
+ for (auto &x : array) { x += 23; }
+}
+{
+ // Test lambda expressions
+ using cxx11test::eval;
+ assert (eval ([](int x) { return x*2; }, 21) == 42);
+ double d = 2.0;
+ assert (eval ([&](double x) { return d += x; }, 3.0) == 5.0);
+ assert (d == 5.0);
+ assert (eval ([=](double x) mutable { return d += x; }, 4.0) == 9.0);
+ assert (d == 5.0);
+}
+{
+ // Test use of variadic templates
+ using cxx11test::sum;
+ auto a = sum(1);
+ auto b = sum(1, 2);
+ auto c = sum(1.0, 2.0, 3.0);
+}
+{
+ // Test constructor delegation
+ cxx11test::delegate d1;
+ cxx11test::delegate d2();
+ cxx11test::delegate d3(45);
+}
+{
+ // Test override and final
+ cxx11test::overridden o1(55464);
+}
+{
+ // Test nullptr
+ char *c = nullptr;
+}
+{
+ // Test template brackets
+ test_template<::test_template<int>> v(test_template<int>(12));
+}
+{
+ // Unicode literals
+ char const *utf8 = u8"UTF-8 string \u2500";
+ char16_t const *utf16 = u"UTF-8 string \u2500";
+ char32_t const *utf32 = U"UTF-32 string \u2500";
+}
+'
+
+# Test code for whether the C compiler supports C++11 (complete).
+ac_cxx_conftest_cxx11_program="${ac_cxx_conftest_cxx98_globals}
+${ac_cxx_conftest_cxx11_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_cxx_conftest_cxx98_main}
+ ${ac_cxx_conftest_cxx11_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C++98 (complete).
+ac_cxx_conftest_cxx98_program="${ac_cxx_conftest_cxx98_globals}
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_cxx_conftest_cxx98_main}
+ return ok;
+}
+"
+
+as_fn_append ac_header_c_list " utime.h utime_h HAVE_UTIME_H"
+
+# Auxiliary files required by this configure script.
+ac_aux_files="install-sh config.guess config.sub"
+
+# Locations in which to look for auxiliary files.
+ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.."
+
+# Search for a directory containing all of the required auxiliary files,
+# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates.
+# If we don't find one directory that contains all the files we need,
+# we report the set of missing files from the *first* directory in
+# $ac_aux_dir_candidates and give up.
+ac_missing_aux_files=""
+ac_first_candidate=:
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in $ac_aux_dir_candidates
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ as_found=:
+
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5
+ ac_aux_dir_found=yes
+ ac_install_sh=
+ for ac_aux in $ac_aux_files
+ do
+ # As a special case, if "install-sh" is required, that requirement
+ # can be satisfied by any of "install-sh", "install.sh", or "shtool",
+ # and $ac_install_sh is set appropriately for whichever one is found.
+ if test x"$ac_aux" = x"install-sh"
+ then
+ if test -f "${as_dir}install-sh"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5
+ ac_install_sh="${as_dir}install-sh -c"
+ elif test -f "${as_dir}install.sh"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5
+ ac_install_sh="${as_dir}install.sh -c"
+ elif test -f "${as_dir}shtool"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5
+ ac_install_sh="${as_dir}shtool install -c"
+ else
+ ac_aux_dir_found=no
+ if $ac_first_candidate; then
+ ac_missing_aux_files="${ac_missing_aux_files} install-sh"
+ else
+ break
+ fi
+ fi
+ else
+ if test -f "${as_dir}${ac_aux}"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5
+ else
+ ac_aux_dir_found=no
+ if $ac_first_candidate; then
+ ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}"
+ else
+ break
+ fi
+ fi
+ fi
+ done
+ if test "$ac_aux_dir_found" = yes; then
+ ac_aux_dir="$as_dir"
+ break
+ fi
+ ac_first_candidate=false
+
+ as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+ as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5
+fi
+
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+if test -f "${ac_aux_dir}config.guess"; then
+ ac_config_guess="$SHELL ${ac_aux_dir}config.guess"
+fi
+if test -f "${ac_aux_dir}config.sub"; then
+ ac_config_sub="$SHELL ${ac_aux_dir}config.sub"
+fi
+if test -f "$ac_aux_dir/configure"; then
+ ac_configure="$SHELL ${ac_aux_dir}configure"
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+ and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+
+
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion -version; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+printf %s "checking whether the C compiler works... " >&6; }
+ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else $as_nop
+ ac_file=''
+fi
+if test -z "$ac_file"
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+printf %s "checking for C compiler default output file name... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf "%s\n" "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+printf %s "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else $as_nop
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf "%s\n" "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main (void)
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+printf %s "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+printf "%s\n" "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+printf %s "checking for suffix of object files... " >&6; }
+if test ${ac_cv_objext+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf "%s\n" "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+printf %s "checking whether the compiler supports GNU C... " >&6; }
+if test ${ac_cv_c_compiler_gnu+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_compiler_gnu=yes
+else $as_nop
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+y}
+ac_save_CFLAGS=$CFLAGS
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+printf %s "checking whether $CC accepts -g... " >&6; }
+if test ${ac_cv_prog_cc_g+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_g=yes
+else $as_nop
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+printf %s "checking for $CC option to enable C11 features... " >&6; }
+if test ${ac_cv_prog_cc_c11+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c11=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c11_program
+_ACEOF
+for ac_arg in '' -std=gnu11
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c11" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c11" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+ CC="$CC $ac_cv_prog_cc_c11"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+ ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+printf %s "checking for $CC option to enable C99 features... " >&6; }
+if test ${ac_cv_prog_cc_c99+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c99_program
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c99" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c99" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+ CC="$CC $ac_cv_prog_cc_c99"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+ ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+printf %s "checking for $CC option to enable C89 features... " >&6; }
+if test ${ac_cv_prog_cc_c89+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c89_program
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c89" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c89" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+ CC="$CC $ac_cv_prog_cc_c89"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+ ac_prog_cc_stdc=c89
+fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_header= ac_cache=
+for ac_item in $ac_header_c_list
+do
+ if test $ac_cache; then
+ ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
+ if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
+ printf "%s\n" "#define $ac_item 1" >> confdefs.h
+ fi
+ ac_header= ac_cache=
+ elif test $ac_header; then
+ ac_cache=$ac_item
+ else
+ ac_header=$ac_item
+ fi
+done
+
+
+
+
+
+
+
+
+if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
+then :
+
+printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+printf %s "checking whether byte ordering is bigendian... " >&6; }
+if test ${ac_cv_c_bigendian+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_c_bigendian=unknown
+ # See if we're dealing with a universal compiler.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifndef __APPLE_CC__
+ not a universal capable compiler
+ #endif
+ typedef int dummy;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+ # Check for potential -arch flags. It is not universal unless
+ # there are at least two -arch flags with different values.
+ ac_arch=
+ ac_prev=
+ for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+ if test -n "$ac_prev"; then
+ case $ac_word in
+ i?86 | x86_64 | ppc | ppc64)
+ if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+ ac_arch=$ac_word
+ else
+ ac_cv_c_bigendian=universal
+ break
+ fi
+ ;;
+ esac
+ ac_prev=
+ elif test "x$ac_word" = "x-arch"; then
+ ac_prev=arch
+ fi
+ done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if sys/param.h defines the BYTE_ORDER macro.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main (void)
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+ && LITTLE_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main (void)
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_c_bigendian=yes
+else $as_nop
+ ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main (void)
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ # It does; now see whether it defined to _BIG_ENDIAN or not.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main (void)
+{
+#ifndef _BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_c_bigendian=yes
+else $as_nop
+ ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # Compile a test program.
+ if test "$cross_compiling" = yes
+then :
+ # Try to guess by grepping values from an object file.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+unsigned short int ascii_mm[] =
+ { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+ unsigned short int ascii_ii[] =
+ { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+ int use_ascii (int i) {
+ return ascii_mm[i] + ascii_ii[i];
+ }
+ unsigned short int ebcdic_ii[] =
+ { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+ unsigned short int ebcdic_mm[] =
+ { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+ int use_ebcdic (int i) {
+ return ebcdic_mm[i] + ebcdic_ii[i];
+ }
+ extern int foo;
+
+int
+main (void)
+{
+return use_ascii (foo) == use_ebcdic (foo);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+ ac_cv_c_bigendian=yes
+ fi
+ if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+ if test "$ac_cv_c_bigendian" = unknown; then
+ ac_cv_c_bigendian=no
+ else
+ # finding both strings is unlikely to happen, but who knows?
+ ac_cv_c_bigendian=unknown
+ fi
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main (void)
+{
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+ u.l = 1;
+ return u.c[sizeof (long int) - 1] == 1;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ ac_cv_c_bigendian=no
+else $as_nop
+ ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+printf "%s\n" "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+ yes)
+ printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+ no)
+ ;; #(
+ universal)
+
+printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+ ;; #(
+ *)
+ as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+printf %s "checking for $ac_hdr that defines DIR... " >&6; }
+if eval test \${$as_ac_Header+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main (void)
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "$as_ac_Header=yes"
+else $as_nop
+ eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"
+then :
+ cat >>confdefs.h <<_ACEOF
+#define `printf "%s\n" "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+printf %s "checking for library containing opendir... " >&6; }
+if test ${ac_cv_search_opendir+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char opendir ();
+int
+main (void)
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_opendir+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_opendir+y}
+then :
+
+else $as_nop
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+printf "%s\n" "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+printf %s "checking for library containing opendir... " >&6; }
+if test ${ac_cv_search_opendir+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char opendir ();
+int
+main (void)
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_opendir+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_opendir+y}
+then :
+
+else $as_nop
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+printf "%s\n" "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+printf %s "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if test ${ac_cv_header_sys_wait_h+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main (void)
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_header_sys_wait_h=yes
+else $as_nop
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+printf "%s\n" "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+printf "%s\n" "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "sys/fcntl.h" "ac_cv_header_sys_fcntl_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_fcntl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_FCNTL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_select_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_SELECT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default"
+if test "x$ac_cv_header_fcntl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_time_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/unistd.h" "ac_cv_header_sys_unistd_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_unistd_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_UNISTD_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default"
+if test "x$ac_cv_header_unistd_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_UNISTD_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "utime.h" "ac_cv_header_utime_h" "$ac_includes_default"
+if test "x$ac_cv_header_utime_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_UTIME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "compat.h" "ac_cv_header_compat_h" "$ac_includes_default"
+if test "x$ac_cv_header_compat_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_COMPAT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_param_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "ctype.h" "ac_cv_header_ctype_h" "$ac_includes_default"
+if test "x$ac_cv_header_ctype_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_CTYPE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/wait.h" "ac_cv_header_sys_wait_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_wait_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/stat.h" "ac_cv_header_sys_stat_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_stat_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_STAT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/ioctl.h" "ac_cv_header_sys_ioctl_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_ioctl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_IOCTL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/filio.h" "ac_cv_header_sys_filio_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_filio_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_FILIO_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default"
+if test "x$ac_cv_header_string_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRING_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdlib_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_STDLIB_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_socket_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/mode.h" "ac_cv_header_sys_mode_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_mode_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_MODE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "grp.h" "ac_cv_header_grp_h" "$ac_includes_default"
+if test "x$ac_cv_header_grp_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_GRP_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/un.h" "ac_cv_header_sys_un_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_un_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_UN_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/attr.h" "ac_cv_header_sys_attr_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_attr_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_ATTR_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "arpa/inet.h" "ac_cv_header_arpa_inet_h" "$ac_includes_default"
+if test "x$ac_cv_header_arpa_inet_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ARPA_INET_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "arpa/nameser.h" "ac_cv_header_arpa_nameser_h" "$ac_includes_default"
+if test "x$ac_cv_header_arpa_nameser_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ARPA_NAMESER_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "locale.h" "ac_cv_header_locale_h" "$ac_includes_default"
+if test "x$ac_cv_header_locale_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LOCALE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_TYPES_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default"
+if test "x$ac_cv_header_netdb_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_NETDB_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default"
+if test "x$ac_cv_header_malloc_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_MALLOC_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "float.h" "ac_cv_header_float_h" "$ac_includes_default"
+if test "x$ac_cv_header_float_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_FLOAT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "iconv.h" "ac_cv_header_iconv_h" "$ac_includes_default"
+if test "x$ac_cv_header_iconv_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ICONV_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "libcharset.h" "ac_cv_header_libcharset_h" "$ac_includes_default"
+if test "x$ac_cv_header_libcharset_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBCHARSET_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "langinfo.h" "ac_cv_header_langinfo_h" "$ac_includes_default"
+if test "x$ac_cv_header_langinfo_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "mcheck.h" "ac_cv_header_mcheck_h" "$ac_includes_default"
+if test "x$ac_cv_header_mcheck_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_MCHECK_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/acl.h" "ac_cv_header_sys_acl_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_acl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_ACL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "acl/libacl.h" "ac_cv_header_acl_libacl_h" "$ac_includes_default"
+if test "x$ac_cv_header_acl_libacl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ACL_LIBACL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "attr/xattr.h" "ac_cv_header_attr_xattr_h" "$ac_includes_default"
+if test "x$ac_cv_header_attr_xattr_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ATTR_XATTR_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/xattr.h" "ac_cv_header_sys_xattr_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_xattr_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_XATTR_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/extattr.h" "ac_cv_header_sys_extattr_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_extattr_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_EXTATTR_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "dl.h" "ac_cv_header_dl_h" "$ac_includes_default"
+if test "x$ac_cv_header_dl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_DL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "popt.h" "ac_cv_header_popt_h" "$ac_includes_default"
+if test "x$ac_cv_header_popt_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_POPT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "popt/popt.h" "ac_cv_header_popt_popt_h" "$ac_includes_default"
+if test "x$ac_cv_header_popt_popt_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_POPT_POPT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/falloc.h" "ac_cv_header_linux_falloc_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_falloc_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LINUX_FALLOC_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "netinet/in_systm.h" "ac_cv_header_netinet_in_systm_h" "$ac_includes_default"
+if test "x$ac_cv_header_netinet_in_systm_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_NETINET_IN_SYSTM_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "netgroup.h" "ac_cv_header_netgroup_h" "$ac_includes_default"
+if test "x$ac_cv_header_netgroup_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_NETGROUP_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_zlib_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ZLIB_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "xxhash.h" "ac_cv_header_xxhash_h" "$ac_includes_default"
+if test "x$ac_cv_header_xxhash_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_XXHASH_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "openssl/md4.h" "ac_cv_header_openssl_md4_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_md4_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_OPENSSL_MD4_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "openssl/md5.h" "ac_cv_header_openssl_md5_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_md5_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_OPENSSL_MD5_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "zstd.h" "ac_cv_header_zstd_h" "$ac_includes_default"
+if test "x$ac_cv_header_zstd_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ZSTD_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "lz4.h" "ac_cv_header_lz4_h" "$ac_includes_default"
+if test "x$ac_cv_header_lz4_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LZ4_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/file.h" "ac_cv_header_sys_file_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_file_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_FILE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "bsd/string.h" "ac_cv_header_bsd_string_h" "$ac_includes_default"
+if test "x$ac_cv_header_bsd_string_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_BSD_STRING_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "netinet/ip.h" "ac_cv_header_netinet_ip_h" "#include <netinet/in.h>
+"
+if test "x$ac_cv_header_netinet_ip_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_NETINET_IP_H 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether sys/types.h defines makedev" >&5
+printf %s "checking whether sys/types.h defines makedev... " >&6; }
+if test ${ac_cv_header_sys_types_h_makedev+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+int
+main (void)
+{
+return makedev(0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ if grep sys/sysmacros.h conftest.err >/dev/null; then
+ ac_cv_header_sys_types_h_makedev=no
+ else
+ ac_cv_header_sys_types_h_makedev=yes
+ fi
+else $as_nop
+ ac_cv_header_sys_types_h_makedev=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_types_h_makedev" >&5
+printf "%s\n" "$ac_cv_header_sys_types_h_makedev" >&6; }
+
+if test $ac_cv_header_sys_types_h_makedev = no; then
+ac_fn_c_check_header_compile "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_mkdev_h" = xyes
+then :
+
+printf "%s\n" "#define MAJOR_IN_MKDEV 1" >>confdefs.h
+
+fi
+
+
+ if test $ac_cv_header_sys_mkdev_h = no; then
+ ac_fn_c_check_header_compile "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_sysmacros_h" = xyes
+then :
+
+printf "%s\n" "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h
+
+fi
+
+ fi
+fi
+
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+
+PACKAGE_VERSION=`sed -n 's/.*RSYNC_VERSION.*"\(.*\)".*/\1/p' <$srcdir/version.h`
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Configuring rsync $PACKAGE_VERSION" >&5
+printf "%s\n" "$as_me: Configuring rsync $PACKAGE_VERSION" >&6;}
+
+LDFLAGS=${LDFLAGS-""}
+
+
+
+
+ # Make sure we can run config.sub.
+$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+printf %s "checking build system type... " >&6; }
+if test ${ac_cv_build+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+printf "%s\n" "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+printf %s "checking host system type... " >&6; }
+if test ${ac_cv_host+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+printf "%s\n" "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+
+
+# We must decide this before testing the compiler.
+
+# Please allow this to default to yes, so that your users have more
+# chance of getting a useful stack trace if problems occur.
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to include debugging symbols" >&5
+printf %s "checking whether to include debugging symbols... " >&6; }
+# Check whether --enable-debug was given.
+if test ${enable_debug+y}
+then :
+ enableval=$enable_debug;
+fi
+
+
+if test x"$enable_debug" = x"no"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ ac_cv_prog_cc_g=no
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ # leave ac_cv_prog_cc_g alone; AC_PROG_CC will try to include -g if it can
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+
+
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion -version; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+printf %s "checking whether the compiler supports GNU C... " >&6; }
+if test ${ac_cv_c_compiler_gnu+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_compiler_gnu=yes
+else $as_nop
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+y}
+ac_save_CFLAGS=$CFLAGS
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+printf %s "checking whether $CC accepts -g... " >&6; }
+if test ${ac_cv_prog_cc_g+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_g=yes
+else $as_nop
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+printf %s "checking for $CC option to enable C11 features... " >&6; }
+if test ${ac_cv_prog_cc_c11+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c11=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c11_program
+_ACEOF
+for ac_arg in '' -std=gnu11
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c11" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c11" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+ CC="$CC $ac_cv_prog_cc_c11"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+ ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+printf %s "checking for $CC option to enable C99 features... " >&6; }
+if test ${ac_cv_prog_cc_c99+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c99_program
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c99" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c99" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+ CC="$CC $ac_cv_prog_cc_c99"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+ ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+printf %s "checking for $CC option to enable C89 features... " >&6; }
+if test ${ac_cv_prog_cc_c89+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c89_program
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c89" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c89" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+ CC="$CC $ac_cv_prog_cc_c89"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+ ac_prog_cc_stdc=c89
+fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+printf %s "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test ${ac_cv_prog_CPP+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ # Double quotes because $CC needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else $as_nop
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+ # Broken: success on invalid input.
+continue
+else $as_nop
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok
+then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+printf "%s\n" "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else $as_nop
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+ # Broken: success on invalid input.
+continue
+else $as_nop
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok
+then :
+
+else $as_nop
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+ if test -n "$CCC"; then
+ CXX=$CCC
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CXX+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+printf "%s\n" "$CXX" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ test -n "$CXX" && break
+ done
+fi
+if test -z "$CXX"; then
+ ac_ct_CXX=$CXX
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CXX+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CXX"; then
+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CXX="$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+printf "%s\n" "$ac_ct_CXX" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CXX" && break
+done
+
+ if test "x$ac_ct_CXX" = x; then
+ CXX="g++"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CXX=$ac_ct_CXX
+ fi
+fi
+
+ fi
+fi
+# Provide some information about the compiler.
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5
+printf %s "checking whether the compiler supports GNU C++... " >&6; }
+if test ${ac_cv_cxx_compiler_gnu+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+ ac_compiler_gnu=yes
+else $as_nop
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+if test $ac_compiler_gnu = yes; then
+ GXX=yes
+else
+ GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+y}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+printf %s "checking whether $CXX accepts -g... " >&6; }
+if test ${ac_cv_prog_cxx_g+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+ ac_cxx_werror_flag=yes
+ ac_cv_prog_cxx_g=no
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+ ac_cv_prog_cxx_g=yes
+else $as_nop
+ CXXFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+
+else $as_nop
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+ ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+printf "%s\n" "$ac_cv_prog_cxx_g" >&6; }
+if test $ac_test_CXXFLAGS; then
+ CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+ if test "$GXX" = yes; then
+ CXXFLAGS="-g -O2"
+ else
+ CXXFLAGS="-g"
+ fi
+else
+ if test "$GXX" = yes; then
+ CXXFLAGS="-O2"
+ else
+ CXXFLAGS=
+ fi
+fi
+ac_prog_cxx_stdcxx=no
+if test x$ac_prog_cxx_stdcxx = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
+printf %s "checking for $CXX option to enable C++11 features... " >&6; }
+if test ${ac_cv_prog_cxx_11+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cxx_11=no
+ac_save_CXX=$CXX
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_cxx_conftest_cxx11_program
+_ACEOF
+for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA
+do
+ CXX="$ac_save_CXX $ac_arg"
+ if ac_fn_cxx_try_compile "$LINENO"
+then :
+ ac_cv_prog_cxx_cxx11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cxx_cxx11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CXX=$ac_save_CXX
+fi
+
+if test "x$ac_cv_prog_cxx_cxx11" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cxx_cxx11" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5
+printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; }
+ CXX="$CXX $ac_cv_prog_cxx_cxx11"
+fi
+ ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
+ ac_prog_cxx_stdcxx=cxx11
+fi
+fi
+if test x$ac_prog_cxx_stdcxx = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5
+printf %s "checking for $CXX option to enable C++98 features... " >&6; }
+if test ${ac_cv_prog_cxx_98+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cxx_98=no
+ac_save_CXX=$CXX
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_cxx_conftest_cxx98_program
+_ACEOF
+for ac_arg in '' -std=gnu++98 -std=c++98 -qlanglvl=extended -AA
+do
+ CXX="$ac_save_CXX $ac_arg"
+ if ac_fn_cxx_try_compile "$LINENO"
+then :
+ ac_cv_prog_cxx_cxx98=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cxx_cxx98" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CXX=$ac_save_CXX
+fi
+
+if test "x$ac_cv_prog_cxx_cxx98" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cxx_cxx98" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5
+printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; }
+ CXX="$CXX $ac_cv_prog_cxx_cxx98"
+fi
+ ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
+ ac_prog_cxx_stdcxx=cxx98
+fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_AWK+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AWK="$ac_prog"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+printf "%s\n" "$AWK" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+printf %s "checking for grep that handles long lines and -e... " >&6; }
+if test ${ac_cv_path_GREP+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_prog in grep ggrep
+ do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ printf %s 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ printf "%s\n" 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+printf "%s\n" "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+printf %s "checking for egrep... " >&6; }
+if test ${ac_cv_path_EGREP+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_prog in egrep
+ do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ printf %s 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ printf "%s\n" 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+printf "%s\n" "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+
+ # Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+printf %s "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test ${ac_cv_path_install+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ # Account for fact that we put trailing slashes in our PATH walk.
+case $as_dir in #((
+ ./ | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test ${ac_cv_path_install+y}; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+printf "%s\n" "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5
+printf %s "checking for a race-free mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if test ${ac_cv_path_mkdir+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue
+ case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir ('*'coreutils) '* | \
+ 'BusyBox '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test ${ac_cv_path_mkdir+y}; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+printf "%s\n" "$MKDIR_P" >&6; }
+
+
+# Extract the first word of "perl", so it can be a program name with args.
+set dummy perl; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_path_PERL+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ case $PERL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PERL="$PERL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_path_PERL="$as_dir$ac_word$ac_exec_ext"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PERL=$ac_cv_path_PERL
+if test -n "$PERL"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5
+printf "%s\n" "$PERL" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+# Extract the first word of "python3", so it can be a program name with args.
+set dummy python3; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_path_PYTHON3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ case $PYTHON3 in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PYTHON3="$PYTHON3" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_path_PYTHON3="$as_dir$ac_word$ac_exec_ext"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PYTHON3=$ac_cv_path_PYTHON3
+if test -n "$PYTHON3"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON3" >&5
+printf "%s\n" "$PYTHON3" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+
+
+printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h
+
+
+if test x"$ac_cv_prog_cc_stdc" = x"no"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: rsync requires an ANSI C compiler and you do not seem to have one" >&5
+printf "%s\n" "$as_me: WARNING: rsync requires an ANSI C compiler and you do not seem to have one" >&2;}
+fi
+
+no_lib=''
+err_msg=''
+nl='
+'
+
+# Check whether --enable-profile was given.
+if test ${enable_profile+y}
+then :
+ enableval=$enable_profile;
+fi
+
+if test x"$enable_profile" = x"yes"; then
+ CFLAGS="$CFLAGS -pg"
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if md2man can create manpages" >&5
+printf %s "checking if md2man can create manpages... " >&6; }
+if test x"$ac_cv_path_PYTHON3" = x; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no - python3 not found" >&5
+printf "%s\n" "no - python3 not found" >&6; }
+ md2man_works=no
+else
+ md2man_out=`"$srcdir/md2man" --test "$srcdir/rsync-ssl.1.md" 2>&1`
+ if test $? = 0; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ md2man_works=yes
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ md2man_works=no
+ echo "$md2man_out"
+ fi
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we require man-page building" >&5
+printf %s "checking if we require man-page building... " >&6; }
+# Check whether --enable-md2man was given.
+if test ${enable_md2man+y}
+then :
+ enableval=$enable_md2man;
+fi
+
+if test x"$enable_md2man" != x"no"; then
+ if test -f "$srcdir/rsync.1"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: optional" >&5
+printf "%s\n" "optional" >&6; }
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: required" >&5
+printf "%s\n" "required" >&6; }
+ if test x"$md2man_works" = x"no"; then
+ err_msg="$err_msg$nl- You need python3 and either the cmarkgfm OR commonmark python3 lib in order"
+ err_msg="$err_msg$nl to build manpages based on the git source (manpages are included in the"
+ err_msg="$err_msg$nl official release tar files)."
+ no_lib="$no_lib md2man"
+ fi
+ fi
+ MAKE_MAN=man
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+# Specifically, this turns on panic_action handling.
+# Check whether --enable-maintainer-mode was given.
+if test ${enable_maintainer_mode+y}
+then :
+ enableval=$enable_maintainer_mode;
+fi
+
+if test x"$enable_maintainer_mode" = x"yes"; then
+ CFLAGS="$CFLAGS -DMAINTAINER_MODE"
+fi
+
+# This is needed for our included version of popt. Kind of silly, but
+# I don't want our version too far out of sync.
+CFLAGS="$CFLAGS -DHAVE_CONFIG_H"
+
+# If GCC, turn on warnings.
+if test x"$GCC" = x"yes"; then
+ CFLAGS="$CFLAGS -Wall -W"
+fi
+
+
+# Check whether --with-openssl-conf was given.
+if test ${with_openssl_conf+y}
+then :
+ withval=$with_openssl_conf;
+fi
+
+case "$with_openssl_conf" in
+ *^-/a-zA-Z0-9.,=@+_*) as_fn_error $? "Invalid path given to --with-openssl-conf" "$LINENO" 5 ;;
+ /*) CFLAGS="$CFLAGS -DSET_OPENSSL_CONF=$with_openssl_conf" ;;
+ no|'') ;;
+ yes) as_fn_error $? "No path given to --with-openssl-conf" "$LINENO" 5 ;;
+ *) as_fn_error $? "Non absolute path given to --with-openssl-conf" "$LINENO" 5 ;;
+esac
+
+
+# Check whether --with-rrsync was given.
+if test ${with_rrsync+y}
+then :
+ withval=$with_rrsync;
+fi
+
+if test x"$with_rrsync" != x"yes"; then
+ with_rrsync=no
+else
+ MAKE_RRSYNC='rrsync'
+ MAKE_RRSYNC_1='rrsync.1'
+ GEN_RRSYNC='rrsync.1 rrsync.1.html'
+fi
+
+
+
+# Check whether --with-included-popt was given.
+if test ${with_included_popt+y}
+then :
+ withval=$with_included_popt;
+fi
+
+
+
+# Check whether --with-included-zlib was given.
+if test ${with_included_zlib+y}
+then :
+ withval=$with_included_zlib;
+fi
+
+
+
+# Check whether --with-secluded-args was given.
+if test ${with_secluded_args+y}
+then :
+ withval=$with_secluded_args;
+fi
+
+if test x"$with_secluded_args" = x"yes"; then
+
+printf "%s\n" "#define RSYNC_USE_SECLUDED_ARGS 1" >>confdefs.h
+
+fi
+
+
+# Check whether --with-rsync-path was given.
+if test ${with_rsync_path+y}
+then :
+ withval=$with_rsync_path; RSYNC_PATH="$with_rsync_path"
+else $as_nop
+ RSYNC_PATH="rsync"
+fi
+
+
+
+printf "%s\n" "#define RSYNC_PATH \"$RSYNC_PATH\"" >>confdefs.h
+
+
+
+# Check whether --with-rsyncd-conf was given.
+if test ${with_rsyncd_conf+y}
+then :
+ withval=$with_rsyncd_conf; if test ! -z "$with_rsyncd_conf" ; then
+ case $with_rsyncd_conf in
+ yes|no)
+ RSYNCD_SYSCONF="/etc/rsyncd.conf"
+ ;;
+ /*)
+ RSYNCD_SYSCONF="$with_rsyncd_conf"
+ ;;
+ *)
+ as_fn_error $? "You must specify an absolute path to --with-rsyncd-conf=PATH" "$LINENO" 5
+ ;;
+ esac
+ else
+ RSYNCD_SYSCONF="/etc/rsyncd.conf"
+ fi
+else $as_nop
+ RSYNCD_SYSCONF="/etc/rsyncd.conf"
+fi
+
+
+
+printf "%s\n" "#define RSYNCD_SYSCONF \"$RSYNCD_SYSCONF\"" >>confdefs.h
+
+
+
+# Check whether --with-rsh was given.
+if test ${with_rsh+y}
+then :
+ withval=$with_rsh;
+fi
+
+
+# Extract the first word of "remsh", so it can be a program name with args.
+set dummy remsh; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_HAVE_REMSH+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$HAVE_REMSH"; then
+ ac_cv_prog_HAVE_REMSH="$HAVE_REMSH" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_HAVE_REMSH="1"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_HAVE_REMSH" && ac_cv_prog_HAVE_REMSH="0"
+fi
+fi
+HAVE_REMSH=$ac_cv_prog_HAVE_REMSH
+if test -n "$HAVE_REMSH"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $HAVE_REMSH" >&5
+printf "%s\n" "$HAVE_REMSH" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+if test x$HAVE_REMSH = x1; then
+
+printf "%s\n" "#define HAVE_REMSH 1" >>confdefs.h
+
+fi
+
+if test x"$with_rsh" != x; then
+ RSYNC_RSH="$with_rsh"
+else
+ RSYNC_RSH="ssh"
+fi
+
+printf "%s\n" "#define RSYNC_RSH \"$RSYNC_RSH\"" >>confdefs.h
+
+
+# Some programs on solaris are only found in /usr/xpg4/bin (or work better than others versions).
+# Extract the first word of "sh", so it can be a program name with args.
+set dummy sh; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_path_SHELL_PATH+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ case $SHELL_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SHELL_PATH="$SHELL_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /usr/xpg4/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_path_SHELL_PATH="$as_dir$ac_word$ac_exec_ext"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SHELL_PATH" && ac_cv_path_SHELL_PATH="/bin/sh"
+ ;;
+esac
+fi
+SHELL_PATH=$ac_cv_path_SHELL_PATH
+if test -n "$SHELL_PATH"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SHELL_PATH" >&5
+printf "%s\n" "$SHELL_PATH" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+# Extract the first word of "fakeroot", so it can be a program name with args.
+set dummy fakeroot; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_path_FAKEROOT_PATH+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ case $FAKEROOT_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_FAKEROOT_PATH="$FAKEROOT_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /usr/xpg4/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_path_FAKEROOT_PATH="$as_dir$ac_word$ac_exec_ext"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_FAKEROOT_PATH" && ac_cv_path_FAKEROOT_PATH="/usr/bin/fakeroot"
+ ;;
+esac
+fi
+FAKEROOT_PATH=$ac_cv_path_FAKEROOT_PATH
+if test -n "$FAKEROOT_PATH"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FAKEROOT_PATH" >&5
+printf "%s\n" "$FAKEROOT_PATH" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+
+
+# Check whether --with-nobody-user was given.
+if test ${with_nobody_user+y}
+then :
+ withval=$with_nobody_user; NOBODY_USER="$with_nobody_user"
+else $as_nop
+ NOBODY_USER="nobody"
+fi
+
+
+
+# Check whether --with-nobody-group was given.
+if test ${with_nobody_group+y}
+then :
+ withval=$with_nobody_group; NOBODY_GROUP="$with_nobody_group"
+fi
+
+
+if test x"$with_nobody_group" = x; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the group for user \"nobody\"" >&5
+printf %s "checking the group for user \"nobody\"... " >&6; }
+ if grep '^nobody:' /etc/group >/dev/null 2>&1; then
+ NOBODY_GROUP=nobody
+ elif grep '^nogroup:' /etc/group >/dev/null 2>&1; then
+ NOBODY_GROUP=nogroup
+ else
+ NOBODY_GROUP=nobody # test for others?
+ fi
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NOBODY_GROUP" >&5
+printf "%s\n" "$NOBODY_GROUP" >&6; }
+fi
+
+
+printf "%s\n" "#define NOBODY_USER \"$NOBODY_USER\"" >>confdefs.h
+
+
+printf "%s\n" "#define NOBODY_GROUP \"$NOBODY_GROUP\"" >>confdefs.h
+
+
+# rolling-checksum SIMD optimizations
+ROLL_SIMD=
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable rolling-checksum SIMD optimizations" >&5
+printf %s "checking whether to enable rolling-checksum SIMD optimizations... " >&6; }
+# Check whether --enable-roll-simd was given.
+if test ${enable_roll_simd+y}
+then :
+ enableval=$enable_roll_simd;
+fi
+
+
+# Clag is crashing with -g -O2, so we'll get rid of -g for now.
+CXXFLAGS=`echo "$CXXFLAGS" | sed 's/-g //'`
+
+
+if test x"$enable_roll_simd" = x""; then
+ case "$host_os" in
+ *linux*) ;;
+ *) enable_roll_simd=no ;;
+ esac
+fi
+
+if test x"$enable_roll_simd" != x"no"; then
+ # For x86-64 SIMD, g++ >=5 or clang++ >=7 is required
+ if test x"$host_cpu" = x"x86_64" || test x"$host_cpu" = x"amd64"; then
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+ if test x"$host" = x"$build"; then
+
+if test "$cross_compiling" = yes
+then :
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run test program while cross compiling
+See \`config.log' for more details" "$LINENO" 5; }
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <immintrin.h>
+__attribute__ ((target("default"))) int test_ssse3(int x) { return x; }
+__attribute__ ((target("default"))) int test_sse2(int x) { return x; }
+__attribute__ ((target("default"))) int test_avx2(int x) { return x; }
+__attribute__ ((target("ssse3"))) int test_ssse3(int x) { return x; }
+__attribute__ ((target("sse2"))) int test_sse2(int x) { return x; }
+__attribute__ ((target("avx2"))) int test_avx2(int x) { return x; }
+typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(1)));
+typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(1)));
+__attribute__ ((target("default"))) void more_testing(char* buf, int len) { }
+__attribute__ ((target("ssse3"))) void more_testing(char* buf, int len)
+{
+ int i;
+ for (i = 0; i < (len-32); i+=32) {
+ __m128i in8_1, in8_2;
+ in8_1 = _mm_lddqu_si128((__m128i_u*)&buf[i]);
+ in8_2 = _mm_lddqu_si128((__m128i_u*)&buf[i + 16]);
+ }
+}
+
+int
+main (void)
+{
+if (test_ssse3(42) != 42 || test_sse2(42) != 42 || test_avx2(42) != 42) exit(1);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"
+then :
+ CXX_OK=yes
+else $as_nop
+ CXX_OK=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <immintrin.h>
+__attribute__ ((target("default"))) int test_ssse3(int x) { return x; }
+__attribute__ ((target("default"))) int test_sse2(int x) { return x; }
+__attribute__ ((target("default"))) int test_avx2(int x) { return x; }
+__attribute__ ((target("ssse3"))) int test_ssse3(int x) { return x; }
+__attribute__ ((target("sse2"))) int test_sse2(int x) { return x; }
+__attribute__ ((target("avx2"))) int test_avx2(int x) { return x; }
+typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(1)));
+typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(1)));
+__attribute__ ((target("default"))) void more_testing(char* buf, int len) { }
+__attribute__ ((target("ssse3"))) void more_testing(char* buf, int len)
+{
+ int i;
+ for (i = 0; i < (len-32); i+=32) {
+ __m128i in8_1, in8_2;
+ in8_1 = _mm_lddqu_si128((__m128i_u*)&buf[i]);
+ in8_2 = _mm_lddqu_si128((__m128i_u*)&buf[i + 16]);
+ }
+}
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+ CXX_OK=yes
+else $as_nop
+ CXX_OK=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ fi
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ if test x"$CXX_OK" = x"yes"; then
+ # AC_MSG_RESULT() is called below.
+ ROLL_SIMD="$host_cpu"
+ elif test x"$enable_roll_simd" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: error" >&5
+printf "%s\n" "error" >&6; }
+ as_fn_error $? "The rolling-checksum SIMD compilation test failed.
+Omit --enable-roll-simd to continue without it." "$LINENO" 5
+ fi
+ elif test x"$enable_roll_simd" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unavailable" >&5
+printf "%s\n" "unavailable" >&6; }
+ as_fn_error $? "The rolling-checksum SIMD optimizations are currently x86_64|amd64 only.
+Omit --enable-roll-simd to continue without it." "$LINENO" 5
+ fi
+fi
+
+if test x"$ROLL_SIMD" != x""; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes ($ROLL_SIMD)" >&5
+printf "%s\n" "yes ($ROLL_SIMD)" >&6; }
+
+printf "%s\n" "#define USE_ROLL_SIMD 1" >>confdefs.h
+
+ ROLL_SIMD='$(ROLL_SIMD_'"$ROLL_SIMD)"
+ # We only use c++ for its target attribute dispatching, disable unneeded bulky features
+ CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-rtti"
+ # Apple often has "g++" as a symlink for clang. Try to find out the truth.
+ CXX_VERSION=`$CXX --version 2>/dev/null | head -n 2`
+ case "$CXX_VERSION" in
+ *clang*) CXXFLAGS="$CXXFLAGS -fno-slp-vectorize" ;; # avoid a performance hit
+ esac
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if assembler accepts noexecstack" >&5
+printf %s "checking if assembler accepts noexecstack... " >&6; }
+OLD_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -Wa,--noexecstack"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ NOEXECSTACK='-Wa,--noexecstack' ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ NOEXECSTACK='' ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+CFLAGS="$OLD_CFLAGS"
+
+
+# arrgh. libc in some old debian version screwed up the largefile
+# stuff, getting byte range locking wrong
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for broken largefile support" >&5
+printf %s "checking for broken largefile support... " >&6; }
+if test ${rsync_cv_HAVE_BROKEN_LARGEFILE+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_HAVE_BROKEN_LARGEFILE=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#define _FILE_OFFSET_BITS 64
+$ac_includes_default
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#elif defined HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+int main(void)
+{
+ struct flock lock;
+ int status;
+ char tpl[32] = "/tmp/locktest.XXXXXX";
+ int fd = mkstemp(tpl);
+ if (fd < 0) {
+ strcpy(tpl, "conftest.dat");
+ fd = open(tpl, O_CREAT|O_RDWR, 0600);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+ lock.l_pid = 0;
+ fcntl(fd,F_SETLK,&lock);
+ if (fork() == 0) {
+ lock.l_start = 1;
+ _exit(fcntl(fd,F_SETLK,&lock) == 0);
+ }
+ wait(&status);
+ unlink(tpl);
+ return WEXITSTATUS(status);
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_HAVE_BROKEN_LARGEFILE=yes
+else $as_nop
+ rsync_cv_HAVE_BROKEN_LARGEFILE=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_BROKEN_LARGEFILE" >&5
+printf "%s\n" "$rsync_cv_HAVE_BROKEN_LARGEFILE" >&6; }
+if test x"$rsync_cv_HAVE_BROKEN_LARGEFILE" != x"yes"; then
+ # Check whether --enable-largefile was given.
+if test ${enable_largefile+y}
+then :
+ enableval=$enable_largefile;
+fi
+
+if test "$enable_largefile" != no; then
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5
+printf %s "checking for special C compiler options needed for large files... " >&6; }
+if test ${ac_cv_sys_largefile_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_sys_largefile_CC=no
+ if test "$GCC" != yes; then
+ ac_save_CC=$CC
+ while :; do
+ # IRIX 6.2 and later do not support large files by default,
+ # so use the C compiler's -n32 option if that helps.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ CC="$CC -n32"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_sys_largefile_CC=' -n32'; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ break
+ done
+ CC=$ac_save_CC
+ rm -f conftest.$ac_ext
+ fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5
+printf "%s\n" "$ac_cv_sys_largefile_CC" >&6; }
+ if test "$ac_cv_sys_largefile_CC" != no; then
+ CC=$CC$ac_cv_sys_largefile_CC
+ fi
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5
+printf %s "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; }
+if test ${ac_cv_sys_file_offset_bits+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_sys_file_offset_bits=no; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_sys_file_offset_bits=64; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ac_cv_sys_file_offset_bits=unknown
+ break
+done
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5
+printf "%s\n" "$ac_cv_sys_file_offset_bits" >&6; }
+case $ac_cv_sys_file_offset_bits in #(
+ no | unknown) ;;
+ *)
+printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h
+;;
+esac
+rm -rf conftest*
+ if test $ac_cv_sys_file_offset_bits = unknown; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5
+printf %s "checking for _LARGE_FILES value needed for large files... " >&6; }
+if test ${ac_cv_sys_large_files+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_sys_large_files=no; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#define _LARGE_FILES 1
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_sys_large_files=1; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ac_cv_sys_large_files=unknown
+ break
+done
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5
+printf "%s\n" "$ac_cv_sys_large_files" >&6; }
+case $ac_cv_sys_large_files in #(
+ no | unknown) ;;
+ *)
+printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h
+;;
+esac
+rm -rf conftest*
+ fi
+fi
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable ipv6" >&5
+printf %s "checking whether to enable ipv6... " >&6; }
+# Check whether --enable-ipv6 was given.
+if test ${enable_ipv6+y}
+then :
+ enableval=$enable_ipv6; case "$enableval" in
+ no)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ ;;
+ *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define INET6 1" >>confdefs.h
+
+ ;;
+ esac
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+ /* AF_INET6 availability check */
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+main()
+{
+ if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
+ exit(1);
+ else
+ exit(0);
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define INET6 1" >>confdefs.h
+
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+
+
+# Check whether --enable-locale was given.
+if test ${enable_locale+y}
+then :
+ enableval=$enable_locale;
+fi
+
+
+if test x"$enable_locale" != x"no"; then
+ printf "%s\n" "#define CONFIG_LOCALE 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to call shutdown on all sockets" >&5
+printf %s "checking whether to call shutdown on all sockets... " >&6; }
+case $host_os in
+ *cygwin* ) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define SHUTDOWN_ALL_SOCKETS 1" >>confdefs.h
+
+ ;;
+ * ) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; };;
+esac
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable use of openssl crypto library" >&5
+printf %s "checking whether to enable use of openssl crypto library... " >&6; }
+# Check whether --enable-openssl was given.
+if test ${enable_openssl+y}
+then :
+ enableval=$enable_openssl;
+fi
+
+
+if test x"$enable_openssl" != x"no"; then
+ if test x"$ac_cv_header_openssl_md4_h" = x"yes" && test x"$ac_cv_header_openssl_md5_h" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing MD5_Init" >&5
+printf %s "checking for library containing MD5_Init... " >&6; }
+if test ${ac_cv_search_MD5_Init+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char MD5_Init ();
+int
+main (void)
+{
+return MD5_Init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' crypto
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_MD5_Init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_MD5_Init+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_MD5_Init+y}
+then :
+
+else $as_nop
+ ac_cv_search_MD5_Init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_MD5_Init" >&5
+printf "%s\n" "$ac_cv_search_MD5_Init" >&6; }
+ac_res=$ac_cv_search_MD5_Init
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ printf "%s\n" "#define USE_OPENSSL 1" >>confdefs.h
+
+ enable_openssl=yes
+else $as_nop
+ err_msg="$err_msg$nl- Failed to find MD5_Init function in openssl crypto lib.";
+ no_lib="$no_lib openssl"
+fi
+
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ err_msg="$err_msg$nl- Failed to find openssl/md4.h and openssl/md5.h for openssl crypto lib support."
+ no_lib="$no_lib openssl"
+ fi
+ if test x"$enable_md5_asm" != x"yes"; then
+ enable_md5_asm=no
+ fi
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+MD5_ASM=
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable MD5 ASM optimizations" >&5
+printf %s "checking whether to enable MD5 ASM optimizations... " >&6; }
+# Check whether --enable-md5-asm was given.
+if test ${enable_md5_asm+y}
+then :
+ enableval=$enable_md5_asm;
+fi
+
+
+if test x"$enable_md5_asm" = x""; then
+ case "$host_os" in
+ *linux*) ;;
+ *) enable_md5_asm=no ;;
+ esac
+fi
+
+if test x"$enable_md5_asm" != x"no"; then
+ if test x"$host_cpu" = x"x86_64" || test x"$host_cpu" = x"amd64"; then
+ MD5_ASM="$host_cpu"
+ elif test x"$enable_md5_asm" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unavailable" >&5
+printf "%s\n" "unavailable" >&6; }
+ as_fn_error $? "The ASM optimizations are currently x86_64|amd64 only.
+Omit --enable-md5-asm to continue without it." "$LINENO" 5
+ fi
+fi
+
+if test x"$MD5_ASM" != x""; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes ($MD5_ASM)" >&5
+printf "%s\n" "yes ($MD5_ASM)" >&6; }
+
+printf "%s\n" "#define USE_MD5_ASM 1" >>confdefs.h
+
+ MD5_ASM='$(MD5_ASM_'"$MD5_ASM)"
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+
+ROLL_ASM=
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable rolling-checksum ASM optimizations" >&5
+printf %s "checking whether to enable rolling-checksum ASM optimizations... " >&6; }
+# Check whether --enable-roll-asm was given.
+if test ${enable_roll_asm+y}
+then :
+ enableval=$enable_roll_asm;
+fi
+
+
+if test x"$ROLL_SIMD" = x""; then
+ enable_roll_asm=no
+fi
+
+if test x"$enable_roll_asm" = x"yes"; then
+ ROLL_ASM="$host_cpu"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes ($ROLL_ASM)" >&5
+printf "%s\n" "yes ($ROLL_ASM)" >&6; }
+
+printf "%s\n" "#define USE_ROLL_ASM 1" >>confdefs.h
+
+ ROLL_ASM='$(ROLL_ASM_'"$ROLL_ASM)"
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable xxhash checksum support" >&5
+printf %s "checking whether to enable xxhash checksum support... " >&6; }
+# Check whether --enable-xxhash was given.
+if test ${enable_xxhash+y}
+then :
+ enableval=$enable_xxhash;
+fi
+
+
+if test x"$enable_xxhash" != x"no"; then
+ if test x"$ac_cv_header_xxhash_h" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing XXH64_createState" >&5
+printf %s "checking for library containing XXH64_createState... " >&6; }
+if test ${ac_cv_search_XXH64_createState+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char XXH64_createState ();
+int
+main (void)
+{
+return XXH64_createState ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' xxhash
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_XXH64_createState=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_XXH64_createState+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_XXH64_createState+y}
+then :
+
+else $as_nop
+ ac_cv_search_XXH64_createState=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_XXH64_createState" >&5
+printf "%s\n" "$ac_cv_search_XXH64_createState" >&6; }
+ac_res=$ac_cv_search_XXH64_createState
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ printf "%s\n" "#define SUPPORT_XXHASH 1" >>confdefs.h
+
+else $as_nop
+ err_msg="$err_msg$nl- Failed to find XXH64_createState function in xxhash lib.";
+ no_lib="$no_lib xxhash"
+fi
+
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ err_msg="$err_msg$nl- Failed to find xxhash.h for xxhash checksum support.";
+ no_lib="$no_lib xxhash"
+ fi
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable zstd compression" >&5
+printf %s "checking whether to enable zstd compression... " >&6; }
+# Check whether --enable-zstd was given.
+if test ${enable_zstd+y}
+then :
+ enableval=$enable_zstd;
+fi
+
+
+if test x"$enable_zstd" != x"no"; then
+ if test x"$ac_cv_header_zstd_h" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing ZSTD_minCLevel" >&5
+printf %s "checking for library containing ZSTD_minCLevel... " >&6; }
+if test ${ac_cv_search_ZSTD_minCLevel+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char ZSTD_minCLevel ();
+int
+main (void)
+{
+return ZSTD_minCLevel ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' zstd
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_ZSTD_minCLevel=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_ZSTD_minCLevel+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_ZSTD_minCLevel+y}
+then :
+
+else $as_nop
+ ac_cv_search_ZSTD_minCLevel=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ZSTD_minCLevel" >&5
+printf "%s\n" "$ac_cv_search_ZSTD_minCLevel" >&6; }
+ac_res=$ac_cv_search_ZSTD_minCLevel
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ printf "%s\n" "#define SUPPORT_ZSTD 1" >>confdefs.h
+
+else $as_nop
+ err_msg="$err_msg$nl- Failed to find ZSTD_minCLevel function in zstd lib.";
+ no_lib="$no_lib zstd"
+fi
+
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ err_msg="$err_msg$nl- Failed to find zstd.h for zstd compression support.";
+ no_lib="$no_lib zstd"
+ fi
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable LZ4 compression" >&5
+printf %s "checking whether to enable LZ4 compression... " >&6; }
+# Check whether --enable-lz4 was given.
+if test ${enable_lz4+y}
+then :
+ enableval=$enable_lz4;
+fi
+
+
+if test x"$enable_lz4" != x"no"; then
+ if test x"$ac_cv_header_lz4_h" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing LZ4_compress_default" >&5
+printf %s "checking for library containing LZ4_compress_default... " >&6; }
+if test ${ac_cv_search_LZ4_compress_default+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char LZ4_compress_default ();
+int
+main (void)
+{
+return LZ4_compress_default ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' lz4
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_LZ4_compress_default=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_LZ4_compress_default+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_LZ4_compress_default+y}
+then :
+
+else $as_nop
+ ac_cv_search_LZ4_compress_default=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_LZ4_compress_default" >&5
+printf "%s\n" "$ac_cv_search_LZ4_compress_default" >&6; }
+ac_res=$ac_cv_search_LZ4_compress_default
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ printf "%s\n" "#define SUPPORT_LZ4 1" >>confdefs.h
+
+else $as_nop
+ err_msg="$err_msg$nl- Failed to find LZ4_compress_default function in lz4 lib.";
+ no_lib="$no_lib lz4"
+fi
+
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ err_msg="$err_msg$nl- Failed to find lz4.h for lz4 compression support."
+ no_lib="$no_lib lz4"
+ fi
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+if test x"$no_lib" != x; then
+ echo ""
+ echo "Configure found the following issues:"
+ echo "$err_msg"
+ echo ""
+ echo "See the INSTALL file for hints on how to install the missing libraries and/or"
+ echo "how to generate (or fetch) manpages:"
+ echo " https://github.com/WayneD/rsync/blob/master/INSTALL.md"
+ echo ""
+ echo "To disable one or more features, the relevant configure options are:"
+ for lib in $no_lib; do
+ echo " --disable-$lib"
+ done
+ echo ""
+ as_fn_error $? "Aborting configure run" "$LINENO" 5
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if makedev takes 3 args" >&5
+printf %s "checking if makedev takes 3 args... " >&6; }
+if test ${rsync_cv_MAKEDEV_TAKES_3_ARGS+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_MAKEDEV_TAKES_3_ARGS=no
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
+# define makedev mkdev
+# endif
+#elif defined MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif
+
+int main(void)
+{
+ dev_t dev = makedev(0, 5, 7);
+ if (major(dev) != 5 || minor(dev) != 7)
+ return 1;
+ return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_MAKEDEV_TAKES_3_ARGS=yes
+else $as_nop
+ rsync_cv_MAKEDEV_TAKES_3_ARGS=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_MAKEDEV_TAKES_3_ARGS" >&5
+printf "%s\n" "$rsync_cv_MAKEDEV_TAKES_3_ARGS" >&6; }
+if test x"$rsync_cv_MAKEDEV_TAKES_3_ARGS" = x"yes"; then
+
+printf "%s\n" "#define MAKEDEV_TAKES_3_ARGS 1" >>confdefs.h
+
+fi
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int" >&5
+printf %s "checking size of int... " >&6; }
+if test ${ac_cv_sizeof_int+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_int" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_int=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5
+printf "%s\n" "$ac_cv_sizeof_int" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_INT $ac_cv_sizeof_int" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
+printf %s "checking size of long... " >&6; }
+if test ${ac_cv_sizeof_long+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_long" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_long=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
+printf "%s\n" "$ac_cv_sizeof_long" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_LONG $ac_cv_sizeof_long" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+printf %s "checking size of long long... " >&6; }
+if test ${ac_cv_sizeof_long_long+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_long_long" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_long_long=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+printf "%s\n" "$ac_cv_sizeof_long_long" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of short" >&5
+printf %s "checking size of short... " >&6; }
+if test ${ac_cv_sizeof_short+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_short" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (short)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_short=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5
+printf "%s\n" "$ac_cv_sizeof_short" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_SHORT $ac_cv_sizeof_short" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int16_t" >&5
+printf %s "checking size of int16_t... " >&6; }
+if test ${ac_cv_sizeof_int16_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int16_t))" "ac_cv_sizeof_int16_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_int16_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int16_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_int16_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int16_t" >&5
+printf "%s\n" "$ac_cv_sizeof_int16_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_INT16_T $ac_cv_sizeof_int16_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of uint16_t" >&5
+printf %s "checking size of uint16_t... " >&6; }
+if test ${ac_cv_sizeof_uint16_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (uint16_t))" "ac_cv_sizeof_uint16_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_uint16_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (uint16_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_uint16_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_uint16_t" >&5
+printf "%s\n" "$ac_cv_sizeof_uint16_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_UINT16_T $ac_cv_sizeof_uint16_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int32_t" >&5
+printf %s "checking size of int32_t... " >&6; }
+if test ${ac_cv_sizeof_int32_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int32_t))" "ac_cv_sizeof_int32_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_int32_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int32_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_int32_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int32_t" >&5
+printf "%s\n" "$ac_cv_sizeof_int32_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_INT32_T $ac_cv_sizeof_int32_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of uint32_t" >&5
+printf %s "checking size of uint32_t... " >&6; }
+if test ${ac_cv_sizeof_uint32_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (uint32_t))" "ac_cv_sizeof_uint32_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_uint32_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (uint32_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_uint32_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_uint32_t" >&5
+printf "%s\n" "$ac_cv_sizeof_uint32_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_UINT32_T $ac_cv_sizeof_uint32_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int64_t" >&5
+printf %s "checking size of int64_t... " >&6; }
+if test ${ac_cv_sizeof_int64_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int64_t))" "ac_cv_sizeof_int64_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_int64_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int64_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_int64_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int64_t" >&5
+printf "%s\n" "$ac_cv_sizeof_int64_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_INT64_T $ac_cv_sizeof_int64_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of off_t" >&5
+printf %s "checking size of off_t... " >&6; }
+if test ${ac_cv_sizeof_off_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off_t))" "ac_cv_sizeof_off_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_off_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (off_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_off_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_off_t" >&5
+printf "%s\n" "$ac_cv_sizeof_off_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_OFF_T $ac_cv_sizeof_off_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of off64_t" >&5
+printf %s "checking size of off64_t... " >&6; }
+if test ${ac_cv_sizeof_off64_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off64_t))" "ac_cv_sizeof_off64_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_off64_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (off64_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_off64_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_off64_t" >&5
+printf "%s\n" "$ac_cv_sizeof_off64_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_OFF64_T $ac_cv_sizeof_off64_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5
+printf %s "checking size of time_t... " >&6; }
+if test ${ac_cv_sizeof_time_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_time_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (time_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_time_t=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5
+printf "%s\n" "$ac_cv_sizeof_time_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_TIME_T $ac_cv_sizeof_time_t" >>confdefs.h
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of char*" >&5
+printf %s "checking size of char*... " >&6; }
+if test ${ac_cv_sizeof_charp+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (char*))" "ac_cv_sizeof_charp" "$ac_includes_default"
+then :
+
+else $as_nop
+ if test "$ac_cv_type_charp" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (char*)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_charp=0
+ fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_charp" >&5
+printf "%s\n" "$ac_cv_sizeof_charp" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_CHARP $ac_cv_sizeof_charp" >>confdefs.h
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5
+printf %s "checking for inline... " >&6; }
+if test ${ac_cv_c_inline+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo (void) {return 0; }
+$ac_kw foo_t foo (void) {return 0; }
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_c_inline=$ac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5
+printf "%s\n" "$ac_cv_c_inline" >&6; }
+
+case $ac_cv_c_inline in
+ inline | yes) ;;
+ *)
+ case $ac_cv_c_inline in
+ no) ac_val=;;
+ *) ac_val=$ac_cv_c_inline;;
+ esac
+ cat >>confdefs.h <<_ACEOF
+#ifndef __cplusplus
+#define inline $ac_val
+#endif
+_ACEOF
+ ;;
+esac
+
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for long double with more range or precision than double" >&5
+printf %s "checking for long double with more range or precision than double... " >&6; }
+if test ${ac_cv_type_long_double_wider+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <float.h>
+ long double const a[] =
+ {
+ 0.0L, DBL_MIN, DBL_MAX, DBL_EPSILON,
+ LDBL_MIN, LDBL_MAX, LDBL_EPSILON
+ };
+ long double
+ f (long double x)
+ {
+ return ((x + (unsigned long int) 10) * (-1 / x) + a[0]
+ + (x ? f (x) : 'c'));
+ }
+
+int
+main (void)
+{
+static int test_array [1 - 2 * !((0 < ((DBL_MAX_EXP < LDBL_MAX_EXP)
+ + (DBL_MANT_DIG < LDBL_MANT_DIG)
+ - (LDBL_MAX_EXP < DBL_MAX_EXP)
+ - (LDBL_MANT_DIG < DBL_MANT_DIG)))
+ && (int) LDBL_EPSILON == 0
+ )];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_type_long_double_wider=yes
+else $as_nop
+ ac_cv_type_long_double_wider=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_double_wider" >&5
+printf "%s\n" "$ac_cv_type_long_double_wider" >&6; }
+ if test $ac_cv_type_long_double_wider = yes; then
+
+printf "%s\n" "#define HAVE_LONG_DOUBLE_WIDER 1" >>confdefs.h
+
+ fi
+
+ac_cv_c_long_double=$ac_cv_type_long_double_wider
+if test $ac_cv_c_long_double = yes; then
+
+printf "%s\n" "#define HAVE_LONG_DOUBLE 1" >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+printf %s "checking for uid_t in sys/types.h... " >&6; }
+if test ${ac_cv_type_uid_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1
+then :
+ ac_cv_type_uid_t=yes
+else $as_nop
+ ac_cv_type_uid_t=no
+fi
+rm -rf conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+printf "%s\n" "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+printf "%s\n" "#define uid_t int" >>confdefs.h
+
+
+printf "%s\n" "#define gid_t int" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
+if test "x$ac_cv_type_mode_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_MODE_T 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
+if test "x$ac_cv_type_off_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_OFF_T 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_SIZE_T 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_PID_T 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_type "$LINENO" "id_t" "ac_cv_type_id_t" "$ac_includes_default"
+if test "x$ac_cv_type_id_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_ID_T 1" >>confdefs.h
+
+
+fi
+
+if test "$cross_compiling" = no; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking type of array argument to getgroups" >&5
+printf %s "checking type of array argument to getgroups... " >&6; }
+if test ${ac_cv_type_getgroups+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
+ ac_cv_type_getgroups=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Thanks to Mike Rendell for this test. */
+$ac_includes_default
+#define NGID 256
+#undef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+int
+main (void)
+{
+ gid_t gidset[NGID];
+ int i, n;
+ union { gid_t gval; long int lval; } val;
+
+ val.lval = -1;
+ for (i = 0; i < NGID; i++)
+ gidset[i] = val.gval;
+ n = getgroups (sizeof (gidset) / MAX (sizeof (int), sizeof (gid_t)) - 1,
+ gidset);
+ /* Exit non-zero if getgroups seems to require an array of ints. This
+ happens when gid_t is short int but getgroups modifies an array
+ of ints. */
+ return n > 0 && gidset[n] != val.gval;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ ac_cv_type_getgroups=gid_t
+else $as_nop
+ ac_cv_type_getgroups=int
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+if test $ac_cv_type_getgroups = cross; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <unistd.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "getgroups.*int.*gid_t" >/dev/null 2>&1
+then :
+ ac_cv_type_getgroups=gid_t
+else $as_nop
+ ac_cv_type_getgroups=int
+fi
+rm -rf conftest*
+
+fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_getgroups" >&5
+printf "%s\n" "$ac_cv_type_getgroups" >&6; }
+
+printf "%s\n" "#define GETGROUPS_T $ac_cv_type_getgroups" >>confdefs.h
+
+
+else
+
+printf "%s\n" "#define GETGROUPS_T gid_t" >>confdefs.h
+
+fi
+ac_fn_c_check_member "$LINENO" "struct stat" "st_rdev" "ac_cv_member_struct_stat_st_rdev" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+"
+if test "x$ac_cv_member_struct_stat_st_rdev" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_RDEV 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimensec" "ac_cv_member_struct_stat_st_mtimensec" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+"
+if test "x$ac_cv_member_struct_stat_st_mtimensec" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimespec.tv_nsec" "ac_cv_member_struct_stat_st_mtimespec_tv_nsec" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+"
+if test "x$ac_cv_member_struct_stat_st_mtimespec_tv_nsec" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim.tv_nsec" "ac_cv_member_struct_stat_st_mtim_tv_nsec" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+"
+if test "x$ac_cv_member_struct_stat_st_mtim_tv_nsec" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1" >>confdefs.h
+
+
+fi
+
+
+
+ ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "#include <sys/types.h>
+#include <sys/socket.h>
+"
+if test "x$ac_cv_type_socklen_t" = xyes
+then :
+
+else $as_nop
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for socklen_t equivalent" >&5
+printf %s "checking for socklen_t equivalent... " >&6; }
+ if test ${rsync_cv_socklen_t_equiv+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ rsync_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+ int getpeername (int, $arg2 *, $t *);
+
+int
+main (void)
+{
+
+ $t len;
+ getpeername(0,0,&len);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+ rsync_cv_socklen_t_equiv="$t"
+ break
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ done
+ done
+
+ if test "x$rsync_cv_socklen_t_equiv" = x; then
+ as_fn_error $? "Cannot find a type to use in place of socklen_t" "$LINENO" 5
+ fi
+
+fi
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_socklen_t_equiv" >&5
+printf "%s\n" "$rsync_cv_socklen_t_equiv" >&6; }
+
+printf "%s\n" "#define socklen_t $rsync_cv_socklen_t_equiv" >>confdefs.h
+
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for errno in errno.h" >&5
+printf %s "checking for errno in errno.h... " >&6; }
+if test ${rsync_cv_errno+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <errno.h>
+int
+main (void)
+{
+int i = errno
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ rsync_cv_errno=yes
+else $as_nop
+ rsync_cv_have_errno_decl=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_errno" >&5
+printf "%s\n" "$rsync_cv_errno" >&6; }
+if test x"$rsync_cv_errno" = x"yes"; then
+
+printf "%s\n" "#define HAVE_ERRNO_DECL 1" >>confdefs.h
+
+fi
+
+# The following test taken from the cvs sources
+# If we can't find connect, try looking in -lsocket, -lnsl, and -linet.
+# These need checks to be before checks for any other functions that
+# might be in the same libraries.
+# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has
+# libsocket.so which has a bad implementation of gethostbyname (it
+# only looks in /etc/hosts), so we only look for -lsocket if we need
+# it.
+ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect"
+if test "x$ac_cv_func_connect" = xyes
+then :
+ printf "%s\n" "#define HAVE_CONNECT 1" >>confdefs.h
+
+fi
+
+if test x"$ac_cv_func_connect" = x"no"; then
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for printf in -lnsl_s" >&5
+printf %s "checking for printf in -lnsl_s... " >&6; }
+if test ${ac_cv_lib_nsl_s_printf+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl_s $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char printf ();
+int
+main (void)
+{
+return printf ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_nsl_s_printf=yes
+else $as_nop
+ ac_cv_lib_nsl_s_printf=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_s_printf" >&5
+printf "%s\n" "$ac_cv_lib_nsl_s_printf" >&6; }
+if test "x$ac_cv_lib_nsl_s_printf" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBNSL_S 1" >>confdefs.h
+
+ LIBS="-lnsl_s $LIBS"
+
+fi
+ ;;
+ esac
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for printf in -lnsl" >&5
+printf %s "checking for printf in -lnsl... " >&6; }
+if test ${ac_cv_lib_nsl_printf+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char printf ();
+int
+main (void)
+{
+return printf ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_nsl_printf=yes
+else $as_nop
+ ac_cv_lib_nsl_printf=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_printf" >&5
+printf "%s\n" "$ac_cv_lib_nsl_printf" >&6; }
+if test "x$ac_cv_lib_nsl_printf" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBNSL 1" >>confdefs.h
+
+ LIBS="-lnsl $LIBS"
+
+fi
+ ;;
+ esac
+ case "$LIBS" in
+ *-lsocket*) ;;
+ *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for connect in -lsocket" >&5
+printf %s "checking for connect in -lsocket... " >&6; }
+if test ${ac_cv_lib_socket_connect+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char connect ();
+int
+main (void)
+{
+return connect ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_socket_connect=yes
+else $as_nop
+ ac_cv_lib_socket_connect=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_connect" >&5
+printf "%s\n" "$ac_cv_lib_socket_connect" >&6; }
+if test "x$ac_cv_lib_socket_connect" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBSOCKET 1" >>confdefs.h
+
+ LIBS="-lsocket $LIBS"
+
+fi
+ ;;
+ esac
+ case "$LIBS" in
+ *-linet*) ;;
+ *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for connect in -linet" >&5
+printf %s "checking for connect in -linet... " >&6; }
+if test ${ac_cv_lib_inet_connect+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-linet $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char connect ();
+int
+main (void)
+{
+return connect ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_inet_connect=yes
+else $as_nop
+ ac_cv_lib_inet_connect=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inet_connect" >&5
+printf "%s\n" "$ac_cv_lib_inet_connect" >&6; }
+if test "x$ac_cv_lib_inet_connect" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBINET 1" >>confdefs.h
+
+ LIBS="-linet $LIBS"
+
+fi
+ ;;
+ esac
+ if test x"$ac_cv_lib_socket_connect" = x"yes" ||
+ test x"$ac_cv_lib_inet_connect" = x"yes"; then
+ # ac_cv_func_connect=yes
+ # don't! it would cause AC_CHECK_FUNC to succeed next time configure is run
+
+printf "%s\n" "#define HAVE_CONNECT 1" >>confdefs.h
+
+ fi
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing inet_ntop" >&5
+printf %s "checking for library containing inet_ntop... " >&6; }
+if test ${ac_cv_search_inet_ntop+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char inet_ntop ();
+int
+main (void)
+{
+return inet_ntop ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' resolv
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_inet_ntop=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_inet_ntop+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_inet_ntop+y}
+then :
+
+else $as_nop
+ ac_cv_search_inet_ntop=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_ntop" >&5
+printf "%s\n" "$ac_cv_search_inet_ntop" >&6; }
+ac_res=$ac_cv_search_inet_ntop
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+# For OS X, Solaris, HP-UX, etc.: figure out if -liconv is needed. We'll
+# accept either iconv_open or libiconv_open, since some include files map
+# the former to the latter.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing iconv_open" >&5
+printf %s "checking for library containing iconv_open... " >&6; }
+if test ${ac_cv_search_iconv_open+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char iconv_open ();
+int
+main (void)
+{
+return iconv_open ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' iconv
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_iconv_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_iconv_open+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_iconv_open+y}
+then :
+
+else $as_nop
+ ac_cv_search_iconv_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_iconv_open" >&5
+printf "%s\n" "$ac_cv_search_iconv_open" >&6; }
+ac_res=$ac_cv_search_iconv_open
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing libiconv_open" >&5
+printf %s "checking for library containing libiconv_open... " >&6; }
+if test ${ac_cv_search_libiconv_open+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char libiconv_open ();
+int
+main (void)
+{
+return libiconv_open ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' iconv
+do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_search_libiconv_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext
+ if test ${ac_cv_search_libiconv_open+y}
+then :
+ break
+fi
+done
+if test ${ac_cv_search_libiconv_open+y}
+then :
+
+else $as_nop
+ ac_cv_search_libiconv_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_libiconv_open" >&5
+printf "%s\n" "$ac_cv_search_libiconv_open" >&6; }
+ac_res=$ac_cv_search_libiconv_open
+if test "$ac_res" != no
+then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for iconv declaration" >&5
+printf %s "checking for iconv declaration... " >&6; }
+if test ${am_cv_proto_iconv+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ am_cv_proto_iconv_arg1=""
+else $as_nop
+ am_cv_proto_iconv_arg1="const"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"
+fi
+
+ am_cv_proto_iconv=`echo "$am_cv_proto_iconv" | tr -s ' ' | sed 's/( /(/'`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${ac_t:-
+ }$am_cv_proto_iconv" >&5
+printf "%s\n" "${ac_t:-
+ }$am_cv_proto_iconv" >&6; }
+
+printf "%s\n" "#define ICONV_CONST $am_cv_proto_iconv_arg1" >>confdefs.h
+
+
+
+ac_fn_c_check_func "$LINENO" "inet_ntop" "ac_cv_func_inet_ntop"
+if test "x$ac_cv_func_inet_ntop" = xyes
+then :
+ printf "%s\n" "#define HAVE_INET_NTOP 1" >>confdefs.h
+
+else $as_nop
+ case " $LIBOBJS " in
+ *" inet_ntop.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS inet_ntop.$ac_objext"
+ ;;
+esac
+
+fi
+ac_fn_c_check_func "$LINENO" "inet_pton" "ac_cv_func_inet_pton"
+if test "x$ac_cv_func_inet_pton" = xyes
+then :
+ printf "%s\n" "#define HAVE_INET_PTON 1" >>confdefs.h
+
+else $as_nop
+ case " $LIBOBJS " in
+ *" inet_pton.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS inet_pton.$ac_objext"
+ ;;
+esac
+
+fi
+
+
+
+cv=`echo "struct addrinfo" | sed 'y%./+- %__p__%'`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct addrinfo" >&5
+printf %s "checking for struct addrinfo... " >&6; }
+if eval test \${ac_cv_type_$cv+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+$ac_includes_default
+#include <netdb.h>
+int
+main (void)
+{
+struct addrinfo foo;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "ac_cv_type_$cv=yes"
+else $as_nop
+ eval "ac_cv_type_$cv=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+ac_foo=`eval echo \\$ac_cv_type_$cv`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_foo" >&5
+printf "%s\n" "$ac_foo" >&6; }
+if test "$ac_foo" = yes; then
+ ac_tr_hdr=HAVE_`echo struct addrinfo | sed 'y%abcdefghijklmnopqrstuvwxyz./- %ABCDEFGHIJKLMNOPQRSTUVWXYZ____%'`
+if false; then
+ ac_fn_c_check_type "$LINENO" "struct addrinfo" "ac_cv_type_struct_addrinfo" "$ac_includes_default"
+if test "x$ac_cv_type_struct_addrinfo" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_ADDRINFO 1" >>confdefs.h
+
+
+fi
+
+fi
+
+printf "%s\n" "#define $ac_tr_hdr 1" >>confdefs.h
+
+fi
+
+
+cv=`echo "struct sockaddr_storage" | sed 'y%./+- %__p__%'`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct sockaddr_storage" >&5
+printf %s "checking for struct sockaddr_storage... " >&6; }
+if eval test \${ac_cv_type_$cv+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+$ac_includes_default
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+int
+main (void)
+{
+struct sockaddr_storage foo;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "ac_cv_type_$cv=yes"
+else $as_nop
+ eval "ac_cv_type_$cv=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+ac_foo=`eval echo \\$ac_cv_type_$cv`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_foo" >&5
+printf "%s\n" "$ac_foo" >&6; }
+if test "$ac_foo" = yes; then
+ ac_tr_hdr=HAVE_`echo struct sockaddr_storage | sed 'y%abcdefghijklmnopqrstuvwxyz./- %ABCDEFGHIJKLMNOPQRSTUVWXYZ____%'`
+if false; then
+ ac_fn_c_check_type "$LINENO" "struct sockaddr_storage" "ac_cv_type_struct_sockaddr_storage" "$ac_includes_default"
+if test "x$ac_cv_type_struct_sockaddr_storage" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_SOCKADDR_STORAGE 1" >>confdefs.h
+
+
+fi
+
+fi
+
+printf "%s\n" "#define $ac_tr_hdr 1" >>confdefs.h
+
+fi
+
+
+# Irix 6.5 has getaddrinfo but not the corresponding defines, so use
+# builtin getaddrinfo if one of the defines don't exist
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether defines needed by getaddrinfo exist" >&5
+printf %s "checking whether defines needed by getaddrinfo exist... " >&6; }
+if test ${rsync_cv_HAVE_GETADDR_DEFINES+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef AI_PASSIVE
+yes
+#endif
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1
+then :
+ rsync_cv_HAVE_GETADDR_DEFINES=yes
+else $as_nop
+ rsync_cv_HAVE_GETADDR_DEFINES=no
+fi
+rm -rf conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_GETADDR_DEFINES" >&5
+printf "%s\n" "$rsync_cv_HAVE_GETADDR_DEFINES" >&6; }
+if test x"$rsync_cv_HAVE_GETADDR_DEFINES" = x"yes" && test x"$ac_cv_type_struct_addrinfo" = x"yes"
+then :
+
+ # Tru64 UNIX has getaddrinfo() but has it renamed in libc as
+ # something else so we must include <netdb.h> to get the
+ # redefinition.
+
+ for ac_func in getaddrinfo
+do :
+ ac_fn_c_check_func "$LINENO" "getaddrinfo" "ac_cv_func_getaddrinfo"
+if test "x$ac_cv_func_getaddrinfo" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETADDRINFO 1" >>confdefs.h
+
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getaddrinfo by including <netdb.h>" >&5
+printf %s "checking for getaddrinfo by including <netdb.h>... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netdb.h>
+int
+main (void)
+{
+getaddrinfo(NULL, NULL, NULL, NULL);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define HAVE_GETADDRINFO 1" >>confdefs.h
+
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ case " $LIBOBJS " in
+ *" getaddrinfo.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS getaddrinfo.$ac_objext"
+ ;;
+esac
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+
+done
+
+else $as_nop
+ case " $LIBOBJS " in
+ *" getaddrinfo.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS getaddrinfo.$ac_objext"
+ ;;
+esac
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct sockaddr" "sa_len" "ac_cv_member_struct_sockaddr_sa_len" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+"
+if test "x$ac_cv_member_struct_sockaddr_sa_len" = xyes
+then :
+
+printf "%s\n" "#define HAVE_SOCKADDR_LEN 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_member "$LINENO" "struct sockaddr_in" "sin_len" "ac_cv_member_struct_sockaddr_in_sin_len" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+
+"
+if test "x$ac_cv_member_struct_sockaddr_in_sin_len" = xyes
+then :
+
+printf "%s\n" "#define HAVE_SOCKADDR_IN_LEN 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_member "$LINENO" "struct sockaddr_un" "sun_len" "ac_cv_member_struct_sockaddr_un_sun_len" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+
+"
+if test "x$ac_cv_member_struct_sockaddr_un_sun_len" = xyes
+then :
+
+printf "%s\n" "#define HAVE_SOCKADDR_UN_LEN 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_member "$LINENO" "struct sockaddr_in6" "sin6_scope_id" "ac_cv_member_struct_sockaddr_in6_sin6_scope_id" "
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+
+"
+if test "x$ac_cv_member_struct_sockaddr_in6_sin6_scope_id" = xyes
+then :
+
+printf "%s\n" "#define HAVE_SOCKADDR_IN6_SCOPE_ID 1" >>confdefs.h
+
+fi
+
+
+
+cv=`echo "struct stat64" | sed 'y%./+- %__p__%'`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct stat64" >&5
+printf %s "checking for struct stat64... " >&6; }
+if eval test \${ac_cv_type_$cv+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+$ac_includes_default
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+
+int
+main (void)
+{
+struct stat64 foo;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "ac_cv_type_$cv=yes"
+else $as_nop
+ eval "ac_cv_type_$cv=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+ac_foo=`eval echo \\$ac_cv_type_$cv`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_foo" >&5
+printf "%s\n" "$ac_foo" >&6; }
+if test "$ac_foo" = yes; then
+ ac_tr_hdr=HAVE_`echo struct stat64 | sed 'y%abcdefghijklmnopqrstuvwxyz./- %ABCDEFGHIJKLMNOPQRSTUVWXYZ____%'`
+if false; then
+ ac_fn_c_check_type "$LINENO" "struct stat64" "ac_cv_type_struct_stat64" "$ac_includes_default"
+if test "x$ac_cv_type_struct_stat64" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT64 1" >>confdefs.h
+
+
+fi
+
+fi
+
+printf "%s\n" "#define $ac_tr_hdr 1" >>confdefs.h
+
+fi
+
+
+# if we can't find strcasecmp, look in -lresolv (for Unixware at least)
+#
+ac_fn_c_check_func "$LINENO" "strcasecmp" "ac_cv_func_strcasecmp"
+if test "x$ac_cv_func_strcasecmp" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRCASECMP 1" >>confdefs.h
+
+fi
+
+if test x"$ac_cv_func_strcasecmp" = x"no"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for strcasecmp in -lresolv" >&5
+printf %s "checking for strcasecmp in -lresolv... " >&6; }
+if test ${ac_cv_lib_resolv_strcasecmp+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lresolv $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char strcasecmp ();
+int
+main (void)
+{
+return strcasecmp ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_resolv_strcasecmp=yes
+else $as_nop
+ ac_cv_lib_resolv_strcasecmp=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_strcasecmp" >&5
+printf "%s\n" "$ac_cv_lib_resolv_strcasecmp" >&6; }
+if test "x$ac_cv_lib_resolv_strcasecmp" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBRESOLV 1" >>confdefs.h
+
+ LIBS="-lresolv $LIBS"
+
+fi
+
+fi
+
+ac_fn_c_check_func "$LINENO" "aclsort" "ac_cv_func_aclsort"
+if test "x$ac_cv_func_aclsort" = xyes
+then :
+ printf "%s\n" "#define HAVE_ACLSORT 1" >>confdefs.h
+
+fi
+
+if test x"$ac_cv_func_aclsort" = x"no"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for aclsort in -lsec" >&5
+printf %s "checking for aclsort in -lsec... " >&6; }
+if test ${ac_cv_lib_sec_aclsort+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsec $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char aclsort ();
+int
+main (void)
+{
+return aclsort ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_sec_aclsort=yes
+else $as_nop
+ ac_cv_lib_sec_aclsort=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sec_aclsort" >&5
+printf "%s\n" "$ac_cv_lib_sec_aclsort" >&6; }
+if test "x$ac_cv_lib_sec_aclsort" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBSEC 1" >>confdefs.h
+
+ LIBS="-lsec $LIBS"
+
+fi
+
+fi
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether utime accepts a null argument" >&5
+printf %s "checking whether utime accepts a null argument... " >&6; }
+if test ${ac_cv_func_utime_null+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ rm -f conftest.data; >conftest.data
+# Sequent interprets utime(file, 0) to mean use start of epoch. Wrong.
+if test "$cross_compiling" = yes
+then :
+ ac_cv_func_utime_null='guessing yes'
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+ #ifdef HAVE_UTIME_H
+ # include <utime.h>
+ #endif
+int
+main (void)
+{
+struct stat s, t;
+ return ! (stat ("conftest.data", &s) == 0
+ && utime ("conftest.data", 0) == 0
+ && stat ("conftest.data", &t) == 0
+ && t.st_mtime >= s.st_mtime
+ && t.st_mtime - s.st_mtime < 120);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ ac_cv_func_utime_null=yes
+else $as_nop
+ ac_cv_func_utime_null=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_utime_null" >&5
+printf "%s\n" "$ac_cv_func_utime_null" >&6; }
+if test "x$ac_cv_func_utime_null" != xno; then
+ ac_cv_func_utime_null=yes
+
+printf "%s\n" "#define HAVE_UTIME_NULL 1" >>confdefs.h
+
+fi
+rm -f conftest.data
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes
+then :
+
+else $as_nop
+
+printf "%s\n" "#define size_t unsigned int" >>confdefs.h
+
+fi
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments. Useless!
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working alloca.h" >&5
+printf %s "checking for working alloca.h... " >&6; }
+if test ${ac_cv_working_alloca_h+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <alloca.h>
+int
+main (void)
+{
+char *p = (char *) alloca (2 * sizeof (int));
+ if (p) return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_working_alloca_h=yes
+else $as_nop
+ ac_cv_working_alloca_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_alloca_h" >&5
+printf "%s\n" "$ac_cv_working_alloca_h" >&6; }
+if test $ac_cv_working_alloca_h = yes; then
+
+printf "%s\n" "#define HAVE_ALLOCA_H 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for alloca" >&5
+printf %s "checking for alloca... " >&6; }
+if test ${ac_cv_func_alloca_works+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test $ac_cv_working_alloca_h = yes; then
+ ac_cv_func_alloca_works=yes
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stddef.h>
+#ifndef alloca
+# ifdef __GNUC__
+# define alloca __builtin_alloca
+# elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+# else
+# ifdef __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+# endif
+#endif
+
+int
+main (void)
+{
+char *p = (char *) alloca (1);
+ if (p) return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_func_alloca_works=yes
+else $as_nop
+ ac_cv_func_alloca_works=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_alloca_works" >&5
+printf "%s\n" "$ac_cv_func_alloca_works" >&6; }
+fi
+
+if test $ac_cv_func_alloca_works = yes; then
+
+printf "%s\n" "#define HAVE_ALLOCA 1" >>confdefs.h
+
+else
+ # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+# that cause trouble. Some versions do not even contain alloca or
+# contain a buggy version. If you still want to use their alloca,
+# use ar to extract alloca.o from them instead of compiling alloca.c.
+
+ALLOCA=\${LIBOBJDIR}alloca.$ac_objext
+
+printf "%s\n" "#define C_ALLOCA 1" >>confdefs.h
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking stack direction for C alloca" >&5
+printf %s "checking stack direction for C alloca... " >&6; }
+if test ${ac_cv_c_stack_direction+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
+ ac_cv_c_stack_direction=0
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+find_stack_direction (int *addr, int depth)
+{
+ int dir, dummy = 0;
+ if (! addr)
+ addr = &dummy;
+ *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1;
+ dir = depth ? find_stack_direction (addr, depth - 1) : 0;
+ return dir + dummy;
+}
+
+int
+main (int argc, char **argv)
+{
+ return find_stack_direction (0, argc + !argv + 20) < 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ ac_cv_c_stack_direction=1
+else $as_nop
+ ac_cv_c_stack_direction=-1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_stack_direction" >&5
+printf "%s\n" "$ac_cv_c_stack_direction" >&6; }
+printf "%s\n" "#define STACK_DIRECTION $ac_cv_c_stack_direction" >>confdefs.h
+
+
+fi
+
+ac_fn_c_check_func "$LINENO" "waitpid" "ac_cv_func_waitpid"
+if test "x$ac_cv_func_waitpid" = xyes
+then :
+ printf "%s\n" "#define HAVE_WAITPID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wait4" "ac_cv_func_wait4"
+if test "x$ac_cv_func_wait4" = xyes
+then :
+ printf "%s\n" "#define HAVE_WAIT4 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd"
+if test "x$ac_cv_func_getcwd" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETCWD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "chown" "ac_cv_func_chown"
+if test "x$ac_cv_func_chown" = xyes
+then :
+ printf "%s\n" "#define HAVE_CHOWN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "chmod" "ac_cv_func_chmod"
+if test "x$ac_cv_func_chmod" = xyes
+then :
+ printf "%s\n" "#define HAVE_CHMOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lchmod" "ac_cv_func_lchmod"
+if test "x$ac_cv_func_lchmod" = xyes
+then :
+ printf "%s\n" "#define HAVE_LCHMOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mknod" "ac_cv_func_mknod"
+if test "x$ac_cv_func_mknod" = xyes
+then :
+ printf "%s\n" "#define HAVE_MKNOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mkfifo" "ac_cv_func_mkfifo"
+if test "x$ac_cv_func_mkfifo" = xyes
+then :
+ printf "%s\n" "#define HAVE_MKFIFO 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fchmod" "ac_cv_func_fchmod"
+if test "x$ac_cv_func_fchmod" = xyes
+then :
+ printf "%s\n" "#define HAVE_FCHMOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fstat" "ac_cv_func_fstat"
+if test "x$ac_cv_func_fstat" = xyes
+then :
+ printf "%s\n" "#define HAVE_FSTAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "ftruncate" "ac_cv_func_ftruncate"
+if test "x$ac_cv_func_ftruncate" = xyes
+then :
+ printf "%s\n" "#define HAVE_FTRUNCATE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strchr" "ac_cv_func_strchr"
+if test "x$ac_cv_func_strchr" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRCHR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "readlink" "ac_cv_func_readlink"
+if test "x$ac_cv_func_readlink" = xyes
+then :
+ printf "%s\n" "#define HAVE_READLINK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "link" "ac_cv_func_link"
+if test "x$ac_cv_func_link" = xyes
+then :
+ printf "%s\n" "#define HAVE_LINK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "utime" "ac_cv_func_utime"
+if test "x$ac_cv_func_utime" = xyes
+then :
+ printf "%s\n" "#define HAVE_UTIME 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "utimes" "ac_cv_func_utimes"
+if test "x$ac_cv_func_utimes" = xyes
+then :
+ printf "%s\n" "#define HAVE_UTIMES 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lutimes" "ac_cv_func_lutimes"
+if test "x$ac_cv_func_lutimes" = xyes
+then :
+ printf "%s\n" "#define HAVE_LUTIMES 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime"
+if test "x$ac_cv_func_strftime" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRFTIME 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "chflags" "ac_cv_func_chflags"
+if test "x$ac_cv_func_chflags" = xyes
+then :
+ printf "%s\n" "#define HAVE_CHFLAGS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getattrlist" "ac_cv_func_getattrlist"
+if test "x$ac_cv_func_getattrlist" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETATTRLIST 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mktime" "ac_cv_func_mktime"
+if test "x$ac_cv_func_mktime" = xyes
+then :
+ printf "%s\n" "#define HAVE_MKTIME 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "innetgr" "ac_cv_func_innetgr"
+if test "x$ac_cv_func_innetgr" = xyes
+then :
+ printf "%s\n" "#define HAVE_INNETGR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "linkat" "ac_cv_func_linkat"
+if test "x$ac_cv_func_linkat" = xyes
+then :
+ printf "%s\n" "#define HAVE_LINKAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "memmove" "ac_cv_func_memmove"
+if test "x$ac_cv_func_memmove" = xyes
+then :
+ printf "%s\n" "#define HAVE_MEMMOVE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lchown" "ac_cv_func_lchown"
+if test "x$ac_cv_func_lchown" = xyes
+then :
+ printf "%s\n" "#define HAVE_LCHOWN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf"
+if test "x$ac_cv_func_vsnprintf" = xyes
+then :
+ printf "%s\n" "#define HAVE_VSNPRINTF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
+if test "x$ac_cv_func_snprintf" = xyes
+then :
+ printf "%s\n" "#define HAVE_SNPRINTF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "vasprintf" "ac_cv_func_vasprintf"
+if test "x$ac_cv_func_vasprintf" = xyes
+then :
+ printf "%s\n" "#define HAVE_VASPRINTF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "asprintf" "ac_cv_func_asprintf"
+if test "x$ac_cv_func_asprintf" = xyes
+then :
+ printf "%s\n" "#define HAVE_ASPRINTF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setsid" "ac_cv_func_setsid"
+if test "x$ac_cv_func_setsid" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETSID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strpbrk" "ac_cv_func_strpbrk"
+if test "x$ac_cv_func_strpbrk" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRPBRK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
+if test "x$ac_cv_func_strlcat" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRLCAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy"
+if test "x$ac_cv_func_strlcpy" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strtol" "ac_cv_func_strtol"
+if test "x$ac_cv_func_strtol" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRTOL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mallinfo" "ac_cv_func_mallinfo"
+if test "x$ac_cv_func_mallinfo" = xyes
+then :
+ printf "%s\n" "#define HAVE_MALLINFO 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mallinfo2" "ac_cv_func_mallinfo2"
+if test "x$ac_cv_func_mallinfo2" = xyes
+then :
+ printf "%s\n" "#define HAVE_MALLINFO2 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getgroups" "ac_cv_func_getgroups"
+if test "x$ac_cv_func_getgroups" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETGROUPS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setgroups" "ac_cv_func_setgroups"
+if test "x$ac_cv_func_setgroups" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETGROUPS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "geteuid" "ac_cv_func_geteuid"
+if test "x$ac_cv_func_geteuid" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETEUID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getegid" "ac_cv_func_getegid"
+if test "x$ac_cv_func_getegid" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETEGID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setlocale" "ac_cv_func_setlocale"
+if test "x$ac_cv_func_setlocale" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETLOCALE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setmode" "ac_cv_func_setmode"
+if test "x$ac_cv_func_setmode" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETMODE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "open64" "ac_cv_func_open64"
+if test "x$ac_cv_func_open64" = xyes
+then :
+ printf "%s\n" "#define HAVE_OPEN64 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lseek64" "ac_cv_func_lseek64"
+if test "x$ac_cv_func_lseek64" = xyes
+then :
+ printf "%s\n" "#define HAVE_LSEEK64 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mkstemp64" "ac_cv_func_mkstemp64"
+if test "x$ac_cv_func_mkstemp64" = xyes
+then :
+ printf "%s\n" "#define HAVE_MKSTEMP64 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mtrace" "ac_cv_func_mtrace"
+if test "x$ac_cv_func_mtrace" = xyes
+then :
+ printf "%s\n" "#define HAVE_MTRACE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "va_copy" "ac_cv_func_va_copy"
+if test "x$ac_cv_func_va_copy" = xyes
+then :
+ printf "%s\n" "#define HAVE_VA_COPY 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "__va_copy" "ac_cv_func___va_copy"
+if test "x$ac_cv_func___va_copy" = xyes
+then :
+ printf "%s\n" "#define HAVE___VA_COPY 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "seteuid" "ac_cv_func_seteuid"
+if test "x$ac_cv_func_seteuid" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETEUID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror"
+if test "x$ac_cv_func_strerror" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRERROR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "putenv" "ac_cv_func_putenv"
+if test "x$ac_cv_func_putenv" = xyes
+then :
+ printf "%s\n" "#define HAVE_PUTENV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "iconv_open" "ac_cv_func_iconv_open"
+if test "x$ac_cv_func_iconv_open" = xyes
+then :
+ printf "%s\n" "#define HAVE_ICONV_OPEN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "locale_charset" "ac_cv_func_locale_charset"
+if test "x$ac_cv_func_locale_charset" = xyes
+then :
+ printf "%s\n" "#define HAVE_LOCALE_CHARSET 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "nl_langinfo" "ac_cv_func_nl_langinfo"
+if test "x$ac_cv_func_nl_langinfo" = xyes
+then :
+ printf "%s\n" "#define HAVE_NL_LANGINFO 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getxattr" "ac_cv_func_getxattr"
+if test "x$ac_cv_func_getxattr" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETXATTR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "extattr_get_link" "ac_cv_func_extattr_get_link"
+if test "x$ac_cv_func_extattr_get_link" = xyes
+then :
+ printf "%s\n" "#define HAVE_EXTATTR_GET_LINK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction"
+if test "x$ac_cv_func_sigaction" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigprocmask" "ac_cv_func_sigprocmask"
+if test "x$ac_cv_func_sigprocmask" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGPROCMASK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setattrlist" "ac_cv_func_setattrlist"
+if test "x$ac_cv_func_setattrlist" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETATTRLIST 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getgrouplist" "ac_cv_func_getgrouplist"
+if test "x$ac_cv_func_getgrouplist" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETGROUPLIST 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "initgroups" "ac_cv_func_initgroups"
+if test "x$ac_cv_func_initgroups" = xyes
+then :
+ printf "%s\n" "#define HAVE_INITGROUPS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "utimensat" "ac_cv_func_utimensat"
+if test "x$ac_cv_func_utimensat" = xyes
+then :
+ printf "%s\n" "#define HAVE_UTIMENSAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "posix_fallocate" "ac_cv_func_posix_fallocate"
+if test "x$ac_cv_func_posix_fallocate" = xyes
+then :
+ printf "%s\n" "#define HAVE_POSIX_FALLOCATE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "attropen" "ac_cv_func_attropen"
+if test "x$ac_cv_func_attropen" = xyes
+then :
+ printf "%s\n" "#define HAVE_ATTROPEN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setvbuf" "ac_cv_func_setvbuf"
+if test "x$ac_cv_func_setvbuf" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETVBUF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "nanosleep" "ac_cv_func_nanosleep"
+if test "x$ac_cv_func_nanosleep" = xyes
+then :
+ printf "%s\n" "#define HAVE_NANOSLEEP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "usleep" "ac_cv_func_usleep"
+if test "x$ac_cv_func_usleep" = xyes
+then :
+ printf "%s\n" "#define HAVE_USLEEP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv"
+if test "x$ac_cv_func_setenv" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETENV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "unsetenv" "ac_cv_func_unsetenv"
+if test "x$ac_cv_func_unsetenv" = xyes
+then :
+ printf "%s\n" "#define HAVE_UNSETENV 1" >>confdefs.h
+
+fi
+
+
+if test x"$ac_cv_func_iconv_open" != x"yes"; then
+ ac_fn_c_check_func "$LINENO" "libiconv_open" "ac_cv_func_libiconv_open"
+if test "x$ac_cv_func_libiconv_open" = xyes
+then :
+ ac_cv_func_iconv_open=yes; printf "%s\n" "#define HAVE_ICONV_OPEN 1" >>confdefs.h
+
+fi
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for useable fallocate" >&5
+printf %s "checking for useable fallocate... " >&6; }
+if test ${rsync_cv_have_fallocate+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <fcntl.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+int
+main (void)
+{
+fallocate(0, 0, 0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ rsync_cv_have_fallocate=yes
+else $as_nop
+ rsync_cv_have_fallocate=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_have_fallocate" >&5
+printf "%s\n" "$rsync_cv_have_fallocate" >&6; }
+if test x"$rsync_cv_have_fallocate" = x"yes"; then
+
+printf "%s\n" "#define HAVE_FALLOCATE 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for FALLOC_FL_PUNCH_HOLE" >&5
+printf %s "checking for FALLOC_FL_PUNCH_HOLE... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #define _GNU_SOURCE 1
+ #include <linux/falloc.h>
+ #ifndef FALLOC_FL_PUNCH_HOLE
+ #error FALLOC_FL_PUNCH_HOLE is missing
+ #endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define HAVE_FALLOC_FL_PUNCH_HOLE 1" >>confdefs.h
+
+
+else $as_nop
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for FALLOC_FL_ZERO_RANGE" >&5
+printf %s "checking for FALLOC_FL_ZERO_RANGE... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #define _GNU_SOURCE 1
+ #include <linux/falloc.h>
+ #ifndef FALLOC_FL_ZERO_RANGE
+ #error FALLOC_FL_ZERO_RANGE is missing
+ #endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define HAVE_FALLOC_FL_ZERO_RANGE 1" >>confdefs.h
+
+
+else $as_nop
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SYS_fallocate" >&5
+printf %s "checking for SYS_fallocate... " >&6; }
+if test ${rsync_cv_have_sys_fallocate+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/syscall.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+int
+main (void)
+{
+syscall(SYS_fallocate, 0, 0, (loff_t)0, (loff_t)0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ rsync_cv_have_sys_fallocate=yes
+else $as_nop
+ rsync_cv_have_sys_fallocate=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_have_sys_fallocate" >&5
+printf "%s\n" "$rsync_cv_have_sys_fallocate" >&6; }
+if test x"$rsync_cv_have_sys_fallocate" = x"yes"; then
+
+printf "%s\n" "#define HAVE_SYS_FALLOCATE 1" >>confdefs.h
+
+fi
+
+if test x"$ac_cv_func_posix_fallocate" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether posix_fallocate is efficient" >&5
+printf %s "checking whether posix_fallocate is efficient... " >&6; }
+ case $host_os in
+ *cygwin*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define HAVE_EFFICIENT_POSIX_FALLOCATE 1" >>confdefs.h
+
+ ;;
+ *)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ ;;
+ esac
+fi
+
+
+ac_fn_c_check_func "$LINENO" "getpgrp" "ac_cv_func_getpgrp"
+if test "x$ac_cv_func_getpgrp" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETPGRP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "tcgetpgrp" "ac_cv_func_tcgetpgrp"
+if test "x$ac_cv_func_tcgetpgrp" = xyes
+then :
+ printf "%s\n" "#define HAVE_TCGETPGRP 1" >>confdefs.h
+
+fi
+
+if test $ac_cv_func_getpgrp = yes; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether getpgrp requires zero arguments" >&5
+printf %s "checking whether getpgrp requires zero arguments... " >&6; }
+if test ${ac_cv_func_getpgrp_void+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ # Use it with a single arg.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main (void)
+{
+getpgrp (0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_func_getpgrp_void=no
+else $as_nop
+ ac_cv_func_getpgrp_void=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getpgrp_void" >&5
+printf "%s\n" "$ac_cv_func_getpgrp_void" >&6; }
+if test $ac_cv_func_getpgrp_void = yes; then
+
+printf "%s\n" "#define GETPGRP_VOID 1" >>confdefs.h
+
+fi
+
+fi
+
+# Check whether --enable-iconv-open was given.
+if test ${enable_iconv_open+y}
+then :
+ enableval=$enable_iconv_open;
+else $as_nop
+ enable_iconv_open=$ac_cv_func_iconv_open
+fi
+
+
+if test x"$enable_iconv_open" != x"no"; then
+
+printf "%s\n" "#define USE_ICONV_OPEN 1" >>confdefs.h
+
+fi
+
+# Check whether --enable-iconv was given.
+if test ${enable_iconv+y}
+then :
+ enableval=$enable_iconv;
+else $as_nop
+ enable_iconv=$enable_iconv_open
+fi
+
+
+if test x"$enable_iconv" != x"no"; then
+ if test x"$enable_iconv" = x"yes"; then
+ printf "%s\n" "#define ICONV_OPTION NULL" >>confdefs.h
+
+ else
+ printf "%s\n" "#define ICONV_OPTION \"$enable_iconv\"" >>confdefs.h
+
+ fi
+
+printf "%s\n" "#define UTF8_CHARSET \"UTF-8\"" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether chown() modifies symlinks" >&5
+printf %s "checking whether chown() modifies symlinks... " >&6; }
+if test ${rsync_cv_chown_modifies_symlink+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ if test "$cross_compiling" = yes
+then :
+ rsync_cv_chown_modifies_symlink=no
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+int main(void) {
+ char const *dangling_symlink = "conftest.dangle";
+ unlink(dangling_symlink);
+ if (symlink("conftest.no-such", dangling_symlink) < 0) abort();
+ if (chown(dangling_symlink, getuid(), getgid()) < 0 && errno == ENOENT) return 1;
+ return 0;
+ }
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_chown_modifies_symlink=yes
+else $as_nop
+ rsync_cv_chown_modifies_symlink=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_chown_modifies_symlink" >&5
+printf "%s\n" "$rsync_cv_chown_modifies_symlink" >&6; }
+if test $rsync_cv_chown_modifies_symlink = yes; then
+
+printf "%s\n" "#define CHOWN_MODIFIES_SYMLINK 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether link() can hard-link symlinks" >&5
+printf %s "checking whether link() can hard-link symlinks... " >&6; }
+if test ${rsync_cv_can_hardlink_symlink+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ if test "$cross_compiling" = yes
+then :
+ rsync_cv_can_hardlink_symlink=no
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#elif defined HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#define FILENAME "conftest.dangle"
+int main(void) {
+ unlink(FILENAME);
+ if (symlink("conftest.no-such", FILENAME) < 0) abort();
+ unlink(FILENAME "2");
+#ifdef HAVE_LINKAT
+ if (linkat(AT_FDCWD, FILENAME, AT_FDCWD, FILENAME "2", 0) < 0) return 1;
+#else
+ if (link(FILENAME, FILENAME "2") < 0) return 1;
+#endif
+ return 0;
+ }
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_can_hardlink_symlink=yes
+else $as_nop
+ rsync_cv_can_hardlink_symlink=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_can_hardlink_symlink" >&5
+printf "%s\n" "$rsync_cv_can_hardlink_symlink" >&6; }
+if test $rsync_cv_can_hardlink_symlink = yes; then
+
+printf "%s\n" "#define CAN_HARDLINK_SYMLINK 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether link() can hard-link special files" >&5
+printf %s "checking whether link() can hard-link special files... " >&6; }
+if test ${rsync_cv_can_hardlink_special+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ if test "$cross_compiling" = yes
+then :
+ rsync_cv_can_hardlink_special=no
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#define FILENAME "conftest.fifi"
+int main(void) {
+ unlink(FILENAME);
+ if (mkfifo(FILENAME, 0777) < 0) abort();
+ unlink(FILENAME "2");
+ if (link(FILENAME, FILENAME "2") < 0) return 1;
+ return 0;
+ }
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_can_hardlink_special=yes
+else $as_nop
+ rsync_cv_can_hardlink_special=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_can_hardlink_special" >&5
+printf "%s\n" "$rsync_cv_can_hardlink_special" >&6; }
+if test $rsync_cv_can_hardlink_special = yes; then
+
+printf "%s\n" "#define CAN_HARDLINK_SPECIAL 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working socketpair" >&5
+printf %s "checking for working socketpair... " >&6; }
+if test ${rsync_cv_HAVE_SOCKETPAIR+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_HAVE_SOCKETPAIR=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+int main(void) {
+ int fd[2];
+ return (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != -1) ? 0 : 1;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_HAVE_SOCKETPAIR=yes
+else $as_nop
+ rsync_cv_HAVE_SOCKETPAIR=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_SOCKETPAIR" >&5
+printf "%s\n" "$rsync_cv_HAVE_SOCKETPAIR" >&6; }
+if test x"$rsync_cv_HAVE_SOCKETPAIR" = x"yes"; then
+
+printf "%s\n" "#define HAVE_SOCKETPAIR 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "getpass" "ac_cv_func_getpass"
+if test "x$ac_cv_func_getpass" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETPASS 1" >>confdefs.h
+
+else $as_nop
+ case " $LIBOBJS " in
+ *" getpass.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS getpass.$ac_objext"
+ ;;
+esac
+
+fi
+
+
+if test x"$with_included_popt" != x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for poptGetContext in -lpopt" >&5
+printf %s "checking for poptGetContext in -lpopt... " >&6; }
+if test ${ac_cv_lib_popt_poptGetContext+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpopt $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char poptGetContext ();
+int
+main (void)
+{
+return poptGetContext ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_popt_poptGetContext=yes
+else $as_nop
+ ac_cv_lib_popt_poptGetContext=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_popt_poptGetContext" >&5
+printf "%s\n" "$ac_cv_lib_popt_poptGetContext" >&6; }
+if test "x$ac_cv_lib_popt_poptGetContext" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBPOPT 1" >>confdefs.h
+
+ LIBS="-lpopt $LIBS"
+
+else $as_nop
+ with_included_popt=yes
+fi
+
+fi
+if test x"$ac_cv_header_popt_popt_h" = x"yes"; then
+ # If the system has /usr/include/popt/popt.h, we enable the
+ # included popt because an attempt to "#include <popt/popt.h>"
+ # would use our included header file anyway (due to -I.), and
+ # might conflict with the system popt.
+ with_included_popt=yes
+elif test x"$ac_cv_header_popt_h" != x"yes"; then
+ with_included_popt=yes
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use included libpopt" >&5
+printf %s "checking whether to use included libpopt... " >&6; }
+if test x"$with_included_popt" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $srcdir/popt" >&5
+printf "%s\n" "$srcdir/popt" >&6; }
+ BUILD_POPT='$(popt_OBJS)'
+ CFLAGS="-I$srcdir/popt $CFLAGS"
+ if test x"$ALLOCA" != x
+ then
+ # this can be removed when/if we add an included alloca.c;
+ # see autoconf documentation on AC_FUNC_ALLOCA
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: included libpopt will use malloc, not alloca (which wastes a small amount of memory)" >&5
+printf "%s\n" "$as_me: WARNING: included libpopt will use malloc, not alloca (which wastes a small amount of memory)" >&2;}
+ fi
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+# We default to using our zlib unless --with-included-zlib=no is given.
+if test x"$with_included_zlib" != x"no"; then
+ with_included_zlib=yes
+elif test x"$ac_cv_header_zlib_h" != x"yes"; then
+ with_included_zlib=yes
+fi
+if test x"$with_included_zlib" != x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for deflateParams in -lz" >&5
+printf %s "checking for deflateParams in -lz... " >&6; }
+if test ${ac_cv_lib_z_deflateParams+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lz $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char deflateParams ();
+int
+main (void)
+{
+return deflateParams ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_z_deflateParams=yes
+else $as_nop
+ ac_cv_lib_z_deflateParams=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflateParams" >&5
+printf "%s\n" "$ac_cv_lib_z_deflateParams" >&6; }
+if test "x$ac_cv_lib_z_deflateParams" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBZ 1" >>confdefs.h
+
+ LIBS="-lz $LIBS"
+
+else $as_nop
+ with_included_zlib=yes
+fi
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to use included zlib" >&5
+printf %s "checking whether to use included zlib... " >&6; }
+if test x"$with_included_zlib" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $srcdir/zlib" >&5
+printf "%s\n" "$srcdir/zlib" >&6; }
+ BUILD_ZLIB='$(zlib_OBJS)'
+ CFLAGS="-I$srcdir/zlib $CFLAGS"
+else
+
+printf "%s\n" "#define EXTERNAL_ZLIB 1" >>confdefs.h
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for unsigned char" >&5
+printf %s "checking for unsigned char... " >&6; }
+if test ${rsync_cv_SIGNED_CHAR_OK+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+signed char *s = (signed char *)""
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ rsync_cv_SIGNED_CHAR_OK=yes
+else $as_nop
+ rsync_cv_SIGNED_CHAR_OK=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_SIGNED_CHAR_OK" >&5
+printf "%s\n" "$rsync_cv_SIGNED_CHAR_OK" >&6; }
+if test x"$rsync_cv_SIGNED_CHAR_OK" = x"yes"; then
+
+printf "%s\n" "#define SIGNED_CHAR_OK 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for broken readdir" >&5
+printf %s "checking for broken readdir... " >&6; }
+if test ${rsync_cv_HAVE_BROKEN_READDIR+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_HAVE_BROKEN_READDIR=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <dirent.h>
+int main(void) { struct dirent *di; DIR *d = opendir("."); di = readdir(d);
+if (di && di->d_name[-2] == '.' && di->d_name[-1] == 0 &&
+di->d_name[0] == 0) return 0; return 1;}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_HAVE_BROKEN_READDIR=yes
+else $as_nop
+ rsync_cv_HAVE_BROKEN_READDIR=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_BROKEN_READDIR" >&5
+printf "%s\n" "$rsync_cv_HAVE_BROKEN_READDIR" >&6; }
+if test x"$rsync_cv_HAVE_BROKEN_READDIR" = x"yes"; then
+
+printf "%s\n" "#define HAVE_BROKEN_READDIR 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for utimbuf" >&5
+printf %s "checking for utimbuf... " >&6; }
+if test ${rsync_cv_HAVE_STRUCT_UTIMBUF+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <utime.h>
+int
+main (void)
+{
+struct utimbuf tbuf; tbuf.actime = 0; tbuf.modtime = 1; return utime("foo.c",&tbuf);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ rsync_cv_HAVE_STRUCT_UTIMBUF=yes
+else $as_nop
+ rsync_cv_HAVE_STRUCT_UTIMBUF=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_STRUCT_UTIMBUF" >&5
+printf "%s\n" "$rsync_cv_HAVE_STRUCT_UTIMBUF" >&6; }
+if test x"$rsync_cv_HAVE_STRUCT_UTIMBUF" = x"yes"; then
+
+printf "%s\n" "#define HAVE_STRUCT_UTIMBUF 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if gettimeofday takes tz argument" >&5
+printf %s "checking if gettimeofday takes tz argument... " >&6; }
+if test ${rsync_cv_HAVE_GETTIMEOFDAY_TZ+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+int
+main (void)
+{
+struct timeval tv; return gettimeofday(&tv, NULL);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ rsync_cv_HAVE_GETTIMEOFDAY_TZ=yes
+else $as_nop
+ rsync_cv_HAVE_GETTIMEOFDAY_TZ=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_GETTIMEOFDAY_TZ" >&5
+printf "%s\n" "$rsync_cv_HAVE_GETTIMEOFDAY_TZ" >&6; }
+if test x"$rsync_cv_HAVE_GETTIMEOFDAY_TZ" != x"no"; then
+
+printf "%s\n" "#define HAVE_GETTIMEOFDAY_TZ 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C99 vsnprintf" >&5
+printf %s "checking for C99 vsnprintf... " >&6; }
+if test ${rsync_cv_HAVE_C99_VSNPRINTF+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_HAVE_C99_VSNPRINTF=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+void foo(const char *format, ...) {
+ va_list ap;
+ int len;
+ static char buf[] = "12345678901234567890";
+
+ va_start(ap, format);
+ len = vsnprintf(0, 0, format, ap);
+ va_end(ap);
+ if (len != 5) exit(1);
+
+ if (snprintf(buf, 3, "hello") != 5 || strcmp(buf, "he") != 0) exit(1);
+}
+int main(void) { foo("hello"); return 0; }
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_HAVE_C99_VSNPRINTF=yes
+else $as_nop
+ rsync_cv_HAVE_C99_VSNPRINTF=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_C99_VSNPRINTF" >&5
+printf "%s\n" "$rsync_cv_HAVE_C99_VSNPRINTF" >&6; }
+if test x"$rsync_cv_HAVE_C99_VSNPRINTF" = x"yes"; then
+
+printf "%s\n" "#define HAVE_C99_VSNPRINTF 1" >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for secure mkstemp" >&5
+printf %s "checking for secure mkstemp... " >&6; }
+if test ${rsync_cv_HAVE_SECURE_MKSTEMP+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_HAVE_SECURE_MKSTEMP=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+int main(void) {
+ struct stat st;
+ char tpl[20]="/tmp/test.XXXXXX";
+ int fd = mkstemp(tpl);
+ if (fd == -1) return 1;
+ unlink(tpl);
+ if (fstat(fd, &st) != 0) return 1;
+ if ((st.st_mode & 0777) != 0600) return 1;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_HAVE_SECURE_MKSTEMP=yes
+else $as_nop
+ rsync_cv_HAVE_SECURE_MKSTEMP=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_HAVE_SECURE_MKSTEMP" >&5
+printf "%s\n" "$rsync_cv_HAVE_SECURE_MKSTEMP" >&6; }
+if test x"$rsync_cv_HAVE_SECURE_MKSTEMP" = x"yes"; then
+ case $host_os in
+ hpux*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Skipping broken HP-UX mkstemp() -- using mktemp() instead" >&5
+printf "%s\n" "$as_me: WARNING: Skipping broken HP-UX mkstemp() -- using mktemp() instead" >&2;}
+ ;;
+ *)
+
+printf "%s\n" "#define HAVE_SECURE_MKSTEMP 1" >>confdefs.h
+
+ ;;
+ esac
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if mknod creates FIFOs" >&5
+printf %s "checking if mknod creates FIFOs... " >&6; }
+if test ${rsync_cv_MKNOD_CREATES_FIFOS+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_MKNOD_CREATES_FIFOS=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+int main(void) { int rc, ec; char *fn = "fifo-test";
+unlink(fn); rc = mknod(fn,S_IFIFO,0600); ec = errno; unlink(fn);
+if (rc) {printf("(%d %d) ",rc,ec); return ec;}
+return 0;}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_MKNOD_CREATES_FIFOS=yes
+else $as_nop
+ rsync_cv_MKNOD_CREATES_FIFOS=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_MKNOD_CREATES_FIFOS" >&5
+printf "%s\n" "$rsync_cv_MKNOD_CREATES_FIFOS" >&6; }
+if test x"$rsync_cv_MKNOD_CREATES_FIFOS" = x"yes"; then
+
+printf "%s\n" "#define MKNOD_CREATES_FIFOS 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if mknod creates sockets" >&5
+printf %s "checking if mknod creates sockets... " >&6; }
+if test ${rsync_cv_MKNOD_CREATES_SOCKETS+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+if test "$cross_compiling" = yes
+then :
+ rsync_cv_MKNOD_CREATES_SOCKETS=cross
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+int main(void) { int rc, ec; char *fn = "sock-test";
+unlink(fn); rc = mknod(fn,S_IFSOCK,0600); ec = errno; unlink(fn);
+if (rc) {printf("(%d %d) ",rc,ec); return ec;}
+return 0;}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+ rsync_cv_MKNOD_CREATES_SOCKETS=yes
+else $as_nop
+ rsync_cv_MKNOD_CREATES_SOCKETS=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_MKNOD_CREATES_SOCKETS" >&5
+printf "%s\n" "$rsync_cv_MKNOD_CREATES_SOCKETS" >&6; }
+if test x"$rsync_cv_MKNOD_CREATES_SOCKETS" = x"yes"; then
+
+printf "%s\n" "#define MKNOD_CREATES_SOCKETS 1" >>confdefs.h
+
+fi
+
+#
+# The following test was mostly taken from the tcl/tk plus patches
+#
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -c -o works" >&5
+printf %s "checking whether -c -o works... " >&6; }
+if test ${rsync_cv_DASHC_WORKS_WITH_DASHO+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+rm -rf conftest*
+cat > conftest.$ac_ext <<EOF
+int main(void) { return 0; }
+EOF
+${CC-cc} -c -o conftest..o conftest.$ac_ext
+if test -f conftest..o; then
+ rsync_cv_DASHC_WORKS_WITH_DASHO=yes
+else
+ rsync_cv_DASHC_WORKS_WITH_DASHO=no
+fi
+rm -rf conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_cv_DASHC_WORKS_WITH_DASHO" >&5
+printf "%s\n" "$rsync_cv_DASHC_WORKS_WITH_DASHO" >&6; }
+if test x"$rsync_cv_DASHC_WORKS_WITH_DASHO" = x"yes"; then
+ OBJ_SAVE="#"
+ OBJ_RESTORE="#"
+ CC_SHOBJ_FLAG='-o $@'
+else
+ OBJ_SAVE=' @b=`basename $@ .o`;rm -f $$b.o.sav;if test -f $$b.o; then mv $$b.o $$b.o.sav;fi;'
+ OBJ_RESTORE=' @b=`basename $@ .o`;if test "$$b.o" != "$@"; then mv $$b.o $@; if test -f $$b.o.sav; then mv $$b.o.sav $$b.o; fi; fi'
+ CC_SHOBJ_FLAG=""
+fi
+
+
+
+
+
+
+
+
+
+
+
+ac_fn_c_check_func "$LINENO" "_acl" "ac_cv_func__acl"
+if test "x$ac_cv_func__acl" = xyes
+then :
+ printf "%s\n" "#define HAVE__ACL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "__acl" "ac_cv_func___acl"
+if test "x$ac_cv_func___acl" = xyes
+then :
+ printf "%s\n" "#define HAVE___ACL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "_facl" "ac_cv_func__facl"
+if test "x$ac_cv_func__facl" = xyes
+then :
+ printf "%s\n" "#define HAVE__FACL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "__facl" "ac_cv_func___facl"
+if test "x$ac_cv_func___facl" = xyes
+then :
+ printf "%s\n" "#define HAVE___FACL 1" >>confdefs.h
+
+fi
+
+#################################################
+# check for ACL support
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to support ACLs" >&5
+printf %s "checking whether to support ACLs... " >&6; }
+# Check whether --enable-acl-support was given.
+if test ${enable_acl_support+y}
+then :
+ enableval=$enable_acl_support;
+fi
+
+
+if test x"$enable_acl_support" = x"no"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+else
+ case "$host_os" in
+ *sysv5*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using UnixWare ACLs" >&5
+printf "%s\n" "Using UnixWare ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_UNIXWARE_ACLS 1" >>confdefs.h
+
+
+printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ ;;
+ solaris*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using solaris ACLs" >&5
+printf "%s\n" "Using solaris ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_SOLARIS_ACLS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ ;;
+ *irix*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using IRIX ACLs" >&5
+printf "%s\n" "Using IRIX ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_IRIX_ACLS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ ;;
+ *aix*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using AIX ACLs" >&5
+printf "%s\n" "Using AIX ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_AIX_ACLS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ ;;
+ *osf*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using Tru64 ACLs" >&5
+printf "%s\n" "Using Tru64 ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_TRU64_ACLS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ LIBS="$LIBS -lpacl"
+ ;;
+ darwin*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OS X ACLs" >&5
+printf "%s\n" "Using OS X ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_OSX_ACLS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ ;;
+ *hpux*|*nsk*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using HPUX ACLs" >&5
+printf "%s\n" "Using HPUX ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_HPUX_ACLS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ ;;
+ *)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: running tests:" >&5
+printf "%s\n" "running tests:" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for acl_get_file in -lacl" >&5
+printf %s "checking for acl_get_file in -lacl... " >&6; }
+if test ${ac_cv_lib_acl_acl_get_file+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lacl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char acl_get_file ();
+int
+main (void)
+{
+return acl_get_file ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_acl_acl_get_file=yes
+else $as_nop
+ ac_cv_lib_acl_acl_get_file=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_acl_acl_get_file" >&5
+printf "%s\n" "$ac_cv_lib_acl_acl_get_file" >&6; }
+if test "x$ac_cv_lib_acl_acl_get_file" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBACL 1" >>confdefs.h
+
+ LIBS="-lacl $LIBS"
+
+fi
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ACL support" >&5
+printf %s "checking for ACL support... " >&6; }
+if test ${samba_cv_HAVE_POSIX_ACLS+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+int
+main (void)
+{
+ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ samba_cv_HAVE_POSIX_ACLS=yes
+else $as_nop
+ samba_cv_HAVE_POSIX_ACLS=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $samba_cv_HAVE_POSIX_ACLS" >&5
+printf "%s\n" "$samba_cv_HAVE_POSIX_ACLS" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking ACL test results" >&5
+printf %s "checking ACL test results... " >&6; }
+ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using posix ACLs" >&5
+printf "%s\n" "Using posix ACLs" >&6; }
+
+printf "%s\n" "#define HAVE_POSIX_ACLS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_ACLS 1" >>confdefs.h
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for acl_get_perm_np" >&5
+printf %s "checking for acl_get_perm_np... " >&6; }
+if test ${samba_cv_HAVE_ACL_GET_PERM_NP+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+int
+main (void)
+{
+ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ samba_cv_HAVE_ACL_GET_PERM_NP=yes
+else $as_nop
+ samba_cv_HAVE_ACL_GET_PERM_NP=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $samba_cv_HAVE_ACL_GET_PERM_NP" >&5
+printf "%s\n" "$samba_cv_HAVE_ACL_GET_PERM_NP" >&6; }
+ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+
+printf "%s\n" "#define HAVE_ACL_GET_PERM_NP 1" >>confdefs.h
+
+ fi
+ else
+ if test x"$enable_acl_support" = x"yes"; then
+ as_fn_error $? "Failed to find ACL support" "$LINENO" 5
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: No ACL support found" >&5
+printf "%s\n" "No ACL support found" >&6; }
+ fi
+ fi
+ ;;
+ esac
+fi
+
+#################################################
+# check for extended attribute support
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to support extended attributes" >&5
+printf %s "checking whether to support extended attributes... " >&6; }
+# Check whether --enable-xattr-support was given.
+if test ${enable_xattr_support+y}
+then :
+ enableval=$enable_xattr_support;
+else $as_nop
+ case "$ac_cv_func_getxattr$ac_cv_func_extattr_get_link$ac_cv_func_attropen" in
+ *yes*) enable_xattr_support=maybe ;;
+ *) enable_xattr_support=no ;;
+ esac
+fi
+
+
+if test x"$enable_xattr_support" = x"no"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+else
+ case "$host_os" in
+ *linux*|*netbsd*|*cygwin*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using Linux xattrs" >&5
+printf "%s\n" "Using Linux xattrs" >&6; }
+
+printf "%s\n" "#define HAVE_LINUX_XATTRS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_XATTRS 1" >>confdefs.h
+
+
+printf "%s\n" "#define NO_SYMLINK_USER_XATTRS 1" >>confdefs.h
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getxattr in -lattr" >&5
+printf %s "checking for getxattr in -lattr... " >&6; }
+if test ${ac_cv_lib_attr_getxattr+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lattr $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char getxattr ();
+int
+main (void)
+{
+return getxattr ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_attr_getxattr=yes
+else $as_nop
+ ac_cv_lib_attr_getxattr=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_attr_getxattr" >&5
+printf "%s\n" "$ac_cv_lib_attr_getxattr" >&6; }
+if test "x$ac_cv_lib_attr_getxattr" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBATTR 1" >>confdefs.h
+
+ LIBS="-lattr $LIBS"
+
+fi
+
+ ;;
+ darwin*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using OS X xattrs" >&5
+printf "%s\n" "Using OS X xattrs" >&6; }
+
+printf "%s\n" "#define HAVE_OSX_XATTRS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_XATTRS 1" >>confdefs.h
+
+
+printf "%s\n" "#define NO_DEVICE_XATTRS 1" >>confdefs.h
+
+
+printf "%s\n" "#define NO_SPECIAL_XATTRS 1" >>confdefs.h
+
+ ;;
+ freebsd*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using FreeBSD extattrs" >&5
+printf "%s\n" "Using FreeBSD extattrs" >&6; }
+
+printf "%s\n" "#define HAVE_FREEBSD_XATTRS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_XATTRS 1" >>confdefs.h
+
+ ;;
+ solaris*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using Solaris xattrs" >&5
+printf "%s\n" "Using Solaris xattrs" >&6; }
+
+printf "%s\n" "#define HAVE_SOLARIS_XATTRS 1" >>confdefs.h
+
+ printf "%s\n" "#define SUPPORT_XATTRS 1" >>confdefs.h
+
+
+printf "%s\n" "#define NO_SYMLINK_XATTRS 1" >>confdefs.h
+
+ ;;
+ *)
+ if test x"$enable_xattr_support" = x"yes"; then
+ as_fn_error $? "Failed to find extended attribute support" "$LINENO" 5
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: No extended attribute support found" >&5
+printf "%s\n" "No extended attribute support found" >&6; }
+ fi
+ ;;
+ esac
+fi
+
+if test x"$enable_acl_support" = x"no" || test x"$enable_xattr_support" = x"no" || test x"$enable_iconv" = x"no"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-unused-parameter" >&5
+printf %s "checking whether $CC supports -Wno-unused-parameter... " >&6; }
+ OLD_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -Wno-unused-parameter"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main (void)
+{
+printf("hello\n");
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ rsync_warn_flag=yes
+else $as_nop
+ rsync_warn_flag=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $rsync_warn_flag" >&5
+printf "%s\n" "$rsync_warn_flag" >&6; }
+ if test x"$rsync_warn_flag" = x"no"; then
+ CFLAGS="$OLD_CFLAGS"
+ fi
+fi
+
+case "$CC" in
+' checker'*|checker*)
+
+printf "%s\n" "#define FORCE_FD_ZERO_MEMSET 1" >>confdefs.h
+
+ ;;
+esac
+
+ac_config_files="$ac_config_files Makefile lib/dummy zlib/dummy popt/dummy shconfig"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else $as_nop
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" "" $as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2) ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ printf "%s\n" "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else $as_nop
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else $as_nop
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n. New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by rsync $as_me, which was
+generated by GNU Autoconf 2.71. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <https://rsync.samba.org/bug-tracking.html>."
+
+_ACEOF
+ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config='$ac_cs_config_escaped'
+ac_cs_version="\\
+rsync config.status
+configured by $0, generated by GNU Autoconf 2.71,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ printf "%s\n" "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ printf "%s\n" "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ printf "%s\n" "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ printf "%s\n" "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "lib/dummy") CONFIG_FILES="$CONFIG_FILES lib/dummy" ;;
+ "zlib/dummy") CONFIG_FILES="$CONFIG_FILES zlib/dummy" ;;
+ "popt/dummy") CONFIG_FILES="$CONFIG_FILES popt/dummy" ;;
+ "shconfig") CONFIG_FILES="$CONFIG_FILES shconfig" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files
+ test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS "
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf "%s\n" "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`printf "%s\n" "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ printf "%s\n" "/* $configure_input */" >&1 \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ printf "%s\n" "/* $configure_input */" >&1 \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+ ;;
+
+
+ esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5
+printf "%s\n" "" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: rsync $PACKAGE_VERSION configuration successful" >&5
+printf "%s\n" " rsync $PACKAGE_VERSION configuration successful" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5
+printf "%s\n" "" >&6; }
+
diff --git a/connection.c b/connection.c
new file mode 100644
index 0000000..1039115
--- /dev/null
+++ b/connection.c
@@ -0,0 +1,47 @@
+/*
+ * Support the max connections option.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2006-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* A simple routine to do connection counting. This returns 1 on success
+ * and 0 on failure, with errno also being set if the open() failed (errno
+ * will be 0 if the lock request failed). */
+int claim_connection(char *fname, int max_connections)
+{
+ int fd, i;
+
+ if (max_connections == 0)
+ return 1;
+
+ if ((fd = open(fname, O_RDWR|O_CREAT, 0600)) < 0)
+ return 0;
+
+ /* Find a free spot. */
+ for (i = 0; i < max_connections; i++) {
+ if (lock_range(fd, i*4, 4))
+ return 1;
+ }
+
+ close(fd);
+
+ /* A lock failure needs to return an errno of 0. */
+ errno = 0;
+ return 0;
+}
diff --git a/csprotocol.txt b/csprotocol.txt
new file mode 100644
index 0000000..7ba09ab
--- /dev/null
+++ b/csprotocol.txt
@@ -0,0 +1,110 @@
+This is kind of informal and may be wrong, but it helped me. It's
+basically a summary of clientserver.c and authenticate.c.
+
+ -- Martin Pool <mbp@samba.org>
+
+
+This is the protocol used for rsync --daemon; i.e. connections to port
+873 rather than invocations over a remote shell.
+
+When the server accepts a connection, it prints a newline-terminated
+greeting line:
+
+ @RSYNCD: <version>.<subprotocol> <digest1> <digestN>
+
+The <version> is the numeric version (see PROTOCOL_VERSION in rsync.h)
+The <subprotocol> is the numeric subprotocol version (which is 0 for a
+final protocol version, as the SUBPROTOCOL_VERSION define discusses).
+The <digestN> names are the authentication digest algorithms that the
+daemon supports, listed in order of preference.
+
+An rsync prior to 3.2.7 omits the digest names. An rsync prior to 3.0.0
+also omits the period and the <subprotocol> value. Since a final
+protocol has a subprotocol value of 0, a missing subprotocol value is
+assumed to be 0 for any protocol prior to 30. It is considered a fatal
+error for protocol 30 and above to omit it. It is considered a fatal
+error for protocol 32 and above to omit the digest name list (currently
+31 is the newest protocol).
+
+The daemon expects to see a similar greeting line back from the client.
+Once received, the daemon follows the opening line with a free-format
+text message-of-the-day (if any is defined).
+
+The server is now in the connected state. The client can either send
+the command:
+
+ #list
+
+(to get a listing of modules) or the name of a module. After this, the
+connection is now bound to a particular module. Access per host for
+this module is now checked, as is per-module connection limits.
+
+If authentication is required to use this module, the server will say:
+
+ @RSYNCD: AUTHREQD <challenge>
+
+where <challenge> is a random string of base64 characters. The client
+must respond with:
+
+ <user> <response>
+
+The <user> is the username they claim to be. The <response> is the
+base64 form of the digest hash of the challenge+password string. The
+chosen digest method is the most preferred client method that is also in
+the server's list. If no digest list was explicitly provided, the side
+expecting a list assumes the other side provided either the single name
+"md5" (for a negotiated protocol 30 or 31), or the single name "md4"
+(for an older protocol).
+
+At this point the server applies all remaining constraints before
+handing control to the client, including switching uid/gid, setting up
+include and exclude lists, moving to the root of the module, and doing
+chroot.
+
+If the login is acceptable, then the server will respond with
+
+ @RSYNCD: OK
+
+The client now writes some rsync options, as if it were remotely
+executing the command. The server parses these arguments as if it had
+just been invoked with them, but they're added to the existing state.
+So if the client specifies a list of files to be included or excluded,
+they'll defer to existing limits specified in the server
+configuration.
+
+At this point the client and server both switch to using a
+multiplexing layer across the socket. The main point of this is to
+allow the server to asynchronously pass errors back, while still
+allowing streamed and pipelined data.
+
+Unfortunately, the multiplex protocol is not used at every stage. We
+start up in plain socket mode and then change over by calling
+io_start_buffering. Of course both the client and the server have to
+do this at the same point.
+
+The server then talks to the client as normal across the socket,
+passing checksums, file lists and so on. For documentation of that,
+stay tuned (or write it yourself!).
+
+
+
+------------
+Protocol version changes
+
+31 (2013-09-28, 3.1.0)
+
+ Initial release of protocol 31 had no changes. Rsync 3.2.7
+ introduced the suffixed list of digest names on the greeting
+ line. The presence of the list is allowed even if the greeting
+ indicates an older protocol version number.
+
+30 (2007-10-04, 3.0.0pre1)
+
+ The use of a ".<subprotocol>" number was added to
+ @RSYNCD: <version>.<subprotocol>
+
+25 (2001-08-20, 2.4.7pre2)
+
+ Send an explicit "@RSYNC EXIT" command at the end of the
+ module listing. We never intentionally end the transmission
+ by just closing the socket anymore.
diff --git a/daemon-parm.awk b/daemon-parm.awk
new file mode 100755
index 0000000..ad52e9d
--- /dev/null
+++ b/daemon-parm.awk
@@ -0,0 +1,114 @@
+#!/usr/bin/awk -f
+
+# The caller must pass arg: daemon-parm.txt
+# The resulting code is output into daemon-parm.h
+
+BEGIN {
+ heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from a list of values in " ARGV[1] "! */\n\n"
+ sect = psect = defines = accessors = prior_ptype = ""
+ parms = "\nstatic struct parm_struct parm_table[] = {"
+ comment_fmt = "\n/********** %s **********/\n"
+ tdstruct = "typedef struct {"
+}
+
+/^\s*$/ { next }
+/^#/ { next }
+
+/^Globals:/ {
+ if (defines != "") {
+ print "The Globals section must come first!"
+ defines = ""
+ exit
+ }
+ defines = tdstruct
+ values = "\nstatic const all_vars Defaults = {\n { /* Globals: */\n"
+ exps = exp_values = sprintf(comment_fmt, "EXP")
+ sect = "GLOBAL"
+ psect = ", P_GLOBAL, &Vars.g."
+ next
+}
+
+/^Locals:/ {
+ if (sect == "") {
+ print "The Locals section must come after the Globals!"
+ exit
+ }
+ defines = defines exps "} global_vars;\n\n" tdstruct
+ values = values exp_values "\n }, { /* Locals: */\n"
+ exps = exp_values = sprintf(comment_fmt, "EXP")
+ sect = "LOCAL"
+ psect = ", P_LOCAL, &Vars.l."
+ next
+}
+
+/^(STRING|CHAR|PATH|INTEGER|ENUM|OCTAL|BOOL|BOOLREV|BOOL3)[ \t]/ {
+ ptype = $1
+ name = $2
+ $1 = $2 = ""
+ sub(/^[ \t]+/, "")
+
+ if (ptype != prior_ptype) {
+ comment = sprintf(comment_fmt, ptype)
+ defines = defines comment
+ values = values comment
+ parms = parms "\n"
+ accessors = accessors "\n"
+ prior_ptype = ptype
+ }
+
+ if (ptype == "STRING" || ptype == "PATH") {
+ atype = "STRING"
+ vtype = "char*"
+ } else if (ptype ~ /BOOL/) {
+ atype = vtype = "BOOL"
+ } else if (ptype == "CHAR") {
+ atype = "CHAR"
+ vtype = "char"
+ } else {
+ atype = "INTEGER"
+ vtype = "int"
+ }
+
+ # The name might be var_name|public_name
+ pubname = name
+ sub(/\|.*/, "", name)
+ sub(/.*\|/, "", pubname)
+ gsub(/_/, " ", pubname)
+ gsub(/-/, "", name)
+
+ if (ptype == "ENUM")
+ enum = "enum_" name
+ else
+ enum = "NULL"
+
+ defines = defines "\t" vtype " " name ";\n"
+ values = values "\t" $0 ", /* " name " */\n"
+ parms = parms " {\"" pubname "\", P_" ptype psect name ", " enum ", 0},\n"
+ accessors = accessors "FN_" sect "_" atype "(lp_" name ", " name ")\n"
+
+ if (vtype == "char*") {
+ exps = exps "\tBOOL " name "_EXP;\n"
+ exp_values = exp_values "\tFalse, /* " name "_EXP */\n"
+ }
+
+ next
+}
+
+/./ {
+ print "Extraneous line:" $0
+ defines = ""
+ exit
+}
+
+END {
+ if (sect != "" && defines != "") {
+ defines = defines exps "} local_vars;\n\n"
+ defines = defines tdstruct "\n\tglobal_vars g;\n\tlocal_vars l;\n} all_vars;\n"
+ values = values exp_values "\n }\n};\n\nstatic all_vars Vars;\n"
+ parms = parms "\n {NULL, P_BOOL, P_NONE, NULL, NULL, 0}\n};\n"
+ print heading defines values parms accessors > "daemon-parm.h"
+ } else {
+ print "Failed to parse the data in " ARGV[1]
+ exit 1
+ }
+}
diff --git a/daemon-parm.txt b/daemon-parm.txt
new file mode 100644
index 0000000..6903417
--- /dev/null
+++ b/daemon-parm.txt
@@ -0,0 +1,68 @@
+Globals: ================================================================
+
+STRING bind_address|address NULL
+STRING daemon_chroot NULL
+STRING daemon_gid NULL
+STRING daemon_uid NULL
+STRING motd_file NULL
+STRING pid_file NULL
+STRING socket_options NULL
+
+INTEGER listen_backlog 5
+INTEGER rsync_port|port 0
+
+BOOL proxy_protocol False
+
+Locals: =================================================================
+
+STRING auth_users NULL
+STRING charset NULL
+STRING comment NULL
+STRING dont_compress DEFAULT_DONT_COMPRESS
+STRING early_exec NULL
+STRING exclude NULL
+STRING exclude_from NULL
+STRING filter NULL
+STRING gid NULL
+STRING hosts_allow NULL
+STRING hosts_deny NULL
+STRING include NULL
+STRING include_from NULL
+STRING incoming_chmod NULL
+STRING lock_file DEFAULT_LOCK_FILE
+STRING log_file NULL
+STRING log_format "%o %h [%a] %m (%u) %f %l"
+STRING name NULL
+STRING name_converter NULL
+STRING outgoing_chmod NULL
+STRING post-xfer_exec NULL
+STRING pre-xfer_exec NULL
+STRING refuse_options NULL
+STRING secrets_file NULL
+STRING syslog_tag "rsyncd"
+STRING uid NULL
+
+PATH path NULL
+PATH temp_dir NULL
+
+INTEGER max_connections 0
+INTEGER max_verbosity 1
+INTEGER timeout 0
+
+ENUM syslog_facility LOG_DAEMON
+
+BOOL fake_super False
+BOOL forward_lookup True
+BOOL ignore_errors False
+BOOL ignore_nonreadable False
+BOOL list True
+BOOL read_only True
+BOOL reverse_lookup True
+BOOL strict_modes True
+BOOL transfer_logging False
+BOOL write_only False
+
+BOOL3 munge_symlinks Unset
+BOOL3 numeric_ids Unset
+BOOL3 open_noatime Unset
+BOOL3 use_chroot Unset
diff --git a/define-from-md.awk b/define-from-md.awk
new file mode 100755
index 0000000..9f7f2bf
--- /dev/null
+++ b/define-from-md.awk
@@ -0,0 +1,41 @@
+#!/usr/bin/awk -f
+
+# The caller must pass args: -v hfile=NAME rsync.1.md
+
+BEGIN {
+ heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from a list of values in " ARGV[1] "! */"
+ if (hfile ~ /compress/) {
+ define = "#define DEFAULT_DONT_COMPRESS"
+ prefix = "*."
+ } else {
+ define = "#define DEFAULT_CVSIGNORE"
+ prefix = ""
+ }
+ value_list = ""
+}
+
+/^ > [^ ]+$/ {
+ gsub(/`/, "")
+ if (value_list != "") value_list = value_list " "
+ value_list = value_list prefix $2
+ next
+}
+
+value_list ~ /\.gz / && hfile ~ /compress/ {
+ exit
+}
+
+value_list ~ /SCCS / && hfile ~ /cvsignore/ {
+ exit
+}
+
+value_list = ""
+
+END {
+ if (value_list != "")
+ print heading "\n\n" define " \"" value_list "\"" > hfile
+ else {
+ print "Failed to find a value list in " ARGV[1] " for " hfile
+ exit 1
+ }
+}
diff --git a/delete.c b/delete.c
new file mode 100644
index 0000000..4a29485
--- /dev/null
+++ b/delete.c
@@ -0,0 +1,240 @@
+/*
+ * Deletion routines used in rsync.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int am_root;
+extern int make_backups;
+extern int max_delete;
+extern char *backup_dir;
+extern char *backup_suffix;
+extern int backup_suffix_len;
+extern struct stats stats;
+
+int ignore_perishable = 0;
+int non_perishable_cnt = 0;
+int skipped_deletes = 0;
+
+static inline int is_backup_file(char *fn)
+{
+ int k = strlen(fn) - backup_suffix_len;
+ return k > 0 && strcmp(fn+k, backup_suffix) == 0;
+}
+
+/* The directory is about to be deleted: if DEL_RECURSE is given, delete all
+ * its contents, otherwise just checks for content. Returns DR_SUCCESS or
+ * DR_NOT_EMPTY. Note that fname must point to a MAXPATHLEN buffer! (The
+ * buffer is used for recursion, but returned unchanged.)
+ */
+static enum delret delete_dir_contents(char *fname, uint16 flags)
+{
+ struct file_list *dirlist;
+ enum delret ret;
+ unsigned remainder;
+ void *save_filters;
+ int j, dlen;
+ char *p;
+
+ if (DEBUG_GTE(DEL, 3)) {
+ rprintf(FINFO, "delete_dir_contents(%s) flags=%d\n",
+ fname, flags);
+ }
+
+ dlen = strlen(fname);
+ save_filters = push_local_filters(fname, dlen);
+
+ non_perishable_cnt = 0;
+ dirlist = get_dirlist(fname, dlen, 0);
+ ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS;
+
+ if (!dirlist->used)
+ goto done;
+
+ if (!(flags & DEL_RECURSE)) {
+ ret = DR_NOT_EMPTY;
+ goto done;
+ }
+
+ p = fname + dlen;
+ if (dlen != 1 || *fname != '/')
+ *p++ = '/';
+ remainder = MAXPATHLEN - (p - fname);
+
+ /* We do our own recursion, so make delete_item() non-recursive. */
+ flags = (flags & ~(DEL_RECURSE|DEL_MAKE_ROOM|DEL_NO_UID_WRITE))
+ | DEL_DIR_IS_EMPTY;
+
+ for (j = dirlist->used; j--; ) {
+ struct file_struct *fp = dirlist->files[j];
+
+ if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) {
+ if (DEBUG_GTE(DEL, 1)) {
+ rprintf(FINFO,
+ "mount point, %s, pins parent directory\n",
+ f_name(fp, NULL));
+ }
+ ret = DR_NOT_EMPTY;
+ continue;
+ }
+
+ strlcpy(p, fp->basename, remainder);
+ if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
+ do_chmod(fname, fp->mode | S_IWUSR);
+ /* Save stack by recursing to ourself directly. */
+ if (S_ISDIR(fp->mode)) {
+ if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
+ ret = DR_NOT_EMPTY;
+ }
+ if (delete_item(fname, fp->mode, flags) != DR_SUCCESS)
+ ret = DR_NOT_EMPTY;
+ }
+
+ fname[dlen] = '\0';
+
+ done:
+ flist_free(dirlist);
+ pop_local_filters(save_filters);
+
+ if (ret == DR_NOT_EMPTY) {
+ rprintf(FINFO, "cannot delete non-empty directory: %s\n",
+ fname);
+ }
+ return ret;
+}
+
+/* Delete a file or directory. If DEL_RECURSE is set in the flags, this will
+ * delete recursively.
+ *
+ * Note that fbuf must point to a MAXPATHLEN buffer if the mode indicates it's
+ * a directory! (The buffer is used for recursion, but returned unchanged.)
+ */
+enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
+{
+ enum delret ret;
+ char *what;
+ int ok;
+
+ if (DEBUG_GTE(DEL, 2)) {
+ rprintf(FINFO, "delete_item(%s) mode=%o flags=%d\n",
+ fbuf, (int)mode, (int)flags);
+ }
+
+ if (flags & DEL_NO_UID_WRITE)
+ do_chmod(fbuf, mode | S_IWUSR);
+
+ if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
+ /* This only happens on the first call to delete_item() since
+ * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
+ ignore_perishable = 1;
+ /* If DEL_RECURSE is not set, this just reports emptiness. */
+ ret = delete_dir_contents(fbuf, flags);
+ ignore_perishable = 0;
+ if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT)
+ goto check_ret;
+ /* OK: try to delete the directory. */
+ }
+
+ if (!(flags & DEL_MAKE_ROOM) && max_delete >= 0 && stats.deleted_files >= max_delete) {
+ skipped_deletes++;
+ return DR_AT_LIMIT;
+ }
+
+ if (S_ISDIR(mode)) {
+ what = "rmdir";
+ ok = do_rmdir(fbuf) == 0;
+ } else {
+ if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
+ what = "make_backup";
+ ok = make_backup(fbuf, True);
+ if (ok == 2) {
+ what = "unlink";
+ ok = robust_unlink(fbuf) == 0;
+ }
+ } else {
+ what = "unlink";
+ ok = robust_unlink(fbuf) == 0;
+ }
+ }
+
+ if (ok) {
+ if (!(flags & DEL_MAKE_ROOM)) {
+ log_delete(fbuf, mode);
+ stats.deleted_files++;
+ if (S_ISREG(mode)) {
+ /* Nothing more to count */
+ } else if (S_ISDIR(mode))
+ stats.deleted_dirs++;
+#ifdef SUPPORT_LINKS
+ else if (S_ISLNK(mode))
+ stats.deleted_symlinks++;
+#endif
+ else if (IS_DEVICE(mode))
+ stats.deleted_symlinks++;
+ else
+ stats.deleted_specials++;
+ }
+ ret = DR_SUCCESS;
+ } else {
+ if (S_ISDIR(mode) && errno == ENOTEMPTY) {
+ rprintf(FINFO, "cannot delete non-empty directory: %s\n",
+ fbuf);
+ ret = DR_NOT_EMPTY;
+ } else if (errno != ENOENT) {
+ rsyserr(FERROR_XFER, errno, "delete_file: %s(%s) failed",
+ what, fbuf);
+ ret = DR_FAILURE;
+ } else
+ ret = DR_SUCCESS;
+ }
+
+ check_ret:
+ if (ret != DR_SUCCESS && flags & DEL_MAKE_ROOM) {
+ const char *desc;
+ switch (flags & DEL_MAKE_ROOM) {
+ case DEL_FOR_FILE: desc = "regular file"; break;
+ case DEL_FOR_DIR: desc = "directory"; break;
+ case DEL_FOR_SYMLINK: desc = "symlink"; break;
+ case DEL_FOR_DEVICE: desc = "device file"; break;
+ case DEL_FOR_SPECIAL: desc = "special file"; break;
+ default: exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE */
+ }
+ rprintf(FERROR_XFER, "could not make way for %s %s: %s\n",
+ flags & DEL_FOR_BACKUP ? "backup" : "new",
+ desc, fbuf);
+ }
+ return ret;
+}
+
+uint16 get_del_for_flag(uint16 mode)
+{
+ if (S_ISREG(mode))
+ return DEL_FOR_FILE;
+ if (S_ISDIR(mode))
+ return DEL_FOR_DIR;
+ if (S_ISLNK(mode))
+ return DEL_FOR_SYMLINK;
+ if (IS_DEVICE(mode))
+ return DEL_FOR_DEVICE;
+ if (IS_SPECIAL(mode))
+ return DEL_FOR_SPECIAL;
+ exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE */
+}
diff --git a/doc/README-SGML b/doc/README-SGML
new file mode 100644
index 0000000..ce5a8e1
--- /dev/null
+++ b/doc/README-SGML
@@ -0,0 +1,20 @@
+Handling the rsync SGML documentation
+
+rsync documentation is now primarily in Docbook format. Docbook is an
+SGML/XML documentation format that is becoming standard on free
+operating systems. It's also used for Samba documentation.
+
+The SGML files are source code that can be translated into various
+useful output formats, primarily PDF, HTML, Postscript and plain text.
+
+To do this transformation on Debian, you should install the
+docbook-utils package. Having done that, you can say
+
+ docbook2pdf rsync.sgml
+
+and so on.
+
+On other systems you probably need James Clark's "sp" and "JadeTeX"
+packages. Work it out for yourself and send a note to the mailing
+list.
+
diff --git a/doc/profile.txt b/doc/profile.txt
new file mode 100644
index 0000000..b911d0a
--- /dev/null
+++ b/doc/profile.txt
@@ -0,0 +1,42 @@
+Notes on rsync profiling
+
+strlcpy is hot:
+
+ 0.00 0.00 1/7735635 push_dir [68]
+ 0.00 0.00 1/7735635 pop_dir [71]
+ 0.00 0.00 1/7735635 send_file_list [15]
+ 0.01 0.00 18857/7735635 send_files [4]
+ 0.04 0.00 129260/7735635 send_file_entry [18]
+ 0.04 0.00 129260/7735635 make_file [20]
+ 0.04 0.00 141666/7735635 send_directory <cycle 1> [36]
+ 2.29 0.00 7316589/7735635 f_name [13]
+[14] 11.7 2.42 0.00 7735635 strlcpy [14]
+
+
+Here's the top few functions:
+
+ 46.23 9.57 9.57 13160929 0.00 0.00 mdfour64
+ 14.78 12.63 3.06 13160929 0.00 0.00 copy64
+ 11.69 15.05 2.42 7735635 0.00 0.00 strlcpy
+ 10.05 17.13 2.08 41438 0.05 0.38 sum_update
+ 4.11 17.98 0.85 13159996 0.00 0.00 mdfour_update
+ 1.50 18.29 0.31 file_compare
+ 1.45 18.59 0.30 129261 0.00 0.01 send_file_entry
+ 1.23 18.84 0.26 2557585 0.00 0.00 f_name
+ 1.11 19.07 0.23 1483750 0.00 0.00 u_strcmp
+ 1.11 19.30 0.23 118129 0.00 0.00 writefd_unbuffered
+ 0.92 19.50 0.19 1085011 0.00 0.00 writefd
+ 0.43 19.59 0.09 156987 0.00 0.00 read_timeout
+ 0.43 19.68 0.09 129261 0.00 0.00 clean_fname
+ 0.39 19.75 0.08 32887 0.00 0.38 matched
+ 0.34 19.82 0.07 1 70.00 16293.92 send_files
+ 0.29 19.89 0.06 129260 0.00 0.00 make_file
+ 0.29 19.95 0.06 75430 0.00 0.00 read_unbuffered
+
+
+
+mdfour could perhaps be made faster:
+
+/* NOTE: This code makes no attempt to be fast! */
+
+There might be an optimized version somewhere that we can borrow.
diff --git a/doc/rsync.sgml b/doc/rsync.sgml
new file mode 100644
index 0000000..0f90059
--- /dev/null
+++ b/doc/rsync.sgml
@@ -0,0 +1,351 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+<book id="rsync">
+ <bookinfo>
+ <title>rsync</title>
+ <copyright>
+ <year>1996 -- 2002</year>
+ <holder>Martin Pool</holder>
+ <holder>Andrew Tridgell</holder>
+ </copyright>
+ <author>
+ <firstname>Martin</firstname>
+ <surname>Pool</surname>
+ </author>
+ </bookinfo>
+
+ <chapter>
+ <title>Introduction</title>
+
+ <para>rsync is a flexible program for efficiently copying files or
+ directory trees.
+
+ <para>rsync has many options to select which files will be copied
+ and how they are to be transferred. It may be used as an
+ alternative to ftp, http, scp or rcp.
+
+ <para>The rsync remote-update protocol allows rsync to transfer just
+ the differences between two sets of files across the network link,
+ using an efficient checksum-search algorithm described in the
+ technical report that accompanies this package.</para>
+
+ <para>Some of the additional features of rsync are:</para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>support for copying links, devices, owners, groups and
+ permissions
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ exclude and exclude-from options similar to GNU tar
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ a CVS exclude mode for ignoring the same files that CVS would ignore
+ </listitem>
+
+ <listitem>
+ <para>
+ can use any transparent remote shell, including rsh or ssh
+ </listitem>
+
+ <listitem>
+ <para>
+ does not require root privileges
+ </listitem>
+
+ <listitem>
+ <para>
+ pipelining of file transfers to minimize latency costs
+ </listitem>
+
+ <listitem>
+ <para>
+ support for anonymous or authenticated rsync servers (ideal for
+ mirroring)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </chapter>
+
+
+
+ <chapter>
+ <title>Using rsync</title>
+ <section>
+ <title>
+ Introductory example
+ </title>
+
+ <para>
+ Probably the most common case of rsync usage is to copy files
+ to or from a remote machine using
+ <application>ssh</application> as a network transport. In
+ this situation rsync is a good alternative to
+ <application>scp</application>.
+ </para>
+
+ <para>
+ The most commonly used arguments for rsync are
+ </para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>-v</option></term>
+ <listitem>
+ <para>Be verbose. Primarily, display the name of each file as it is copied.</para>
+ </listitem>
+ </varlistentry>
+
+
+ <varlistentry>
+ <term><option>-a</option></term>
+ <listitem>
+ <para>
+ Reproduce the structure and attributes of the origin files as exactly
+ as possible: this includes copying subdirectories, symlinks, special
+ files, ownership and permissions. (@xref{Attributes to
+ copy}.)
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+
+
+ <para><option>-v </option>
+
+ <para><option>-z</option>
+ Compress network traffic, using a modified version of the
+ @command{zlib} library.</para>
+
+ <para><option>-P</option>
+ Display a progress indicator while files are transferred. This should
+ normally be omitted if rsync is not run on a terminal.
+ </para>
+ </section>
+
+
+
+
+ <section>
+ <title>Local and remote</title>
+
+ <para>There are six different ways of using rsync. They
+ are:</para>
+
+
+
+ <!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE MSGSET PROCEDURE SIDEBAR QANDASET ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS ABSTRACT AUTHORBLURB EPIGRAPH INDEXTERM REFENTRY SECTION) -->
+ <orderedlist>
+ <listitem>
+ <para>
+ for copying local files. This is invoked when neither
+ source nor destination path contains a @code{:} separator
+
+ <listitem>
+ <para>
+ for copying from the local machine to a remote machine using
+ a remote shell program as the transport (such as rsh or
+ ssh). This is invoked when the destination path contains a
+ single @code{:} separator.
+
+ <listitem>
+ <para>
+ for copying from a remote machine to the local machine
+ using a remote shell program. This is invoked when the source
+ contains a @code{:} separator.
+
+ <listitem>
+ <para>
+ for copying from a remote rsync server to the local
+ machine. This is invoked when the source path contains a @code{::}
+ separator or a @code{rsync://} URL.
+
+ <listitem>
+ <para>
+ for copying from the local machine to a remote rsync
+ server. This is invoked when the destination path contains a @code{::}
+ separator.
+
+ <listitem>
+ <para>
+ for listing files on a remote machine. This is done the
+ same way as rsync transfers except that you leave off the
+ local destination.
+
+ </listitem>
+ </orderedlist>
+ <para>
+Note that in all cases (other than listing) at least one of the source
+and destination paths must be local.
+
+ <para>
+Any one invocation of rsync makes a copy in a single direction. rsync
+currently has no equivalent of @command{ftp}'s interactive mode.
+
+@cindex @sc{nfs}
+@cindex network filesystems
+@cindex remote filesystems
+
+ <para>
+rsync's network protocol is generally faster at copying files than
+network filesystems such as @sc{nfs} or @sc{cifs}. It is better to
+run rsync on the file server either as a daemon or over ssh than
+running rsync giving the network directory.
+ </para>
+ </section>
+ </chapter>
+
+
+
+ <chapter>
+ <title>Frequently asked questions</title>
+
+
+ <!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE MSGSET PROCEDURE SIDEBAR QANDASET ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS ABSTRACT AUTHORBLURB EPIGRAPH INDEXTERM SECTION SIMPLESECT REFENTRY SECT1) -->
+ <qandaset>
+ <!-- one of (QANDADIV QANDAENTRY) -->
+
+ <qandaentry>
+ <question>
+ <!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST
+ SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE
+ TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO
+ SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS
+ CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS
+ DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA
+ ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT
+ MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE
+ INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE
+ PROCEDURE ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS INDEXTERM) -->
+ <para>Are there mailing lists for rsync?
+ </question>
+
+ <answer>
+ <para>Yes, and you can subscribe and unsubscribe through a
+ web interface at
+ <ulink
+ url="http://lists.samba.org/">http://lists.samba.org/</ulink>
+ </para>
+
+ <para>
+ If you are having trouble with the mailing list, please
+ send mail to the administrator
+
+ <email>rsync-admin@lists.samba.org</email>
+
+ not to the list itself.
+ </para>
+
+ <para>
+ The mailing list archives are searchable. Use
+ <ulink url="http://google.com/">Google</ulink> and prepend
+ the search with <userinput>site:lists.samba.org
+ rsync</userinput>, plus relevant keywords.
+ </para>
+ </answer>
+ </qandaentry>
+
+
+ <qandaentry>
+ <question>
+ <para>
+ Why is rsync so much bigger when I build it with
+ <command>gcc</command>?
+ </para>
+ </question>
+ <answer>
+ <para>
+ On gcc, rsync builds by default with debug symbols
+ included. If you strip both executables, they should end
+ up about the same size. (Use <command>make
+ install-strip</command>.)
+ </para>
+ </answer>
+ </qandaentry>
+
+
+ <qandaentry>
+ <question>
+ <para>Is rsync useful for a single large file like an ISO image?</para>
+ </question>
+ <answer>
+ <para>
+ Yes, but note the following:
+
+ <para>
+ Background: A common use of rsync is to update a file (or set of files) in one location from a more
+ correct or up-to-date copy in another location, taking advantage of portions of the files that are
+ identical to speed up the process. (Note that rsync will transfer a file in its entirety if no copy
+ exists at the destination.)
+
+ <para>
+ (This discussion is written in terms of updating a local copy of a file from a correct file in a
+ remote location, although rsync can work in either direction.)
+
+ <para>
+ The file to be updated (the local file) must be in a destination directory that has enough space for
+ two copies of the file. (In addition, keep an extra copy of the file to be updated in a different
+ location for safety -- see the discussion (below) about rsync's behavior when the rsync process is
+ interrupted before completion.)
+
+ <para>
+ The local file must have the same name as the remote file being sync'd to (I think?). If you are
+ trying to upgrade an iso from, for example, beta1 to beta2, rename the local file to the same name
+ as the beta2 file. *(This is a useful thing to do -- only the changed portions will be
+ transmitted.)*
+
+ <para>
+ The extra copy of the local file kept in a different location is because of rsync's behavior if
+ interrupted before completion:
+
+ <para>
+ * If you specify the --partial option and rsync is interrupted, rsync will save the partially
+ rsync'd file and throw away the original local copy. (The partially rsync'd file is correct but
+ truncated.) If rsync is restarted, it will not have a local copy of the file to check for duplicate
+ blocks beyond the section of the file that has already been rsync'd, thus the remainder of the rsync
+ process will be a "pure transfer" of the file rather than taking advantage of the rsync algorithm.
+
+ <para>
+ * If you don't specify the --partial option and rsync is interrupted, rsync will throw away the
+ partially rsync'd file, and, when rsync is restarted starts the rsync process over from the
+ beginning.
+
+ <para>
+ Which of these is most desirable depends on the degree of commonality between the local and remote
+ copies of the file *and how much progress was made before the interruption*.
+
+ <para>
+ The ideal approach after an interruption would be to create a new file by taking the original file
+ and deleting a portion equal in size to the portion already rsync'd and then appending *the
+ remaining* portion to the portion of the file that has already been rsync'd. (There has been some
+ discussion about creating an option to do this automatically.)
+
+ The --compare-dest option is useful when transferring multiple files, but is of no benefit in
+ transferring a single file. (AFAIK)
+
+ *Other potentially useful information can be found at:
+ -[3]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFile
+
+ This answer, formatted with "real" bullets, can be found at:
+ -[4]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFileFAQ*
+
+ </para>
+ </answer>
+ </qandaentry>
+ </qandaset>
+ </chapter>
+
+
+ <appendix>
+ <title>Other Resources</title>
+
+ <para><ulink url="http://www.ccp14.ac.uk/ccp14admin/rsync/"></ulink></para>
+ </appendix>
+</book>
diff --git a/errcode.h b/errcode.h
new file mode 100644
index 0000000..9824a34
--- /dev/null
+++ b/errcode.h
@@ -0,0 +1,64 @@
+/*
+ * Error codes returned by rsync.
+ *
+ * Copyright (C) 1998-2000 Andrew Tridgell
+ * Copyright (C) 2003-2019 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/* If you change these, please also update the string mappings in log.c and
+ * the EXIT VALUES in rsync.yo. */
+
+#define RERR_OK 0
+#define RERR_SYNTAX 1 /* syntax or usage error */
+#define RERR_PROTOCOL 2 /* protocol incompatibility */
+#define RERR_FILESELECT 3 /* errors selecting input/output files, dirs */
+#define RERR_UNSUPPORTED 4 /* requested action not supported */
+#define RERR_STARTCLIENT 5 /* error starting client-server protocol */
+
+#define RERR_SOCKETIO 10 /* error in socket IO */
+#define RERR_FILEIO 11 /* error in file IO */
+#define RERR_STREAMIO 12 /* error in rsync protocol data stream */
+#define RERR_MESSAGEIO 13 /* errors with program diagnostics */
+#define RERR_IPC 14 /* error in IPC code */
+#define RERR_CRASHED 15 /* sibling crashed */
+#define RERR_TERMINATED 16 /* sibling terminated abnormally */
+
+#define RERR_SIGNAL1 19 /* status returned when sent SIGUSR1 */
+#define RERR_SIGNAL 20 /* status returned when sent SIGINT, SIGTERM, SIGHUP */
+#define RERR_WAITCHILD 21 /* some error returned by waitpid() */
+#define RERR_MALLOC 22 /* error allocating core memory buffers */
+#define RERR_PARTIAL 23 /* partial transfer */
+#define RERR_VANISHED 24 /* file(s) vanished on sender side */
+#define RERR_DEL_LIMIT 25 /* skipped some deletes due to --max-delete */
+
+#define RERR_TIMEOUT 30 /* timeout in data send/receive */
+#define RERR_CONTIMEOUT 35 /* timeout waiting for daemon connection */
+
+/* Although it doesn't seem to be specified anywhere,
+ * ssh and the shell seem to return these values:
+ *
+ * 124 if the command exited with status 255
+ * 125 if the command is killed by a signal
+ * 126 if the command cannot be run
+ * 127 if the command is not found
+ *
+ * and we could use this to give a better explanation if the remote
+ * command is not found.
+ */
+#define RERR_CMD_FAILED 124
+#define RERR_CMD_KILLED 125
+#define RERR_CMD_RUN 126
+#define RERR_CMD_NOTFOUND 127
diff --git a/exclude.c b/exclude.c
new file mode 100644
index 0000000..ffe55b1
--- /dev/null
+++ b/exclude.c
@@ -0,0 +1,1697 @@
+/*
+ * The filter include/exclude routines.
+ *
+ * Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+
+extern int am_server;
+extern int am_sender;
+extern int am_generator;
+extern int eol_nulls;
+extern int io_error;
+extern int xfer_dirs;
+extern int recurse;
+extern int local_server;
+extern int prune_empty_dirs;
+extern int ignore_perishable;
+extern int relative_paths;
+extern int delete_mode;
+extern int delete_excluded;
+extern int cvs_exclude;
+extern int sanitize_paths;
+extern int protocol_version;
+extern int trust_sender_args;
+extern int module_id;
+
+extern char curr_dir[MAXPATHLEN];
+extern unsigned int curr_dir_len;
+extern unsigned int module_dirlen;
+
+filter_rule_list filter_list = { .debug_type = "" };
+filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
+filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
+filter_rule_list implied_filter_list = { .debug_type = " [implied]" };
+
+int saw_xattr_filter = 0;
+int trust_sender_args = 0;
+int trust_sender_filter = 0;
+
+/* Need room enough for ":MODS " prefix plus some room to grow. */
+#define MAX_RULE_PREFIX (16)
+
+#define SLASH_WILD3_SUFFIX "/***"
+
+/* The dirbuf is set by push_local_filters() to the current subdirectory
+ * relative to curr_dir that is being processed. The path always has a
+ * trailing slash appended, and the variable dirbuf_len contains the length
+ * of this path prefix. The path is always absolute. */
+static char dirbuf[MAXPATHLEN+1];
+static unsigned int dirbuf_len = 0;
+static int dirbuf_depth;
+
+/* This is True when we're scanning parent dirs for per-dir merge-files. */
+static BOOL parent_dirscan = False;
+
+/* This array contains a list of all the currently active per-dir merge
+ * files. This makes it easier to save the appropriate values when we
+ * "push" down into each subdirectory. */
+static filter_rule **mergelist_parents;
+static int mergelist_cnt = 0;
+static int mergelist_size = 0;
+
+#define LOCAL_RULE 1
+#define REMOTE_RULE 2
+static uchar cur_elide_value = REMOTE_RULE;
+
+/* Each filter_list_struct describes a singly-linked list by keeping track
+ * of both the head and tail pointers. The list is slightly unusual in that
+ * a parent-dir's content can be appended to the end of the local list in a
+ * special way: the last item in the local list has its "next" pointer set
+ * to point to the inherited list, but the local list's tail pointer points
+ * at the end of the local list. Thus, if the local list is empty, the head
+ * will be pointing at the inherited content but the tail will be NULL. To
+ * help you visualize this, here are the possible list arrangements:
+ *
+ * Completely Empty Local Content Only
+ * ================================== ====================================
+ * head -> NULL head -> Local1 -> Local2 -> NULL
+ * tail -> NULL tail -------------^
+ *
+ * Inherited Content Only Both Local and Inherited Content
+ * ================================== ====================================
+ * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL
+ * tail -> NULL tail ---------^
+ *
+ * This means that anyone wanting to traverse the whole list to use it just
+ * needs to start at the head and use the "next" pointers until it goes
+ * NULL. To add new local content, we insert the item after the tail item
+ * and update the tail (obviously, if "tail" was NULL, we insert it at the
+ * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT
+ * because it is shared between the current list and our parent list(s).
+ * The easiest way to handle this is to simply truncate the list after the
+ * tail item and then free the local list from the head. When inheriting
+ * the list for a new local dir, we just save off the filter_list_struct
+ * values (so we can pop back to them later) and set the tail to NULL.
+ */
+
+static void teardown_mergelist(filter_rule *ex)
+{
+ int j;
+
+ if (!ex->u.mergelist)
+ return;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] deactivating mergelist #%d%s\n",
+ who_am_i(), mergelist_cnt - 1,
+ ex->u.mergelist->debug_type);
+ }
+
+ free(ex->u.mergelist->debug_type);
+ free(ex->u.mergelist);
+
+ for (j = 0; j < mergelist_cnt; j++) {
+ if (mergelist_parents[j] == ex) {
+ mergelist_parents[j] = NULL;
+ break;
+ }
+ }
+ while (mergelist_cnt && mergelist_parents[mergelist_cnt-1] == NULL)
+ mergelist_cnt--;
+}
+
+static void free_filter(filter_rule *ex)
+{
+ if (ex->rflags & FILTRULE_PERDIR_MERGE)
+ teardown_mergelist(ex);
+ free(ex->pattern);
+ free(ex);
+}
+
+static void free_filters(filter_rule *ent)
+{
+ while (ent) {
+ filter_rule *next = ent->next;
+ free_filter(ent);
+ ent = next;
+ }
+}
+
+/* Build a filter structure given a filter pattern. The value in "pat"
+ * is not null-terminated. "rule" is either held or freed, so the
+ * caller should not free it. */
+static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_len,
+ filter_rule *rule, int xflags)
+{
+ const char *cp;
+ unsigned int pre_len, suf_len, slash_cnt = 0;
+ char *mention_rule_suffix;
+
+ if (DEBUG_GTE(FILTER, 1) && pat_len && (pat[pat_len-1] == ' ' || pat[pat_len-1] == '\t'))
+ mention_rule_suffix = " -- CAUTION: trailing whitespace!";
+ else
+ mention_rule_suffix = DEBUG_GTE(FILTER, 2) ? "" : NULL;
+ if (mention_rule_suffix) {
+ rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s%s\n",
+ who_am_i(), get_rule_prefix(rule, pat, 0, NULL),
+ (int)pat_len, pat, (rule->rflags & FILTRULE_DIRECTORY) ? "/" : "",
+ listp->debug_type, mention_rule_suffix);
+ }
+
+ /* These flags also indicate that we're reading a list that
+ * needs to be filtered now, not post-filtered later. */
+ if (xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH)
+ && (rule->rflags & FILTRULES_SIDES)
+ == (am_sender ? FILTRULE_RECEIVER_SIDE : FILTRULE_SENDER_SIDE)) {
+ /* This filter applies only to the other side. Drop it. */
+ free_filter(rule);
+ return;
+ }
+
+ if (pat_len > 1 && pat[pat_len-1] == '/') {
+ pat_len--;
+ rule->rflags |= FILTRULE_DIRECTORY;
+ }
+
+ for (cp = pat; cp < pat + pat_len; cp++) {
+ if (*cp == '/')
+ slash_cnt++;
+ }
+
+ if (!(rule->rflags & (FILTRULE_ABS_PATH | FILTRULE_MERGE_FILE))
+ && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/')
+ || (xflags & XFLG_ABS_IF_SLASH && slash_cnt))) {
+ rule->rflags |= FILTRULE_ABS_PATH;
+ if (*pat == '/')
+ pre_len = dirbuf_len - module_dirlen - 1;
+ else
+ pre_len = 0;
+ } else
+ pre_len = 0;
+
+ /* The daemon wants dir-exclude rules to get an appended "/" + "***". */
+ if (xflags & XFLG_DIR2WILD3
+ && BITS_SETnUNSET(rule->rflags, FILTRULE_DIRECTORY, FILTRULE_INCLUDE)) {
+ rule->rflags &= ~FILTRULE_DIRECTORY;
+ suf_len = sizeof SLASH_WILD3_SUFFIX - 1;
+ } else
+ suf_len = 0;
+
+ rule->pattern = new_array(char, pre_len + pat_len + suf_len + 1);
+ if (pre_len) {
+ memcpy(rule->pattern, dirbuf + module_dirlen, pre_len);
+ for (cp = rule->pattern; cp < rule->pattern + pre_len; cp++) {
+ if (*cp == '/')
+ slash_cnt++;
+ }
+ }
+ rule->elide = 0;
+ strlcpy(rule->pattern + pre_len, pat, pat_len + 1);
+ pat_len += pre_len;
+ if (suf_len) {
+ memcpy(rule->pattern + pat_len, SLASH_WILD3_SUFFIX, suf_len+1);
+ pat_len += suf_len;
+ slash_cnt++;
+ }
+
+ if (strpbrk(rule->pattern, "*[?")) {
+ rule->rflags |= FILTRULE_WILD;
+ if ((cp = strstr(rule->pattern, "**")) != NULL) {
+ rule->rflags |= FILTRULE_WILD2;
+ /* If the pattern starts with **, note that. */
+ if (cp == rule->pattern)
+ rule->rflags |= FILTRULE_WILD2_PREFIX;
+ /* If the pattern ends with ***, note that. */
+ if (pat_len >= 3
+ && rule->pattern[pat_len-3] == '*'
+ && rule->pattern[pat_len-2] == '*'
+ && rule->pattern[pat_len-1] == '*')
+ rule->rflags |= FILTRULE_WILD3_SUFFIX;
+ }
+ }
+
+ if (rule->rflags & FILTRULE_PERDIR_MERGE) {
+ filter_rule_list *lp;
+ unsigned int len;
+ int i;
+
+ if ((cp = strrchr(rule->pattern, '/')) != NULL)
+ cp++;
+ else
+ cp = rule->pattern;
+
+ /* If the local merge file was already mentioned, don't
+ * add it again. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ filter_rule *ex = mergelist_parents[i];
+ const char *s;
+ if (!ex)
+ continue;
+ s = strrchr(ex->pattern, '/');
+ if (s)
+ s++;
+ else
+ s = ex->pattern;
+ len = strlen(s);
+ if (len == pat_len - (cp - rule->pattern) && memcmp(s, cp, len) == 0) {
+ free_filter(rule);
+ return;
+ }
+ }
+
+ lp = new_array0(filter_rule_list, 1);
+ if (asprintf(&lp->debug_type, " [per-dir %s]", cp) < 0)
+ out_of_memory("add_rule");
+ rule->u.mergelist = lp;
+
+ if (mergelist_cnt == mergelist_size) {
+ mergelist_size += 5;
+ mergelist_parents = realloc_array(mergelist_parents, filter_rule *, mergelist_size);
+ }
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] activating mergelist #%d%s\n",
+ who_am_i(), mergelist_cnt, lp->debug_type);
+ }
+ mergelist_parents[mergelist_cnt++] = rule;
+ } else
+ rule->u.slash_cnt = slash_cnt;
+
+ if (!listp->tail) {
+ rule->next = listp->head;
+ listp->head = listp->tail = rule;
+ } else {
+ rule->next = listp->tail->next;
+ listp->tail->next = rule;
+ listp->tail = rule;
+ }
+}
+
+/* If the wildcards failed, the remote shell might give us a file matching the literal
+ * wildcards. Since "*" & "?" already match themselves, this just needs to deal with
+ * failed "[foo]" idioms.
+ */
+static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg_len)
+{
+ filter_rule *rule;
+ const char *arg = based_on->pattern, *cp;
+ char *p;
+ int cnt = 0;
+
+ if (arg_len < 0)
+ arg_len = strlen(arg);
+
+ for (cp = arg; *cp; cp++) {
+ if (*cp == '\\' && cp[1]) {
+ cp++;
+ } else if (*cp == '[')
+ cnt++;
+ }
+ if (!cnt)
+ return;
+
+ rule = new0(filter_rule);
+ rule->rflags = based_on->rflags;
+ rule->u.slash_cnt = based_on->u.slash_cnt;
+ p = rule->pattern = new_array(char, arg_len + cnt + 1);
+ for (cp = arg; *cp; ) {
+ if (*cp == '\\' && cp[1]) {
+ *p++ = *cp++;
+ } else if (*cp == '[')
+ *p++ = '\\';
+ *p++ = *cp++;
+ }
+ *p++ = '\0';
+
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ if (DEBUG_GTE(FILTER, 3)) {
+ rprintf(FINFO, "[%s] add_implied_include(%s%s)\n", who_am_i(), rule->pattern,
+ rule->rflags & FILTRULE_DIRECTORY ? "/" : "");
+ }
+}
+
+static char *partial_string_buf = NULL;
+static int partial_string_len = 0;
+void implied_include_partial_string(const char *s_start, const char *s_end)
+{
+ partial_string_len = s_end - s_start;
+ if (partial_string_len <= 0 || partial_string_len >= MAXPATHLEN) { /* too-large should be impossible... */
+ partial_string_len = 0;
+ return;
+ }
+ if (!partial_string_buf)
+ partial_string_buf = new_array(char, MAXPATHLEN);
+ memcpy(partial_string_buf, s_start, partial_string_len);
+}
+
+void free_implied_include_partial_string()
+{
+ if (partial_string_buf) {
+ if (partial_string_len)
+ add_implied_include("", 0);
+ free(partial_string_buf);
+ partial_string_buf = NULL;
+ }
+ partial_string_len = 0; /* paranoia */
+}
+
+/* Each arg the client sends to the remote sender turns into an implied include
+ * that the receiver uses to validate the file list from the sender. */
+void add_implied_include(const char *arg, int skip_daemon_module)
+{
+ int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;
+ int slash_cnt = 0;
+ const char *cp;
+ char *p;
+ if (trust_sender_args)
+ return;
+ if (partial_string_len) {
+ arg_len = strlen(arg);
+ if (partial_string_len + arg_len >= MAXPATHLEN) {
+ partial_string_len = 0;
+ return; /* Should be impossible... */
+ }
+ memcpy(partial_string_buf + partial_string_len, arg, arg_len + 1);
+ partial_string_len = 0;
+ arg = partial_string_buf;
+ }
+ if (skip_daemon_module) {
+ if ((cp = strchr(arg, '/')) != NULL)
+ arg = cp + 1;
+ else
+ arg = "";
+ }
+ if (relative_paths) {
+ if ((cp = strstr(arg, "/./")) != NULL)
+ arg = cp + 3;
+ } else if ((cp = strrchr(arg, '/')) != NULL) {
+ arg = cp + 1;
+ }
+ if (*arg == '.' && arg[1] == '\0')
+ arg++;
+ arg_len = strlen(arg);
+ if (arg_len) {
+ char *new_pat;
+ if (strpbrk(arg, "*[?")) {
+ /* We need to add room to escape backslashes if wildcard chars are present. */
+ for (cp = arg; (cp = strchr(cp, '\\')) != NULL; cp++)
+ arg_len++;
+ saw_wild = 1;
+ }
+ arg_len++; /* Leave room for the prefixed slash */
+ p = new_pat = new_array(char, arg_len + 1);
+ *p++ = '/';
+ slash_cnt++;
+ for (cp = arg; *cp; ) {
+ switch (*cp) {
+ case '\\':
+ if (cp[1] == ']') {
+ if (!saw_wild)
+ cp++; /* A \] in a non-wild filter causes a problem, so drop the \ . */
+ } else if (!strchr("*[?", cp[1])) {
+ backslash_cnt++;
+ if (saw_wild)
+ *p++ = '\\';
+ }
+ *p++ = *cp++;
+ break;
+ case '/':
+ if (p[-1] == '/') { /* This is safe because of the initial slash. */
+ if (*++cp == '\0') {
+ slash_cnt--;
+ p--;
+ }
+ } else if (cp[1] == '\0') {
+ cp++;
+ } else {
+ slash_cnt++;
+ *p++ = *cp++;
+ }
+ break;
+ case '.':
+ if (p[-1] == '/') {
+ if (cp[1] == '/') {
+ cp += 2;
+ if (!*cp) {
+ slash_cnt--;
+ p--;
+ }
+ } else if (cp[1] == '\0') {
+ cp++;
+ slash_cnt--;
+ p--;
+ } else
+ *p++ = *cp++;
+ } else
+ *p++ = *cp++;
+ break;
+ case '[':
+ saw_live_open_brkt = 1;
+ *p++ = *cp++;
+ break;
+ default:
+ *p++ = *cp++;
+ break;
+ }
+ }
+ *p = '\0';
+ arg_len = p - new_pat;
+ if (!arg_len)
+ free(new_pat);
+ else {
+ filter_rule *rule = new0(filter_rule);
+ rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
+ rule->u.slash_cnt = slash_cnt;
+ arg = rule->pattern = new_pat;
+ if (!implied_filter_list.head)
+ implied_filter_list.head = implied_filter_list.tail = rule;
+ else {
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ }
+ if (DEBUG_GTE(FILTER, 3))
+ rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), arg);
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(rule, arg_len);
+ if (relative_paths && slash_cnt) {
+ int sub_slash_cnt = slash_cnt;
+ while ((p = strrchr(new_pat, '/')) != NULL && p != new_pat) {
+ filter_rule const *ent;
+ filter_rule *R_rule;
+ int found = 0;
+ *p = '\0';
+ for (ent = implied_filter_list.head; ent; ent = ent->next) {
+ if (ent != rule && strcmp(ent->pattern, new_pat) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ *p = '/';
+ break; /* We added all parent dirs already */
+ }
+ R_rule = new0(filter_rule);
+ R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY;
+ /* Check if our sub-path has wildcards or escaped backslashes */
+ if (saw_wild && strpbrk(new_pat, "*[?\\"))
+ R_rule->rflags |= FILTRULE_WILD;
+ R_rule->pattern = strdup(new_pat);
+ R_rule->u.slash_cnt = --sub_slash_cnt;
+ R_rule->next = implied_filter_list.head;
+ implied_filter_list.head = R_rule;
+ if (DEBUG_GTE(FILTER, 3)) {
+ rprintf(FINFO, "[%s] add_implied_include(%s/)\n",
+ who_am_i(), R_rule->pattern);
+ }
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(R_rule, -1);
+ }
+ for (p = new_pat; sub_slash_cnt < slash_cnt; sub_slash_cnt++) {
+ p += strlen(p);
+ *p = '/';
+ }
+ }
+ }
+ }
+
+ if (recurse || xfer_dirs) {
+ /* Now create a rule with an added "/" & "**" or "*" at the end */
+ filter_rule *rule = new0(filter_rule);
+ rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
+ if (recurse)
+ rule->rflags |= FILTRULE_WILD2;
+ /* We must leave enough room for / * * \0. */
+ if (!saw_wild && backslash_cnt) {
+ /* We are appending a wildcard, so now the backslashes need to be escaped. */
+ p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
+ for (cp = arg; *cp; ) { /* Note that arg_len != 0 because backslash_cnt > 0 */
+ if (*cp == '\\')
+ *p++ = '\\';
+ *p++ = *cp++;
+ }
+ } else {
+ p = rule->pattern = new_array(char, arg_len + 3 + 1);
+ if (arg_len) {
+ memcpy(p, arg, arg_len);
+ p += arg_len;
+ }
+ }
+ *p++ = '/';
+ *p++ = '*';
+ if (recurse)
+ *p++ = '*';
+ *p = '\0';
+ rule->u.slash_cnt = slash_cnt + 1;
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ if (DEBUG_GTE(FILTER, 3))
+ rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern);
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(rule, p - rule->pattern);
+ }
+}
+
+/* This frees any non-inherited items, leaving just inherited items on the list. */
+static void pop_filter_list(filter_rule_list *listp)
+{
+ filter_rule *inherited;
+
+ if (!listp->tail)
+ return;
+
+ inherited = listp->tail->next;
+
+ /* Truncate any inherited items from the local list. */
+ listp->tail->next = NULL;
+ /* Now free everything that is left. */
+ free_filters(listp->head);
+
+ listp->head = inherited;
+ listp->tail = NULL;
+}
+
+/* This returns an expanded (absolute) filename for the merge-file name if
+ * the name has any slashes in it OR if the parent_dirscan var is True;
+ * otherwise it returns the original merge_file name. If the len_ptr value
+ * is non-NULL the merge_file name is limited by the referenced length
+ * value and will be updated with the length of the resulting name. We
+ * always return a name that is null terminated, even if the merge_file
+ * name was not. */
+static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
+ unsigned int prefix_skip)
+{
+ static char buf[MAXPATHLEN];
+ char *fn, tmpbuf[MAXPATHLEN];
+ unsigned int fn_len;
+
+ if (!parent_dirscan && *merge_file != '/') {
+ /* Return the name unchanged it doesn't have any slashes. */
+ if (len_ptr) {
+ const char *p = merge_file + *len_ptr;
+ while (--p > merge_file && *p != '/') {}
+ if (p == merge_file) {
+ strlcpy(buf, merge_file, *len_ptr + 1);
+ return buf;
+ }
+ } else if (strchr(merge_file, '/') == NULL)
+ return (char *)merge_file;
+ }
+
+ fn = *merge_file == '/' ? buf : tmpbuf;
+ if (sanitize_paths) {
+ const char *r = prefix_skip ? "/" : NULL;
+ /* null-terminate the name if it isn't already */
+ if (len_ptr && merge_file[*len_ptr]) {
+ char *to = fn == buf ? tmpbuf : buf;
+ strlcpy(to, merge_file, *len_ptr + 1);
+ merge_file = to;
+ }
+ if (!sanitize_path(fn, merge_file, r, dirbuf_depth, SP_DEFAULT)) {
+ rprintf(FERROR, "merge-file name overflows: %s\n",
+ merge_file);
+ return NULL;
+ }
+ fn_len = strlen(fn);
+ } else {
+ strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
+ fn_len = clean_fname(fn, CFN_COLLAPSE_DOT_DOT_DIRS);
+ }
+
+ /* If the name isn't in buf yet, it wasn't absolute. */
+ if (fn != buf) {
+ int d_len = dirbuf_len - prefix_skip;
+ if (d_len + fn_len >= MAXPATHLEN) {
+ rprintf(FERROR, "merge-file name overflows: %s\n", fn);
+ return NULL;
+ }
+ memcpy(buf, dirbuf + prefix_skip, d_len);
+ memcpy(buf + d_len, fn, fn_len + 1);
+ fn_len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS);
+ }
+
+ if (len_ptr)
+ *len_ptr = fn_len;
+ return buf;
+}
+
+/* Sets the dirbuf and dirbuf_len values. */
+void set_filter_dir(const char *dir, unsigned int dirlen)
+{
+ unsigned int len;
+ if (*dir != '/') {
+ memcpy(dirbuf, curr_dir, curr_dir_len);
+ dirbuf[curr_dir_len] = '/';
+ len = curr_dir_len + 1;
+ if (len + dirlen >= MAXPATHLEN)
+ dirlen = 0;
+ } else
+ len = 0;
+ memcpy(dirbuf + len, dir, dirlen);
+ dirbuf[dirlen + len] = '\0';
+ dirbuf_len = clean_fname(dirbuf, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (dirbuf_len > 1 && dirbuf[dirbuf_len-1] == '.'
+ && dirbuf[dirbuf_len-2] == '/')
+ dirbuf_len -= 2;
+ if (dirbuf_len != 1)
+ dirbuf[dirbuf_len++] = '/';
+ dirbuf[dirbuf_len] = '\0';
+ if (sanitize_paths)
+ dirbuf_depth = count_dir_elements(dirbuf + module_dirlen);
+}
+
+/* This routine takes a per-dir merge-file entry and finishes its setup.
+ * If the name has a path portion then we check to see if it refers to a
+ * parent directory of the first transfer dir. If it does, we scan all the
+ * dirs from that point through the parent dir of the transfer dir looking
+ * for the per-dir merge-file in each one. */
+static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
+ filter_rule_list *lp)
+{
+ char buf[MAXPATHLEN];
+ char *x, *y, *pat = ex->pattern;
+ unsigned int len;
+
+ if (!(x = parse_merge_name(pat, NULL, 0)) || *x != '/')
+ return 0;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] performing parent_dirscan for mergelist #%d%s\n",
+ who_am_i(), mergelist_num, lp->debug_type);
+ }
+ y = strrchr(x, '/');
+ *y = '\0';
+ ex->pattern = strdup(y+1);
+ if (!*x)
+ x = "/";
+ if (*x == '/')
+ strlcpy(buf, x, MAXPATHLEN);
+ else
+ pathjoin(buf, MAXPATHLEN, dirbuf, x);
+
+ len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (len != 1 && len < MAXPATHLEN-1) {
+ buf[len++] = '/';
+ buf[len] = '\0';
+ }
+ /* This ensures that the specified dir is a parent of the transfer. */
+ for (x = buf, y = dirbuf; *x && *x == *y; x++, y++) {}
+ if (*x)
+ y += strlen(y); /* nope -- skip the scan */
+
+ parent_dirscan = True;
+ while (*y) {
+ char save[MAXPATHLEN];
+ strlcpy(save, y, MAXPATHLEN);
+ *y = '\0';
+ dirbuf_len = y - dirbuf;
+ strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
+ parse_filter_file(lp, buf, ex, XFLG_ANCHORED2ABS);
+ if (ex->rflags & FILTRULE_NO_INHERIT) {
+ /* Free the undesired rules to clean up any per-dir
+ * mergelists they defined. Otherwise pop_local_filters
+ * may crash trying to restore nonexistent state for
+ * those mergelists. */
+ free_filters(lp->head);
+ lp->head = NULL;
+ }
+ lp->tail = NULL;
+ strlcpy(y, save, MAXPATHLEN);
+ while ((*x++ = *y++) != '/') {}
+ }
+ parent_dirscan = False;
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] completed parent_dirscan for mergelist #%d%s\n",
+ who_am_i(), mergelist_num, lp->debug_type);
+ }
+ free(pat);
+ return 1;
+}
+
+struct local_filter_state {
+ int mergelist_cnt;
+ filter_rule_list mergelists[1];
+};
+
+/* Each time rsync changes to a new directory it call this function to
+ * handle all the per-dir merge-files. The "dir" value is the current path
+ * relative to curr_dir (which might not be null-terminated). We copy it
+ * into dirbuf so that we can easily append a file name on the end. */
+void *push_local_filters(const char *dir, unsigned int dirlen)
+{
+ struct local_filter_state *push;
+ int i;
+
+ set_filter_dir(dir, dirlen);
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] pushing local filters for %s\n",
+ who_am_i(), dirbuf);
+ }
+
+ if (!mergelist_cnt) {
+ /* No old state to save and no new merge files to push. */
+ return NULL;
+ }
+
+ push = (struct local_filter_state *)new_array(char,
+ sizeof (struct local_filter_state)
+ + (mergelist_cnt-1) * sizeof (filter_rule_list));
+
+ push->mergelist_cnt = mergelist_cnt;
+ for (i = 0; i < mergelist_cnt; i++) {
+ filter_rule *ex = mergelist_parents[i];
+ if (!ex)
+ continue;
+ memcpy(&push->mergelists[i], ex->u.mergelist, sizeof (filter_rule_list));
+ }
+
+ /* Note: parse_filter_file() might increase mergelist_cnt, so keep
+ * this loop separate from the above loop. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ filter_rule *ex = mergelist_parents[i];
+ filter_rule_list *lp;
+ if (!ex)
+ continue;
+ lp = ex->u.mergelist;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] pushing mergelist #%d%s\n",
+ who_am_i(), i, lp->debug_type);
+ }
+
+ lp->tail = NULL; /* Switch any local rules to inherited. */
+ if (ex->rflags & FILTRULE_NO_INHERIT)
+ lp->head = NULL;
+
+ if (ex->rflags & FILTRULE_FINISH_SETUP) {
+ ex->rflags &= ~FILTRULE_FINISH_SETUP;
+ if (setup_merge_file(i, ex, lp))
+ set_filter_dir(dir, dirlen);
+ }
+
+ if (strlcpy(dirbuf + dirbuf_len, ex->pattern,
+ MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len) {
+ parse_filter_file(lp, dirbuf, ex,
+ XFLG_ANCHORED2ABS);
+ } else {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR,
+ "cannot add local filter rules in long-named directory: %s\n",
+ full_fname(dirbuf));
+ }
+ dirbuf[dirbuf_len] = '\0';
+ }
+
+ return (void*)push;
+}
+
+void pop_local_filters(void *mem)
+{
+ struct local_filter_state *pop = (struct local_filter_state *)mem;
+ int i;
+ int old_mergelist_cnt = pop ? pop->mergelist_cnt : 0;
+
+ if (DEBUG_GTE(FILTER, 2))
+ rprintf(FINFO, "[%s] popping local filters\n", who_am_i());
+
+ for (i = mergelist_cnt; i-- > 0; ) {
+ filter_rule *ex = mergelist_parents[i];
+ filter_rule_list *lp;
+ if (!ex)
+ continue;
+ lp = ex->u.mergelist;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] popping mergelist #%d%s\n",
+ who_am_i(), i, lp->debug_type);
+ }
+
+ pop_filter_list(lp);
+ if (i >= old_mergelist_cnt && lp->head) {
+ /* This mergelist does not exist in the state to be restored, but it
+ * still has inherited rules. This can sometimes happen if a per-dir
+ * merge file calls setup_merge_file() in push_local_filters() and that
+ * leaves some inherited rules that aren't in the pushed list state. */
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] freeing parent_dirscan filters of mergelist #%d%s\n",
+ who_am_i(), i, ex->u.mergelist->debug_type);
+ }
+ pop_filter_list(lp);
+ }
+ }
+
+ if (!pop)
+ return; /* No state to restore. */
+
+ for (i = 0; i < old_mergelist_cnt; i++) {
+ filter_rule *ex = mergelist_parents[i];
+ if (!ex)
+ continue;
+ memcpy(ex->u.mergelist, &pop->mergelists[i], sizeof (filter_rule_list));
+ }
+
+ free(pop);
+}
+
+void change_local_filter_dir(const char *dname, int dlen, int dir_depth)
+{
+ static int cur_depth = -1;
+ static void *filt_array[MAXPATHLEN/2+1];
+
+ if (!dname) {
+ for ( ; cur_depth >= 0; cur_depth--) {
+ if (filt_array[cur_depth]) {
+ pop_local_filters(filt_array[cur_depth]);
+ filt_array[cur_depth] = NULL;
+ }
+ }
+ return;
+ }
+
+ assert(dir_depth < MAXPATHLEN/2+1);
+
+ for ( ; cur_depth >= dir_depth; cur_depth--) {
+ if (filt_array[cur_depth]) {
+ pop_local_filters(filt_array[cur_depth]);
+ filt_array[cur_depth] = NULL;
+ }
+ }
+
+ cur_depth = dir_depth;
+ filt_array[cur_depth] = push_local_filters(dname, dlen);
+}
+
+static int rule_matches(const char *fname, filter_rule *ex, int name_flags)
+{
+ int slash_handling, str_cnt = 0, anchored_match = 0;
+ int ret_match = ex->rflags & FILTRULE_NEGATE ? 0 : 1;
+ char *p, *pattern = ex->pattern;
+ const char *strings[16]; /* more than enough */
+ const char *name = fname + (*fname == '/');
+
+ if (!*name || ex->elide == cur_elide_value)
+ return 0;
+
+ if (!(name_flags & NAME_IS_XATTR) ^ !(ex->rflags & FILTRULE_XATTR))
+ return 0;
+
+ if (!ex->u.slash_cnt && !(ex->rflags & FILTRULE_WILD2)) {
+ /* If the pattern does not have any slashes AND it does
+ * not have a "**" (which could match a slash), then we
+ * just match the name portion of the path. */
+ if ((p = strrchr(name,'/')) != NULL)
+ name = p+1;
+ } else if (ex->rflags & FILTRULE_ABS_PATH && *fname != '/'
+ && curr_dir_len > module_dirlen + 1) {
+ /* If we're matching against an absolute-path pattern,
+ * we need to prepend our full path info. */
+ strings[str_cnt++] = curr_dir + module_dirlen + 1;
+ strings[str_cnt++] = "/";
+ } else if (ex->rflags & FILTRULE_WILD2_PREFIX && *fname != '/') {
+ /* Allow "**"+"/" to match at the start of the string. */
+ strings[str_cnt++] = "/";
+ }
+ strings[str_cnt++] = name;
+ if (name_flags & NAME_IS_DIR) {
+ /* Allow a trailing "/"+"***" to match the directory. */
+ if (ex->rflags & FILTRULE_WILD3_SUFFIX)
+ strings[str_cnt++] = "/";
+ } else if (ex->rflags & FILTRULE_DIRECTORY)
+ return !ret_match;
+ strings[str_cnt] = NULL;
+
+ if (*pattern == '/') {
+ anchored_match = 1;
+ pattern++;
+ }
+
+ if (!anchored_match && ex->u.slash_cnt
+ && !(ex->rflags & FILTRULE_WILD2)) {
+ /* A non-anchored match with an infix slash and no "**"
+ * needs to match the last slash_cnt+1 name elements. */
+ slash_handling = ex->u.slash_cnt + 1;
+ } else if (!anchored_match && !(ex->rflags & FILTRULE_WILD2_PREFIX)
+ && ex->rflags & FILTRULE_WILD2) {
+ /* A non-anchored match with an infix or trailing "**" (but not
+ * a prefixed "**") needs to try matching after every slash. */
+ slash_handling = -1;
+ } else {
+ /* The pattern matches only at the start of the path or name. */
+ slash_handling = 0;
+ }
+
+ if (ex->rflags & FILTRULE_WILD) {
+ if (wildmatch_array(pattern, strings, slash_handling))
+ return ret_match;
+ } else if (str_cnt > 1) {
+ if (litmatch_array(pattern, strings, slash_handling))
+ return ret_match;
+ } else if (anchored_match) {
+ if (strcmp(name, pattern) == 0)
+ return ret_match;
+ } else {
+ int l1 = strlen(name);
+ int l2 = strlen(pattern);
+ if (l2 <= l1 &&
+ strcmp(name+(l1-l2),pattern) == 0 &&
+ (l1==l2 || name[l1-(l2+1)] == '/')) {
+ return ret_match;
+ }
+ }
+
+ return !ret_match;
+}
+
+static void report_filter_result(enum logcode code, char const *name,
+ filter_rule const *ent,
+ int name_flags, const char *type)
+{
+ int log_level = am_sender || am_generator ? 1 : 3;
+
+ /* If a trailing slash is present to match only directories,
+ * then it is stripped out by add_rule(). So as a special
+ * case we add it back in the log output. */
+ if (DEBUG_GTE(FILTER, log_level)) {
+ static char *actions[2][2]
+ = { {"show", "hid"}, {"risk", "protect"} };
+ const char *w = who_am_i();
+ const char *t = name_flags & NAME_IS_XATTR ? "xattr"
+ : name_flags & NAME_IS_DIR ? "directory"
+ : "file";
+ rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n",
+ w, actions[*w=='g'][!(ent->rflags & FILTRULE_INCLUDE)],
+ t, name, ent->pattern,
+ ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type);
+ }
+}
+
+/* This function is used to check if a file should be included/excluded
+ * from the list of files based on its name and type etc. The value of
+ * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. */
+int name_is_excluded(const char *fname, int name_flags, int filter_level)
+{
+ if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, name_flags) < 0) {
+ if (!(name_flags & NAME_IS_XATTR))
+ errno = ENOENT;
+ return 1;
+ }
+
+ if (filter_level != ALL_FILTERS)
+ return 0;
+
+ if (filter_list.head && check_filter(&filter_list, FINFO, fname, name_flags) < 0)
+ return 1;
+
+ return 0;
+}
+
+int check_server_filter(filter_rule_list *listp, enum logcode code, const char *name, int name_flags)
+{
+ int ret;
+ cur_elide_value = LOCAL_RULE;
+ ret = check_filter(listp, code, name, name_flags);
+ cur_elide_value = REMOTE_RULE;
+ return ret;
+}
+
+/* Return -1 if file "name" is defined to be excluded by the specified
+ * exclude list, 1 if it is included, and 0 if it was not matched. */
+int check_filter(filter_rule_list *listp, enum logcode code,
+ const char *name, int name_flags)
+{
+ filter_rule *ent;
+
+ for (ent = listp->head; ent; ent = ent->next) {
+ if (ignore_perishable && ent->rflags & FILTRULE_PERISHABLE)
+ continue;
+ if (ent->rflags & FILTRULE_PERDIR_MERGE) {
+ int rc = check_filter(ent->u.mergelist, code, name, name_flags);
+ if (rc)
+ return rc;
+ continue;
+ }
+ if (ent->rflags & FILTRULE_CVS_IGNORE) {
+ int rc = check_filter(&cvs_filter_list, code, name, name_flags);
+ if (rc)
+ return rc;
+ continue;
+ }
+ if (rule_matches(name, ent, name_flags)) {
+ report_filter_result(code, name, ent, name_flags, listp->debug_type);
+ return ent->rflags & FILTRULE_INCLUDE ? 1 : -1;
+ }
+ }
+
+ return 0;
+}
+
+#define RULE_STRCMP(s,r) rule_strcmp((s), (r), sizeof (r) - 1)
+
+static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len)
+{
+ if (strncmp((char*)str, rule, rule_len) != 0)
+ return NULL;
+ if (isspace(str[rule_len]) || str[rule_len] == '_' || !str[rule_len])
+ return str + rule_len - 1;
+ if (str[rule_len] == ',')
+ return str + rule_len;
+ return NULL;
+}
+
+#define FILTRULES_FROM_CONTAINER (FILTRULE_ABS_PATH | FILTRULE_INCLUDE \
+ | FILTRULE_DIRECTORY | FILTRULE_NEGATE \
+ | FILTRULE_PERISHABLE)
+
+/* Gets the next include/exclude rule from *rulestr_ptr and advances
+ * *rulestr_ptr to point beyond it. Stores the pattern's start (within
+ * *rulestr_ptr) and length in *pat_ptr and *pat_len_ptr, and returns a newly
+ * allocated filter_rule containing the rest of the information. Returns
+ * NULL if there are no more rules in the input.
+ *
+ * The template provides defaults for the new rule to inherit, and the
+ * template rflags and the xflags additionally affect parsing. */
+static filter_rule *parse_rule_tok(const char **rulestr_ptr,
+ const filter_rule *template, int xflags,
+ const char **pat_ptr, unsigned int *pat_len_ptr)
+{
+ const uchar *s = (const uchar *)*rulestr_ptr;
+ filter_rule *rule;
+ unsigned int len;
+
+ if (template->rflags & FILTRULE_WORD_SPLIT) {
+ /* Skip over any initial whitespace. */
+ while (isspace(*s))
+ s++;
+ /* Update to point to real start of rule. */
+ *rulestr_ptr = (const char *)s;
+ }
+ if (!*s)
+ return NULL;
+
+ rule = new0(filter_rule);
+
+ /* Inherit from the template. Don't inherit FILTRULES_SIDES; we check
+ * that later. */
+ rule->rflags = template->rflags & FILTRULES_FROM_CONTAINER;
+
+ /* Figure out what kind of a filter rule "s" is pointing at. Note
+ * that if FILTRULE_NO_PREFIXES is set, the rule is either an include
+ * or an exclude based on the inheritance of the FILTRULE_INCLUDE
+ * flag (above). XFLG_OLD_PREFIXES indicates a compatibility mode
+ * for old include/exclude patterns where just "+ " and "- " are
+ * allowed as optional prefixes. */
+ if (template->rflags & FILTRULE_NO_PREFIXES) {
+ if (*s == '!' && template->rflags & FILTRULE_CVS_IGNORE)
+ rule->rflags |= FILTRULE_CLEAR_LIST; /* Tentative! */
+ } else if (xflags & XFLG_OLD_PREFIXES) {
+ if (*s == '-' && s[1] == ' ') {
+ rule->rflags &= ~FILTRULE_INCLUDE;
+ s += 2;
+ } else if (*s == '+' && s[1] == ' ') {
+ rule->rflags |= FILTRULE_INCLUDE;
+ s += 2;
+ } else if (*s == '!')
+ rule->rflags |= FILTRULE_CLEAR_LIST; /* Tentative! */
+ } else {
+ char ch = 0;
+ BOOL prefix_specifies_side = False;
+ switch (*s) {
+ case 'c':
+ if ((s = RULE_STRCMP(s, "clear")) != NULL)
+ ch = '!';
+ break;
+ case 'd':
+ if ((s = RULE_STRCMP(s, "dir-merge")) != NULL)
+ ch = ':';
+ break;
+ case 'e':
+ if ((s = RULE_STRCMP(s, "exclude")) != NULL)
+ ch = '-';
+ break;
+ case 'h':
+ if ((s = RULE_STRCMP(s, "hide")) != NULL)
+ ch = 'H';
+ break;
+ case 'i':
+ if ((s = RULE_STRCMP(s, "include")) != NULL)
+ ch = '+';
+ break;
+ case 'm':
+ if ((s = RULE_STRCMP(s, "merge")) != NULL)
+ ch = '.';
+ break;
+ case 'p':
+ if ((s = RULE_STRCMP(s, "protect")) != NULL)
+ ch = 'P';
+ break;
+ case 'r':
+ if ((s = RULE_STRCMP(s, "risk")) != NULL)
+ ch = 'R';
+ break;
+ case 's':
+ if ((s = RULE_STRCMP(s, "show")) != NULL)
+ ch = 'S';
+ break;
+ default:
+ ch = *s;
+ if (s[1] == ',')
+ s++;
+ break;
+ }
+ switch (ch) {
+ case ':':
+ trust_sender_filter = 1;
+ rule->rflags |= FILTRULE_PERDIR_MERGE
+ | FILTRULE_FINISH_SETUP;
+ /* FALL THROUGH */
+ case '.':
+ rule->rflags |= FILTRULE_MERGE_FILE;
+ break;
+ case '+':
+ rule->rflags |= FILTRULE_INCLUDE;
+ break;
+ case '-':
+ break;
+ case 'S':
+ rule->rflags |= FILTRULE_INCLUDE;
+ /* FALL THROUGH */
+ case 'H':
+ rule->rflags |= FILTRULE_SENDER_SIDE;
+ prefix_specifies_side = True;
+ break;
+ case 'R':
+ rule->rflags |= FILTRULE_INCLUDE;
+ /* FALL THROUGH */
+ case 'P':
+ rule->rflags |= FILTRULE_RECEIVER_SIDE;
+ prefix_specifies_side = True;
+ break;
+ case '!':
+ rule->rflags |= FILTRULE_CLEAR_LIST;
+ break;
+ default:
+ rprintf(FERROR, "Unknown filter rule: `%s'\n", *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ while (ch != '!' && *++s && *s != ' ' && *s != '_') {
+ if (template->rflags & FILTRULE_WORD_SPLIT && isspace(*s)) {
+ s--;
+ break;
+ }
+ switch (*s) {
+ default:
+ invalid:
+ rprintf(FERROR,
+ "invalid modifier '%c' at position %d in filter rule: %s\n",
+ *s, (int)(s - (const uchar *)*rulestr_ptr), *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ case '-':
+ if (!BITS_SETnUNSET(rule->rflags, FILTRULE_MERGE_FILE, FILTRULE_NO_PREFIXES))
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_PREFIXES;
+ break;
+ case '+':
+ if (!BITS_SETnUNSET(rule->rflags, FILTRULE_MERGE_FILE, FILTRULE_NO_PREFIXES))
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_PREFIXES
+ | FILTRULE_INCLUDE;
+ break;
+ case '/':
+ rule->rflags |= FILTRULE_ABS_PATH;
+ break;
+ case '!':
+ /* Negation really goes with the pattern, so it
+ * isn't useful as a merge-file default. */
+ if (rule->rflags & FILTRULE_MERGE_FILE)
+ goto invalid;
+ rule->rflags |= FILTRULE_NEGATE;
+ break;
+ case 'C':
+ if (rule->rflags & FILTRULE_NO_PREFIXES || prefix_specifies_side)
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_PREFIXES
+ | FILTRULE_WORD_SPLIT
+ | FILTRULE_NO_INHERIT
+ | FILTRULE_CVS_IGNORE;
+ break;
+ case 'e':
+ if (!(rule->rflags & FILTRULE_MERGE_FILE))
+ goto invalid;
+ rule->rflags |= FILTRULE_EXCLUDE_SELF;
+ break;
+ case 'n':
+ if (!(rule->rflags & FILTRULE_MERGE_FILE))
+ goto invalid;
+ rule->rflags |= FILTRULE_NO_INHERIT;
+ break;
+ case 'p':
+ rule->rflags |= FILTRULE_PERISHABLE;
+ break;
+ case 'r':
+ if (prefix_specifies_side)
+ goto invalid;
+ rule->rflags |= FILTRULE_RECEIVER_SIDE;
+ break;
+ case 's':
+ if (prefix_specifies_side)
+ goto invalid;
+ rule->rflags |= FILTRULE_SENDER_SIDE;
+ break;
+ case 'w':
+ if (!(rule->rflags & FILTRULE_MERGE_FILE))
+ goto invalid;
+ rule->rflags |= FILTRULE_WORD_SPLIT;
+ break;
+ case 'x':
+ rule->rflags |= FILTRULE_XATTR;
+ saw_xattr_filter = 1;
+ break;
+ }
+ }
+ if (*s)
+ s++;
+ }
+ if (template->rflags & FILTRULES_SIDES) {
+ if (rule->rflags & FILTRULES_SIDES) {
+ /* The filter and template both specify side(s). This
+ * is dodgy (and won't work correctly if the template is
+ * a one-sided per-dir merge rule), so reject it. */
+ rprintf(FERROR,
+ "specified-side merge file contains specified-side filter: %s\n",
+ *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ rule->rflags |= template->rflags & FILTRULES_SIDES;
+ }
+
+ if (template->rflags & FILTRULE_WORD_SPLIT) {
+ const uchar *cp = s;
+ /* Token ends at whitespace or the end of the string. */
+ while (!isspace(*cp) && *cp != '\0')
+ cp++;
+ len = cp - s;
+ } else
+ len = strlen((char*)s);
+
+ if (rule->rflags & FILTRULE_CLEAR_LIST) {
+ if (!(rule->rflags & FILTRULE_NO_PREFIXES)
+ && !(xflags & XFLG_OLD_PREFIXES) && len) {
+ rprintf(FERROR,
+ "'!' rule has trailing characters: %s\n", *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (len > 1)
+ rule->rflags &= ~FILTRULE_CLEAR_LIST;
+ } else if (!len && !(rule->rflags & FILTRULE_CVS_IGNORE)) {
+ rprintf(FERROR, "unexpected end of filter rule: %s\n", *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ /* --delete-excluded turns an un-modified include/exclude into a sender-side rule. */
+ if (delete_excluded
+ && !(rule->rflags & (FILTRULES_SIDES|FILTRULE_MERGE_FILE|FILTRULE_PERDIR_MERGE)))
+ rule->rflags |= FILTRULE_SENDER_SIDE;
+
+ *pat_ptr = (const char *)s;
+ *pat_len_ptr = len;
+ *rulestr_ptr = *pat_ptr + len;
+ return rule;
+}
+
+static void get_cvs_excludes(uint32 rflags)
+{
+ static int initialized = 0;
+ char *p, fname[MAXPATHLEN];
+
+ if (initialized)
+ return;
+ initialized = 1;
+
+ parse_filter_str(&cvs_filter_list, default_cvsignore(),
+ rule_template(rflags | (protocol_version >= 30 ? FILTRULE_PERISHABLE : 0)),
+ 0);
+
+ p = module_id >= 0 && lp_use_chroot(module_id) ? "/" : getenv("HOME");
+ if (p && pathjoin(fname, MAXPATHLEN, p, ".cvsignore") < MAXPATHLEN)
+ parse_filter_file(&cvs_filter_list, fname, rule_template(rflags), 0);
+
+ parse_filter_str(&cvs_filter_list, getenv("CVSIGNORE"), rule_template(rflags), 0);
+}
+
+const filter_rule *rule_template(uint32 rflags)
+{
+ static filter_rule template; /* zero-initialized */
+ template.rflags = rflags;
+ return &template;
+}
+
+void parse_filter_str(filter_rule_list *listp, const char *rulestr,
+ const filter_rule *template, int xflags)
+{
+ filter_rule *rule;
+ const char *pat;
+ unsigned int pat_len;
+
+ if (!rulestr)
+ return;
+
+ while (1) {
+ uint32 new_rflags;
+
+ /* Remember that the returned string is NOT '\0' terminated! */
+ if (!(rule = parse_rule_tok(&rulestr, template, xflags, &pat, &pat_len)))
+ break;
+
+ if (pat_len >= MAXPATHLEN) {
+ rprintf(FERROR, "discarding over-long filter: %.*s\n",
+ (int)pat_len, pat);
+ free_continue:
+ free_filter(rule);
+ continue;
+ }
+
+ new_rflags = rule->rflags;
+ if (new_rflags & FILTRULE_CLEAR_LIST) {
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO,
+ "[%s] clearing filter list%s\n",
+ who_am_i(), listp->debug_type);
+ }
+ pop_filter_list(listp);
+ listp->head = NULL;
+ goto free_continue;
+ }
+
+ if (new_rflags & FILTRULE_MERGE_FILE) {
+ if (!pat_len) {
+ pat = ".cvsignore";
+ pat_len = 10;
+ }
+ if (new_rflags & FILTRULE_EXCLUDE_SELF) {
+ const char *name;
+ filter_rule *excl_self;
+
+ excl_self = new0(filter_rule);
+ /* Find the beginning of the basename and add an exclude for it. */
+ for (name = pat + pat_len; name > pat && name[-1] != '/'; name--) {}
+ add_rule(listp, name, (pat + pat_len) - name, excl_self, 0);
+ rule->rflags &= ~FILTRULE_EXCLUDE_SELF;
+ }
+ if (new_rflags & FILTRULE_PERDIR_MERGE) {
+ if (parent_dirscan) {
+ const char *p;
+ unsigned int len = pat_len;
+ if ((p = parse_merge_name(pat, &len, module_dirlen)))
+ add_rule(listp, p, len, rule, 0);
+ else
+ free_filter(rule);
+ continue;
+ }
+ } else {
+ const char *p;
+ unsigned int len = pat_len;
+ if ((p = parse_merge_name(pat, &len, 0)))
+ parse_filter_file(listp, p, rule, XFLG_FATAL_ERRORS);
+ free_filter(rule);
+ continue;
+ }
+ }
+
+ add_rule(listp, pat, pat_len, rule, xflags);
+
+ if (new_rflags & FILTRULE_CVS_IGNORE
+ && !(new_rflags & FILTRULE_MERGE_FILE))
+ get_cvs_excludes(new_rflags);
+ }
+}
+
+void parse_filter_file(filter_rule_list *listp, const char *fname, const filter_rule *template, int xflags)
+{
+ FILE *fp;
+ char line[BIGPATHBUFLEN];
+ char *eob = line + sizeof line - 1;
+ BOOL word_split = (template->rflags & FILTRULE_WORD_SPLIT) != 0;
+
+ if (!fname || !*fname)
+ return;
+
+ if (*fname != '-' || fname[1] || am_server) {
+ if (daemon_filter_list.head) {
+ strlcpy(line, fname, sizeof line);
+ clean_fname(line, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(&daemon_filter_list, FLOG, line, 0) < 0)
+ fp = NULL;
+ else
+ fp = fopen(line, "rb");
+ } else
+ fp = fopen(fname, "rb");
+ } else
+ fp = stdin;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] parse_filter_file(%s,%x,%x)%s\n",
+ who_am_i(), fname, template->rflags, xflags,
+ fp ? "" : " [not found]");
+ }
+
+ if (!fp) {
+ if (xflags & XFLG_FATAL_ERRORS) {
+ rsyserr(FERROR, errno,
+ "failed to open %sclude file %s",
+ template->rflags & FILTRULE_INCLUDE ? "in" : "ex",
+ fname);
+ exit_cleanup(RERR_FILEIO);
+ }
+ return;
+ }
+ dirbuf[dirbuf_len] = '\0';
+
+ while (1) {
+ char *s = line;
+ int ch, overflow = 0;
+ while (1) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp) && errno == EINTR) {
+ clearerr(fp);
+ continue;
+ }
+ break;
+ }
+ if (word_split && isspace(ch))
+ break;
+ if (eol_nulls? !ch : (ch == '\n' || ch == '\r'))
+ break;
+ if (s < eob)
+ *s++ = ch;
+ else
+ overflow = 1;
+ }
+ if (overflow) {
+ rprintf(FERROR, "discarding over-long filter: %s...\n", line);
+ s = line;
+ }
+ *s = '\0';
+ /* Skip an empty token and (when line parsing) comments. */
+ if (*line && (word_split || (*line != ';' && *line != '#')))
+ parse_filter_str(listp, line, template, xflags);
+ if (ch == EOF)
+ break;
+ }
+ fclose(fp);
+}
+
+/* If the "for_xfer" flag is set, the prefix is made compatible with the
+ * current protocol_version (if possible) or a NULL is returned (if not
+ * possible). */
+char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
+ unsigned int *plen_ptr)
+{
+ static char buf[MAX_RULE_PREFIX+1];
+ char *op = buf;
+ int legal_len = for_xfer && protocol_version < 29 ? 1 : MAX_RULE_PREFIX-1;
+
+ if (rule->rflags & FILTRULE_PERDIR_MERGE) {
+ if (legal_len == 1)
+ return NULL;
+ *op++ = ':';
+ } else if (rule->rflags & FILTRULE_INCLUDE)
+ *op++ = '+';
+ else if (legal_len != 1
+ || ((*pat == '-' || *pat == '+') && pat[1] == ' '))
+ *op++ = '-';
+ else
+ legal_len = 0;
+
+ if (rule->rflags & FILTRULE_ABS_PATH)
+ *op++ = '/';
+ if (rule->rflags & FILTRULE_NEGATE)
+ *op++ = '!';
+ if (rule->rflags & FILTRULE_CVS_IGNORE)
+ *op++ = 'C';
+ else {
+ if (rule->rflags & FILTRULE_NO_INHERIT)
+ *op++ = 'n';
+ if (rule->rflags & FILTRULE_WORD_SPLIT)
+ *op++ = 'w';
+ if (rule->rflags & FILTRULE_NO_PREFIXES) {
+ if (rule->rflags & FILTRULE_INCLUDE)
+ *op++ = '+';
+ else
+ *op++ = '-';
+ }
+ }
+ if (rule->rflags & FILTRULE_EXCLUDE_SELF)
+ *op++ = 'e';
+ if (rule->rflags & FILTRULE_XATTR)
+ *op++ = 'x';
+ if (rule->rflags & FILTRULE_SENDER_SIDE
+ && (!for_xfer || protocol_version >= 29))
+ *op++ = 's';
+ if (rule->rflags & FILTRULE_RECEIVER_SIDE
+ && (!for_xfer || protocol_version >= 29
+ || (delete_excluded && am_sender)))
+ *op++ = 'r';
+ if (rule->rflags & FILTRULE_PERISHABLE) {
+ if (!for_xfer || protocol_version >= 30)
+ *op++ = 'p';
+ else if (am_sender)
+ return NULL;
+ }
+ if (op - buf > legal_len)
+ return NULL;
+ if (legal_len)
+ *op++ = ' ';
+ *op = '\0';
+ if (plen_ptr)
+ *plen_ptr = op - buf;
+ return buf;
+}
+
+static void send_rules(int f_out, filter_rule_list *flp)
+{
+ filter_rule *ent;
+
+ for (ent = flp->head; ent; ent = ent->next) {
+ unsigned int len, plen, dlen;
+ int elide = 0;
+ char *p;
+
+ /* Note we need to check delete_excluded here in addition to
+ * the code in parse_rule_tok() because some rules may have
+ * been added before we found the --delete-excluded option.
+ * We must also elide any CVS merge-file rules to avoid a
+ * backward compatibility problem, and we elide any no-prefix
+ * merge files as an optimization (since they can only have
+ * include/exclude rules). */
+ if (ent->rflags & FILTRULE_SENDER_SIDE)
+ elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
+ if (ent->rflags & FILTRULE_RECEIVER_SIDE)
+ elide = elide ? 0 : am_sender ? REMOTE_RULE : LOCAL_RULE;
+ else if (delete_excluded && !elide
+ && (!(ent->rflags & FILTRULE_PERDIR_MERGE)
+ || ent->rflags & FILTRULE_NO_PREFIXES))
+ elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
+ ent->elide = elide;
+ if (elide == LOCAL_RULE)
+ continue;
+ if (ent->rflags & FILTRULE_CVS_IGNORE
+ && !(ent->rflags & FILTRULE_MERGE_FILE)) {
+ int f = am_sender || protocol_version < 29 ? f_out : -2;
+ send_rules(f, &cvs_filter_list);
+ if (f == f_out)
+ continue;
+ }
+ p = get_rule_prefix(ent, ent->pattern, 1, &plen);
+ if (!p) {
+ rprintf(FERROR,
+ "filter rules are too modern for remote rsync.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (f_out < 0)
+ continue;
+ len = strlen(ent->pattern);
+ dlen = ent->rflags & FILTRULE_DIRECTORY ? 1 : 0;
+ if (!(plen + len + dlen))
+ continue;
+ write_int(f_out, plen + len + dlen);
+ if (plen)
+ write_buf(f_out, p, plen);
+ write_buf(f_out, ent->pattern, len);
+ if (dlen)
+ write_byte(f_out, '/');
+ }
+}
+
+/* This is only called by the client. */
+void send_filter_list(int f_out)
+{
+ int receiver_wants_list = prune_empty_dirs
+ || (delete_mode && (!delete_excluded || protocol_version >= 29));
+
+ if (local_server || (am_sender && !receiver_wants_list))
+ f_out = -1;
+ if (cvs_exclude && am_sender) {
+ if (protocol_version >= 29)
+ parse_filter_str(&filter_list, ":C", rule_template(0), 0);
+ parse_filter_str(&filter_list, "-C", rule_template(0), 0);
+ }
+
+ send_rules(f_out, &filter_list);
+
+ if (f_out >= 0)
+ write_int(f_out, 0);
+
+ if (cvs_exclude) {
+ if (!am_sender || protocol_version < 29)
+ parse_filter_str(&filter_list, ":C", rule_template(0), 0);
+ if (!am_sender)
+ parse_filter_str(&filter_list, "-C", rule_template(0), 0);
+ }
+}
+
+/* This is only called by the server. */
+void recv_filter_list(int f_in)
+{
+ char line[BIGPATHBUFLEN];
+ int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES;
+ int receiver_wants_list = prune_empty_dirs
+ || (delete_mode && (!delete_excluded || protocol_version >= 29));
+ unsigned int len;
+
+ if (!local_server && (am_sender || receiver_wants_list)) {
+ while ((len = read_int(f_in)) != 0) {
+ if (len >= sizeof line)
+ overflow_exit("recv_rules");
+ read_sbuf(f_in, line, len);
+ parse_filter_str(&filter_list, line, rule_template(0), xflags);
+ }
+ }
+
+ if (cvs_exclude) {
+ if (local_server || am_sender || protocol_version < 29)
+ parse_filter_str(&filter_list, ":C", rule_template(0), 0);
+ if (local_server || am_sender)
+ parse_filter_str(&filter_list, "-C", rule_template(0), 0);
+ }
+
+ if (local_server) /* filter out any rules that aren't for us. */
+ send_rules(-1, &filter_list);
+}
diff --git a/fileio.c b/fileio.c
new file mode 100644
index 0000000..f80af19
--- /dev/null
+++ b/fileio.c
@@ -0,0 +1,328 @@
+/*
+ * File IO utilities used in rsync.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2004-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+#ifndef ENODATA
+#define ENODATA EAGAIN
+#endif
+
+/* We want all reads to be aligned on 1K boundaries. */
+#define ALIGN_BOUNDARY 1024
+/* How far past the boundary is an offset? */
+#define ALIGNED_OVERSHOOT(oft) ((oft) & (ALIGN_BOUNDARY-1))
+/* Round up a length to the next boundary */
+#define ALIGNED_LENGTH(len) ((((len) - 1) | (ALIGN_BOUNDARY-1)) + 1)
+
+extern int sparse_files;
+
+OFF_T preallocated_len = 0;
+
+static OFF_T sparse_seek = 0;
+static OFF_T sparse_past_write = 0;
+
+int sparse_end(int f, OFF_T size)
+{
+ int ret;
+
+ sparse_past_write = 0;
+
+ if (!sparse_seek)
+ return 0;
+
+#ifdef HAVE_FTRUNCATE
+ ret = do_ftruncate(f, size);
+#else
+ if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
+ ret = -1;
+ else {
+ do {
+ ret = write(f, "", 1);
+ } while (ret < 0 && errno == EINTR);
+
+ ret = ret <= 0 ? -1 : 0;
+ }
+#endif
+
+ sparse_seek = 0;
+
+ return ret;
+}
+
+/* Note that the offset is just the caller letting us know where
+ * the current file position is in the file. The use_seek arg tells
+ * us that we should seek over matching data instead of writing it. */
+static int write_sparse(int f, int use_seek, OFF_T offset, const char *buf, int len)
+{
+ int l1 = 0, l2 = 0;
+ int ret;
+
+ for (l1 = 0; l1 < len && buf[l1] == 0; l1++) {}
+ for (l2 = 0; l2 < len-l1 && buf[len-(l2+1)] == 0; l2++) {}
+
+ sparse_seek += l1;
+
+ if (l1 == len)
+ return len;
+
+ if (sparse_seek) {
+ if (sparse_past_write >= preallocated_len) {
+ if (do_lseek(f, sparse_seek, SEEK_CUR) < 0)
+ return -1;
+ } else if (do_punch_hole(f, sparse_past_write, sparse_seek) < 0) {
+ sparse_seek = 0;
+ return -1;
+ }
+ }
+ sparse_seek = l2;
+ sparse_past_write = offset + len - l2;
+
+ if (use_seek) {
+ /* The in-place data already matches. */
+ if (do_lseek(f, len - (l1+l2), SEEK_CUR) < 0)
+ return -1;
+ return len;
+ }
+
+ while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) {
+ if (ret < 0 && errno == EINTR)
+ continue;
+ sparse_seek = 0;
+ return ret;
+ }
+
+ if (ret != (int)(len - (l1+l2))) {
+ sparse_seek = 0;
+ return l1+ret;
+ }
+
+ return len;
+}
+
+static char *wf_writeBuf;
+static size_t wf_writeBufSize;
+static size_t wf_writeBufCnt;
+
+int flush_write_file(int f)
+{
+ int ret = 0;
+ char *bp = wf_writeBuf;
+
+ while (wf_writeBufCnt > 0) {
+ if ((ret = write(f, bp, wf_writeBufCnt)) < 0) {
+ if (errno == EINTR)
+ continue;
+ return ret;
+ }
+ wf_writeBufCnt -= ret;
+ bp += ret;
+ }
+ return ret;
+}
+
+/* write_file does not allow incomplete writes. It loops internally
+ * until len bytes are written or errno is set. Note that use_seek and
+ * offset are only used in sparse processing (see write_sparse()). */
+int write_file(int f, int use_seek, OFF_T offset, const char *buf, int len)
+{
+ int ret = 0;
+
+ while (len > 0) {
+ int r1;
+ if (sparse_files > 0) {
+ int len1 = MIN(len, SPARSE_WRITE_SIZE);
+ r1 = write_sparse(f, use_seek, offset, buf, len1);
+ offset += r1;
+ } else {
+ if (!wf_writeBuf) {
+ wf_writeBufSize = WRITE_SIZE * 8;
+ wf_writeBufCnt = 0;
+ wf_writeBuf = new_array(char, wf_writeBufSize);
+ }
+ r1 = (int)MIN((size_t)len, wf_writeBufSize - wf_writeBufCnt);
+ if (r1) {
+ memcpy(wf_writeBuf + wf_writeBufCnt, buf, r1);
+ wf_writeBufCnt += r1;
+ }
+ if (wf_writeBufCnt == wf_writeBufSize) {
+ if (flush_write_file(f) < 0)
+ return -1;
+ if (!r1 && len)
+ continue;
+ }
+ }
+ if (r1 <= 0) {
+ if (ret > 0)
+ return ret;
+ return r1;
+ }
+ len -= r1;
+ buf += r1;
+ ret += r1;
+ }
+ return ret;
+}
+
+/* An in-place update found identical data at an identical location. We either
+ * just seek past it, or (for an in-place sparse update), we give the data to
+ * the sparse processor with the use_seek flag set. */
+int skip_matched(int fd, OFF_T offset, const char *buf, int len)
+{
+ OFF_T pos;
+
+ if (sparse_files > 0) {
+ if (write_file(fd, 1, offset, buf, len) != len)
+ return -1;
+ return 0;
+ }
+
+ if (flush_write_file(fd) < 0)
+ return -1;
+
+ if ((pos = do_lseek(fd, len, SEEK_CUR)) != offset + len) {
+ rsyserr(FERROR_XFER, errno, "lseek returned %s, not %s",
+ big_num(pos), big_num(offset));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* This provides functionality somewhat similar to mmap() but using read().
+ * It gives sliding window access to a file. mmap() is not used because of
+ * the possibility of another program (such as a mailer) truncating the
+ * file thus giving us a SIGBUS. */
+struct map_struct *map_file(int fd, OFF_T len, int32 read_size, int32 blk_size)
+{
+ struct map_struct *map;
+
+ map = new0(struct map_struct);
+
+ if (blk_size && (read_size % blk_size))
+ read_size += blk_size - (read_size % blk_size);
+
+ map->fd = fd;
+ map->file_size = len;
+ map->def_window_size = ALIGNED_LENGTH(read_size);
+
+ return map;
+}
+
+
+/* slide the read window in the file */
+char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
+{
+ OFF_T window_start, read_start;
+ int32 window_size, read_size, read_offset, align_fudge;
+
+ if (len == 0)
+ return NULL;
+ if (len < 0) {
+ rprintf(FERROR, "invalid len passed to map_ptr: %ld\n",
+ (long)len);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ /* in most cases the region will already be available */
+ if (offset >= map->p_offset && offset+len <= map->p_offset+map->p_len)
+ return map->p + (offset - map->p_offset);
+
+ /* nope, we are going to have to do a read. Work out our desired window */
+ align_fudge = (int32)ALIGNED_OVERSHOOT(offset);
+ window_start = offset - align_fudge;
+ window_size = map->def_window_size;
+ if (window_start + window_size > map->file_size)
+ window_size = (int32)(map->file_size - window_start);
+ if (window_size < len + align_fudge)
+ window_size = ALIGNED_LENGTH(len + align_fudge);
+
+ /* make sure we have allocated enough memory for the window */
+ if (window_size > map->p_size) {
+ map->p = realloc_array(map->p, char, window_size);
+ map->p_size = window_size;
+ }
+
+ /* Now try to avoid re-reading any bytes by reusing any bytes from the previous buffer. */
+ if (window_start >= map->p_offset && window_start < map->p_offset + map->p_len
+ && window_start + window_size >= map->p_offset + map->p_len) {
+ read_start = map->p_offset + map->p_len;
+ read_offset = (int32)(read_start - window_start);
+ read_size = window_size - read_offset;
+ memmove(map->p, map->p + (map->p_len - read_offset), read_offset);
+ } else {
+ read_start = window_start;
+ read_size = window_size;
+ read_offset = 0;
+ }
+
+ if (read_size <= 0) {
+ rprintf(FERROR, "invalid read_size of %ld in map_ptr\n",
+ (long)read_size);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (map->p_fd_offset != read_start) {
+ OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET);
+ if (ret != read_start) {
+ rsyserr(FERROR, errno, "lseek returned %s, not %s",
+ big_num(ret), big_num(read_start));
+ exit_cleanup(RERR_FILEIO);
+ }
+ map->p_fd_offset = read_start;
+ }
+ map->p_offset = window_start;
+ map->p_len = window_size;
+
+ while (read_size > 0) {
+ int32 nread = read(map->fd, map->p + read_offset, read_size);
+ if (nread <= 0) {
+ if (!map->status)
+ map->status = nread ? errno : ENODATA;
+ /* The best we can do is zero the buffer -- the file
+ * has changed mid transfer! */
+ memset(map->p + read_offset, 0, read_size);
+ break;
+ }
+ map->p_fd_offset += nread;
+ read_offset += nread;
+ read_size -= nread;
+ }
+
+ return map->p + align_fudge;
+}
+
+int unmap_file(struct map_struct *map)
+{
+ int ret;
+
+ if (map->p) {
+ free(map->p);
+ map->p = NULL;
+ }
+ ret = map->status;
+#if 0 /* I don't think we really need this. */
+ force_memzero(map, sizeof map[0]);
+#endif
+ free(map);
+
+ return ret;
+}
diff --git a/flist.c b/flist.c
new file mode 100644
index 0000000..65b459b
--- /dev/null
+++ b/flist.c
@@ -0,0 +1,3410 @@
+/*
+ * Generate and receive file lists.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2002-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "rounding.h"
+#include "inums.h"
+#include "io.h"
+
+extern int am_root;
+extern int am_server;
+extern int am_daemon;
+extern int am_sender;
+extern int am_generator;
+extern int inc_recurse;
+extern int always_checksum;
+extern int module_id;
+extern int ignore_errors;
+extern int numeric_ids;
+extern int quiet;
+extern int recurse;
+extern int use_qsort;
+extern int xfer_dirs;
+extern int filesfrom_fd;
+extern int one_file_system;
+extern int copy_devices;
+extern int copy_dirlinks;
+extern int preserve_uid;
+extern int preserve_gid;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_links;
+extern int preserve_hard_links;
+extern int preserve_devices;
+extern int preserve_specials;
+extern int delete_during;
+extern int missing_args;
+extern int eol_nulls;
+extern int atimes_ndx;
+extern int crtimes_ndx;
+extern int relative_paths;
+extern int implied_dirs;
+extern int ignore_perishable;
+extern int non_perishable_cnt;
+extern int prune_empty_dirs;
+extern int copy_links;
+extern int copy_unsafe_links;
+extern int protocol_version;
+extern int sanitize_paths;
+extern int munge_symlinks;
+extern int use_safe_inc_flist;
+extern int need_unsorted_flist;
+extern int sender_symlink_iconv;
+extern int output_needs_newline;
+extern int sender_keeps_checksum;
+extern int trust_sender_filter;
+extern int unsort_ndx;
+extern uid_t our_uid;
+extern struct stats stats;
+extern char *filesfrom_host;
+extern char *usermap, *groupmap;
+
+extern struct name_num_item *file_sum_nni;
+
+extern char curr_dir[MAXPATHLEN];
+
+extern struct chmod_mode_struct *chmod_modes;
+
+extern filter_rule_list filter_list, implied_filter_list, daemon_filter_list;
+
+#ifdef ICONV_OPTION
+extern int filesfrom_convert;
+extern iconv_t ic_send, ic_recv;
+#endif
+
+#define PTR_SIZE (sizeof (struct file_struct *))
+
+int io_error;
+int flist_csum_len;
+dev_t filesystem_dev; /* used to implement -x */
+
+struct file_list *cur_flist, *first_flist, *dir_flist;
+int send_dir_ndx = -1, send_dir_depth = -1;
+int flist_cnt = 0; /* how many (non-tmp) file list objects exist */
+int file_total = 0; /* total of all active items over all file-lists */
+int file_old_total = 0; /* total of active items that will soon be gone */
+int flist_eof = 0; /* all the file-lists are now known */
+int xfer_flags_as_varint = 0;
+
+#define NORMAL_NAME 0
+#define SLASH_ENDING_NAME 1
+#define DOTDIR_NAME 2
+#define MISSING_NAME 3
+
+/* Starting from protocol version 26, we always use 64-bit ino_t and dev_t
+ * internally, even if this platform does not allow files to have 64-bit inums.
+ * The only exception is if we're on a platform with no 64-bit type at all.
+ *
+ * Because we use read_longint() to get these off the wire, if you transfer
+ * devices or (for protocols < 30) hardlinks with dev or inum > 2**32 to a
+ * machine with no 64-bit types then you will get an overflow error.
+ *
+ * Note that if you transfer devices from a 64-bit-devt machine (say, Solaris)
+ * to a 32-bit-devt machine (say, Linux-2.2/x86) then the device numbers will
+ * be truncated. But it's a kind of silly thing to do anyhow. */
+
+/* The tmp_* vars are used as a cache area by make_file() to store data
+ * that the sender doesn't need to remember in its file list. The data
+ * will survive just long enough to be used by send_file_entry(). */
+static dev_t tmp_rdev;
+#ifdef SUPPORT_HARD_LINKS
+static int64 tmp_dev = -1, tmp_ino;
+#endif
+static char tmp_sum[MAX_DIGEST_LEN];
+
+static char empty_sum[MAX_DIGEST_LEN];
+static int flist_count_offset; /* for --delete --progress */
+static int show_filelist_progress;
+
+static struct file_list *flist_new(int flags, const char *msg);
+static void flist_sort_and_clean(struct file_list *flist, int strip_root);
+static void output_flist(struct file_list *flist);
+
+void init_flist(void)
+{
+ if (DEBUG_GTE(FLIST, 4)) {
+ rprintf(FINFO, "FILE_STRUCT_LEN=%d, EXTRA_LEN=%d\n",
+ (int)FILE_STRUCT_LEN, (int)EXTRA_LEN);
+ }
+ /* Note that this isn't identical to file_sum_len in the case of CSUM_MD4_ARCHAIC: */
+ flist_csum_len = csum_len_for_type(file_sum_nni->num, 1);
+
+ show_filelist_progress = INFO_GTE(FLIST, 1) && xfer_dirs && !am_server && !inc_recurse;
+}
+
+static void start_filelist_progress(char *kind)
+{
+ if (quiet)
+ return;
+ rprintf(FCLIENT, "%s ... ", kind);
+ output_needs_newline = 1;
+ rflush(FINFO);
+}
+
+static void emit_filelist_progress(int count)
+{
+ if (quiet)
+ return;
+ if (output_needs_newline == 2) /* avoid a newline in the middle of this filelist-progress output */
+ output_needs_newline = 0;
+ rprintf(FCLIENT, " %d files...\r", count);
+ output_needs_newline = 2;
+}
+
+static void maybe_emit_filelist_progress(int count)
+{
+ if (INFO_GTE(FLIST, 2) && show_filelist_progress && (count % 100) == 0)
+ emit_filelist_progress(count);
+}
+
+static void finish_filelist_progress(const struct file_list *flist)
+{
+ output_needs_newline = 0;
+ if (INFO_GTE(FLIST, 2)) {
+ /* This overwrites the progress line */
+ rprintf(FINFO, "%d file%sto consider\n",
+ flist->used, flist->used == 1 ? " " : "s ");
+ } else {
+ rprintf(FINFO, "done\n");
+ }
+}
+
+void show_flist_stats(void)
+{
+ /* Nothing yet */
+}
+
+/* Stat either a symlink or its referent, depending on the settings of
+ * copy_links, copy_unsafe_links, etc. Returns -1 on error, 0 on success.
+ *
+ * If path is the name of a symlink, then the linkbuf buffer (which must hold
+ * MAXPATHLEN chars) will be set to the symlink's target string.
+ *
+ * The stat structure pointed to by stp will contain information about the
+ * link or the referent as appropriate, if they exist. */
+static int readlink_stat(const char *path, STRUCT_STAT *stp, char *linkbuf)
+{
+#ifdef SUPPORT_LINKS
+ if (link_stat(path, stp, copy_dirlinks) < 0)
+ return -1;
+ if (S_ISLNK(stp->st_mode)) {
+ int llen = do_readlink(path, linkbuf, MAXPATHLEN - 1);
+ if (llen < 0)
+ return -1;
+ linkbuf[llen] = '\0';
+ if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) {
+ if (INFO_GTE(SYMSAFE, 1)) {
+ rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
+ path, linkbuf);
+ }
+ return x_stat(path, stp, NULL);
+ }
+ if (munge_symlinks && am_sender && llen > SYMLINK_PREFIX_LEN
+ && strncmp(linkbuf, SYMLINK_PREFIX, SYMLINK_PREFIX_LEN) == 0) {
+ memmove(linkbuf, linkbuf + SYMLINK_PREFIX_LEN,
+ llen - SYMLINK_PREFIX_LEN + 1);
+ }
+ }
+ return 0;
+#else
+ return x_stat(path, stp, NULL);
+#endif
+}
+
+int link_stat(const char *path, STRUCT_STAT *stp, int follow_dirlinks)
+{
+#ifdef SUPPORT_LINKS
+ if (copy_links)
+ return x_stat(path, stp, NULL);
+ if (x_lstat(path, stp, NULL) < 0)
+ return -1;
+ if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
+ STRUCT_STAT st;
+ if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
+ *stp = st;
+ }
+ return 0;
+#else
+ return x_stat(path, stp, NULL);
+#endif
+}
+
+static inline int path_is_daemon_excluded(char *path, int ignore_filename)
+{
+ if (daemon_filter_list.head) {
+ char *slash = path;
+
+ while ((slash = strchr(slash+1, '/')) != NULL) {
+ int ret;
+ *slash = '\0';
+ ret = check_filter(&daemon_filter_list, FLOG, path, 1);
+ *slash = '/';
+ if (ret < 0) {
+ errno = ENOENT;
+ return 1;
+ }
+ }
+
+ if (!ignore_filename
+ && check_filter(&daemon_filter_list, FLOG, path, 1) < 0) {
+ errno = ENOENT;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static inline int is_excluded(const char *fname, int is_dir, int filter_level)
+{
+ return name_is_excluded(fname, is_dir ? NAME_IS_DIR : NAME_IS_FILE, filter_level);
+}
+
+static void send_directory(int f, struct file_list *flist,
+ char *fbuf, int len, int flags);
+
+static const char *pathname, *orig_dir;
+static int pathname_len;
+
+/* Make sure flist can hold at least flist->used + extra entries. */
+static void flist_expand(struct file_list *flist, int extra)
+{
+ struct file_struct **new_ptr;
+
+ if (flist->used + extra <= flist->malloced)
+ return;
+
+ if (flist->malloced < FLIST_START)
+ flist->malloced = FLIST_START;
+ else if (flist->malloced >= FLIST_LINEAR)
+ flist->malloced += FLIST_LINEAR;
+ else if (flist->malloced < FLIST_START_LARGE/16)
+ flist->malloced *= 4;
+ else
+ flist->malloced *= 2;
+
+ /* In case count jumped or we are starting the list
+ * with a known size just set it. */
+ if (flist->malloced < flist->used + extra)
+ flist->malloced = flist->used + extra;
+
+ new_ptr = realloc_array(flist->files, struct file_struct *, flist->malloced);
+
+ if (DEBUG_GTE(FLIST, 1) && flist->files) {
+ rprintf(FCLIENT, "[%s] expand file_list pointer array to %s bytes, did%s move\n",
+ who_am_i(),
+ big_num(sizeof flist->files[0] * flist->malloced),
+ (new_ptr == flist->files) ? " not" : "");
+ }
+
+ flist->files = new_ptr;
+}
+
+static void flist_done_allocating(struct file_list *flist)
+{
+ void *ptr = pool_boundary(flist->file_pool, 8*1024);
+ if (flist->pool_boundary == ptr)
+ flist->pool_boundary = NULL; /* list didn't use any pool memory */
+ else
+ flist->pool_boundary = ptr;
+}
+
+/* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
+ * F_PATHNAME(), or (2) "NULL, dir, dirlen" to chdir() to the supplied dir,
+ * with dir == NULL taken to be the starting directory, and dirlen < 0
+ * indicating that strdup(dir) should be called and then the -dirlen length
+ * value checked to ensure that it is not daemon-excluded. */
+int change_pathname(struct file_struct *file, const char *dir, int dirlen)
+{
+ if (dirlen < 0) {
+ char *cpy = strdup(dir);
+ if (*cpy != '/')
+ change_dir(orig_dir, CD_SKIP_CHDIR);
+ if (path_is_daemon_excluded(cpy, 0))
+ goto chdir_error;
+ dir = cpy;
+ dirlen = -dirlen;
+ } else {
+ if (file) {
+ if (pathname == F_PATHNAME(file))
+ return 1;
+ dir = F_PATHNAME(file);
+ if (dir)
+ dirlen = strlen(dir);
+ } else if (pathname == dir)
+ return 1;
+ if (dir && *dir != '/')
+ change_dir(orig_dir, CD_SKIP_CHDIR);
+ }
+
+ pathname = dir;
+ pathname_len = dirlen;
+
+ if (!dir)
+ dir = orig_dir;
+
+ if (!change_dir(dir, CD_NORMAL)) {
+ chdir_error:
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "change_dir %s failed", full_fname(dir));
+ if (dir != orig_dir)
+ change_dir(orig_dir, CD_NORMAL);
+ pathname = NULL;
+ pathname_len = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void send_file_entry(int f, const char *fname, struct file_struct *file,
+#ifdef SUPPORT_LINKS
+ const char *symlink_name, int symlink_len,
+#endif
+ int ndx, int first_ndx)
+{
+ static time_t modtime, atime;
+#ifdef SUPPORT_CRTIMES
+ static time_t crtime;
+#endif
+ static mode_t mode;
+#ifdef SUPPORT_HARD_LINKS
+ static int64 dev;
+#endif
+ static dev_t rdev;
+ static uint32 rdev_major;
+ static uid_t uid;
+ static gid_t gid;
+ static const char *user_name, *group_name;
+ static char lastname[MAXPATHLEN];
+#ifdef SUPPORT_HARD_LINKS
+ int first_hlink_ndx = -1;
+#endif
+ int l1, l2;
+ int xflags;
+
+ /* Initialize starting value of xflags and adjust counts. */
+ if (S_ISREG(file->mode))
+ xflags = 0;
+ else if (S_ISDIR(file->mode)) {
+ stats.num_dirs++;
+ if (protocol_version >= 30) {
+ if (file->flags & FLAG_CONTENT_DIR)
+ xflags = file->flags & FLAG_TOP_DIR;
+ else if (file->flags & FLAG_IMPLIED_DIR)
+ xflags = XMIT_TOP_DIR | XMIT_NO_CONTENT_DIR;
+ else
+ xflags = XMIT_NO_CONTENT_DIR;
+ } else
+ xflags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */
+ } else {
+ if (S_ISLNK(file->mode))
+ stats.num_symlinks++;
+ else if (IS_DEVICE(file->mode))
+ stats.num_devices++;
+ else if (IS_SPECIAL(file->mode))
+ stats.num_specials++;
+ xflags = 0;
+ }
+
+ if (file->mode == mode)
+ xflags |= XMIT_SAME_MODE;
+ else
+ mode = file->mode;
+
+ if (preserve_devices && IS_DEVICE(mode)) {
+ if (protocol_version < 28) {
+ if (tmp_rdev == rdev)
+ xflags |= XMIT_SAME_RDEV_pre28;
+ else
+ rdev = tmp_rdev;
+ } else {
+ rdev = tmp_rdev;
+ if ((uint32)major(rdev) == rdev_major)
+ xflags |= XMIT_SAME_RDEV_MAJOR;
+ else
+ rdev_major = major(rdev);
+ if (protocol_version < 30 && (uint32)minor(rdev) <= 0xFFu)
+ xflags |= XMIT_RDEV_MINOR_8_pre30;
+ }
+ } else if (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31) {
+ /* Special files don't need an rdev number, so just make
+ * the historical transmission of the value efficient. */
+ if (protocol_version < 28)
+ xflags |= XMIT_SAME_RDEV_pre28;
+ else {
+ rdev = MAKEDEV(rdev_major, 0);
+ xflags |= XMIT_SAME_RDEV_MAJOR;
+ if (protocol_version < 30)
+ xflags |= XMIT_RDEV_MINOR_8_pre30;
+ }
+ } else if (protocol_version < 28)
+ rdev = MAKEDEV(0, 0);
+ if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname))
+ xflags |= XMIT_SAME_UID;
+ else {
+ uid = F_OWNER(file);
+ if (!numeric_ids) {
+ user_name = add_uid(uid);
+ if (inc_recurse && user_name)
+ xflags |= XMIT_USER_NAME_FOLLOWS;
+ }
+ }
+ if (!preserve_gid || ((gid_t)F_GROUP(file) == gid && *lastname))
+ xflags |= XMIT_SAME_GID;
+ else {
+ gid = F_GROUP(file);
+ if (!numeric_ids) {
+ group_name = add_gid(gid);
+ if (inc_recurse && group_name)
+ xflags |= XMIT_GROUP_NAME_FOLLOWS;
+ }
+ }
+ if (file->modtime == modtime)
+ xflags |= XMIT_SAME_TIME;
+ else
+ modtime = file->modtime;
+ if (NSEC_BUMP(file) && protocol_version >= 31)
+ xflags |= XMIT_MOD_NSEC;
+ if (atimes_ndx && !S_ISDIR(mode)) {
+ if (F_ATIME(file) == atime)
+ xflags |= XMIT_SAME_ATIME;
+ else
+ atime = F_ATIME(file);
+ }
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx) {
+ crtime = F_CRTIME(file);
+ if (crtime == modtime)
+ xflags |= XMIT_CRTIME_EQ_MTIME;
+ }
+#endif
+
+#ifdef SUPPORT_HARD_LINKS
+ if (tmp_dev != -1) {
+ if (protocol_version >= 30) {
+ struct ht_int64_node *np = idev_find(tmp_dev, tmp_ino);
+ first_hlink_ndx = (int32)(long)np->data; /* is -1 when new */
+ if (first_hlink_ndx < 0) {
+ np->data = (void*)(long)(first_ndx + ndx);
+ xflags |= XMIT_HLINK_FIRST;
+ }
+ if (DEBUG_GTE(HLINK, 1)) {
+ if (first_hlink_ndx >= 0) {
+ rprintf(FINFO, "[%s] #%d hard-links #%d (%sabbrev)\n",
+ who_am_i(), first_ndx + ndx, first_hlink_ndx,
+ first_hlink_ndx >= first_ndx ? "" : "un");
+ } else if (DEBUG_GTE(HLINK, 3)) {
+ rprintf(FINFO, "[%s] dev:inode for #%d is %s:%s\n",
+ who_am_i(), first_ndx + ndx,
+ big_num(tmp_dev), big_num(tmp_ino));
+ }
+ }
+ } else {
+ if (tmp_dev == dev) {
+ if (protocol_version >= 28)
+ xflags |= XMIT_SAME_DEV_pre30;
+ } else
+ dev = tmp_dev;
+ }
+ xflags |= XMIT_HLINKED;
+ }
+#endif
+
+ for (l1 = 0;
+ lastname[l1] && (fname[l1] == lastname[l1]) && (l1 < 255);
+ l1++) {}
+ l2 = strlen(fname+l1);
+
+ if (l1 > 0)
+ xflags |= XMIT_SAME_NAME;
+ if (l2 > 255)
+ xflags |= XMIT_LONG_NAME;
+
+ /* We must avoid sending a flag value of 0 (or an initial byte of
+ * 0 for the older xflags protocol) or it will signal the end of
+ * the list. Note that the use of XMIT_TOP_DIR on a non-dir has
+ * no meaning, so it's a harmless way to add a bit to the first
+ * flag byte. */
+ if (xfer_flags_as_varint)
+ write_varint(f, xflags ? xflags : XMIT_EXTENDED_FLAGS);
+ else if (protocol_version >= 28) {
+ if (!xflags && !S_ISDIR(mode))
+ xflags |= XMIT_TOP_DIR;
+ if ((xflags & 0xFF00) || !xflags) {
+ xflags |= XMIT_EXTENDED_FLAGS;
+ write_shortint(f, xflags);
+ } else
+ write_byte(f, xflags);
+ } else {
+ if (!(xflags & 0xFF))
+ xflags |= S_ISDIR(mode) ? XMIT_LONG_NAME : XMIT_TOP_DIR;
+ write_byte(f, xflags);
+ }
+ if (xflags & XMIT_SAME_NAME)
+ write_byte(f, l1);
+ if (xflags & XMIT_LONG_NAME)
+ write_varint30(f, l2);
+ else
+ write_byte(f, l2);
+ write_buf(f, fname + l1, l2);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (first_hlink_ndx >= 0) {
+ write_varint(f, first_hlink_ndx);
+ if (first_hlink_ndx >= first_ndx)
+ goto the_end;
+ }
+#endif
+
+ write_varlong30(f, F_LENGTH(file), 3);
+ if (!(xflags & XMIT_SAME_TIME)) {
+ if (protocol_version >= 30)
+ write_varlong(f, modtime, 4);
+ else
+ write_int(f, modtime);
+ }
+ if (xflags & XMIT_MOD_NSEC)
+ write_varint(f, F_MOD_NSEC(file));
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx && !(xflags & XMIT_CRTIME_EQ_MTIME))
+ write_varlong(f, crtime, 4);
+#endif
+ if (!(xflags & XMIT_SAME_MODE))
+ write_int(f, to_wire_mode(mode));
+ if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
+ write_varlong(f, atime, 4);
+ if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
+ if (protocol_version < 30)
+ write_int(f, uid);
+ else {
+ write_varint(f, uid);
+ if (xflags & XMIT_USER_NAME_FOLLOWS) {
+ int len = strlen(user_name);
+ write_byte(f, len);
+ write_buf(f, user_name, len);
+ }
+ }
+ }
+ if (preserve_gid && !(xflags & XMIT_SAME_GID)) {
+ if (protocol_version < 30)
+ write_int(f, gid);
+ else {
+ write_varint(f, gid);
+ if (xflags & XMIT_GROUP_NAME_FOLLOWS) {
+ int len = strlen(group_name);
+ write_byte(f, len);
+ write_buf(f, group_name, len);
+ }
+ }
+ }
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) {
+ if (protocol_version < 28) {
+ if (!(xflags & XMIT_SAME_RDEV_pre28))
+ write_int(f, (int)rdev);
+ } else {
+ if (!(xflags & XMIT_SAME_RDEV_MAJOR))
+ write_varint30(f, major(rdev));
+ if (protocol_version >= 30)
+ write_varint(f, minor(rdev));
+ else if (xflags & XMIT_RDEV_MINOR_8_pre30)
+ write_byte(f, minor(rdev));
+ else
+ write_int(f, minor(rdev));
+ }
+ }
+
+#ifdef SUPPORT_LINKS
+ if (symlink_len) {
+ write_varint30(f, symlink_len);
+ write_buf(f, symlink_name, symlink_len);
+ }
+#endif
+
+#ifdef SUPPORT_HARD_LINKS
+ if (tmp_dev != -1 && protocol_version < 30) {
+ /* Older protocols expect the dev number to be transmitted
+ * 1-incremented so that it is never zero. */
+ if (protocol_version < 26) {
+ /* 32-bit dev_t and ino_t */
+ write_int(f, (int32)(dev+1));
+ write_int(f, (int32)tmp_ino);
+ } else {
+ /* 64-bit dev_t and ino_t */
+ if (!(xflags & XMIT_SAME_DEV_pre30))
+ write_longint(f, dev+1);
+ write_longint(f, tmp_ino);
+ }
+ }
+#endif
+
+ if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
+ const char *sum;
+ if (S_ISREG(mode))
+ sum = tmp_sum;
+ else {
+ /* Prior to 28, we sent a useless set of nulls. */
+ sum = empty_sum;
+ }
+ write_buf(f, sum, flist_csum_len);
+ }
+
+#ifdef SUPPORT_HARD_LINKS
+ the_end:
+#endif
+ strlcpy(lastname, fname, MAXPATHLEN);
+
+ if (S_ISREG(mode) || S_ISLNK(mode))
+ stats.total_size += F_LENGTH(file);
+}
+
+static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
+{
+ static int64 modtime, atime;
+#ifdef SUPPORT_CRTIMES
+ static time_t crtime;
+#endif
+ static mode_t mode;
+#ifdef SUPPORT_HARD_LINKS
+ static int64 dev;
+#endif
+ static dev_t rdev;
+ static uint32 rdev_major;
+ static uid_t uid;
+ static gid_t gid;
+ static uint16 gid_flags;
+ static char lastname[MAXPATHLEN], *lastdir;
+ static int lastdir_depth, lastdir_len = -1;
+ static unsigned int del_hier_name_len = 0;
+ static int in_del_hier = 0;
+ char thisname[MAXPATHLEN];
+ unsigned int l1 = 0, l2 = 0;
+ int alloc_len, basename_len, linkname_len;
+ int extra_len = file_extra_cnt * EXTRA_LEN;
+ int first_hlink_ndx = -1;
+ char real_ISREG_entry;
+ int64 file_length;
+#ifdef CAN_SET_NSEC
+ uint32 modtime_nsec;
+#endif
+ const char *basename;
+ struct file_struct *file;
+ alloc_pool_t *pool;
+ char *bp;
+
+ if (xflags & XMIT_SAME_NAME)
+ l1 = read_byte(f);
+
+ if (xflags & XMIT_LONG_NAME)
+ l2 = read_varint30(f);
+ else
+ l2 = read_byte(f);
+
+ if (l2 >= MAXPATHLEN - l1) {
+ rprintf(FERROR,
+ "overflow: xflags=0x%x l1=%d l2=%d lastname=%s [%s]\n",
+ xflags, l1, l2, lastname, who_am_i());
+ overflow_exit("recv_file_entry");
+ }
+
+ strlcpy(thisname, lastname, l1 + 1);
+ read_sbuf(f, &thisname[l1], l2);
+ thisname[l1 + l2] = 0;
+
+ /* Abuse basename_len for a moment... */
+ basename_len = strlcpy(lastname, thisname, MAXPATHLEN);
+
+#ifdef ICONV_OPTION
+ if (ic_recv != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+
+ INIT_CONST_XBUF(outbuf, thisname);
+ INIT_XBUF(inbuf, lastname, basename_len, (size_t)-1);
+
+ if (iconvbufs(ic_recv, &inbuf, &outbuf, ICB_INIT) < 0) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_UTF8,
+ "[%s] cannot convert filename: %s (%s)\n",
+ who_am_i(), lastname, strerror(errno));
+ outbuf.len = 0;
+ }
+ thisname[outbuf.len] = '\0';
+ }
+#endif
+
+ if (*thisname
+ && (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) {
+ rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (sanitize_paths)
+ sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
+
+ if ((basename = strrchr(thisname, '/')) != NULL) {
+ int len = basename++ - thisname;
+ if (len != lastdir_len || memcmp(thisname, lastdir, len) != 0) {
+ lastdir = new_array(char, len + 1);
+ memcpy(lastdir, thisname, len);
+ lastdir[len] = '\0';
+ lastdir_len = len;
+ lastdir_depth = count_dir_elements(lastdir);
+ }
+ } else
+ basename = thisname;
+ basename_len = strlen(basename) + 1; /* count the '\0' */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (protocol_version >= 30
+ && BITS_SETnUNSET(xflags, XMIT_HLINKED, XMIT_HLINK_FIRST)) {
+ first_hlink_ndx = read_varint(f);
+ if (first_hlink_ndx < 0 || first_hlink_ndx >= flist->ndx_start + flist->used) {
+ rprintf(FERROR,
+ "hard-link reference out of range: %d (%d)\n",
+ first_hlink_ndx, flist->ndx_start + flist->used);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (DEBUG_GTE(HLINK, 1)) {
+ rprintf(FINFO, "[%s] #%d hard-links #%d (%sabbrev)\n",
+ who_am_i(), flist->used+flist->ndx_start, first_hlink_ndx,
+ first_hlink_ndx >= flist->ndx_start ? "" : "un");
+ }
+ if (first_hlink_ndx >= flist->ndx_start) {
+ struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
+ file_length = F_LENGTH(first);
+ modtime = first->modtime;
+#ifdef CAN_SET_NSEC
+ modtime_nsec = F_MOD_NSEC_or_0(first);
+#endif
+ mode = first->mode;
+ if (atimes_ndx && !S_ISDIR(mode))
+ atime = F_ATIME(first);
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx)
+ crtime = F_CRTIME(first);
+#endif
+ if (preserve_uid)
+ uid = F_OWNER(first);
+ if (preserve_gid)
+ gid = F_GROUP(first);
+ if (preserve_devices && IS_DEVICE(mode)) {
+ uint32 *devp = F_RDEV_P(first);
+ rdev_major = DEV_MAJOR(devp);
+ rdev = MAKEDEV(rdev_major, DEV_MINOR(devp));
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ }
+ if (preserve_links && S_ISLNK(mode))
+ linkname_len = strlen(F_SYMLINK(first)) + 1;
+ else
+ linkname_len = 0;
+ real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
+ goto create_object;
+ }
+ }
+#endif
+
+ file_length = read_varlong30(f, 3);
+ if (!(xflags & XMIT_SAME_TIME)) {
+ if (protocol_version >= 30) {
+ modtime = read_varlong(f, 4);
+#if SIZEOF_TIME_T < SIZEOF_INT64
+ if (!am_generator && (int64)(time_t)modtime != modtime) {
+ rprintf(FERROR_XFER,
+ "Time value of %s truncated on receiver.\n",
+ lastname);
+ }
+#endif
+ } else
+ modtime = read_uint(f);
+ }
+ if (xflags & XMIT_MOD_NSEC)
+#ifndef CAN_SET_NSEC
+ (void)read_varint(f);
+#else
+ modtime_nsec = read_varint(f);
+ else
+ modtime_nsec = 0;
+#endif
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx) {
+ if (xflags & XMIT_CRTIME_EQ_MTIME)
+ crtime = modtime;
+ else
+ crtime = read_varlong(f, 4);
+#if SIZEOF_TIME_T < SIZEOF_INT64
+ if (!am_generator && (int64)(time_t)crtime != crtime) {
+ rprintf(FERROR_XFER,
+ "Create time value of %s truncated on receiver.\n",
+ lastname);
+ }
+#endif
+ }
+#endif
+ if (!(xflags & XMIT_SAME_MODE))
+ mode = from_wire_mode(read_int(f));
+ if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
+ atime = read_varlong(f, 4);
+#if SIZEOF_TIME_T < SIZEOF_INT64
+ if (!am_generator && (int64)(time_t)atime != atime) {
+ rprintf(FERROR_XFER,
+ "Access time value of %s truncated on receiver.\n",
+ lastname);
+ }
+#endif
+ }
+
+ if (chmod_modes && !S_ISLNK(mode) && mode)
+ mode = tweak_mode(mode, chmod_modes);
+
+ if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
+ if (protocol_version < 30)
+ uid = (uid_t)read_int(f);
+ else {
+ uid = (uid_t)read_varint(f);
+ if (xflags & XMIT_USER_NAME_FOLLOWS)
+ uid = recv_user_name(f, uid);
+ else if (inc_recurse && am_root && (!numeric_ids || usermap))
+ uid = match_uid(uid);
+ }
+ }
+ if (preserve_gid && !(xflags & XMIT_SAME_GID)) {
+ if (protocol_version < 30)
+ gid = (gid_t)read_int(f);
+ else {
+ gid = (gid_t)read_varint(f);
+ gid_flags = 0;
+ if (xflags & XMIT_GROUP_NAME_FOLLOWS)
+ gid = recv_group_name(f, gid, &gid_flags);
+ else if (inc_recurse && (!am_root || !numeric_ids || groupmap))
+ gid = match_gid(gid, &gid_flags);
+ }
+ }
+
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) {
+ if (protocol_version < 28) {
+ if (!(xflags & XMIT_SAME_RDEV_pre28))
+ rdev = (dev_t)read_int(f);
+ } else {
+ uint32 rdev_minor;
+ if (!(xflags & XMIT_SAME_RDEV_MAJOR))
+ rdev_major = read_varint30(f);
+ if (protocol_version >= 30)
+ rdev_minor = read_varint(f);
+ else if (xflags & XMIT_RDEV_MINOR_8_pre30)
+ rdev_minor = read_byte(f);
+ else
+ rdev_minor = read_int(f);
+ rdev = MAKEDEV(rdev_major, rdev_minor);
+ }
+ if (IS_DEVICE(mode))
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ file_length = 0;
+ } else if (protocol_version < 28)
+ rdev = MAKEDEV(0, 0);
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(mode)) {
+ linkname_len = read_varint30(f) + 1; /* count the '\0' */
+ if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
+ rprintf(FERROR, "overflow: linkname_len=%d\n",
+ linkname_len - 1);
+ overflow_exit("recv_file_entry");
+ }
+#ifdef ICONV_OPTION
+ /* We don't know how much extra room we need to convert
+ * the as-yet-unread symlink data, so let's hope that a
+ * double-size buffer is plenty. */
+ if (sender_symlink_iconv)
+ linkname_len *= 2;
+#endif
+ if (munge_symlinks)
+ linkname_len += SYMLINK_PREFIX_LEN;
+ }
+ else
+#endif
+ linkname_len = 0;
+
+ if (copy_devices && IS_DEVICE(mode)) {
+ /* This is impossible in the official release, but some pre-release patches
+ * didn't convert the device into a file before sending, so we'll do it here
+ * (even though the length is typically 0 and any checksum data is zeros). */
+ mode = S_IFREG | (mode & ACCESSPERMS);
+ modtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
+ real_ISREG_entry = 0;
+ } else
+ real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
+
+#ifdef SUPPORT_HARD_LINKS
+ create_object:
+ if (preserve_hard_links) {
+ if (protocol_version < 28 && real_ISREG_entry)
+ xflags |= XMIT_HLINKED;
+ if (xflags & XMIT_HLINKED)
+ extra_len += (inc_recurse+1) * EXTRA_LEN;
+ }
+#endif
+
+#ifdef SUPPORT_ACLS
+ /* Directories need an extra int32 for the default ACL. */
+ if (preserve_acls && S_ISDIR(mode))
+ extra_len += EXTRA_LEN;
+#endif
+
+ if (always_checksum && S_ISREG(mode))
+ extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+
+#if SIZEOF_INT64 >= 8
+ if (file_length > 0xFFFFFFFFu && S_ISREG(mode))
+ extra_len += EXTRA_LEN;
+#endif
+#ifdef CAN_SET_NSEC
+ if (modtime_nsec)
+ extra_len += EXTRA_LEN;
+#endif
+ if (file_length < 0) {
+ rprintf(FERROR, "Offset underflow: file-length is negative\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (*thisname == '/' ? thisname[1] != '.' || thisname[2] != '\0' : *thisname != '.' || thisname[1] != '\0') {
+ int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
+ if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
+ && filter_list.head && check_server_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
+ rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) {
+ rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ }
+
+ if (inc_recurse && S_ISDIR(mode)) {
+ if (one_file_system) {
+ /* Room to save the dir's device for -x */
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ }
+ pool = dir_flist->file_pool;
+ } else
+ pool = flist->file_pool;
+
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len
+ + linkname_len;
+ bp = pool_alloc(pool, alloc_len, "recv_file_entry");
+
+ memset(bp, 0, extra_len + FILE_STRUCT_LEN);
+ bp += extra_len;
+ file = (struct file_struct *)bp;
+ bp += FILE_STRUCT_LEN;
+
+ memcpy(bp, basename, basename_len);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (xflags & XMIT_HLINKED
+#ifndef CAN_HARDLINK_SYMLINK
+ && !S_ISLNK(mode)
+#endif
+#ifndef CAN_HARDLINK_SPECIAL
+ && !IS_SPECIAL(mode) && !IS_DEVICE(mode)
+#endif
+ )
+ file->flags |= FLAG_HLINKED;
+#endif
+ file->modtime = (time_t)modtime;
+#ifdef CAN_SET_NSEC
+ if (modtime_nsec) {
+ file->flags |= FLAG_MOD_NSEC;
+ F_MOD_NSEC(file) = modtime_nsec;
+ }
+#endif
+ file->len32 = (uint32)file_length;
+#if SIZEOF_INT64 >= 8
+ if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) {
+#if SIZEOF_CAPITAL_OFF_T < 8
+ rprintf(FERROR, "Offset overflow: attempted 64-bit file-length\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+#else
+ file->flags |= FLAG_LENGTH64;
+ F_HIGH_LEN(file) = (uint32)(file_length >> 32);
+#endif
+ }
+#endif
+ file->mode = mode;
+ if (preserve_uid)
+ F_OWNER(file) = uid;
+ if (preserve_gid) {
+ F_GROUP(file) = gid;
+ file->flags |= gid_flags;
+ }
+ if (atimes_ndx && !S_ISDIR(mode))
+ F_ATIME(file) = atime;
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx)
+ F_CRTIME(file) = crtime;
+#endif
+ if (unsort_ndx)
+ F_NDX(file) = flist->used + flist->ndx_start;
+
+ if (basename != thisname) {
+ file->dirname = lastdir;
+ F_DEPTH(file) = lastdir_depth + 1;
+ } else
+ F_DEPTH(file) = 1;
+
+ if (S_ISDIR(mode)) {
+ if (basename_len == 1+1 && *basename == '.') /* +1 for '\0' */
+ F_DEPTH(file)--;
+ if (protocol_version >= 30) {
+ if (!(xflags & XMIT_NO_CONTENT_DIR)) {
+ if (xflags & XMIT_TOP_DIR)
+ file->flags |= FLAG_TOP_DIR;
+ file->flags |= FLAG_CONTENT_DIR;
+ } else if (xflags & XMIT_TOP_DIR)
+ file->flags |= FLAG_IMPLIED_DIR;
+ } else if (xflags & XMIT_TOP_DIR) {
+ in_del_hier = recurse;
+ del_hier_name_len = F_DEPTH(file) == 0 ? 0 : l1 + l2;
+ if (relative_paths && del_hier_name_len > 2
+ && lastname[del_hier_name_len-1] == '.'
+ && lastname[del_hier_name_len-2] == '/')
+ del_hier_name_len -= 2;
+ file->flags |= FLAG_TOP_DIR | FLAG_CONTENT_DIR;
+ } else if (in_del_hier) {
+ if (!relative_paths || !del_hier_name_len
+ || (l1 >= del_hier_name_len
+ && lastname[del_hier_name_len] == '/'))
+ file->flags |= FLAG_CONTENT_DIR;
+ else
+ in_del_hier = 0;
+ }
+ }
+
+ if (preserve_devices && IS_DEVICE(mode)) {
+ uint32 *devp = F_RDEV_P(file);
+ DEV_MAJOR(devp) = major(rdev);
+ DEV_MINOR(devp) = minor(rdev);
+ }
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len) {
+ bp += basename_len;
+ if (first_hlink_ndx >= flist->ndx_start) {
+ struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
+ memcpy(bp, F_SYMLINK(first), linkname_len);
+ } else {
+ if (munge_symlinks) {
+ strlcpy(bp, SYMLINK_PREFIX, linkname_len);
+ bp += SYMLINK_PREFIX_LEN;
+ linkname_len -= SYMLINK_PREFIX_LEN;
+ }
+#ifdef ICONV_OPTION
+ if (sender_symlink_iconv) {
+ xbuf outbuf, inbuf;
+
+ alloc_len = linkname_len;
+ linkname_len /= 2;
+
+ /* Read the symlink data into the end of our double-sized
+ * buffer and then convert it into the right spot. */
+ INIT_XBUF(inbuf, bp + alloc_len - linkname_len,
+ linkname_len - 1, (size_t)-1);
+ read_sbuf(f, inbuf.buf, inbuf.len);
+ INIT_XBUF(outbuf, bp, 0, alloc_len);
+
+ if (iconvbufs(ic_recv, &inbuf, &outbuf, ICB_INIT) < 0) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "[%s] cannot convert symlink data for: %s (%s)\n",
+ who_am_i(), full_fname(thisname), strerror(errno));
+ bp = (char*)file->basename;
+ *bp++ = '\0';
+ outbuf.len = 0;
+ }
+ bp[outbuf.len] = '\0';
+ } else
+#endif
+ read_sbuf(f, bp, linkname_len - 1);
+ if (sanitize_paths && !munge_symlinks && *bp)
+ sanitize_path(bp, bp, "", lastdir_depth, SP_DEFAULT);
+ }
+ }
+#endif
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && xflags & XMIT_HLINKED) {
+ if (protocol_version >= 30) {
+ if (xflags & XMIT_HLINK_FIRST) {
+ F_HL_GNUM(file) = flist->ndx_start + flist->used;
+ } else
+ F_HL_GNUM(file) = first_hlink_ndx;
+ } else {
+ static int32 cnt = 0;
+ struct ht_int64_node *np;
+ int64 ino;
+ int32 ndx;
+ if (protocol_version < 26) {
+ dev = read_int(f);
+ ino = read_int(f);
+ } else {
+ if (!(xflags & XMIT_SAME_DEV_pre30))
+ dev = read_longint(f);
+ ino = read_longint(f);
+ }
+ np = idev_find(dev, ino);
+ ndx = (int32)(long)np->data; /* is -1 when new */
+ if (ndx < 0) {
+ np->data = (void*)(long)cnt;
+ ndx = cnt++;
+ }
+ F_HL_GNUM(file) = ndx;
+ }
+ }
+#endif
+
+ if (always_checksum && (real_ISREG_entry || protocol_version < 28)) {
+ if (real_ISREG_entry)
+ bp = F_SUM(file);
+ else {
+ /* Prior to 28, we get a useless set of nulls. */
+ bp = tmp_sum;
+ }
+ if (first_hlink_ndx >= flist->ndx_start) {
+ struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
+ memcpy(bp, F_SUM(first), flist_csum_len);
+ } else
+ read_buf(f, bp, flist_csum_len);
+ }
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(mode))
+ receive_acl(f, file);
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ receive_xattr(f, file);
+#endif
+
+ if (S_ISREG(mode) || S_ISLNK(mode))
+ stats.total_size += file_length;
+
+ return file;
+}
+
+/* Create a file_struct for a named file by reading its stat() information
+ * and performing extensive checks against global options.
+ *
+ * Returns a pointer to the new file struct, or NULL if there was an error
+ * or this file should be excluded.
+ *
+ * Note: Any error (here or in send_file_name) that results in the omission of
+ * an existent source file from the file list should set
+ * "io_error |= IOERR_GENERAL" to avoid deletion of the file from the
+ * destination if --delete is on. */
+struct file_struct *make_file(const char *fname, struct file_list *flist,
+ STRUCT_STAT *stp, int flags, int filter_level)
+{
+ static char *lastdir;
+ static int lastdir_len = -1;
+ struct file_struct *file;
+ char thisname[MAXPATHLEN];
+ char linkname[MAXPATHLEN];
+ int alloc_len, basename_len, linkname_len;
+ int extra_len = file_extra_cnt * EXTRA_LEN;
+ const char *basename;
+ alloc_pool_t *pool;
+ STRUCT_STAT st;
+ char *bp;
+
+ if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "skipping overly long name: %s\n", fname);
+ return NULL;
+ }
+ clean_fname(thisname, 0);
+ if (sanitize_paths)
+ sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
+
+ if (stp && (S_ISDIR(stp->st_mode) || IS_MISSING_FILE(*stp))) {
+ /* This is needed to handle a "symlink/." with a --relative
+ * dir, or a request to delete a specific file. */
+ st = *stp;
+ *linkname = '\0'; /* make IBM code checker happy */
+ } else if (readlink_stat(thisname, &st, linkname) != 0) {
+ int save_errno = errno;
+ /* See if file is excluded before reporting an error. */
+ if (filter_level != NO_FILTERS
+ && (is_excluded(thisname, 0, filter_level)
+ || is_excluded(thisname, 1, filter_level))) {
+ if (ignore_perishable && save_errno != ENOENT)
+ non_perishable_cnt++;
+ return NULL;
+ }
+ if (save_errno == ENOENT) {
+#ifdef SUPPORT_LINKS
+ /* When our options tell us to follow a symlink that
+ * points nowhere, tell the user about the symlink
+ * instead of giving a "vanished" message. We only
+ * dereference a symlink if one of the --copy*links
+ * options was specified, so there's no need for the
+ * extra lstat() if one of these options isn't on. */
+ if ((copy_links || copy_unsafe_links || copy_dirlinks)
+ && x_lstat(thisname, &st, NULL) == 0
+ && S_ISLNK(st.st_mode)) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "symlink has no referent: %s\n",
+ full_fname(thisname));
+ } else
+#endif
+ {
+ enum logcode c = am_daemon && protocol_version < 28
+ ? FERROR : FWARNING;
+ io_error |= IOERR_VANISHED;
+ rprintf(c, "file has vanished: %s\n",
+ full_fname(thisname));
+ }
+ } else {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, save_errno, "readlink_stat(%s) failed",
+ full_fname(thisname));
+ }
+ return NULL;
+ } else if (IS_MISSING_FILE(st)) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FINFO, "skipping file with bogus (zero) st_mode: %s\n",
+ full_fname(thisname));
+ return NULL;
+ }
+
+ if (filter_level == NO_FILTERS)
+ goto skip_filters;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (!xfer_dirs) {
+ rprintf(FINFO, "skipping directory %s\n", thisname);
+ return NULL;
+ }
+ /* -x only affects dirs because we need to avoid recursing
+ * into a mount-point directory, not to avoid copying a
+ * symlinked file if -L (or similar) was specified. */
+ if (one_file_system && st.st_dev != filesystem_dev
+ && BITS_SETnUNSET(flags, FLAG_CONTENT_DIR, FLAG_TOP_DIR)) {
+ if (one_file_system > 1) {
+ if (INFO_GTE(MOUNT, 1)) {
+ rprintf(FINFO,
+ "[%s] skipping mount-point dir %s\n",
+ who_am_i(), thisname);
+ }
+ return NULL;
+ }
+ flags |= FLAG_MOUNT_DIR;
+ flags &= ~FLAG_CONTENT_DIR;
+ }
+ } else
+ flags &= ~FLAG_CONTENT_DIR;
+
+ if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
+ if (ignore_perishable)
+ non_perishable_cnt++;
+ return NULL;
+ }
+
+ if (lp_ignore_nonreadable(module_id)) {
+#ifdef SUPPORT_LINKS
+ if (!S_ISLNK(st.st_mode))
+#endif
+ if (access(thisname, R_OK) != 0)
+ return NULL;
+ }
+
+ skip_filters:
+
+ /* Only divert a directory in the main transfer. */
+ if (flist) {
+ if (flist->prev && S_ISDIR(st.st_mode)
+ && flags & FLAG_DIVERT_DIRS) {
+ /* Room for parent/sibling/next-child info. */
+ extra_len += DIRNODE_EXTRA_CNT * EXTRA_LEN;
+ if (relative_paths)
+ extra_len += PTR_EXTRA_CNT * EXTRA_LEN;
+ pool = dir_flist->file_pool;
+ } else
+ pool = flist->file_pool;
+ } else {
+#ifdef SUPPORT_ACLS
+ /* Directories need an extra int32 for the default ACL. */
+ if (preserve_acls && S_ISDIR(st.st_mode))
+ extra_len += EXTRA_LEN;
+#endif
+ pool = NULL;
+ }
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] make_file(%s,*,%d)\n",
+ who_am_i(), thisname, filter_level);
+ }
+
+ if ((basename = strrchr(thisname, '/')) != NULL) {
+ int len = basename++ - thisname;
+ if (len != lastdir_len || memcmp(thisname, lastdir, len) != 0) {
+ lastdir = new_array(char, len + 1);
+ memcpy(lastdir, thisname, len);
+ lastdir[len] = '\0';
+ lastdir_len = len;
+ }
+ } else
+ basename = thisname;
+ basename_len = strlen(basename) + 1; /* count the '\0' */
+
+#ifdef SUPPORT_LINKS
+ linkname_len = S_ISLNK(st.st_mode) ? strlen(linkname) + 1 : 0;
+#else
+ linkname_len = 0;
+#endif
+
+ if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
+ if (st.st_size == 0) {
+ int fd = do_open(fname, O_RDONLY, 0);
+ if (fd >= 0) {
+ st.st_size = get_device_size(fd, fname);
+ close(fd);
+ }
+ }
+ st.st_mode = S_IFREG | (st.st_mode & ACCESSPERMS);
+ st.st_mtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
+ }
+
+#ifdef ST_MTIME_NSEC
+ if (st.ST_MTIME_NSEC && protocol_version >= 31)
+ extra_len += EXTRA_LEN;
+#endif
+#if SIZEOF_CAPITAL_OFF_T >= 8
+ if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
+ extra_len += EXTRA_LEN;
+#endif
+
+ if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
+ file_checksum(thisname, &st, tmp_sum);
+ if (sender_keeps_checksum)
+ extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+ }
+
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len
+ + linkname_len;
+ if (pool)
+ bp = pool_alloc(pool, alloc_len, "make_file");
+ else
+ bp = new_array(char, alloc_len);
+
+ memset(bp, 0, extra_len + FILE_STRUCT_LEN);
+ bp += extra_len;
+ file = (struct file_struct *)bp;
+ bp += FILE_STRUCT_LEN;
+
+ memcpy(bp, basename, basename_len);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && flist && flist->prev) {
+ if (protocol_version >= 28
+ ? (!S_ISDIR(st.st_mode) && st.st_nlink > 1)
+ : S_ISREG(st.st_mode)) {
+ tmp_dev = (int64)st.st_dev;
+ tmp_ino = (int64)st.st_ino;
+ } else
+ tmp_dev = -1;
+ }
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+ if (IS_DEVICE(st.st_mode)) {
+ tmp_rdev = st.st_rdev;
+ st.st_size = 0;
+ } else if (IS_SPECIAL(st.st_mode))
+ st.st_size = 0;
+#endif
+
+ file->flags = flags;
+ file->modtime = st.st_mtime;
+#ifdef ST_MTIME_NSEC
+ if (st.ST_MTIME_NSEC && protocol_version >= 31) {
+ file->flags |= FLAG_MOD_NSEC;
+ F_MOD_NSEC(file) = st.ST_MTIME_NSEC;
+ }
+#endif
+ file->len32 = (uint32)st.st_size;
+#if SIZEOF_CAPITAL_OFF_T >= 8
+ if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) {
+ file->flags |= FLAG_LENGTH64;
+ F_HIGH_LEN(file) = (uint32)(st.st_size >> 32);
+ }
+#endif
+ file->mode = st.st_mode;
+ if (preserve_uid)
+ F_OWNER(file) = st.st_uid;
+ if (preserve_gid)
+ F_GROUP(file) = st.st_gid;
+ if (am_generator && st.st_uid == our_uid)
+ file->flags |= FLAG_OWNED_BY_US;
+ if (atimes_ndx && !S_ISDIR(file->mode))
+ F_ATIME(file) = st.st_atime;
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx)
+ F_CRTIME(file) = get_create_time(fname, &st);
+#endif
+
+ if (basename != thisname)
+ file->dirname = lastdir;
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len)
+ memcpy(bp + basename_len, linkname, linkname_len);
+#endif
+
+ if (am_sender)
+ F_PATHNAME(file) = pathname;
+ else if (!pool)
+ F_DEPTH(file) = extra_len / EXTRA_LEN;
+
+ if (basename_len == 0+1) {
+ if (!pool)
+ unmake_file(file);
+ return NULL;
+ }
+
+ if (sender_keeps_checksum && S_ISREG(st.st_mode))
+ memcpy(F_SUM(file), tmp_sum, flist_csum_len);
+
+ if (unsort_ndx)
+ F_NDX(file) = stats.num_dirs;
+
+ return file;
+}
+
+OFF_T get_device_size(int fd, const char *fname)
+{
+ OFF_T off = lseek(fd, 0, SEEK_END);
+
+ if (off == (OFF_T) -1) {
+ rsyserr(FERROR, errno, "failed to get device size via seek: %s", fname);
+ return 0;
+ }
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ rsyserr(FERROR, errno, "failed to seek device back to start: %s", fname);
+
+ return off;
+}
+
+/* Only called for temporary file_struct entries created by make_file(). */
+void unmake_file(struct file_struct *file)
+{
+ free(REQ_EXTRA(file, F_DEPTH(file)));
+}
+
+static struct file_struct *send_file_name(int f, struct file_list *flist,
+ const char *fname, STRUCT_STAT *stp,
+ int flags, int filter_level)
+{
+ struct file_struct *file;
+
+ file = make_file(fname, flist, stp, flags, filter_level);
+ if (!file)
+ return NULL;
+
+ if (chmod_modes && !S_ISLNK(file->mode) && file->mode)
+ file->mode = tweak_mode(file->mode, chmod_modes);
+
+ if (f >= 0) {
+ char fbuf[MAXPATHLEN];
+#ifdef SUPPORT_LINKS
+ const char *symlink_name;
+ int symlink_len;
+#ifdef ICONV_OPTION
+ char symlink_buf[MAXPATHLEN];
+#endif
+#endif
+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
+ stat_x sx;
+ init_stat_x(&sx);
+#endif
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(file->mode)) {
+ symlink_name = F_SYMLINK(file);
+ symlink_len = strlen(symlink_name);
+ if (symlink_len == 0) {
+ io_error |= IOERR_GENERAL;
+ f_name(file, fbuf);
+ rprintf(FERROR_XFER,
+ "skipping symlink with 0-length value: %s\n",
+ full_fname(fbuf));
+ return NULL;
+ }
+ } else {
+ symlink_name = NULL;
+ symlink_len = 0;
+ }
+#endif
+
+#ifdef ICONV_OPTION
+ if (ic_send != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+
+ INIT_CONST_XBUF(outbuf, fbuf);
+
+ if (file->dirname) {
+ INIT_XBUF_STRLEN(inbuf, (char*)file->dirname);
+ outbuf.size -= 2; /* Reserve room for '/' & 1 more char. */
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0)
+ goto convert_error;
+ outbuf.size += 2;
+ fbuf[outbuf.len++] = '/';
+ }
+
+ INIT_XBUF_STRLEN(inbuf, (char*)file->basename);
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0) {
+ convert_error:
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "[%s] cannot convert filename: %s (%s)\n",
+ who_am_i(), f_name(file, fbuf), strerror(errno));
+ return NULL;
+ }
+ fbuf[outbuf.len] = '\0';
+
+#ifdef SUPPORT_LINKS
+ if (symlink_len && sender_symlink_iconv) {
+ INIT_XBUF(inbuf, (char*)symlink_name, symlink_len, (size_t)-1);
+ INIT_CONST_XBUF(outbuf, symlink_buf);
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0) {
+ io_error |= IOERR_GENERAL;
+ f_name(file, fbuf);
+ rprintf(FERROR_XFER,
+ "[%s] cannot convert symlink data for: %s (%s)\n",
+ who_am_i(), full_fname(fbuf), strerror(errno));
+ return NULL;
+ }
+ symlink_buf[outbuf.len] = '\0';
+
+ symlink_name = symlink_buf;
+ symlink_len = outbuf.len;
+ }
+#endif
+ } else
+#endif
+ f_name(file, fbuf);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ sx.st.st_mode = file->mode;
+ if (get_acl(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
+ return NULL;
+ }
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ sx.st.st_mode = file->mode;
+ if (get_xattr(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
+ return NULL;
+ }
+ }
+#endif
+
+ send_file_entry(f, fbuf, file,
+#ifdef SUPPORT_LINKS
+ symlink_name, symlink_len,
+#endif
+ flist->used, flist->ndx_start);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ send_acl(f, &sx);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ F_XATTR(file) = send_xattr(f, &sx);
+ free_xattr(&sx);
+ }
+#endif
+ }
+
+ maybe_emit_filelist_progress(flist->used + flist_count_offset);
+
+ flist_expand(flist, 1);
+ flist->files[flist->used++] = file;
+
+ return file;
+}
+
+static void send_if_directory(int f, struct file_list *flist,
+ struct file_struct *file,
+ char *fbuf, unsigned int ol,
+ int flags)
+{
+ char is_dot_dir = fbuf[ol-1] == '.' && (ol == 1 || fbuf[ol-2] == '/');
+
+ if (S_ISDIR(file->mode)
+ && !(file->flags & FLAG_MOUNT_DIR) && f_name(file, fbuf)) {
+ void *save_filters;
+ unsigned int len = strlen(fbuf);
+ if (len > 1 && fbuf[len-1] == '/')
+ fbuf[--len] = '\0';
+ save_filters = push_local_filters(fbuf, len);
+ send_directory(f, flist, fbuf, len, flags);
+ pop_local_filters(save_filters);
+ fbuf[ol] = '\0';
+ if (is_dot_dir)
+ fbuf[ol-1] = '.';
+ }
+}
+
+static int file_compare(const void *file1, const void *file2)
+{
+ return f_name_cmp(*(struct file_struct **)file1,
+ *(struct file_struct **)file2);
+}
+
+/* The guts of a merge-sort algorithm. This was derived from the glibc
+ * version, but I (Wayne) changed the merge code to do less copying and
+ * to require only half the amount of temporary memory. */
+static void fsort_tmp(struct file_struct **fp, size_t num,
+ struct file_struct **tmp)
+{
+ struct file_struct **f1, **f2, **t;
+ size_t n1, n2;
+
+ n1 = num / 2;
+ n2 = num - n1;
+ f1 = fp;
+ f2 = fp + n1;
+
+ if (n1 > 1)
+ fsort_tmp(f1, n1, tmp);
+ if (n2 > 1)
+ fsort_tmp(f2, n2, tmp);
+
+ while (f_name_cmp(*f1, *f2) <= 0) {
+ if (!--n1)
+ return;
+ f1++;
+ }
+
+ t = tmp;
+ memcpy(t, f1, n1 * PTR_SIZE);
+
+ *f1++ = *f2++, n2--;
+
+ while (n1 > 0 && n2 > 0) {
+ if (f_name_cmp(*t, *f2) <= 0)
+ *f1++ = *t++, n1--;
+ else
+ *f1++ = *f2++, n2--;
+ }
+
+ if (n1 > 0)
+ memcpy(f1, t, n1 * PTR_SIZE);
+}
+
+/* This file-struct sorting routine makes sure that any identical names in
+ * the file list stay in the same order as they were in the original list.
+ * This is particularly vital in inc_recurse mode where we expect a sort
+ * on the flist to match the exact order of a sort on the dir_flist. */
+static void fsort(struct file_struct **fp, size_t num)
+{
+ if (num <= 1)
+ return;
+
+ if (use_qsort)
+ qsort(fp, num, PTR_SIZE, file_compare);
+ else {
+ struct file_struct **tmp = new_array(struct file_struct *, (num+1) / 2);
+ fsort_tmp(fp, num, tmp);
+ free(tmp);
+ }
+}
+
+/* We take an entire set of sibling dirs from the sorted flist and link them
+ * into the tree, setting the appropriate parent/child/sibling pointers. */
+static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist,
+ int dir_cnt)
+{
+ int i;
+ int32 *dp = NULL;
+ int32 *parent_dp = parent_ndx < 0 ? NULL
+ : F_DIR_NODE_P(dir_flist->sorted[parent_ndx]);
+
+ /* The sending side is adding entries to dir_flist in sorted order, so sorted & files are the same. */
+ flist_expand(dir_flist, dir_cnt);
+ dir_flist->sorted = dir_flist->files;
+
+ for (i = 0; dir_cnt; i++) {
+ struct file_struct *file = from_flist->sorted[i];
+
+ if (!S_ISDIR(file->mode))
+ continue;
+
+ dir_flist->files[dir_flist->used++] = file;
+ dir_cnt--;
+
+ if (file->basename[0] == '.' && file->basename[1] == '\0')
+ continue;
+
+ if (dp)
+ DIR_NEXT_SIBLING(dp) = dir_flist->used - 1;
+ else if (parent_dp)
+ DIR_FIRST_CHILD(parent_dp) = dir_flist->used - 1;
+ else
+ send_dir_ndx = dir_flist->used - 1;
+
+ dp = F_DIR_NODE_P(file);
+ DIR_PARENT(dp) = parent_ndx;
+ DIR_FIRST_CHILD(dp) = -1;
+ }
+ if (dp)
+ DIR_NEXT_SIBLING(dp) = -1;
+}
+
+static void interpret_stat_error(const char *fname, int is_dir)
+{
+ if (errno == ENOENT) {
+ io_error |= IOERR_VANISHED;
+ rprintf(FWARNING, "%s has vanished: %s\n",
+ is_dir ? "directory" : "file", full_fname(fname));
+ } else {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+ full_fname(fname));
+ }
+}
+
+/* This function is normally called by the sender, but the receiving side also
+ * calls it from get_dirlist() with f set to -1 so that we just construct the
+ * file list in memory without sending it over the wire. Also, get_dirlist()
+ * might call this with f set to -2, which also indicates that local filter
+ * rules should be ignored. */
+static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
+ int flags)
+{
+ struct dirent *di;
+ unsigned remainder;
+ char *p;
+ DIR *d;
+ int divert_dirs = (flags & FLAG_DIVERT_DIRS) != 0;
+ int start = flist->used;
+ int filter_level = f == -2 ? SERVER_FILTERS : ALL_FILTERS;
+
+ assert(flist != NULL);
+
+ if (!(d = opendir(fbuf))) {
+ if (errno == ENOENT) {
+ if (am_sender) /* Can abuse this for vanished error w/ENOENT: */
+ interpret_stat_error(fbuf, True);
+ return;
+ }
+ if (errno == ENOTDIR && (flags & FLAG_PERHAPS_DIR))
+ return;
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf));
+ return;
+ }
+
+ p = fbuf + len;
+ if (len == 1 && *fbuf == '/')
+ remainder = MAXPATHLEN - 1;
+ else if (len < MAXPATHLEN-1) {
+ *p++ = '/';
+ *p = '\0';
+ remainder = MAXPATHLEN - (len + 1);
+ } else
+ remainder = 0;
+
+ for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
+ unsigned name_len;
+ char *dname = d_name(di);
+ if (dname[0] == '.' && (dname[1] == '\0'
+ || (dname[1] == '.' && dname[2] == '\0')))
+ continue;
+ name_len = strlcpy(p, dname, remainder);
+ if (name_len >= remainder) {
+ char save = fbuf[len];
+ fbuf[len] = '\0';
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "filename overflows max-path len by %u: %s/%s\n",
+ name_len - remainder + 1, fbuf, dname);
+ fbuf[len] = save;
+ continue;
+ }
+ if (dname[0] == '\0') {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "cannot send file with empty name in %s\n",
+ full_fname(fbuf));
+ continue;
+ }
+
+ send_file_name(f, flist, fbuf, NULL, flags, filter_level);
+ }
+
+ fbuf[len] = '\0';
+
+ if (errno) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "readdir(%s)", full_fname(fbuf));
+ }
+
+ closedir(d);
+
+ if (f >= 0 && recurse && !divert_dirs) {
+ int i, end = flist->used - 1;
+ /* send_if_directory() bumps flist->used, so use "end". */
+ for (i = start; i <= end; i++)
+ send_if_directory(f, flist, flist->files[i], fbuf, len, flags);
+ }
+}
+
+static void send_implied_dirs(int f, struct file_list *flist, char *fname,
+ char *start, char *limit, int flags, char name_type)
+{
+ static char lastpath[MAXPATHLEN] = "";
+ static int lastpath_len = 0;
+ static struct file_struct *lastpath_struct = NULL;
+ struct file_struct *file;
+ item_list *relname_list;
+ relnamecache **rnpp;
+ int len, need_new_dir, depth = 0;
+ filter_rule_list save_filter_list = filter_list;
+
+ flags = (flags | FLAG_IMPLIED_DIR) & ~(FLAG_TOP_DIR | FLAG_CONTENT_DIR);
+ filter_list.head = filter_list.tail = NULL; /* Don't filter implied dirs. */
+
+ if (inc_recurse) {
+ if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname
+ && lastpath_len == limit - fname
+ && strncmp(lastpath, fname, lastpath_len) == 0)
+ need_new_dir = 0;
+ else
+ need_new_dir = 1;
+ } else {
+ char *tp = fname, *lp = lastpath;
+ /* Skip any initial directories in our path that we
+ * have in common with lastpath. */
+ assert(start == fname);
+ for ( ; ; tp++, lp++) {
+ if (tp == limit) {
+ if (*lp == '/' || *lp == '\0')
+ goto done;
+ break;
+ }
+ if (*lp != *tp)
+ break;
+ if (*tp == '/') {
+ start = tp;
+ depth++;
+ }
+ }
+ need_new_dir = 1;
+ }
+
+ if (need_new_dir) {
+ int save_copy_links = copy_links;
+ int save_xfer_dirs = xfer_dirs;
+ char *slash;
+
+ copy_links = xfer_dirs = 1;
+
+ *limit = '\0';
+
+ for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) {
+ *slash = '\0';
+ file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+ depth++;
+ if (!inc_recurse && file && S_ISDIR(file->mode))
+ change_local_filter_dir(fname, strlen(fname), depth);
+ *slash = '/';
+ }
+
+ file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+ if (inc_recurse) {
+ if (file && !S_ISDIR(file->mode))
+ file = NULL;
+ lastpath_struct = file;
+ } else if (file && S_ISDIR(file->mode))
+ change_local_filter_dir(fname, strlen(fname), ++depth);
+
+ strlcpy(lastpath, fname, sizeof lastpath);
+ lastpath_len = limit - fname;
+
+ *limit = '/';
+
+ copy_links = save_copy_links;
+ xfer_dirs = save_xfer_dirs;
+
+ if (!inc_recurse)
+ goto done;
+ }
+
+ if (!lastpath_struct)
+ goto done; /* dir must have vanished */
+
+ len = strlen(limit+1);
+ memcpy(&relname_list, F_DIR_RELNAMES_P(lastpath_struct), sizeof relname_list);
+ if (!relname_list) {
+ relname_list = new0(item_list);
+ memcpy(F_DIR_RELNAMES_P(lastpath_struct), &relname_list, sizeof relname_list);
+ }
+ rnpp = EXPAND_ITEM_LIST(relname_list, relnamecache *, 32);
+ *rnpp = (relnamecache*)new_array(char, RELNAMECACHE_LEN + len + 1);
+ (*rnpp)->name_type = name_type;
+ strlcpy((*rnpp)->fname, limit+1, len + 1);
+
+done:
+ filter_list = save_filter_list;
+}
+
+static NORETURN void fatal_unsafe_io_error(void)
+{
+ /* This (sadly) can only happen when pushing data because
+ * the sender does not know about what kind of delete
+ * is in effect on the receiving side when pulling. */
+ rprintf(FERROR_XFER, "FATAL I/O ERROR: dying to avoid a --delete-%s issue with a pre-3.0.7 receiver.\n",
+ delete_during == 2 ? "delay" : "during");
+ exit_cleanup(RERR_UNSUPPORTED);
+}
+
+static void send1extra(int f, struct file_struct *file, struct file_list *flist)
+{
+ char fbuf[MAXPATHLEN];
+ item_list *relname_list;
+ int len, dlen, flags = FLAG_DIVERT_DIRS | FLAG_CONTENT_DIR;
+ size_t j;
+
+ f_name(file, fbuf);
+ dlen = strlen(fbuf);
+
+ if (!change_pathname(file, NULL, 0))
+ exit_cleanup(RERR_FILESELECT);
+
+ change_local_filter_dir(fbuf, dlen, send_dir_depth);
+
+ if (file->flags & FLAG_CONTENT_DIR) {
+ if (one_file_system) {
+ STRUCT_STAT st;
+ if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
+ interpret_stat_error(fbuf, True);
+ return;
+ }
+ filesystem_dev = st.st_dev;
+ }
+ send_directory(f, flist, fbuf, dlen, flags);
+ }
+
+ if (!relative_paths)
+ return;
+
+ memcpy(&relname_list, F_DIR_RELNAMES_P(file), sizeof relname_list);
+ if (!relname_list)
+ return;
+
+ for (j = 0; j < relname_list->count; j++) {
+ char *slash;
+ relnamecache *rnp = ((relnamecache**)relname_list->items)[j];
+ char name_type = rnp->name_type;
+
+ fbuf[dlen] = '/';
+ len = strlcpy(fbuf + dlen + 1, rnp->fname, sizeof fbuf - dlen - 1);
+ free(rnp);
+ if (len >= (int)sizeof fbuf)
+ continue; /* Impossible... */
+
+ slash = strchr(fbuf+dlen+1, '/');
+ if (slash) {
+ send_implied_dirs(f, flist, fbuf, fbuf+dlen+1, slash, flags, name_type);
+ continue;
+ }
+
+ if (name_type != NORMAL_NAME) {
+ STRUCT_STAT st;
+ if (name_type == MISSING_NAME)
+ memset(&st, 0, sizeof st);
+ else if (link_stat(fbuf, &st, 1) != 0) {
+ interpret_stat_error(fbuf, True);
+ continue;
+ }
+ send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS);
+ } else
+ send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS);
+ }
+
+ free(relname_list);
+}
+
+static void write_end_of_flist(int f, int send_io_error)
+{
+ if (xfer_flags_as_varint) {
+ write_varint(f, 0);
+ write_varint(f, send_io_error ? io_error : 0);
+ } else if (send_io_error) {
+ write_shortint(f, XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST);
+ write_varint(f, io_error);
+ } else
+ write_byte(f, 0);
+}
+
+void send_extra_file_list(int f, int at_least)
+{
+ struct file_list *flist;
+ int64 start_write;
+ uint16 prev_flags;
+ int save_io_error = io_error;
+
+ if (flist_eof)
+ return;
+
+ if (at_least < 0)
+ at_least = file_total - file_old_total + 1;
+
+ /* Keep sending data until we have the requested number of
+ * files in the upcoming file-lists. */
+ while (file_total - file_old_total < at_least) {
+ struct file_struct *file = dir_flist->sorted[send_dir_ndx];
+ int dir_ndx, dstart = stats.num_dirs;
+ const char *pathname = F_PATHNAME(file);
+ int32 *dp;
+
+ flist = flist_new(0, "send_extra_file_list");
+ start_write = stats.total_written;
+
+ if (unsort_ndx)
+ dir_ndx = F_NDX(file);
+ else
+ dir_ndx = send_dir_ndx;
+ write_ndx(f, NDX_FLIST_OFFSET - dir_ndx);
+ flist->parent_ndx = send_dir_ndx; /* the sending side must remember the sorted ndx value */
+
+ send1extra(f, file, flist);
+ prev_flags = file->flags;
+ dp = F_DIR_NODE_P(file);
+
+ /* If there are any duplicate directory names that follow, we
+ * send all the dirs together in one file-list. The dir_flist
+ * tree links all the child subdirs onto the last dup dir. */
+ while ((dir_ndx = DIR_NEXT_SIBLING(dp)) >= 0
+ && dir_flist->sorted[dir_ndx]->flags & FLAG_DUPLICATE) {
+ send_dir_ndx = dir_ndx;
+ file = dir_flist->sorted[dir_ndx];
+ /* Try to avoid some duplicate scanning of identical dirs. */
+ if (F_PATHNAME(file) == pathname && prev_flags & FLAG_CONTENT_DIR)
+ file->flags &= ~FLAG_CONTENT_DIR;
+ send1extra(f, file, flist);
+ prev_flags = file->flags;
+ dp = F_DIR_NODE_P(file);
+ }
+
+ if (io_error == save_io_error || ignore_errors)
+ write_end_of_flist(f, 0);
+ else if (use_safe_inc_flist)
+ write_end_of_flist(f, 1);
+ else {
+ if (delete_during)
+ fatal_unsafe_io_error();
+ write_end_of_flist(f, 0);
+ }
+
+ if (need_unsorted_flist) {
+ flist->sorted = new_array(struct file_struct *, flist->used);
+ memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
+ } else
+ flist->sorted = flist->files;
+
+ flist_sort_and_clean(flist, 0);
+
+ add_dirs_to_tree(send_dir_ndx, flist, stats.num_dirs - dstart);
+ flist_done_allocating(flist);
+
+ file_total += flist->used;
+ stats.flist_size += stats.total_written - start_write;
+ stats.num_files += flist->used;
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(flist);
+
+ if (DIR_FIRST_CHILD(dp) >= 0) {
+ send_dir_ndx = DIR_FIRST_CHILD(dp);
+ send_dir_depth++;
+ } else {
+ while (DIR_NEXT_SIBLING(dp) < 0) {
+ if ((send_dir_ndx = DIR_PARENT(dp)) < 0) {
+ write_ndx(f, NDX_FLIST_EOF);
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ change_local_filter_dir(NULL, 0, 0);
+ goto finish;
+ }
+ send_dir_depth--;
+ file = dir_flist->sorted[send_dir_ndx];
+ dp = F_DIR_NODE_P(file);
+ }
+ send_dir_ndx = DIR_NEXT_SIBLING(dp);
+ }
+ }
+
+ finish:
+ if (io_error != save_io_error && protocol_version == 30 && !ignore_errors)
+ send_msg_int(MSG_IO_ERROR, io_error);
+}
+
+struct file_list *send_file_list(int f, int argc, char *argv[])
+{
+ static const char *lastdir;
+ static int lastdir_len = -1;
+ int len, dirlen;
+ STRUCT_STAT st;
+ char *p, *dir;
+ struct file_list *flist;
+ struct timeval start_tv, end_tv;
+ int64 start_write;
+ int use_ff_fd = 0;
+ int disable_buffering, reenable_multiplex = -1;
+ int flags = recurse ? FLAG_CONTENT_DIR : 0;
+ int reading_remotely = filesfrom_host != NULL;
+ int rl_flags = (reading_remotely ? 0 : RL_DUMP_COMMENTS)
+#ifdef ICONV_OPTION
+ | (filesfrom_convert ? RL_CONVERT : 0)
+#endif
+ | (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0);
+ int implied_dot_dir = 0;
+
+ rprintf(FLOG, "building file list\n");
+ if (show_filelist_progress)
+ start_filelist_progress("building file list");
+ else if (inc_recurse && INFO_GTE(FLIST, 1) && !am_server)
+ rprintf(FCLIENT, "sending incremental file list\n");
+
+ start_write = stats.total_written;
+ gettimeofday(&start_tv, NULL);
+
+ if (relative_paths && protocol_version >= 30)
+ implied_dirs = 1; /* We send flagged implied dirs */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && protocol_version >= 30 && !cur_flist)
+ init_hard_links();
+#endif
+
+ flist = cur_flist = flist_new(0, "send_file_list");
+ flist_expand(flist, FLIST_START_LARGE);
+ if (inc_recurse) {
+ dir_flist = flist_new(FLIST_TEMP, "send_file_list");
+ flist_expand(dir_flist, FLIST_START_LARGE);
+ flags |= FLAG_DIVERT_DIRS;
+ } else
+ dir_flist = cur_flist;
+
+ disable_buffering = io_start_buffering_out(f);
+ if (filesfrom_fd >= 0) {
+ if (argv[0] && !change_dir(argv[0], CD_NORMAL)) {
+ rsyserr(FERROR_XFER, errno, "change_dir %s failed",
+ full_fname(argv[0]));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (protocol_version < 31) {
+ /* Older protocols send the files-from data w/o packaging
+ * it in multiplexed I/O packets, so temporarily switch
+ * to buffered I/O to match this behavior. */
+ reenable_multiplex = io_end_multiplex_in(MPLX_TO_BUFFERED);
+ }
+ use_ff_fd = 1;
+ }
+
+ if (!orig_dir)
+ orig_dir = strdup(curr_dir);
+
+ while (1) {
+ char fbuf[MAXPATHLEN], *fn, name_type;
+
+ if (use_ff_fd) {
+ if (read_line(filesfrom_fd, fbuf, sizeof fbuf, rl_flags) == 0)
+ break;
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
+ } else {
+ if (argc-- == 0)
+ break;
+ strlcpy(fbuf, *argv++, MAXPATHLEN);
+ if (sanitize_paths)
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
+ }
+
+ len = strlen(fbuf);
+ if (relative_paths) {
+ /* We clean up fbuf below. */
+ name_type = NORMAL_NAME;
+ } else if (!len || fbuf[len - 1] == '/') {
+ if (len == 2 && fbuf[0] == '.') {
+ /* Turn "./" into just "." rather than "./." */
+ fbuf[--len] = '\0';
+ } else {
+ if (len + 1 >= MAXPATHLEN)
+ overflow_exit("send_file_list");
+ fbuf[len++] = '.';
+ fbuf[len] = '\0';
+ }
+ name_type = DOTDIR_NAME;
+ } else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.'
+ && (len == 2 || fbuf[len-3] == '/')) {
+ if (len + 2 >= MAXPATHLEN)
+ overflow_exit("send_file_list");
+ fbuf[len++] = '/';
+ fbuf[len++] = '.';
+ fbuf[len] = '\0';
+ name_type = DOTDIR_NAME;
+ } else if (fbuf[len-1] == '.' && (len == 1 || fbuf[len-2] == '/'))
+ name_type = DOTDIR_NAME;
+ else
+ name_type = NORMAL_NAME;
+
+ dir = NULL;
+
+ if (!relative_paths) {
+ p = strrchr(fbuf, '/');
+ if (p) {
+ *p = '\0';
+ if (p == fbuf)
+ dir = "/";
+ else
+ dir = fbuf;
+ len -= p - fbuf + 1;
+ fn = p + 1;
+ } else
+ fn = fbuf;
+ } else {
+ if ((p = strstr(fbuf, "/./")) != NULL) {
+ *p = '\0';
+ if (p == fbuf)
+ dir = "/";
+ else {
+ dir = fbuf;
+ clean_fname(dir, 0);
+ }
+ fn = p + 3;
+ while (*fn == '/')
+ fn++;
+ if (!*fn)
+ *--fn = '\0'; /* ensure room for '.' */
+ } else
+ fn = fbuf;
+ /* A leading ./ can be used in relative mode to affect
+ * the dest dir without its name being in the path. */
+ if (*fn == '.' && fn[1] == '/' && fn[2] && !implied_dot_dir)
+ implied_dot_dir = -1;
+ len = clean_fname(fn, CFN_KEEP_TRAILING_SLASH
+ | CFN_DROP_TRAILING_DOT_DIR);
+ if (len == 1) {
+ if (fn[0] == '/') {
+ fn = "/.";
+ len = 2;
+ name_type = DOTDIR_NAME;
+ } else if (fn[0] == '.')
+ name_type = DOTDIR_NAME;
+ } else if (fn[len-1] == '/') {
+ fn[--len] = '\0';
+ if (len == 1 && *fn == '.')
+ name_type = DOTDIR_NAME;
+ else
+ name_type = SLASH_ENDING_NAME;
+ }
+ /* Reject a ".." dir in the active part of the path. */
+ for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) {
+ if ((p[2] == '/' || p[2] == '\0')
+ && (p == fn || p[-1] == '/')) {
+ rprintf(FERROR,
+ "found \"..\" dir in relative path: %s\n",
+ fn);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+ }
+
+ if (!*fn) {
+ len = 1;
+ fn = ".";
+ name_type = DOTDIR_NAME;
+ }
+
+ dirlen = dir ? strlen(dir) : 0;
+ if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
+ if (!change_pathname(NULL, dir, -dirlen))
+ goto bad_path;
+ lastdir = pathname;
+ lastdir_len = pathname_len;
+ } else if (!change_pathname(NULL, lastdir, lastdir_len)) {
+ bad_path:
+ if (implied_dot_dir < 0)
+ implied_dot_dir = 0;
+ continue;
+ }
+
+ if (implied_dot_dir < 0) {
+ implied_dot_dir = 1;
+ send_file_name(f, flist, ".", NULL, (flags | FLAG_IMPLIED_DIR) & ~FLAG_CONTENT_DIR, ALL_FILTERS);
+ }
+
+ if (fn != fbuf)
+ memmove(fbuf, fn, len + 1);
+
+ if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0
+ || (name_type != DOTDIR_NAME && is_excluded(fbuf, S_ISDIR(st.st_mode) != 0, SERVER_FILTERS))
+ || (relative_paths && path_is_daemon_excluded(fbuf, 1))) {
+ if (errno != ENOENT || missing_args == 0) {
+ /* This is a transfer error, but inhibit deletion
+ * only if we might be omitting an existing file. */
+ if (errno != ENOENT)
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+ full_fname(fbuf));
+ continue;
+ } else if (missing_args == 1) {
+ /* Just ignore the arg. */
+ continue;
+ } else /* (missing_args == 2) */ {
+ /* Send the arg as a "missing" entry with
+ * mode 0, which tells the generator to delete it. */
+ memset(&st, 0, sizeof st);
+ }
+ }
+
+ /* A dot-dir should not be excluded! */
+ if (name_type != DOTDIR_NAME && st.st_mode != 0
+ && is_excluded(fbuf, S_ISDIR(st.st_mode) != 0, ALL_FILTERS))
+ continue;
+
+ if (S_ISDIR(st.st_mode) && !xfer_dirs) {
+ rprintf(FINFO, "skipping directory %s\n", fbuf);
+ continue;
+ }
+
+ if (inc_recurse && relative_paths && *fbuf) {
+ if ((p = strchr(fbuf+1, '/')) != NULL) {
+ if (p - fbuf == 1 && *fbuf == '.') {
+ if ((fn = strchr(p+1, '/')) != NULL)
+ p = fn;
+ } else
+ fn = p;
+ send_implied_dirs(f, flist, fbuf, fbuf, p, flags,
+ IS_MISSING_FILE(st) ? MISSING_NAME : name_type);
+ if (fn == p)
+ continue;
+ }
+ } else if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) {
+ /* Send the implied directories at the start of the
+ * source spec, so we get their permissions right. */
+ send_implied_dirs(f, flist, fbuf, fbuf, p, flags, 0);
+ }
+
+ if (one_file_system)
+ filesystem_dev = st.st_dev;
+
+ if (recurse || (xfer_dirs && name_type != NORMAL_NAME)) {
+ struct file_struct *file;
+ file = send_file_name(f, flist, fbuf, &st,
+ FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
+ NO_FILTERS);
+ if (!file)
+ continue;
+ if (inc_recurse) {
+ if (name_type == DOTDIR_NAME) {
+ if (send_dir_depth < 0) {
+ send_dir_depth = 0;
+ change_local_filter_dir(fbuf, len, send_dir_depth);
+ }
+ send_directory(f, flist, fbuf, len, flags);
+ }
+ } else
+ send_if_directory(f, flist, file, fbuf, len, flags);
+ } else
+ send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS);
+ }
+
+ if (reenable_multiplex >= 0)
+ io_start_multiplex_in(reenable_multiplex);
+
+ gettimeofday(&end_tv, NULL);
+ stats.flist_buildtime = (int64)(end_tv.tv_sec - start_tv.tv_sec) * 1000
+ + (end_tv.tv_usec - start_tv.tv_usec) / 1000;
+ if (stats.flist_buildtime == 0)
+ stats.flist_buildtime = 1;
+ start_tv = end_tv;
+
+ /* Indicate end of file list */
+ if (io_error == 0 || ignore_errors)
+ write_end_of_flist(f, 0);
+ else if (use_safe_inc_flist)
+ write_end_of_flist(f, 1);
+ else {
+ if (delete_during && inc_recurse)
+ fatal_unsafe_io_error();
+ write_end_of_flist(f, 0);
+ }
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && protocol_version >= 30 && !inc_recurse)
+ idev_destroy();
+#endif
+
+ if (show_filelist_progress)
+ finish_filelist_progress(flist);
+
+ gettimeofday(&end_tv, NULL);
+ stats.flist_xfertime = (int64)(end_tv.tv_sec - start_tv.tv_sec) * 1000
+ + (end_tv.tv_usec - start_tv.tv_usec) / 1000;
+
+ /* When converting names, both sides keep an unsorted file-list array
+ * because the names will differ on the sending and receiving sides
+ * (both sides will use the unsorted index number for each item). */
+
+ /* Sort the list without removing any duplicates. This allows the
+ * receiving side to ask for whatever name it kept. For incremental
+ * recursion mode, the sender marks duplicate dirs so that it can
+ * send them together in a single file-list. */
+ if (need_unsorted_flist) {
+ flist->sorted = new_array(struct file_struct *, flist->used);
+ memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
+ } else
+ flist->sorted = flist->files;
+ flist_sort_and_clean(flist, 0);
+ file_total += flist->used;
+ file_old_total += flist->used;
+
+ if (numeric_ids <= 0 && !inc_recurse)
+ send_id_lists(f);
+
+ /* send the io_error flag */
+ if (protocol_version < 30)
+ write_int(f, ignore_errors ? 0 : io_error);
+ else if (!use_safe_inc_flist && io_error && !ignore_errors)
+ send_msg_int(MSG_IO_ERROR, io_error);
+
+ if (disable_buffering)
+ io_end_buffering_out(IOBUF_FREE_BUFS);
+
+ stats.flist_size = stats.total_written - start_write;
+ stats.num_files = flist->used;
+
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(flist);
+
+ if (DEBUG_GTE(FLIST, 2))
+ rprintf(FINFO, "send_file_list done\n");
+
+ if (inc_recurse) {
+ send_dir_depth = 1;
+ add_dirs_to_tree(-1, flist, stats.num_dirs);
+ if (!file_total || strcmp(flist->sorted[flist->low]->basename, ".") != 0)
+ flist->parent_ndx = -1;
+ flist_done_allocating(flist);
+ if (send_dir_ndx < 0) {
+ write_ndx(f, NDX_FLIST_EOF);
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ }
+ else if (file_total == 1) {
+ /* If we're creating incremental file-lists and there
+ * was just 1 item in the first file-list, send 1 more
+ * file-list to check if this is a 1-file xfer. */
+ send_extra_file_list(f, 1);
+ }
+ } else {
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ }
+
+ return flist;
+}
+
+struct file_list *recv_file_list(int f, int dir_ndx)
+{
+ const char *good_dirname = NULL;
+ struct file_list *flist;
+ int dstart, flags;
+ int64 start_read;
+
+ if (!first_flist) {
+ if (show_filelist_progress)
+ start_filelist_progress("receiving file list");
+ else if (inc_recurse && INFO_GTE(FLIST, 1) && !am_server)
+ rprintf(FCLIENT, "receiving incremental file list\n");
+ rprintf(FLOG, "receiving file list\n");
+ if (usermap)
+ parse_name_map(usermap, True);
+ if (groupmap)
+ parse_name_map(groupmap, False);
+ }
+
+ start_read = stats.total_read;
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && !first_flist)
+ init_hard_links();
+#endif
+
+ flist = flist_new(0, "recv_file_list");
+ flist_expand(flist, FLIST_START_LARGE);
+
+ if (inc_recurse) {
+ if (flist->ndx_start == 1) {
+ dir_flist = flist_new(FLIST_TEMP, "recv_file_list");
+ flist_expand(dir_flist, FLIST_START_LARGE);
+ }
+ dstart = dir_flist->used;
+ } else {
+ dir_flist = flist;
+ dstart = 0;
+ }
+
+ while (1) {
+ struct file_struct *file;
+
+ if (xfer_flags_as_varint) {
+ if ((flags = read_varint(f)) == 0) {
+ int err = read_varint(f);
+ if (!ignore_errors)
+ io_error |= err;
+ break;
+ }
+ } else {
+ if ((flags = read_byte(f)) == 0)
+ break;
+
+ if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
+ flags |= read_byte(f) << 8;
+
+ if (flags == (XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST)) {
+ int err;
+ if (!use_safe_inc_flist) {
+ rprintf(FERROR, "Invalid flist flag: %x\n", flags);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ err = read_varint(f);
+ if (!ignore_errors)
+ io_error |= err;
+ break;
+ }
+ }
+
+ flist_expand(flist, 1);
+ file = recv_file_entry(f, flist, flags);
+
+ if (inc_recurse) {
+ static const char empty_dir[] = "\0";
+ const char *cur_dir = file->dirname ? file->dirname : empty_dir;
+ if (relative_paths && *cur_dir == '/')
+ cur_dir++;
+ if (cur_dir != good_dirname) {
+ const char *d = dir_ndx >= 0 ? f_name(dir_flist->files[dir_ndx], NULL) : empty_dir;
+ if (strcmp(cur_dir, d) != 0) {
+ rprintf(FERROR,
+ "ABORTING due to invalid path from sender: %s/%s\n",
+ cur_dir, file->basename);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ good_dirname = cur_dir;
+ }
+ }
+
+ if (S_ISREG(file->mode)) {
+ /* Already counted */
+ } else if (S_ISDIR(file->mode)) {
+ if (inc_recurse) {
+ flist_expand(dir_flist, 1);
+ dir_flist->files[dir_flist->used++] = file;
+ }
+ stats.num_dirs++;
+ } else if (S_ISLNK(file->mode))
+ stats.num_symlinks++;
+ else if (IS_DEVICE(file->mode))
+ stats.num_symlinks++;
+ else
+ stats.num_specials++;
+
+ flist->files[flist->used++] = file;
+
+ maybe_emit_filelist_progress(flist->used);
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ char *name = f_name(file, NULL);
+ rprintf(FINFO, "recv_file_name(%s)\n", NS(name));
+ }
+ }
+ file_total += flist->used;
+
+ if (DEBUG_GTE(FLIST, 2))
+ rprintf(FINFO, "received %d names\n", flist->used);
+
+ if (show_filelist_progress)
+ finish_filelist_progress(flist);
+
+ if (need_unsorted_flist) {
+ /* Create an extra array of index pointers that we can sort for
+ * the generator's use (for wading through the files in sorted
+ * order and for calling flist_find()). We keep the "files"
+ * list unsorted for our exchange of index numbers with the
+ * other side (since their names may not sort the same). */
+ flist->sorted = new_array(struct file_struct *, flist->used);
+ memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
+ if (inc_recurse && dir_flist->used > dstart) {
+ static int dir_flist_malloced = 0;
+ if (dir_flist_malloced < dir_flist->malloced) {
+ dir_flist->sorted = realloc_array(dir_flist->sorted,
+ struct file_struct *,
+ dir_flist->malloced);
+ dir_flist_malloced = dir_flist->malloced;
+ }
+ memcpy(dir_flist->sorted + dstart, dir_flist->files + dstart,
+ (dir_flist->used - dstart) * PTR_SIZE);
+ fsort(dir_flist->sorted + dstart, dir_flist->used - dstart);
+ }
+ } else {
+ flist->sorted = flist->files;
+ if (inc_recurse && dir_flist->used > dstart) {
+ dir_flist->sorted = dir_flist->files;
+ fsort(dir_flist->sorted + dstart, dir_flist->used - dstart);
+ }
+ }
+
+ if (inc_recurse)
+ flist_done_allocating(flist);
+ else if (f >= 0) {
+ recv_id_list(f, flist);
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ }
+
+ /* The --relative option sends paths with a leading slash, so we need
+ * to specify the strip_root option here. We rejected leading slashes
+ * for a non-relative transfer in recv_file_entry(). */
+ flist_sort_and_clean(flist, relative_paths);
+
+ if (protocol_version < 30) {
+ /* Recv the io_error flag */
+ int err = read_int(f);
+ if (!ignore_errors)
+ io_error |= err;
+ } else if (inc_recurse && flist->ndx_start == 1) {
+ if (!file_total || strcmp(flist->sorted[flist->low]->basename, ".") != 0)
+ flist->parent_ndx = -1;
+ }
+
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(flist);
+
+ if (DEBUG_GTE(FLIST, 2))
+ rprintf(FINFO, "recv_file_list done\n");
+
+ stats.flist_size += stats.total_read - start_read;
+ stats.num_files += flist->used;
+
+ return flist;
+}
+
+/* This is only used once by the receiver if the very first file-list
+ * has exactly one item in it. */
+void recv_additional_file_list(int f)
+{
+ struct file_list *flist;
+ int ndx = read_ndx(f);
+ if (ndx == NDX_FLIST_EOF) {
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ change_local_filter_dir(NULL, 0, 0);
+ } else {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ if (ndx < 0 || ndx >= dir_flist->used) {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ rprintf(FERROR,
+ "[%s] Invalid dir index: %d (%d - %d)\n",
+ who_am_i(), ndx, NDX_FLIST_OFFSET,
+ NDX_FLIST_OFFSET - dir_flist->used + 1);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (DEBUG_GTE(FLIST, 3)) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ flist = recv_file_list(f, ndx);
+ flist->parent_ndx = ndx;
+ }
+}
+
+/* Search for an identically-named item in the file list. Note that the
+ * items must agree in their directory-ness, or no match is returned. */
+int flist_find(struct file_list *flist, struct file_struct *f)
+{
+ int low = flist->low, high = flist->high;
+ int diff, mid, mid_up;
+
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (F_IS_ACTIVE(flist->sorted[mid]))
+ mid_up = mid;
+ else {
+ /* Scan for the next non-empty entry using the cached
+ * distance values. If the value isn't fully up-to-
+ * date, update it. */
+ mid_up = mid + F_DEPTH(flist->sorted[mid]);
+ if (!F_IS_ACTIVE(flist->sorted[mid_up])) {
+ do {
+ mid_up += F_DEPTH(flist->sorted[mid_up]);
+ } while (!F_IS_ACTIVE(flist->sorted[mid_up]));
+ F_DEPTH(flist->sorted[mid]) = mid_up - mid;
+ }
+ if (mid_up > high) {
+ /* If there's nothing left above us, set high to
+ * a non-empty entry below us and continue. */
+ high = mid - (int)flist->sorted[mid]->len32;
+ if (!F_IS_ACTIVE(flist->sorted[high])) {
+ do {
+ high -= (int)flist->sorted[high]->len32;
+ } while (!F_IS_ACTIVE(flist->sorted[high]));
+ flist->sorted[mid]->len32 = mid - high;
+ }
+ continue;
+ }
+ }
+ diff = f_name_cmp(flist->sorted[mid_up], f);
+ if (diff == 0) {
+ if (protocol_version < 29
+ && S_ISDIR(flist->sorted[mid_up]->mode)
+ != S_ISDIR(f->mode))
+ return -1;
+ return mid_up;
+ }
+ if (diff < 0)
+ low = mid_up + 1;
+ else
+ high = mid - 1;
+ }
+ return -1;
+}
+
+/* Search for a name in the file list. You must specify want_dir_match as:
+ * 1=match directories, 0=match non-directories, or -1=match either. */
+int flist_find_name(struct file_list *flist, const char *fname, int want_dir_match)
+{
+ static struct file_struct *f;
+ char fbuf[MAXPATHLEN];
+ const char *slash = strrchr(fname, '/');
+ const char *basename = slash ? slash+1 : fname;
+
+ if (!f)
+ f = (struct file_struct*)new_array(char, FILE_STRUCT_LEN + MAXPATHLEN + 1);
+
+ memset(f, 0, FILE_STRUCT_LEN);
+ memcpy((void*)f->basename, basename, strlen(basename)+1);
+
+ if (slash) {
+ strlcpy(fbuf, fname, slash - fname + 1);
+ f->dirname = fbuf;
+ } else
+ f->dirname = NULL;
+
+ f->mode = want_dir_match > 0 ? S_IFDIR : S_IFREG;
+
+ if (want_dir_match < 0)
+ return flist_find_ignore_dirness(flist, f);
+ return flist_find(flist, f);
+}
+
+/* Search for an identically-named item in the file list. Differs from
+ * flist_find in that an item that agrees with "f" in directory-ness is
+ * preferred but one that does not is still found. */
+int flist_find_ignore_dirness(struct file_list *flist, struct file_struct *f)
+{
+ mode_t save_mode;
+ int ndx;
+
+ /* First look for an item that agrees in directory-ness. */
+ ndx = flist_find(flist, f);
+ if (ndx >= 0)
+ return ndx;
+
+ /* Temporarily flip f->mode to look for an item of opposite
+ * directory-ness. */
+ save_mode = f->mode;
+ f->mode = S_ISDIR(f->mode) ? S_IFREG : S_IFDIR;
+ ndx = flist_find(flist, f);
+ f->mode = save_mode;
+ return ndx;
+}
+
+/*
+ * Free up any resources a file_struct has allocated
+ * and clear the file.
+ */
+void clear_file(struct file_struct *file)
+{
+ /* The +1 zeros out the first char of the basename. */
+ memset(file, 0, FILE_STRUCT_LEN + 1);
+ /* In an empty entry, F_DEPTH() is an offset to the next non-empty
+ * entry. Likewise for len32 in the opposite direction. We assume
+ * that we're alone for now since flist_find() will adjust the counts
+ * it runs into that aren't up-to-date. */
+ file->len32 = F_DEPTH(file) = 1;
+}
+
+/* Allocate a new file list. */
+static struct file_list *flist_new(int flags, const char *msg)
+{
+ struct file_list *flist;
+
+ flist = new0(struct file_list);
+
+ if (flags & FLIST_TEMP) {
+ if (!(flist->file_pool = pool_create(SMALL_EXTENT, 0, _out_of_memory, POOL_INTERN)))
+ out_of_memory(msg);
+ } else {
+ /* This is a doubly linked list with prev looping back to
+ * the end of the list, but the last next pointer is NULL. */
+ if (!first_flist) {
+ if (!(flist->file_pool = pool_create(NORMAL_EXTENT, 0, _out_of_memory, POOL_INTERN)))
+ out_of_memory(msg);
+
+ flist->ndx_start = flist->flist_num = inc_recurse ? 1 : 0;
+
+ first_flist = cur_flist = flist->prev = flist;
+ } else {
+ struct file_list *prev = first_flist->prev;
+
+ flist->file_pool = first_flist->file_pool;
+
+ flist->ndx_start = prev->ndx_start + prev->used + 1;
+ flist->flist_num = prev->flist_num + 1;
+
+ flist->prev = prev;
+ prev->next = first_flist->prev = flist;
+ }
+ flist->pool_boundary = pool_boundary(flist->file_pool, 0);
+ flist_cnt++;
+ }
+
+ return flist;
+}
+
+/* Free up all elements in a flist. */
+void flist_free(struct file_list *flist)
+{
+ if (!flist->prev) {
+ /* Was FLIST_TEMP dir-list. */
+ } else if (flist == flist->prev) {
+ first_flist = cur_flist = NULL;
+ file_total = 0;
+ flist_cnt = 0;
+ } else {
+ if (flist == cur_flist)
+ cur_flist = flist->next;
+ if (flist == first_flist)
+ first_flist = first_flist->next;
+ else {
+ flist->prev->next = flist->next;
+ if (!flist->next)
+ flist->next = first_flist;
+ }
+ flist->next->prev = flist->prev;
+ file_total -= flist->used;
+ flist_cnt--;
+ }
+
+ if (!flist->prev || !flist_cnt)
+ pool_destroy(flist->file_pool);
+ else
+ pool_free_old(flist->file_pool, flist->pool_boundary);
+
+ if (flist->sorted && flist->sorted != flist->files)
+ free(flist->sorted);
+ free(flist->files);
+ free(flist);
+}
+
+/* This routine ensures we don't have any duplicate names in our file list.
+ * duplicate names can cause corruption because of the pipelining. */
+static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+{
+ char fbuf[MAXPATHLEN];
+ int i, prev_i;
+
+ if (!flist)
+ return;
+ if (flist->used == 0) {
+ flist->high = -1;
+ flist->low = 0;
+ return;
+ }
+
+ fsort(flist->sorted, flist->used);
+
+ if (!am_sender || inc_recurse) {
+ for (i = prev_i = 0; i < flist->used; i++) {
+ if (F_IS_ACTIVE(flist->sorted[i])) {
+ prev_i = i;
+ break;
+ }
+ }
+ flist->low = prev_i;
+ } else {
+ i = prev_i = flist->used - 1;
+ flist->low = 0;
+ }
+
+ while (++i < flist->used) {
+ int j;
+ struct file_struct *file = flist->sorted[i];
+
+ if (!F_IS_ACTIVE(file))
+ continue;
+ if (f_name_cmp(file, flist->sorted[prev_i]) == 0)
+ j = prev_i;
+ else if (protocol_version >= 29 && S_ISDIR(file->mode)) {
+ int save_mode = file->mode;
+ /* Make sure that this directory doesn't duplicate a
+ * non-directory earlier in the list. */
+ flist->high = prev_i;
+ file->mode = S_IFREG;
+ j = flist_find(flist, file);
+ file->mode = save_mode;
+ } else
+ j = -1;
+ if (j >= 0) {
+ int keep, drop;
+ /* If one is a dir and the other is not, we want to
+ * keep the dir because it might have contents in the
+ * list. Otherwise keep the first one. */
+ if (S_ISDIR(file->mode)) {
+ struct file_struct *fp = flist->sorted[j];
+ if (!S_ISDIR(fp->mode))
+ keep = i, drop = j;
+ else {
+ if (am_sender)
+ file->flags |= FLAG_DUPLICATE;
+ else { /* Make sure we merge our vital flags. */
+ fp->flags |= file->flags & (FLAG_TOP_DIR|FLAG_CONTENT_DIR);
+ fp->flags &= file->flags | ~FLAG_IMPLIED_DIR;
+ }
+ keep = j, drop = i;
+ }
+ } else
+ keep = j, drop = i;
+
+ if (!am_sender) {
+ if (DEBUG_GTE(DUP, 1)) {
+ rprintf(FINFO,
+ "removing duplicate name %s from file list (%d)\n",
+ f_name(file, fbuf), drop + flist->ndx_start);
+ }
+ clear_file(flist->sorted[drop]);
+ }
+
+ if (keep == i) {
+ if (flist->low == drop) {
+ for (j = drop + 1;
+ j < i && !F_IS_ACTIVE(flist->sorted[j]);
+ j++) {}
+ flist->low = j;
+ }
+ prev_i = i;
+ }
+ } else
+ prev_i = i;
+ }
+ flist->high = prev_i;
+
+ if (strip_root) {
+ /* We need to strip off the leading slashes for relative
+ * paths, but this must be done _after_ the sorting phase. */
+ for (i = flist->low; i <= flist->high; i++) {
+ struct file_struct *file = flist->sorted[i];
+
+ if (!file->dirname)
+ continue;
+ while (*file->dirname == '/')
+ file->dirname++;
+ if (!*file->dirname)
+ file->dirname = NULL;
+ }
+ }
+
+ if (prune_empty_dirs && !am_sender) {
+ int j, prev_depth = 0;
+
+ prev_i = 0; /* It's OK that this isn't really true. */
+
+ for (i = flist->low; i <= flist->high; i++) {
+ struct file_struct *fp, *file = flist->sorted[i];
+
+ /* This temporarily abuses the F_DEPTH() value for a
+ * directory that is in a chain that might get pruned.
+ * We restore the old value if it gets a reprieve. */
+ if (S_ISDIR(file->mode) && F_DEPTH(file)) {
+ /* Dump empty dirs when coming back down. */
+ for (j = prev_depth; j >= F_DEPTH(file); j--) {
+ fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ clear_file(fp);
+ }
+ prev_depth = F_DEPTH(file);
+ if (is_excluded(f_name(file, fbuf), 1, ALL_FILTERS)) {
+ /* Keep dirs through this dir. */
+ for (j = prev_depth-1; ; j--) {
+ fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ F_DEPTH(fp) = j;
+ }
+ } else
+ F_DEPTH(file) = -prev_i-1;
+ prev_i = i;
+ } else {
+ /* Keep dirs through this non-dir. */
+ for (j = prev_depth; ; j--) {
+ fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ F_DEPTH(fp) = j;
+ }
+ }
+ }
+ /* Dump all remaining empty dirs. */
+ while (1) {
+ struct file_struct *fp = flist->sorted[prev_i];
+ if (F_DEPTH(fp) >= 0)
+ break;
+ prev_i = -F_DEPTH(fp)-1;
+ clear_file(fp);
+ }
+
+ for (i = flist->low; i <= flist->high; i++) {
+ if (F_IS_ACTIVE(flist->sorted[i]))
+ break;
+ }
+ flist->low = i;
+ for (i = flist->high; i >= flist->low; i--) {
+ if (F_IS_ACTIVE(flist->sorted[i]))
+ break;
+ }
+ flist->high = i;
+ }
+}
+
+static void output_flist(struct file_list *flist)
+{
+ char uidbuf[16], gidbuf[16], depthbuf[16];
+ struct file_struct *file;
+ const char *root, *dir, *slash, *name, *trail;
+ const char *who = who_am_i();
+ int i;
+
+ rprintf(FINFO, "[%s] flist start=%d, used=%d, low=%d, high=%d\n",
+ who, flist->ndx_start, flist->used, flist->low, flist->high);
+ for (i = 0; i < flist->used; i++) {
+ file = flist->files[i];
+ if ((am_root || am_sender) && uid_ndx) {
+ snprintf(uidbuf, sizeof uidbuf, " uid=%u",
+ F_OWNER(file));
+ } else
+ *uidbuf = '\0';
+ if (gid_ndx) {
+ static char parens[] = "(\0)\0\0\0";
+ char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
+ snprintf(gidbuf, sizeof gidbuf, " gid=%s%u%s",
+ pp, F_GROUP(file), pp + 2);
+ } else
+ *gidbuf = '\0';
+ if (!am_sender)
+ snprintf(depthbuf, sizeof depthbuf, "%d", F_DEPTH(file));
+ if (F_IS_ACTIVE(file)) {
+ root = am_sender ? NS(F_PATHNAME(file)) : depthbuf;
+ if ((dir = file->dirname) == NULL)
+ dir = slash = "";
+ else
+ slash = "/";
+ name = file->basename;
+ trail = S_ISDIR(file->mode) ? "/" : "";
+ } else
+ root = dir = slash = name = trail = "";
+ rprintf(FINFO,
+ "[%s] i=%d %s %s%s%s%s mode=0%o len=%s%s%s flags=%x\n",
+ who, i + flist->ndx_start,
+ root, dir, slash, name, trail,
+ (int)file->mode, comma_num(F_LENGTH(file)),
+ uidbuf, gidbuf, file->flags);
+ }
+}
+
+enum fnc_state { s_DIR, s_SLASH, s_BASE, s_TRAILING };
+enum fnc_type { t_PATH, t_ITEM };
+
+static int found_prefix;
+
+/* Compare the names of two file_struct entities, similar to how strcmp()
+ * would do if it were operating on the joined strings.
+ *
+ * Some differences beginning with protocol_version 29: (1) directory names
+ * are compared with an assumed trailing slash so that they compare in a
+ * way that would cause them to sort immediately prior to any content they
+ * may have; (2) a directory of any name compares after a non-directory of
+ * any name at the same depth; (3) a directory with name "." compares prior
+ * to anything else. These changes mean that a directory and a non-dir
+ * with the same name will not compare as equal (protocol_version >= 29).
+ *
+ * The dirname component can be an empty string, but the basename component
+ * cannot (and never is in the current codebase). The basename component
+ * may be NULL (for a removed item), in which case it is considered to be
+ * after any existing item. */
+int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
+{
+ int dif;
+ const uchar *c1, *c2;
+ enum fnc_state state1, state2;
+ enum fnc_type type1, type2;
+ enum fnc_type t_path = protocol_version >= 29 ? t_PATH : t_ITEM;
+
+ if (!f1 || !F_IS_ACTIVE(f1)) {
+ if (!f2 || !F_IS_ACTIVE(f2))
+ return 0;
+ return -1;
+ }
+ if (!f2 || !F_IS_ACTIVE(f2))
+ return 1;
+
+ c1 = (uchar*)f1->dirname;
+ c2 = (uchar*)f2->dirname;
+ if (c1 == c2)
+ c1 = c2 = NULL;
+ if (!c1) {
+ type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
+ c1 = (const uchar*)f1->basename;
+ if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
+ type1 = t_ITEM;
+ state1 = s_TRAILING;
+ c1 = (uchar*)"";
+ } else
+ state1 = s_BASE;
+ } else {
+ type1 = t_path;
+ state1 = s_DIR;
+ }
+ if (!c2) {
+ type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
+ c2 = (const uchar*)f2->basename;
+ if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
+ type2 = t_ITEM;
+ state2 = s_TRAILING;
+ c2 = (uchar*)"";
+ } else
+ state2 = s_BASE;
+ } else {
+ type2 = t_path;
+ state2 = s_DIR;
+ }
+
+ if (type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+
+ do {
+ if (!*c1) {
+ switch (state1) {
+ case s_DIR:
+ state1 = s_SLASH;
+ c1 = (uchar*)"/";
+ break;
+ case s_SLASH:
+ type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
+ c1 = (const uchar*)f1->basename;
+ if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
+ type1 = t_ITEM;
+ state1 = s_TRAILING;
+ c1 = (uchar*)"";
+ } else
+ state1 = s_BASE;
+ break;
+ case s_BASE:
+ state1 = s_TRAILING;
+ if (type1 == t_PATH) {
+ c1 = (uchar*)"/";
+ break;
+ }
+ /* FALL THROUGH */
+ case s_TRAILING:
+ type1 = t_ITEM;
+ break;
+ }
+ if (*c2 && type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+ }
+ if (!*c2) {
+ switch (state2) {
+ case s_DIR:
+ state2 = s_SLASH;
+ c2 = (uchar*)"/";
+ break;
+ case s_SLASH:
+ type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
+ c2 = (const uchar*)f2->basename;
+ if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
+ type2 = t_ITEM;
+ state2 = s_TRAILING;
+ c2 = (uchar*)"";
+ } else
+ state2 = s_BASE;
+ break;
+ case s_BASE:
+ state2 = s_TRAILING;
+ if (type2 == t_PATH) {
+ c2 = (uchar*)"/";
+ break;
+ }
+ /* FALL THROUGH */
+ case s_TRAILING:
+ found_prefix = 1;
+ if (!*c1)
+ return 0;
+ type2 = t_ITEM;
+ break;
+ }
+ if (type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+ }
+ } while ((dif = (int)*c1++ - (int)*c2++) == 0);
+
+ return dif;
+}
+
+/* Returns 1 if f1's filename has all of f2's filename as a prefix. This does
+ * not match if f2's basename is not an exact match of a path element in f1.
+ * E.g. /path/foo is not a prefix of /path/foobar/baz, but /path/foobar is. */
+int f_name_has_prefix(const struct file_struct *f1, const struct file_struct *f2)
+{
+ found_prefix = 0;
+ f_name_cmp(f1, f2);
+ return found_prefix;
+}
+
+char *f_name_buf(void)
+{
+ static char names[5][MAXPATHLEN];
+ static unsigned int n;
+
+ n = (n + 1) % (sizeof names / sizeof names[0]);
+
+ return names[n];
+}
+
+/* Return a copy of the full filename of a flist entry, using the indicated
+ * buffer or one of 5 static buffers if fbuf is NULL. No size-checking is
+ * done because we checked the size when creating the file_struct entry.
+ */
+char *f_name(const struct file_struct *f, char *fbuf)
+{
+ if (!f || !F_IS_ACTIVE(f))
+ return NULL;
+
+ if (!fbuf)
+ fbuf = f_name_buf();
+
+ if (f->dirname) {
+ int len = strlen(f->dirname);
+ memcpy(fbuf, f->dirname, len);
+ fbuf[len] = '/';
+ strlcpy(fbuf + len + 1, f->basename, MAXPATHLEN - (len + 1));
+ } else
+ strlcpy(fbuf, f->basename, MAXPATHLEN);
+
+ return fbuf;
+}
+
+/* Do a non-recursive scan of the named directory, possibly ignoring all
+ * exclude rules except for the daemon's. If "dlen" is >=0, it is the length
+ * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN
+ * buffer (the functions we call will append names onto the end, but the old
+ * dir value will be restored on exit). */
+struct file_list *get_dirlist(char *dirname, int dlen, int flags)
+{
+ struct file_list *dirlist;
+ char dirbuf[MAXPATHLEN];
+ int save_recurse = recurse;
+ int save_xfer_dirs = xfer_dirs;
+ int save_prune_empty_dirs = prune_empty_dirs;
+ int senddir_fd = flags & GDL_IGNORE_FILTER_RULES ? -2 : -1;
+ int senddir_flags = FLAG_CONTENT_DIR;
+
+ if (dlen < 0) {
+ dlen = strlcpy(dirbuf, dirname, MAXPATHLEN);
+ if (dlen >= MAXPATHLEN)
+ return NULL;
+ dirname = dirbuf;
+ }
+
+ dirlist = flist_new(FLIST_TEMP, "get_dirlist");
+
+ if (flags & GDL_PERHAPS_DIR)
+ senddir_flags |= FLAG_PERHAPS_DIR;
+
+ recurse = 0;
+ xfer_dirs = 1;
+ send_directory(senddir_fd, dirlist, dirname, dlen, senddir_flags);
+ xfer_dirs = save_xfer_dirs;
+ recurse = save_recurse;
+ if (INFO_GTE(PROGRESS, 1))
+ flist_count_offset += dirlist->used;
+
+ prune_empty_dirs = 0;
+ dirlist->sorted = dirlist->files;
+ flist_sort_and_clean(dirlist, 0);
+ prune_empty_dirs = save_prune_empty_dirs;
+
+ if (DEBUG_GTE(FLIST, 3))
+ output_flist(dirlist);
+
+ return dirlist;
+}
diff --git a/generator.c b/generator.c
new file mode 100644
index 0000000..21c4a59
--- /dev/null
+++ b/generator.c
@@ -0,0 +1,2434 @@
+/*
+ * Routines that are exclusive to the generator process.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+#include "ifuncs.h"
+
+extern int dry_run;
+extern int do_xfers;
+extern int stdout_format_has_i;
+extern int logfile_format_has_i;
+extern int am_root;
+extern int am_server;
+extern int am_daemon;
+extern int inc_recurse;
+extern int relative_paths;
+extern int implied_dirs;
+extern int keep_dirlinks;
+extern int write_devices;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_links;
+extern int preserve_devices;
+extern int preserve_specials;
+extern int preserve_hard_links;
+extern int preserve_executability;
+extern int preserve_perms;
+extern int preserve_mtimes;
+extern int omit_dir_times;
+extern int omit_link_times;
+extern int delete_mode;
+extern int delete_before;
+extern int delete_during;
+extern int delete_after;
+extern int missing_args;
+extern int msgdone_cnt;
+extern int ignore_errors;
+extern int remove_source_files;
+extern int delay_updates;
+extern int update_only;
+extern int human_readable;
+extern int ignore_existing;
+extern int ignore_non_existing;
+extern int want_xattr_optim;
+extern int modify_window;
+extern int inplace;
+extern int append_mode;
+extern int make_backups;
+extern int csum_length;
+extern int ignore_times;
+extern int size_only;
+extern OFF_T max_size;
+extern OFF_T min_size;
+extern int io_error;
+extern int flist_eof;
+extern int allowed_lull;
+extern int sock_f_out;
+extern int protocol_version;
+extern int file_total;
+extern int fuzzy_basis;
+extern int always_checksum;
+extern int flist_csum_len;
+extern char *partial_dir;
+extern int alt_dest_type;
+extern int whole_file;
+extern int list_only;
+extern int read_batch;
+extern int write_batch;
+extern int safe_symlinks;
+extern int32 block_size;
+extern int unsort_ndx;
+extern int max_delete;
+extern int force_delete;
+extern int one_file_system;
+extern int skipped_deletes;
+extern dev_t filesystem_dev;
+extern mode_t orig_umask;
+extern uid_t our_uid;
+extern char *tmpdir;
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern filter_rule_list filter_list, daemon_filter_list;
+
+int maybe_ATTRS_REPORT = 0;
+int maybe_ATTRS_ACCURATE_TIME = 0;
+
+static dev_t dev_zero;
+static int deldelay_size = 0, deldelay_cnt = 0;
+static char *deldelay_buf = NULL;
+static int deldelay_fd = -1;
+static int loopchk_limit;
+static int dir_tweaking;
+static int symlink_timeset_failed_flags;
+static int need_retouch_dir_times;
+static int need_retouch_dir_perms;
+static const char *solo_file = NULL;
+
+/* Forward declarations. */
+#ifdef SUPPORT_HARD_LINKS
+static void handle_skipped_hlink(struct file_struct *file, int itemizing,
+ enum logcode code, int f_out);
+#endif
+
+#define EARLY_DELAY_DONE_MSG() (!delay_updates)
+#define EARLY_DELETE_DONE_MSG() (!(delete_during == 2 || delete_after))
+
+static int start_delete_delay_temp(void)
+{
+ char fnametmp[MAXPATHLEN];
+ int save_dry_run = dry_run;
+
+ dry_run = 0;
+ if (!get_tmpname(fnametmp, "deldelay", False)
+ || (deldelay_fd = do_mkstemp(fnametmp, 0600)) < 0) {
+ rprintf(FINFO, "NOTE: Unable to create delete-delay temp file%s.\n",
+ inc_recurse ? "" : " -- switching to --delete-after");
+ delete_during = 0;
+ delete_after = !inc_recurse;
+ dry_run = save_dry_run;
+ return 0;
+ }
+ unlink(fnametmp);
+ dry_run = save_dry_run;
+ return 1;
+}
+
+static int flush_delete_delay(void)
+{
+ if (deldelay_fd < 0 && !start_delete_delay_temp())
+ return 0;
+ if (write(deldelay_fd, deldelay_buf, deldelay_cnt) != deldelay_cnt) {
+ rsyserr(FERROR, errno, "flush of delete-delay buffer");
+ delete_during = 0;
+ delete_after = !inc_recurse;
+ close(deldelay_fd);
+ return 0;
+ }
+ deldelay_cnt = 0;
+ return 1;
+}
+
+static int remember_delete(struct file_struct *file, const char *fname, int flags)
+{
+ int len;
+
+ if (deldelay_cnt == deldelay_size && !flush_delete_delay())
+ return 0;
+
+ if (flags & DEL_NO_UID_WRITE)
+ deldelay_buf[deldelay_cnt++] = '!';
+
+ while (1) {
+ len = snprintf(deldelay_buf + deldelay_cnt, deldelay_size - deldelay_cnt,
+ "%x %s%c", (int)file->mode, fname, '\0');
+ if ((deldelay_cnt += len) <= deldelay_size)
+ break;
+ deldelay_cnt -= len;
+ if (!flush_delete_delay())
+ return 0;
+ }
+
+ return 1;
+}
+
+static int read_delay_line(char *buf, int *flags_p)
+{
+ static int read_pos = 0;
+ unsigned int mode;
+ int j, len;
+ char *bp, *past_space;
+
+ while (1) {
+ for (j = read_pos; j < deldelay_cnt && deldelay_buf[j]; j++) {}
+ if (j < deldelay_cnt)
+ break;
+ if (deldelay_fd < 0) {
+ if (j > read_pos)
+ goto invalid_data;
+ return -1;
+ }
+ deldelay_cnt -= read_pos;
+ if (deldelay_cnt == deldelay_size)
+ goto invalid_data;
+ if (deldelay_cnt && read_pos) {
+ memmove(deldelay_buf, deldelay_buf + read_pos,
+ deldelay_cnt);
+ }
+ len = read(deldelay_fd, deldelay_buf + deldelay_cnt,
+ deldelay_size - deldelay_cnt);
+ if (len == 0) {
+ if (deldelay_cnt) {
+ rprintf(FERROR, "ERROR: unexpected EOF in delete-delay file.\n");
+ }
+ return -1;
+ }
+ if (len < 0) {
+ rsyserr(FERROR, errno,
+ "reading delete-delay file");
+ return -1;
+ }
+ deldelay_cnt += len;
+ read_pos = 0;
+ }
+
+ bp = deldelay_buf + read_pos;
+ if (*bp == '!') {
+ bp++;
+ *flags_p = DEL_NO_UID_WRITE;
+ } else
+ *flags_p = 0;
+
+ if (sscanf(bp, "%x ", &mode) != 1) {
+ invalid_data:
+ rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
+ return -1;
+ }
+ past_space = strchr(bp, ' ') + 1;
+ len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
+ read_pos = j + 1;
+
+ if (len > MAXPATHLEN) {
+ rprintf(FERROR, "ERROR: filename too long in delete-delay file.\n");
+ return -1;
+ }
+
+ /* The caller needs the name in a MAXPATHLEN buffer, so we copy it
+ * instead of returning a pointer to our buffer. */
+ memcpy(buf, past_space, len);
+
+ return mode;
+}
+
+static void do_delayed_deletions(char *delbuf)
+{
+ int mode, flags;
+
+ if (deldelay_fd >= 0) {
+ if (deldelay_cnt && !flush_delete_delay())
+ return;
+ lseek(deldelay_fd, 0, 0);
+ }
+ while ((mode = read_delay_line(delbuf, &flags)) >= 0)
+ delete_item(delbuf, mode, flags | DEL_RECURSE);
+ if (deldelay_fd >= 0)
+ close(deldelay_fd);
+}
+
+/* This function is used to implement per-directory deletion, and is used by
+ * all the --delete-WHEN options. Note that the fbuf pointer must point to a
+ * MAXPATHLEN buffer with the name of the directory in it (the functions we
+ * call will append names onto the end, but the old dir value will be restored
+ * on exit). */
+static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t fs_dev)
+{
+ static int already_warned = 0;
+ static struct hashtable *dev_tbl;
+ struct file_list *dirlist;
+ char delbuf[MAXPATHLEN];
+ int dlen, i;
+
+ if (!fbuf) {
+ change_local_filter_dir(NULL, 0, 0);
+ return;
+ }
+
+ if (DEBUG_GTE(DEL, 2))
+ rprintf(FINFO, "delete_in_dir(%s)\n", fbuf);
+
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+
+ if (io_error & IOERR_GENERAL && !ignore_errors) {
+ if (already_warned)
+ return;
+ rprintf(FINFO,
+ "IO error encountered -- skipping file deletion\n");
+ already_warned = 1;
+ return;
+ }
+
+ dlen = strlen(fbuf);
+ change_local_filter_dir(fbuf, dlen, F_DEPTH(file));
+
+ if (one_file_system) {
+ if (!dev_tbl)
+ dev_tbl = hashtable_create(16, HT_KEY64);
+ if (file->flags & FLAG_TOP_DIR) {
+ hashtable_find(dev_tbl, fs_dev+1, "");
+ filesystem_dev = fs_dev;
+ } else if (filesystem_dev != fs_dev) {
+ if (!hashtable_find(dev_tbl, fs_dev+1, NULL))
+ return;
+ filesystem_dev = fs_dev; /* it's a prior top-dir dev */
+ }
+ }
+
+ dirlist = get_dirlist(fbuf, dlen, 0);
+
+ /* If an item in dirlist is not found in flist, delete it
+ * from the filesystem. */
+ for (i = dirlist->used; i--; ) {
+ struct file_struct *fp = dirlist->files[i];
+ if (!F_IS_ACTIVE(fp))
+ continue;
+ if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) {
+ if (INFO_GTE(MOUNT, 1))
+ rprintf(FINFO, "cannot delete mount point: %s\n",
+ f_name(fp, NULL));
+ continue;
+ }
+ /* Here we want to match regardless of file type. Replacement
+ * of a file with one of another type is handled separately by
+ * a delete_item call with a DEL_MAKE_ROOM flag. */
+ if (flist_find_ignore_dirness(cur_flist, fp) < 0) {
+ int flags = DEL_RECURSE;
+ if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
+ flags |= DEL_NO_UID_WRITE;
+ f_name(fp, delbuf);
+ if (delete_during == 2) {
+ if (!remember_delete(fp, delbuf, flags))
+ break;
+ } else
+ delete_item(delbuf, fp->mode, flags);
+ }
+ }
+
+ flist_free(dirlist);
+}
+
+/* This deletes any files on the receiving side that are not present on the
+ * sending side. This is used by --delete-before and --delete-after. */
+static void do_delete_pass(void)
+{
+ char fbuf[MAXPATHLEN];
+ STRUCT_STAT st;
+ int j;
+
+ /* dry_run is incremented when the destination doesn't exist yet. */
+ if (dry_run > 1 || list_only)
+ return;
+
+ for (j = 0; j < cur_flist->used; j++) {
+ struct file_struct *file = cur_flist->sorted[j];
+
+ if (!F_IS_ACTIVE(file))
+ continue;
+
+ f_name(file, fbuf);
+
+ if (!(file->flags & FLAG_CONTENT_DIR)) {
+ change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(file));
+ continue;
+ }
+
+ if (DEBUG_GTE(DEL, 1) && file->flags & FLAG_TOP_DIR)
+ rprintf(FINFO, "deleting in %s\n", fbuf);
+
+ if (link_stat(fbuf, &st, keep_dirlinks) < 0
+ || !S_ISDIR(st.st_mode))
+ continue;
+
+ delete_in_dir(fbuf, file, st.st_dev);
+ }
+ delete_in_dir(NULL, NULL, dev_zero);
+
+ if (INFO_GTE(FLIST, 2) && !am_server)
+ rprintf(FINFO, " \r");
+}
+
+static inline int mtime_differs(STRUCT_STAT *stp, struct file_struct *file)
+{
+#ifdef ST_MTIME_NSEC
+ return !same_time(stp->st_mtime, stp->ST_MTIME_NSEC, file->modtime, F_MOD_NSEC_or_0(file));
+#else
+ return !same_time(stp->st_mtime, 0, file->modtime, 0);
+#endif
+}
+
+static inline int any_time_differs(stat_x *sxp, struct file_struct *file, UNUSED(const char *fname))
+{
+ int differs = mtime_differs(&sxp->st, file);
+#ifdef SUPPORT_CRTIMES
+ if (!differs && crtimes_ndx) {
+ if (sxp->crtime == 0)
+ sxp->crtime = get_create_time(fname, &sxp->st);
+ differs = !same_time(sxp->crtime, 0, F_CRTIME(file), 0);
+ }
+#endif
+ return differs;
+}
+
+static inline int perms_differ(struct file_struct *file, stat_x *sxp)
+{
+ if (preserve_perms)
+ return !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS);
+
+ if (preserve_executability)
+ return (sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0);
+
+ return 0;
+}
+
+static inline int ownership_differs(struct file_struct *file, stat_x *sxp)
+{
+ if (am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file))
+ return 1;
+
+ if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
+ return 1;
+
+ return 0;
+}
+
+#ifdef SUPPORT_ACLS
+static inline int acls_differ(const char *fname, struct file_struct *file, stat_x *sxp)
+{
+ if (preserve_acls) {
+ if (!ACL_READY(*sxp))
+ get_acl(fname, sxp);
+ if (set_acl(NULL, file, sxp, file->mode))
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef SUPPORT_XATTRS
+static inline int xattrs_differ(const char *fname, struct file_struct *file, stat_x *sxp)
+{
+ if (preserve_xattrs) {
+ if (!XATTR_READY(*sxp))
+ get_xattr(fname, sxp);
+ if (xattr_diff(file, sxp, 0))
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
+{
+ if (S_ISLNK(file->mode)) {
+#ifdef CAN_SET_SYMLINK_TIMES
+ if (preserve_mtimes && !omit_link_times && any_time_differs(sxp, file, fname))
+ return 0;
+#endif
+#ifdef CAN_CHMOD_SYMLINK
+ if (perms_differ(file, sxp))
+ return 0;
+#endif
+#ifdef CAN_CHOWN_SYMLINK
+ if (ownership_differs(file, sxp))
+ return 0;
+#endif
+#if defined SUPPORT_ACLS && 0 /* no current symlink-ACL support */
+ if (acls_differ(fname, file, sxp))
+ return 0;
+#endif
+#if defined SUPPORT_XATTRS && !defined NO_SYMLINK_XATTRS
+ if (xattrs_differ(fname, file, sxp))
+ return 0;
+#endif
+ } else {
+ if (preserve_mtimes && any_time_differs(sxp, file, fname))
+ return 0;
+ if (perms_differ(file, sxp))
+ return 0;
+ if (ownership_differs(file, sxp))
+ return 0;
+#ifdef SUPPORT_ACLS
+ if (acls_differ(fname, file, sxp))
+ return 0;
+#endif
+#ifdef SUPPORT_XATTRS
+ if (xattrs_differ(fname, file, sxp))
+ return 0;
+#endif
+ }
+
+ return 1;
+}
+
+void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statret,
+ stat_x *sxp, int32 iflags, uchar fnamecmp_type,
+ const char *xname)
+{
+ if (statret >= 0) { /* A from-dest-dir statret can == 1! */
+ int keep_time = !preserve_mtimes ? 0
+ : S_ISDIR(file->mode) ? !omit_dir_times
+ : S_ISLNK(file->mode) ? !omit_link_times
+ : 1;
+
+ if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
+ iflags |= ITEM_REPORT_SIZE;
+ if (file->flags & FLAG_TIME_FAILED) { /* symlinks only */
+ if (iflags & ITEM_LOCAL_CHANGE)
+ iflags |= symlink_timeset_failed_flags;
+ } else if (keep_time
+ ? mtime_differs(&sxp->st, file)
+ : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
+ && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
+ iflags |= ITEM_REPORT_TIME;
+ if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
+ && !same_time(F_ATIME(file), 0, sxp->st.st_atime, 0))
+ iflags |= ITEM_REPORT_ATIME;
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx) {
+ if (sxp->crtime == 0)
+ sxp->crtime = get_create_time(fnamecmp, &sxp->st);
+ if (!same_time(sxp->crtime, 0, F_CRTIME(file), 0))
+ iflags |= ITEM_REPORT_CRTIME;
+ }
+#endif
+#ifndef CAN_CHMOD_SYMLINK
+ if (S_ISLNK(file->mode)) {
+ ;
+ } else
+#endif
+ if (preserve_perms) {
+ if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
+ iflags |= ITEM_REPORT_PERMS;
+ } else if (preserve_executability
+ && ((sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0)))
+ iflags |= ITEM_REPORT_PERMS;
+ if (uid_ndx && am_root && (uid_t)F_OWNER(file) != sxp->st.st_uid)
+ iflags |= ITEM_REPORT_OWNER;
+ if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
+ iflags |= ITEM_REPORT_GROUP;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ if (!ACL_READY(*sxp))
+ get_acl(fnamecmp, sxp);
+ if (set_acl(NULL, file, sxp, file->mode))
+ iflags |= ITEM_REPORT_ACL;
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ if (!XATTR_READY(*sxp))
+ get_xattr(fnamecmp, sxp);
+ if (xattr_diff(file, sxp, 1))
+ iflags |= ITEM_REPORT_XATTR;
+ }
+#endif
+ } else {
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && xattr_diff(file, NULL, 1))
+ iflags |= ITEM_REPORT_XATTR;
+#endif
+ iflags |= ITEM_IS_NEW;
+ }
+
+ iflags &= 0xffff;
+ if ((iflags & (SIGNIFICANT_ITEM_FLAGS|ITEM_REPORT_XATTR) || INFO_GTE(NAME, 2)
+ || stdout_format_has_i > 1 || (xname && *xname)) && !read_batch) {
+ if (protocol_version >= 29) {
+ if (ndx >= 0)
+ write_ndx(sock_f_out, ndx);
+ write_shortint(sock_f_out, iflags);
+ if (iflags & ITEM_BASIS_TYPE_FOLLOWS)
+ write_byte(sock_f_out, fnamecmp_type);
+ if (iflags & ITEM_XNAME_FOLLOWS)
+ write_vstring(sock_f_out, xname, strlen(xname));
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && do_xfers
+ && iflags & (ITEM_REPORT_XATTR|ITEM_TRANSFER)) {
+ int fd = iflags & ITEM_REPORT_XATTR
+ && !(want_xattr_optim && BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE))
+ ? sock_f_out : -1;
+ send_xattr_request(NULL, file, fd);
+ }
+#endif
+ } else if (ndx >= 0) {
+ enum logcode code = logfile_format_has_i ? FINFO : FCLIENT;
+ log_item(code, file, iflags, xname);
+ }
+ }
+}
+
+static enum filetype get_file_type(mode_t mode)
+{
+ if (S_ISREG(mode))
+ return FT_REG;
+ if (S_ISLNK(mode))
+ return FT_SYMLINK;
+ if (S_ISDIR(mode))
+ return FT_DIR;
+ if (IS_SPECIAL(mode))
+ return FT_SPECIAL;
+ if (IS_DEVICE(mode))
+ return FT_DEVICE;
+ return FT_UNSUPPORTED;
+}
+
+/* Perform our quick-check heuristic for determining if a file is unchanged. */
+int quick_check_ok(enum filetype ftype, const char *fn, struct file_struct *file, STRUCT_STAT *st)
+{
+ switch (ftype) {
+ case FT_REG:
+ if (st->st_size != F_LENGTH(file))
+ return 0;
+
+ /* If always_checksum is set then we use the checksum instead
+ * of the file mtime to determine whether to sync. */
+ if (always_checksum > 0) {
+ char sum[MAX_DIGEST_LEN];
+ file_checksum(fn, st, sum);
+ return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
+ }
+
+ if (size_only > 0)
+ return 1;
+
+ if (ignore_times)
+ return 0;
+
+ if (mtime_differs(st, file))
+ return 0;
+ break;
+ case FT_DIR:
+ break;
+ case FT_SYMLINK: {
+#ifdef SUPPORT_LINKS
+ char lnk[MAXPATHLEN];
+ int len = do_readlink(fn, lnk, MAXPATHLEN-1);
+ if (len <= 0)
+ return 0;
+ lnk[len] = '\0';
+ if (strcmp(lnk, F_SYMLINK(file)) != 0)
+ return 0;
+ break;
+#else
+ return -1;
+#endif
+ }
+ case FT_SPECIAL:
+ if (!BITS_EQUAL(file->mode, st->st_mode, _S_IFMT))
+ return 0;
+ break;
+ case FT_DEVICE: {
+ uint32 *devp = F_RDEV_P(file);
+ if (st->st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
+ return 0;
+ break;
+ }
+ case FT_UNSUPPORTED:
+ return -1;
+ }
+ return 1;
+}
+
+/*
+ * set (initialize) the size entries in the per-file sum_struct
+ * calculating dynamic block and checksum sizes.
+ *
+ * This is only called from generate_and_send_sums() but is a separate
+ * function to encapsulate the logic.
+ *
+ * The block size is a rounded square root of file length.
+ *
+ * The checksum size is determined according to:
+ * blocksum_bits = BLOCKSUM_BIAS + 2*log2(file_len) - log2(block_len)
+ * provided by Donovan Baarda which gives a probability of rsync
+ * algorithm corrupting data and falling back using the whole md4
+ * checksums.
+ *
+ * This might be made one of several selectable heuristics.
+ */
+static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
+{
+ int32 blength;
+ int s2length;
+ int64 l;
+
+ if (len < 0) {
+ /* The file length overflowed our int64 var, so we can't process this file. */
+ sum->count = -1; /* indicate overflow error */
+ return;
+ }
+
+ if (block_size)
+ blength = block_size;
+ else if (len <= BLOCK_SIZE * BLOCK_SIZE)
+ blength = BLOCK_SIZE;
+ else {
+ int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE;
+ int32 c;
+ int cnt;
+ for (c = 1, l = len, cnt = 0; l >>= 2; c <<= 1, cnt++) {}
+ if (c < 0 || c >= max_blength)
+ blength = max_blength;
+ else {
+ blength = 0;
+ do {
+ blength |= c;
+ if (len < (int64)blength * blength)
+ blength &= ~c;
+ c >>= 1;
+ } while (c >= 8); /* round to multiple of 8 */
+ blength = MAX(blength, BLOCK_SIZE);
+ }
+ }
+
+ if (protocol_version < 27) {
+ s2length = csum_length;
+ } else if (csum_length == SUM_LENGTH) {
+ s2length = SUM_LENGTH;
+ } else {
+ int32 c;
+ int b = BLOCKSUM_BIAS;
+ for (l = len; l >>= 1; b += 2) {}
+ for (c = blength; (c >>= 1) && b; b--) {}
+ /* add a bit, subtract rollsum, round up. */
+ s2length = (b + 1 - 32 + 7) / 8; /* --optimize in compiler-- */
+ s2length = MAX(s2length, csum_length);
+ s2length = MIN(s2length, SUM_LENGTH);
+ }
+
+ sum->flength = len;
+ sum->blength = blength;
+ sum->s2length = s2length;
+ sum->remainder = (int32)(len % blength);
+ sum->count = (int32)(l = (len / blength) + (sum->remainder != 0));
+
+ if ((int64)sum->count != l)
+ sum->count = -1;
+
+ if (sum->count && DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO,
+ "count=%s rem=%ld blength=%ld s2length=%d flength=%s\n",
+ big_num(sum->count), (long)sum->remainder, (long)sum->blength,
+ sum->s2length, big_num(sum->flength));
+ }
+}
+
+
+/*
+ * Generate and send a stream of signatures/checksums that describe a buffer
+ *
+ * Generate approximately one checksum every block_len bytes.
+ */
+static int generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy)
+{
+ int32 i;
+ struct map_struct *mapbuf;
+ struct sum_struct sum;
+ OFF_T offset = 0;
+
+ sum_sizes_sqroot(&sum, len);
+ if (sum.count < 0)
+ return -1;
+ write_sum_head(f_out, &sum);
+
+ if (append_mode > 0 && f_copy < 0)
+ return 0;
+
+ if (len > 0)
+ mapbuf = map_file(fd, len, MAX_MAP_SIZE, sum.blength);
+ else
+ mapbuf = NULL;
+
+ for (i = 0; i < sum.count; i++) {
+ int32 n1 = (int32)MIN(len, (OFF_T)sum.blength);
+ char *map = map_ptr(mapbuf, offset, n1);
+ char sum2[SUM_LENGTH];
+ uint32 sum1;
+
+ len -= n1;
+ offset += n1;
+
+ if (f_copy >= 0) {
+ full_write(f_copy, map, n1);
+ if (append_mode > 0)
+ continue;
+ }
+
+ sum1 = get_checksum1(map, n1);
+ get_checksum2(map, n1, sum2);
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,
+ "chunk[%s] offset=%s len=%ld sum1=%08lx\n",
+ big_num(i), big_num(offset - n1), (long)n1,
+ (unsigned long)sum1);
+ }
+ write_int(f_out, sum1);
+ write_buf(f_out, sum2, sum.s2length);
+ }
+
+ if (mapbuf)
+ unmap_file(mapbuf);
+
+ return 0;
+}
+
+
+/* Try to find a filename in the same dir as "fname" with a similar name. */
+static struct file_struct *find_fuzzy(struct file_struct *file, struct file_list *dirlist_array[], uchar *fnamecmp_type_ptr)
+{
+ int fname_len, fname_suf_len;
+ const char *fname_suf, *fname = file->basename;
+ uint32 lowest_dist = 25 << 16; /* ignore a distance greater than 25 */
+ int i, j;
+ struct file_struct *lowest_fp = NULL;
+
+ fname_len = strlen(fname);
+ fname_suf = find_filename_suffix(fname, fname_len, &fname_suf_len);
+
+ /* Try to find an exact size+mtime match first. */
+ for (i = 0; i < fuzzy_basis; i++) {
+ struct file_list *dirlist = dirlist_array[i];
+
+ if (!dirlist)
+ continue;
+
+ for (j = 0; j < dirlist->used; j++) {
+ struct file_struct *fp = dirlist->files[j];
+
+ if (!F_IS_ACTIVE(fp))
+ continue;
+
+ if (!S_ISREG(fp->mode) || !F_LENGTH(fp) || fp->flags & FLAG_FILE_SENT)
+ continue;
+
+ if (F_LENGTH(fp) == F_LENGTH(file) && same_time(fp->modtime, 0, file->modtime, 0)) {
+ if (DEBUG_GTE(FUZZY, 2))
+ rprintf(FINFO, "fuzzy size/modtime match for %s\n", f_name(fp, NULL));
+ *fnamecmp_type_ptr = FNAMECMP_FUZZY + i;
+ return fp;
+ }
+
+ }
+ }
+
+ for (i = 0; i < fuzzy_basis; i++) {
+ struct file_list *dirlist = dirlist_array[i];
+
+ if (!dirlist)
+ continue;
+
+ for (j = 0; j < dirlist->used; j++) {
+ struct file_struct *fp = dirlist->files[j];
+ const char *suf, *name;
+ int len, suf_len;
+ uint32 dist;
+
+ if (!F_IS_ACTIVE(fp))
+ continue;
+
+ if (!S_ISREG(fp->mode) || !F_LENGTH(fp) || fp->flags & FLAG_FILE_SENT)
+ continue;
+
+ name = fp->basename;
+ len = strlen(name);
+ suf = find_filename_suffix(name, len, &suf_len);
+
+ dist = fuzzy_distance(name, len, fname, fname_len, lowest_dist);
+ /* Add some extra weight to how well the suffixes match unless we've already disqualified
+ * this file based on a heuristic. */
+ if (dist < 0xFFFF0000U) {
+ dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len, 0xFFFF0000U) * 10;
+ }
+ if (DEBUG_GTE(FUZZY, 2)) {
+ rprintf(FINFO, "fuzzy distance for %s = %d.%05d\n",
+ f_name(fp, NULL), (int)(dist>>16), (int)(dist&0xFFFF));
+ }
+ if (dist <= lowest_dist) {
+ lowest_dist = dist;
+ lowest_fp = fp;
+ *fnamecmp_type_ptr = FNAMECMP_FUZZY + i;
+ }
+ }
+ }
+
+ return lowest_fp;
+}
+
+/* Copy a file found in our --copy-dest handling. */
+static int copy_altdest_file(const char *src, const char *dest, struct file_struct *file)
+{
+ char buf[MAXPATHLEN];
+ const char *copy_to, *partialptr;
+ int save_preserve_xattrs = preserve_xattrs;
+ int ok, fd_w;
+
+ if (inplace) {
+ /* Let copy_file open the destination in place. */
+ fd_w = -1;
+ copy_to = dest;
+ } else {
+ fd_w = open_tmpfile(buf, dest, file);
+ if (fd_w < 0)
+ return -1;
+ copy_to = buf;
+ }
+ cleanup_set(copy_to, NULL, NULL, -1, -1);
+ if (copy_file(src, copy_to, fd_w, file->mode) < 0) {
+ if (INFO_GTE(COPY, 1)) {
+ rsyserr(FINFO, errno, "copy_file %s => %s",
+ full_fname(src), copy_to);
+ }
+ /* Try to clean up. */
+ unlink(copy_to);
+ cleanup_disable();
+ return -1;
+ }
+ partialptr = partial_dir ? partial_dir_fname(dest) : NULL;
+ preserve_xattrs = 0; /* xattrs were copied with file */
+ ok = finish_transfer(dest, copy_to, src, partialptr, file, 1, 0);
+ preserve_xattrs = save_preserve_xattrs;
+ cleanup_disable();
+ return ok ? 0 : -1;
+}
+
+/* This is only called for regular files. We return -2 if we've finished
+ * handling the file, -1 if no dest-linking occurred, or a non-negative
+ * value if we found an alternate basis file. If we're called with the
+ * find_exact_for_existing flag, the destination file already exists, so
+ * we only try to find an exact alt-dest match. In this case, the returns
+ * are only -2 & -1 (both as above). */
+static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
+ char *cmpbuf, stat_x *sxp, int find_exact_for_existing,
+ int itemizing, enum logcode code)
+{
+ STRUCT_STAT real_st = sxp->st;
+ int best_match = -1;
+ int match_level = 0;
+ int j = 0;
+
+ do {
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0 || !S_ISREG(sxp->st.st_mode))
+ continue;
+ if (match_level == 0) {
+ best_match = j;
+ match_level = 1;
+ }
+ if (!quick_check_ok(FT_REG, cmpbuf, file, &sxp->st))
+ continue;
+ if (match_level == 1) {
+ best_match = j;
+ match_level = 2;
+ }
+ if (unchanged_attrs(cmpbuf, file, sxp)) {
+ best_match = j;
+ match_level = 3;
+ break;
+ }
+ free_stat_x(sxp);
+ } while (basis_dir[++j] != NULL);
+
+ if (!match_level)
+ goto got_nothing_for_ya;
+
+ if (j != best_match) {
+ j = best_match;
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ goto got_nothing_for_ya;
+ }
+
+ if (match_level == 3 && alt_dest_type != COPY_DEST) {
+ if (find_exact_for_existing) {
+ if (alt_dest_type == LINK_DEST && real_st.st_dev == sxp->st.st_dev && real_st.st_ino == sxp->st.st_ino)
+ return -1;
+ if (do_unlink(fname) < 0 && errno != ENOENT)
+ goto got_nothing_for_ya;
+ }
+#ifdef SUPPORT_HARD_LINKS
+ if (alt_dest_type == LINK_DEST) {
+ if (!hard_link_one(file, fname, cmpbuf, 1))
+ goto try_a_copy;
+ if (atimes_ndx)
+ set_file_attrs(fname, file, sxp, NULL, 0);
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
+ if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
+ itemize(cmpbuf, file, ndx, 1, sxp,
+ ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
+ 0, "");
+ }
+ } else
+#endif
+ {
+ if (itemizing)
+ itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
+ }
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ return -2;
+ }
+
+ if (find_exact_for_existing)
+ goto got_nothing_for_ya;
+
+ if (match_level >= 2) {
+#ifdef SUPPORT_HARD_LINKS
+ try_a_copy: /* Copy the file locally. */
+#endif
+ if (!dry_run && copy_altdest_file(cmpbuf, fname, file) < 0) {
+ if (find_exact_for_existing) /* Can get here via hard-link failure */
+ goto got_nothing_for_ya;
+ return -1;
+ }
+ if (itemizing)
+ itemize(cmpbuf, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
+ if (maybe_ATTRS_REPORT
+ && ((!itemizing && INFO_GTE(NAME, 1) && match_level == 2)
+ || (INFO_GTE(NAME, 2) && match_level == 3))) {
+ code = match_level == 3 ? FCLIENT : FINFO;
+ rprintf(code, "%s%s\n", fname,
+ match_level == 3 ? " is uptodate" : "");
+ }
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, -1);
+#endif
+ return -2;
+ }
+
+ return FNAMECMP_BASIS_DIR_LOW + j;
+
+got_nothing_for_ya:
+ sxp->st = real_st;
+ return -1;
+}
+
+/* This is only called for non-regular files. We return -2 if we've finished
+ * handling the file, or -1 if no dest-linking occurred, or a non-negative
+ * value if we found an alternate basis file. */
+static int try_dests_non(struct file_struct *file, char *fname, int ndx,
+ char *cmpbuf, stat_x *sxp, int itemizing,
+ enum logcode code)
+{
+ int best_match = -1;
+ int match_level = 0;
+ enum filetype ftype = get_file_type(file->mode);
+ int j = 0;
+
+#ifndef SUPPORT_LINKS
+ if (ftype == FT_SYMLINK)
+ return -1;
+#endif
+ if (ftype == FT_REG || ftype == FT_UNSUPPORTED) {
+ rprintf(FERROR,
+ "internal: try_dests_non() called with invalid mode (%o)\n",
+ (int)file->mode);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ do {
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ continue;
+ if (ftype != get_file_type(sxp->st.st_mode))
+ continue;
+ if (match_level < 1) {
+ match_level = 1;
+ best_match = j;
+ }
+ if (!quick_check_ok(ftype, cmpbuf, file, &sxp->st))
+ continue;
+ if (match_level < 2) {
+ match_level = 2;
+ best_match = j;
+ }
+ if (unchanged_attrs(cmpbuf, file, sxp)) {
+ match_level = 3;
+ best_match = j;
+ break;
+ }
+ } while (basis_dir[++j] != NULL);
+
+ if (!match_level)
+ return -1;
+
+ if (j != best_match) {
+ j = best_match;
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ return -1;
+ }
+
+ if (match_level == 3) {
+#ifdef SUPPORT_HARD_LINKS
+ if (alt_dest_type == LINK_DEST
+#ifndef CAN_HARDLINK_SYMLINK
+ && !S_ISLNK(file->mode)
+#endif
+#ifndef CAN_HARDLINK_SPECIAL
+ && !IS_SPECIAL(file->mode) && !IS_DEVICE(file->mode)
+#endif
+ && !S_ISDIR(file->mode)) {
+ if (do_link(cmpbuf, fname) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to hard-link %s with %s",
+ cmpbuf, fname);
+ return j;
+ }
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+ } else
+#endif
+ match_level = 2;
+ if (itemizing && stdout_format_has_i
+ && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
+ int chg = alt_dest_type == COMPARE_DEST && ftype != FT_DIR ? 0
+ : ITEM_LOCAL_CHANGE + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
+ char *lp = match_level == 3 ? "" : NULL;
+ itemize(cmpbuf, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
+ }
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT) {
+ rprintf(FCLIENT, "%s%s is uptodate\n",
+ fname, ftype == FT_DIR ? "/" : "");
+ }
+ return -2;
+ }
+
+ return j;
+}
+
+static void list_file_entry(struct file_struct *f)
+{
+ char permbuf[PERMSTRING_SIZE];
+ const char *mtime_str = timestring(f->modtime);
+ int size_width = human_readable ? 14 : 11;
+ int mtime_width = 1 + strlen(mtime_str);
+ int atime_width = atimes_ndx ? mtime_width : 0;
+ int crtime_width = crtimes_ndx ? mtime_width : 0;
+
+ if (!F_IS_ACTIVE(f)) {
+ /* this can happen if duplicate names were removed */
+ return;
+ }
+
+ /* TODO: indicate '+' if the entry has an ACL. */
+
+ if (missing_args == 2 && f->mode == 0) {
+ rprintf(FINFO, "%-*s %s\n",
+ 10 + 1 + size_width + mtime_width + atime_width + crtime_width, "*missing",
+ f_name(f, NULL));
+ } else {
+ const char *atime_str = atimes_ndx && !S_ISDIR(f->mode) ? timestring(F_ATIME(f)) : "";
+ const char *crtime_str = crtimes_ndx ? timestring(F_CRTIME(f)) : "";
+ const char *arrow, *lnk;
+
+ permstring(permbuf, f->mode);
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(f->mode)) {
+ arrow = " -> ";
+ lnk = F_SYMLINK(f);
+ } else
+#endif
+ arrow = lnk = "";
+
+ rprintf(FINFO, "%s %*s %s%*s%*s %s%s%s\n",
+ permbuf, size_width, human_num(F_LENGTH(f)),
+ timestring(f->modtime), atime_width, atime_str, crtime_width, crtime_str,
+ f_name(f, NULL), arrow, lnk);
+ }
+}
+
+static int phase = 0;
+static int dflt_perms;
+
+static int implied_dirs_are_missing;
+/* Helper for recv_generator's skip_dir and dry_missing_dir tests. */
+static BOOL is_below(struct file_struct *file, struct file_struct *subtree)
+{
+ return F_DEPTH(file) > F_DEPTH(subtree)
+ && (!implied_dirs_are_missing || f_name_has_prefix(file, subtree));
+}
+
+/* Acts on the indicated item in cur_flist whose name is fname. If a dir,
+ * make sure it exists, and has the right permissions/timestamp info. For
+ * all other non-regular files (symlinks, etc.) we create them here. For
+ * regular files that have changed, we try to find a basis file and then
+ * start sending checksums. The ndx is the file's unique index value.
+ *
+ * The fname parameter must point to a MAXPATHLEN buffer! (e.g it gets
+ * passed to delete_item(), which can use it during a recursive delete.)
+ *
+ * Note that f_out is set to -1 when doing final directory-permission and
+ * modification-time repair. */
+static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ int itemizing, enum logcode code, int f_out)
+{
+ static const char *parent_dirname = "";
+ static struct file_struct *prior_dir_file = NULL;
+ /* Missing dir not created due to --dry-run; will still be scanned. */
+ static struct file_struct *dry_missing_dir = NULL;
+ /* Missing dir whose contents are skipped altogether due to
+ * --ignore-non-existing, daemon exclude, or mkdir failure. */
+ static struct file_struct *skip_dir = NULL;
+ static struct file_list *fuzzy_dirlist[MAX_BASIS_DIRS+1];
+ static int need_fuzzy_dirlist = 0;
+ struct file_struct *fuzzy_file = NULL;
+ int fd = -1, f_copy = -1;
+ stat_x sx, real_sx;
+ STRUCT_STAT partial_st;
+ struct file_struct *back_file = NULL;
+ int statret, real_ret, stat_errno;
+ char *fnamecmp, *partialptr, *backupptr = NULL;
+ char fnamecmpbuf[MAXPATHLEN];
+ uchar fnamecmp_type;
+ int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
+ enum filetype stype, ftype = get_file_type(file->mode);
+ int is_dir = ftype != FT_DIR ? 0
+ : inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
+ : 1;
+
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "recv_generator(%s,%d)\n", fname, ndx);
+
+ if (list_only) {
+ if (is_dir < 0
+ || (is_dir && !implied_dirs && file->flags & FLAG_IMPLIED_DIR))
+ return;
+ list_file_entry(file);
+ return;
+ }
+
+ maybe_ATTRS_ACCURATE_TIME = always_checksum ? ATTRS_ACCURATE_TIME : 0;
+
+ if (skip_dir) {
+ if (is_below(file, skip_dir)) {
+ if (is_dir)
+ file->flags |= FLAG_MISSING_DIR;
+#ifdef SUPPORT_HARD_LINKS
+ else if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ return;
+ }
+ skip_dir = NULL;
+ }
+
+ init_stat_x(&sx);
+ if (daemon_filter_list.head && (*fname != '.' || fname[1])) {
+ if (check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) {
+ if (is_dir < 0)
+ return;
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ rprintf(FERROR_XFER,
+ "ERROR: daemon refused to receive %s \"%s\"\n",
+ is_dir ? "directory" : "file", fname);
+ if (is_dir)
+ goto skipping_dir_contents;
+ return;
+ }
+ }
+
+ if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
+ int i;
+ parent_is_dry_missing:
+ for (i = 0; i < fuzzy_basis; i++) {
+ if (fuzzy_dirlist[i]) {
+ flist_free(fuzzy_dirlist[i]);
+ fuzzy_dirlist[i] = NULL;
+ }
+ }
+ parent_dirname = "";
+ statret = -1;
+ stat_errno = ENOENT;
+ } else {
+ const char *dn = file->dirname ? file->dirname : ".";
+ dry_missing_dir = NULL;
+ if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
+ /* Each parent dir must be in the file list or the flist data is bad.
+ * Optimization: most of the time the parent dir will be the last dir
+ * this function was asked to process in the file list. */
+ if (!inc_recurse
+ && (*dn != '.' || dn[1]) /* Avoid an issue with --relative and the "." dir. */
+ && (!prior_dir_file || strcmp(dn, f_name(prior_dir_file, NULL)) != 0)) {
+ int ok = 0, j = flist_find_name(cur_flist, dn, -1);
+ if (j >= 0) {
+ struct file_struct *f = cur_flist->sorted[j];
+ if (S_ISDIR(f->mode) || (missing_args == 2 && !file->mode && !f->mode))
+ ok = 1;
+ }
+ /* The --delete-missing-args option can actually put invalid entries into
+ * the file list, so if that option was specified, we'll just complain about
+ * it and allow it. */
+ if (!ok && missing_args == 2 && file->mode == 0 && j < 0)
+ rprintf(FERROR, "WARNING: parent dir is absent in the file list: %s\n", dn);
+ else if (!ok) {
+ rprintf(FERROR, "ABORTING due to invalid path from sender: %s/%s\n",
+ dn, file->basename);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ }
+ if (relative_paths && !implied_dirs && file->mode != 0
+ && do_stat(dn, &sx.st) < 0) {
+ if (dry_run)
+ goto parent_is_dry_missing;
+ if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "recv_generator: mkdir %s failed",
+ full_fname(dn));
+ }
+ }
+ if (fuzzy_basis) {
+ int i;
+ for (i = 0; i < fuzzy_basis; i++) {
+ if (fuzzy_dirlist[i]) {
+ flist_free(fuzzy_dirlist[i]);
+ fuzzy_dirlist[i] = NULL;
+ }
+ }
+ need_fuzzy_dirlist = 1;
+ }
+#ifdef SUPPORT_ACLS
+ if (!preserve_perms)
+ dflt_perms = default_perms_for_dir(dn);
+#endif
+ }
+ parent_dirname = dn;
+
+ statret = link_stat(fname, &sx.st, keep_dirlinks && is_dir);
+ stat_errno = errno;
+ }
+
+ if (missing_args == 2 && file->mode == 0) {
+ if (filter_list.head && check_filter(&filter_list, FINFO, fname, is_dir) < 0)
+ return;
+ if (statret == 0)
+ delete_item(fname, sx.st.st_mode, del_opts);
+ return;
+ }
+
+ if (ignore_non_existing > 0 && statret == -1 && stat_errno == ENOENT) {
+ if (is_dir) {
+ if (is_dir < 0)
+ return;
+ skip_dir = file;
+ file->flags |= FLAG_MISSING_DIR;
+ }
+#ifdef SUPPORT_HARD_LINKS
+ else if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ if (INFO_GTE(SKIP, 1)) {
+ rprintf(FINFO, "not creating new %s \"%s\"\n",
+ is_dir ? "directory" : "file", fname);
+ }
+ return;
+ }
+
+ if (statret == 0 && !(sx.st.st_mode & S_IWUSR)
+ && !am_root && sx.st.st_uid == our_uid)
+ del_opts |= DEL_NO_UID_WRITE;
+
+ if (statret == 0)
+ stype = get_file_type(sx.st.st_mode);
+ else
+ stype = FT_UNSUPPORTED;
+
+ if (ignore_existing > 0 && statret == 0
+ && (!is_dir || stype != FT_DIR)) {
+ if (INFO_GTE(SKIP, 1) && is_dir >= 0) {
+ const char *suf = "";
+ if (INFO_GTE(SKIP, 2)) {
+ if (ftype != stype)
+ suf = " (type change)";
+ else if (!quick_check_ok(ftype, fname, file, &sx.st))
+ suf = always_checksum ? " (sum change)" : " (file change)";
+ else if (!unchanged_attrs(fname, file, &sx))
+ suf = " (attr change)";
+ else
+ suf = " (uptodate)";
+ }
+ rprintf(FINFO, "%s exists%s\n", fname, suf);
+ }
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ goto cleanup;
+ }
+
+ fnamecmp = fname;
+
+ if (is_dir) {
+ mode_t added_perms;
+ if (!implied_dirs && file->flags & FLAG_IMPLIED_DIR)
+ goto cleanup;
+ if (am_root < 0) {
+ /* For --fake-super, the dir must be useable by the copying
+ * user, just like it would be for root. */
+ added_perms = S_IRUSR|S_IWUSR|S_IXUSR;
+ } else
+ added_perms = 0;
+ if (is_dir < 0) {
+ if (!preserve_mtimes || omit_dir_times)
+ goto cleanup;
+ /* In inc_recurse mode we want to make sure any missing
+ * directories get created while we're still processing
+ * the parent dir (which allows us to touch the parent
+ * dir's mtime right away). We will handle the dir in
+ * full later (right before we handle its contents). */
+ if (statret == 0
+ && (stype == FT_DIR
+ || delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0))
+ goto cleanup; /* Any errors get reported later. */
+ if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0)
+ file->flags |= FLAG_DIR_CREATED;
+ goto cleanup;
+ }
+ /* The file to be received is a directory, so we need
+ * to prepare appropriately. If there is already a
+ * file of that name and it is *not* a directory, then
+ * we need to delete it. If it doesn't exist, then
+ * (perhaps recursively) create it. */
+ if (statret == 0 && stype != FT_DIR) {
+ if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0)
+ goto skipping_dir_contents;
+ statret = -1;
+ }
+ if (dry_run && statret != 0) {
+ if (!dry_missing_dir)
+ dry_missing_dir = file;
+ file->flags |= FLAG_MISSING_DIR;
+ }
+ init_stat_x(&real_sx);
+ real_sx.st = sx.st;
+ real_ret = statret;
+ if (file->flags & FLAG_DIR_CREATED)
+ statret = -1;
+ if (!preserve_perms) { /* See comment in non-dir code below. */
+ file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, statret == 0);
+ }
+ if (statret != 0 && basis_dir[0] != NULL) {
+ int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx, itemizing, code);
+ if (j == -2) {
+ itemizing = 0;
+ code = FNONE;
+ statret = 1;
+ } else if (j >= 0) {
+ statret = 1;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+ if (itemizing && f_out != -1) {
+ itemize(fnamecmp, file, ndx, statret, &sx,
+ statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
+ }
+ if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
+ if (!relative_paths || errno != ENOENT
+ || make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0
+ || (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
+ rsyserr(FERROR_XFER, errno,
+ "recv_generator: mkdir %s failed",
+ full_fname(fname));
+ skipping_dir_contents:
+ rprintf(FERROR, "*** Skipping any contents from this failed directory ***\n");
+ skip_dir = file;
+ file->flags |= FLAG_MISSING_DIR;
+ goto cleanup;
+ }
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && statret == 1)
+ copy_xattrs(fnamecmpbuf, fname);
+#endif
+ if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
+ && INFO_GTE(NAME, 1) && code != FNONE && f_out != -1)
+ rprintf(code, "%s/\n", fname);
+
+ /* We need to ensure that the dirs in the transfer have both
+ * readable and writable permissions during the time we are
+ * putting files within them. This is then restored to the
+ * former permissions after the transfer is done. */
+#ifdef HAVE_CHMOD
+ if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
+ mode_t mode = file->mode | S_IRWXU;
+ if (do_chmod(fname, mode) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to modify permissions on %s",
+ full_fname(fname));
+ }
+ need_retouch_dir_perms = 1;
+ }
+#endif
+
+ if (real_ret != 0 && one_file_system)
+ real_sx.st.st_dev = filesystem_dev;
+ if (inc_recurse) {
+ if (one_file_system) {
+ uint32 *devp = F_DIR_DEV_P(file);
+ DEV_MAJOR(devp) = major(real_sx.st.st_dev);
+ DEV_MINOR(devp) = minor(real_sx.st.st_dev);
+ }
+ }
+ else if (delete_during && f_out != -1 && !phase
+ && !(file->flags & FLAG_MISSING_DIR)) {
+ if (file->flags & FLAG_CONTENT_DIR)
+ delete_in_dir(fname, file, real_sx.st.st_dev);
+ else
+ change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
+ }
+ prior_dir_file = file;
+ goto cleanup;
+ }
+
+ /* If we're not preserving permissions, change the file-list's
+ * mode based on the local permissions and some heuristics. */
+ if (!preserve_perms) {
+ int exists = statret == 0 && stype != FT_DIR;
+ file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, exists);
+ }
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
+ && hard_link_check(file, ndx, fname, statret, &sx, itemizing, code))
+ goto cleanup;
+#endif
+
+ if (preserve_links && ftype == FT_SYMLINK) {
+#ifdef SUPPORT_LINKS
+ const char *sl = F_SYMLINK(file);
+ if (safe_symlinks && unsafe_symlink(sl, fname)) {
+ if (INFO_GTE(NAME, 1)) {
+ if (solo_file) {
+ /* fname contains the destination path, but we
+ * want to report the source path. */
+ fname = f_name(file, NULL);
+ }
+ rprintf(FINFO,
+ "ignoring unsafe symlink \"%s\" -> \"%s\"\n",
+ fname, sl);
+ }
+ goto cleanup;
+ }
+ if (statret == 0) {
+ if (stype == FT_SYMLINK && quick_check_ok(stype, fname, file, &sx.st)) {
+ /* The link is pointing to the right place. */
+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+ if (itemizing)
+ itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ if (remove_source_files == 1)
+ goto return_with_success;
+ goto cleanup;
+ }
+ } else if (basis_dir[0] != NULL) {
+ int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx, itemizing, code);
+ if (j == -2) {
+#ifndef CAN_HARDLINK_SYMLINK
+ if (alt_dest_type == LINK_DEST) {
+ /* Resort to --copy-dest behavior. */
+ } else
+#endif
+ if (alt_dest_type != COPY_DEST)
+ goto cleanup;
+ itemizing = 0;
+ code = FNONE;
+ } else if (j >= 0) {
+ statret = 1;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+ if (atomic_create(file, fname, sl, NULL, MAKEDEV(0, 0), &sx, statret == 0 ? DEL_FOR_SYMLINK : 0)) {
+ set_file_attrs(fname, file, NULL, NULL, 0);
+ if (itemizing) {
+ if (statret == 0 && stype != FT_SYMLINK)
+ statret = -1;
+ itemize(fnamecmp, file, ndx, statret, &sx,
+ ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
+ }
+ if (code != FNONE && INFO_GTE(NAME, 1))
+ rprintf(code, "%s -> %s\n", fname, sl);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+#endif
+ /* This does not check remove_source_files == 1
+ * because this is one of the items that the old
+ * --remove-sent-files option would remove. */
+ if (remove_source_files)
+ goto return_with_success;
+ }
+#endif
+ goto cleanup;
+ }
+
+ if ((am_root && preserve_devices && ftype == FT_DEVICE)
+ || (preserve_specials && ftype == FT_SPECIAL)) {
+ dev_t rdev;
+ int del_for_flag;
+ if (ftype == FT_DEVICE) {
+ uint32 *devp = F_RDEV_P(file);
+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ del_for_flag = DEL_FOR_DEVICE;
+ } else {
+ rdev = 0;
+ del_for_flag = DEL_FOR_SPECIAL;
+ }
+ if (statret == 0) {
+ if (ftype != stype)
+ statret = -1;
+ else if (quick_check_ok(ftype, fname, file, &sx.st)) {
+ /* The device or special file is identical. */
+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+ if (itemizing)
+ itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ if (remove_source_files == 1)
+ goto return_with_success;
+ goto cleanup;
+ }
+ } else if (basis_dir[0] != NULL) {
+ int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx, itemizing, code);
+ if (j == -2) {
+#ifndef CAN_HARDLINK_SPECIAL
+ if (alt_dest_type == LINK_DEST) {
+ /* Resort to --copy-dest behavior. */
+ } else
+#endif
+ if (alt_dest_type != COPY_DEST)
+ goto cleanup;
+ itemizing = 0;
+ code = FNONE;
+ } else if (j >= 0) {
+ statret = 1;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+ if (DEBUG_GTE(GENR, 1)) {
+ rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n",
+ fname, (int)file->mode,
+ (long)major(rdev), (long)minor(rdev));
+ }
+ if (atomic_create(file, fname, NULL, NULL, rdev, &sx, del_for_flag)) {
+ set_file_attrs(fname, file, NULL, NULL, 0);
+ if (itemizing) {
+ itemize(fnamecmp, file, ndx, statret, &sx,
+ ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
+ }
+ if (code != FNONE && INFO_GTE(NAME, 1))
+ rprintf(code, "%s\n", fname);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+#endif
+ if (remove_source_files == 1)
+ goto return_with_success;
+ }
+ goto cleanup;
+ }
+
+ if (ftype != FT_REG) {
+ if (INFO_GTE(NONREG, 1)) {
+ if (solo_file)
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
+ }
+ goto cleanup;
+ }
+
+ if (max_size >= 0 && F_LENGTH(file) > max_size) {
+ if (INFO_GTE(SKIP, 1)) {
+ if (solo_file)
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "%s is over max-size\n", fname);
+ }
+ goto cleanup;
+ }
+ if (min_size >= 0 && F_LENGTH(file) < min_size) {
+ if (INFO_GTE(SKIP, 1)) {
+ if (solo_file)
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "%s is under min-size\n", fname);
+ }
+ goto cleanup;
+ }
+
+ if (update_only > 0 && statret == 0 && file->modtime - sx.st.st_mtime < modify_window) {
+ if (INFO_GTE(SKIP, 1))
+ rprintf(FINFO, "%s is newer\n", fname);
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ goto cleanup;
+ }
+
+ fnamecmp_type = FNAMECMP_FNAME;
+
+ if (statret == 0 && !(stype == FT_REG || (write_devices && stype == FT_DEVICE))) {
+ if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_FILE) != 0)
+ goto cleanup;
+ statret = -1;
+ stat_errno = ENOENT;
+ }
+
+ if (basis_dir[0] != NULL && (statret != 0 || alt_dest_type != COPY_DEST)) {
+ int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &sx, statret == 0, itemizing, code);
+ if (j == -2) {
+ if (remove_source_files == 1)
+ goto return_with_success;
+ goto cleanup;
+ }
+ if (j >= 0) {
+ fnamecmp = fnamecmpbuf;
+ fnamecmp_type = j;
+ statret = 0;
+ }
+ }
+
+ init_stat_x(&real_sx);
+ real_sx.st = sx.st; /* Don't copy xattr/acl pointers, as they would free wrong. */
+ real_ret = statret;
+
+ if (partial_dir && (partialptr = partial_dir_fname(fname)) != NULL
+ && link_stat(partialptr, &partial_st, 0) == 0
+ && S_ISREG(partial_st.st_mode)) {
+ if (statret != 0)
+ goto prepare_to_open;
+ } else
+ partialptr = NULL;
+
+ if (statret != 0 && fuzzy_basis) {
+ if (need_fuzzy_dirlist) {
+ const char *dn = file->dirname ? file->dirname : ".";
+ int i;
+ strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
+ for (i = 0; i < fuzzy_basis; i++) {
+ if (i && pathjoin(fnamecmpbuf, MAXPATHLEN, basis_dir[i-1], dn) >= MAXPATHLEN)
+ continue;
+ fuzzy_dirlist[i] = get_dirlist(fnamecmpbuf, -1, GDL_IGNORE_FILTER_RULES | GDL_PERHAPS_DIR);
+ if (fuzzy_dirlist[i] && fuzzy_dirlist[i]->used == 0) {
+ flist_free(fuzzy_dirlist[i]);
+ fuzzy_dirlist[i] = NULL;
+ }
+ }
+ need_fuzzy_dirlist = 0;
+ }
+
+ /* Sets fnamecmp_type to FNAMECMP_FUZZY or above. */
+ fuzzy_file = find_fuzzy(file, fuzzy_dirlist, &fnamecmp_type);
+ if (fuzzy_file) {
+ f_name(fuzzy_file, fnamecmpbuf);
+ if (DEBUG_GTE(FUZZY, 1)) {
+ rprintf(FINFO, "fuzzy basis selected for %s: %s\n",
+ fname, fnamecmpbuf);
+ }
+ sx.st.st_size = F_LENGTH(fuzzy_file);
+ statret = 0;
+ fnamecmp = fnamecmpbuf;
+ }
+ }
+
+ if (statret != 0) {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+ cur_flist->in_progress++;
+ goto cleanup;
+ }
+#endif
+ if (stat_errno == ENOENT)
+ goto notify_others;
+ rsyserr(FERROR_XFER, stat_errno, "recv_generator: failed to stat %s",
+ full_fname(fname));
+ goto cleanup;
+ }
+
+ if (write_devices && IS_DEVICE(sx.st.st_mode) && sx.st.st_size == 0) {
+ /* This early open into fd skips the regular open below. */
+ if ((fd = do_open(fnamecmp, O_RDONLY, 0)) >= 0)
+ real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
+ }
+
+ if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
+ ;
+ else if (fnamecmp_type >= FNAMECMP_FUZZY)
+ ;
+ else if (quick_check_ok(FT_REG, fnamecmp, file, &sx.st)) {
+ if (partialptr) {
+ do_unlink(partialptr);
+ handle_partial_dir(partialptr, PDIR_DELETE);
+ }
+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT | maybe_ATTRS_ACCURATE_TIME);
+ if (itemizing)
+ itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ if (remove_source_files != 1)
+ goto cleanup;
+ return_with_success:
+ if (!dry_run)
+ send_msg_success(fname, ndx);
+ goto cleanup;
+ }
+
+ if (append_mode > 0 && sx.st.st_size >= F_LENGTH(file)) {
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ goto cleanup;
+ }
+
+ prepare_to_open:
+ if (partialptr) {
+ sx.st = partial_st;
+ fnamecmp = partialptr;
+ fnamecmp_type = FNAMECMP_PARTIAL_DIR;
+ statret = 0;
+ }
+
+ if (!do_xfers)
+ goto notify_others;
+
+ if (read_batch || whole_file) {
+ if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
+ if (!(backupptr = get_backup_name(fname)))
+ goto cleanup;
+ if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
+ goto pretend_missing;
+ if (copy_file(fname, backupptr, -1, back_file->mode) < 0) {
+ unmake_file(back_file);
+ back_file = NULL;
+ goto cleanup;
+ }
+ }
+ goto notify_others;
+ }
+
+ if (fuzzy_dirlist[0]) {
+ int j = flist_find(fuzzy_dirlist[0], file);
+ if (j >= 0) /* don't use changing file as future fuzzy basis */
+ fuzzy_dirlist[0]->files[j]->flags |= FLAG_FILE_SENT;
+ }
+
+ /* open the file */
+ if (fd < 0 && (fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
+ rsyserr(FERROR, errno, "failed to open %s, continuing",
+ full_fname(fnamecmp));
+ pretend_missing:
+ /* pretend the file didn't exist */
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+ cur_flist->in_progress++;
+ goto cleanup;
+ }
+#endif
+ statret = real_ret = -1;
+ goto notify_others;
+ }
+
+ if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
+ if (!(backupptr = get_backup_name(fname))) {
+ goto cleanup;
+ }
+ if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
+ goto pretend_missing;
+ }
+ if (robust_unlink(backupptr) && errno != ENOENT) {
+ rsyserr(FERROR_XFER, errno, "unlink %s",
+ full_fname(backupptr));
+ unmake_file(back_file);
+ back_file = NULL;
+ goto cleanup;
+ }
+ if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
+ rsyserr(FERROR_XFER, errno, "open %s", full_fname(backupptr));
+ unmake_file(back_file);
+ back_file = NULL;
+ goto cleanup;
+ }
+ fnamecmp_type = FNAMECMP_BACKUP;
+ }
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO, "gen mapped %s of size %s\n",
+ fnamecmp, big_num(sx.st.st_size));
+ }
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO, "generating and sending sums for %d\n", ndx);
+
+ notify_others:
+ if (remove_source_files && !delay_updates && !phase && !dry_run)
+ increment_active_files(ndx, itemizing, code);
+ if (inc_recurse && (!dry_run || write_batch < 0))
+ cur_flist->in_progress++;
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ file->flags |= FLAG_FILE_SENT;
+#endif
+ write_ndx(f_out, ndx);
+ if (itemizing) {
+ int iflags = ITEM_TRANSFER;
+ if (always_checksum > 0)
+ iflags |= ITEM_REPORT_CHANGE;
+ if (fnamecmp_type != FNAMECMP_FNAME)
+ iflags |= ITEM_BASIS_TYPE_FOLLOWS;
+ if (fnamecmp_type >= FNAMECMP_FUZZY)
+ iflags |= ITEM_XNAME_FOLLOWS;
+ itemize(fnamecmp, file, -1, real_ret, &real_sx, iflags, fnamecmp_type,
+ fuzzy_file ? fuzzy_file->basename : NULL);
+ free_stat_x(&real_sx);
+ }
+
+ if (!do_xfers) {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
+#endif
+ goto cleanup;
+ }
+ if (read_batch)
+ goto cleanup;
+
+ if (statret != 0 || whole_file)
+ write_sum_head(f_out, NULL);
+ else if (sx.st.st_size <= 0) {
+ write_sum_head(f_out, NULL);
+ } else {
+ if (generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy) < 0) {
+ rprintf(FWARNING,
+ "WARNING: file is too large for checksum sending: %s\n",
+ fnamecmp);
+ write_sum_head(f_out, NULL);
+ }
+ }
+
+ cleanup:
+ if (fd >= 0)
+ close(fd);
+ if (back_file) {
+ int save_preserve_xattrs = preserve_xattrs;
+ if (f_copy >= 0)
+ close(f_copy);
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ copy_xattrs(fname, backupptr);
+ preserve_xattrs = 0;
+ }
+#endif
+ set_file_attrs(backupptr, back_file, NULL, NULL, 0);
+ preserve_xattrs = save_preserve_xattrs;
+ if (INFO_GTE(BACKUP, 1)) {
+ rprintf(FINFO, "backed up %s to %s\n",
+ fname, backupptr);
+ }
+ unmake_file(back_file);
+ }
+
+ free_stat_x(&sx);
+}
+
+/* If we are replacing an existing hard link, symlink, device, or special file,
+ * create a temp-name item and rename it into place. A symlimk specifies slnk,
+ * a hard link specifies hlnk, otherwise we create a device based on rdev.
+ * Specify 0 for the del_for_flag if there is not a file to replace. This
+ * returns 1 on success and 0 on failure. */
+int atomic_create(struct file_struct *file, char *fname, const char *slnk, const char *hlnk,
+ dev_t rdev, stat_x *sxp, int del_for_flag)
+{
+ char tmpname[MAXPATHLEN];
+ const char *create_name;
+ int skip_atomic, dir_in_the_way = del_for_flag && S_ISDIR(sxp->st.st_mode);
+
+ if (!del_for_flag || dir_in_the_way || tmpdir || !get_tmpname(tmpname, fname, True))
+ skip_atomic = 1;
+ else
+ skip_atomic = 0;
+
+ if (del_for_flag) {
+ if (make_backups > 0 && !dir_in_the_way) {
+ if (!make_backup(fname, skip_atomic))
+ return 0;
+ } else if (skip_atomic) {
+ int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
+ if (delete_item(fname, sxp->st.st_mode, del_opts | del_for_flag) != 0)
+ return 0;
+ }
+ }
+
+ create_name = skip_atomic ? fname : tmpname;
+
+ if (slnk) {
+#ifdef SUPPORT_LINKS
+ if (do_symlink(slnk, create_name) < 0) {
+ rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
+ full_fname(create_name), slnk);
+ return 0;
+ }
+#else
+ return 0;
+#endif
+ } else if (hlnk) {
+#ifdef SUPPORT_HARD_LINKS
+ if (!hard_link_one(file, create_name, hlnk, 0))
+ return 0;
+#else
+ return 0;
+#endif
+ } else {
+ if (do_mknod(create_name, file->mode, rdev) < 0) {
+ rsyserr(FERROR_XFER, errno, "mknod %s failed",
+ full_fname(create_name));
+ return 0;
+ }
+ }
+
+ if (!skip_atomic) {
+ if (do_rename(tmpname, fname) < 0) {
+ rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
+ full_fname(tmpname), full_fname(fname));
+ do_unlink(tmpname);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#ifdef SUPPORT_HARD_LINKS
+static void handle_skipped_hlink(struct file_struct *file, int itemizing,
+ enum logcode code, int f_out)
+{
+ char fbuf[MAXPATHLEN];
+ int new_last_ndx;
+ struct file_list *save_flist = cur_flist;
+
+ /* If we skip the last item in a chain of links and there was a
+ * prior non-skipped hard-link waiting to finish, finish it now. */
+ if ((new_last_ndx = skip_hard_link(file, &cur_flist)) < 0)
+ return;
+
+ file = cur_flist->files[new_last_ndx - cur_flist->ndx_start];
+ cur_flist->in_progress--; /* undo prior increment */
+ f_name(file, fbuf);
+ recv_generator(fbuf, file, new_last_ndx, itemizing, code, f_out);
+
+ cur_flist = save_flist;
+}
+#endif
+
+static void touch_up_dirs(struct file_list *flist, int ndx)
+{
+ static int counter = 0;
+ struct file_struct *file;
+ char *fname;
+ BOOL fix_dir_perms;
+ int i, start, end;
+
+ if (ndx < 0) {
+ start = 0;
+ end = flist->used - 1;
+ } else
+ start = end = ndx;
+
+ /* Fix any directory permissions that were modified during the
+ * transfer and/or re-set any tweaked modified-time values. */
+ for (i = start; i <= end; i++, counter++) {
+ file = flist->files[i];
+ if (!F_IS_ACTIVE(file))
+ continue;
+ if (!S_ISDIR(file->mode)
+ || (!implied_dirs && file->flags & FLAG_IMPLIED_DIR))
+ continue;
+ if (DEBUG_GTE(TIME, 2)) {
+ fname = f_name(file, NULL);
+ rprintf(FINFO, "touch_up_dirs: %s (%d)\n",
+ NS(fname), i);
+ }
+ /* Be sure not to retouch permissions with --fake-super. */
+ fix_dir_perms = !am_root && !(file->mode & S_IWUSR);
+ if (file->flags & FLAG_MISSING_DIR || !(need_retouch_dir_times || fix_dir_perms))
+ continue;
+ fname = f_name(file, NULL);
+ if (fix_dir_perms)
+ do_chmod(fname, file->mode);
+ if (need_retouch_dir_times) {
+ STRUCT_STAT st;
+ if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
+ st.st_mtime = file->modtime;
+#ifdef ST_MTIME_NSEC
+ st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file);
+#endif
+ set_times(fname, &st);
+ }
+ }
+ if (counter >= loopchk_limit) {
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ else
+ maybe_flush_socket(0);
+ counter = 0;
+ }
+ }
+}
+
+void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
+{
+ struct file_struct *file;
+ struct file_list *flist;
+ char fbuf[MAXPATHLEN];
+ int ndx;
+
+ while (1) {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && (ndx = get_hlink_num()) != -1) {
+ int send_failed = (ndx == -2);
+ if (send_failed)
+ ndx = get_hlink_num();
+ flist = flist_for_ndx(ndx, "check_for_finished_files.1");
+ file = flist->files[ndx - flist->ndx_start];
+ assert(file->flags & FLAG_HLINKED);
+ if (send_failed)
+ handle_skipped_hlink(file, itemizing, code, sock_f_out);
+ else
+ finish_hard_link(file, f_name(file, fbuf), ndx, NULL, itemizing, code, -1);
+ flist->in_progress--;
+ continue;
+ }
+#endif
+
+ if (check_redo && (ndx = get_redo_num()) != -1) {
+ OFF_T save_max_size = max_size;
+ OFF_T save_min_size = min_size;
+ csum_length = SUM_LENGTH;
+ max_size = -1;
+ min_size = -1;
+ ignore_existing = -ignore_existing;
+ ignore_non_existing = -ignore_non_existing;
+ update_only = -update_only;
+ always_checksum = -always_checksum;
+ size_only = -size_only;
+ append_mode = -append_mode;
+ make_backups = -make_backups; /* avoid dup backup w/inplace */
+ ignore_times++;
+
+ flist = cur_flist;
+ cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2");
+
+ file = cur_flist->files[ndx - cur_flist->ndx_start];
+ if (solo_file)
+ strlcpy(fbuf, solo_file, sizeof fbuf);
+ else
+ f_name(file, fbuf);
+ recv_generator(fbuf, file, ndx, itemizing, code, sock_f_out);
+ cur_flist->to_redo--;
+
+ cur_flist = flist;
+
+ csum_length = SHORT_SUM_LENGTH;
+ max_size = save_max_size;
+ min_size = save_min_size;
+ ignore_existing = -ignore_existing;
+ ignore_non_existing = -ignore_non_existing;
+ update_only = -update_only;
+ always_checksum = -always_checksum;
+ size_only = -size_only;
+ append_mode = -append_mode;
+ make_backups = -make_backups;
+ ignore_times--;
+ continue;
+ }
+
+ if (cur_flist == first_flist)
+ break;
+
+ /* We only get here if inc_recurse is enabled. */
+ if (first_flist->in_progress || first_flist->to_redo)
+ break;
+
+ write_ndx(sock_f_out, NDX_DONE);
+ if (!read_batch && !flist_eof) {
+ int old_total = 0;
+ for (flist = first_flist; flist != cur_flist; flist = flist->next)
+ old_total += flist->used;
+ maybe_flush_socket(!flist_eof && file_total - old_total < MIN_FILECNT_LOOKAHEAD/2);
+ }
+
+ if (delete_during == 2 || !dir_tweaking) {
+ /* Skip directory touch-up. */
+ } else if (first_flist->parent_ndx >= 0)
+ touch_up_dirs(dir_flist, first_flist->parent_ndx);
+
+ flist_free(first_flist); /* updates first_flist */
+ }
+}
+
+void generate_files(int f_out, const char *local_name)
+{
+ int i, ndx, next_loopchk = 0;
+ char fbuf[MAXPATHLEN];
+ int itemizing;
+ enum logcode code;
+ int save_info_flist = info_levels[INFO_FLIST];
+ int save_info_progress = info_levels[INFO_PROGRESS];
+
+ if (protocol_version >= 29) {
+ itemizing = 1;
+ maybe_ATTRS_REPORT = stdout_format_has_i ? 0 : ATTRS_REPORT;
+ code = logfile_format_has_i ? FNONE : FLOG;
+ } else if (am_daemon) {
+ itemizing = logfile_format_has_i && do_xfers;
+ maybe_ATTRS_REPORT = ATTRS_REPORT;
+ code = itemizing || !do_xfers ? FCLIENT : FINFO;
+ } else if (!am_server) {
+ itemizing = stdout_format_has_i;
+ maybe_ATTRS_REPORT = stdout_format_has_i ? 0 : ATTRS_REPORT;
+ code = itemizing ? FNONE : FINFO;
+ } else {
+ itemizing = 0;
+ maybe_ATTRS_REPORT = ATTRS_REPORT;
+ code = FINFO;
+ }
+ solo_file = local_name;
+ dir_tweaking = !(list_only || solo_file || dry_run);
+ need_retouch_dir_times = preserve_mtimes && !omit_dir_times;
+ loopchk_limit = allowed_lull ? allowed_lull * 5 : 200;
+ symlink_timeset_failed_flags = ITEM_REPORT_TIME
+ | (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0);
+ implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30;
+
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generator starting pid=%d\n", (int)getpid());
+
+ if (delete_before && !solo_file && cur_flist->used > 0)
+ do_delete_pass();
+ if (delete_during == 2) {
+ deldelay_size = BIGPATHBUFLEN * 4;
+ deldelay_buf = new_array(char, deldelay_size);
+ }
+ info_levels[INFO_FLIST] = info_levels[INFO_PROGRESS] = 0;
+
+ if (append_mode > 0 || whole_file < 0)
+ whole_file = 0;
+ if (DEBUG_GTE(FLIST, 1)) {
+ rprintf(FINFO, "delta-transmission %s\n",
+ whole_file
+ ? "disabled for local transfer or --whole-file"
+ : "enabled");
+ }
+
+ dflt_perms = (ACCESSPERMS & ~orig_umask);
+
+ do {
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && inc_recurse) {
+ while (!flist_eof && file_total < MIN_FILECNT_LOOKAHEAD/2)
+ wait_for_receiver();
+ }
+#endif
+
+ if (inc_recurse && cur_flist->parent_ndx >= 0) {
+ struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
+ if (solo_file)
+ strlcpy(fbuf, solo_file, sizeof fbuf);
+ else
+ f_name(fp, fbuf);
+ ndx = cur_flist->ndx_start - 1;
+ recv_generator(fbuf, fp, ndx, itemizing, code, f_out);
+ if (delete_during && dry_run < 2 && !list_only
+ && !(fp->flags & FLAG_MISSING_DIR)) {
+ if (fp->flags & FLAG_CONTENT_DIR) {
+ dev_t dirdev;
+ if (one_file_system) {
+ uint32 *devp = F_DIR_DEV_P(fp);
+ dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ } else
+ dirdev = MAKEDEV(0, 0);
+ delete_in_dir(fbuf, fp, dirdev);
+ } else
+ change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
+ }
+ }
+ for (i = cur_flist->low; i <= cur_flist->high; i++) {
+ struct file_struct *file = cur_flist->sorted[i];
+
+ if (!F_IS_ACTIVE(file))
+ continue;
+
+ if (unsort_ndx)
+ ndx = F_NDX(file);
+ else
+ ndx = i + cur_flist->ndx_start;
+
+ if (solo_file)
+ strlcpy(fbuf, solo_file, sizeof fbuf);
+ else
+ f_name(file, fbuf);
+ recv_generator(fbuf, file, ndx, itemizing, code, f_out);
+
+ check_for_finished_files(itemizing, code, 0);
+
+ if (i + cur_flist->ndx_start >= next_loopchk) {
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ else
+ maybe_flush_socket(0);
+ next_loopchk += loopchk_limit;
+ }
+ }
+
+ if (!inc_recurse) {
+ write_ndx(f_out, NDX_DONE);
+ break;
+ }
+
+ while (1) {
+ check_for_finished_files(itemizing, code, 1);
+ if (cur_flist->next || flist_eof)
+ break;
+ wait_for_receiver();
+ }
+ } while ((cur_flist = cur_flist->next) != NULL);
+
+ if (delete_during)
+ delete_in_dir(NULL, NULL, dev_zero);
+ phase++;
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files phase=%d\n", phase);
+
+ while (1) {
+ check_for_finished_files(itemizing, code, 1);
+ if (msgdone_cnt)
+ break;
+ wait_for_receiver();
+ }
+
+ phase++;
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files phase=%d\n", phase);
+
+ write_ndx(f_out, NDX_DONE);
+
+ /* Reduce round-trip lag-time for a useless delay-updates phase. */
+ if (protocol_version >= 29 && EARLY_DELAY_DONE_MSG())
+ write_ndx(f_out, NDX_DONE);
+
+ if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG()) {
+ if ((INFO_GTE(STATS, 2) && (delete_mode || force_delete)) || read_batch)
+ write_del_stats(f_out);
+ if (EARLY_DELAY_DONE_MSG()) /* Can't send this before delay */
+ write_ndx(f_out, NDX_DONE);
+ }
+
+ /* Read MSG_DONE for the redo phase (and any prior messages). */
+ while (1) {
+ check_for_finished_files(itemizing, code, 0);
+ if (msgdone_cnt > 1)
+ break;
+ wait_for_receiver();
+ }
+
+ if (protocol_version >= 29) {
+ phase++;
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files phase=%d\n", phase);
+ if (!EARLY_DELAY_DONE_MSG()) {
+ write_ndx(f_out, NDX_DONE);
+ if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG())
+ write_ndx(f_out, NDX_DONE);
+ }
+ /* Read MSG_DONE for delay-updates phase & prior messages. */
+ while (msgdone_cnt == 2)
+ wait_for_receiver();
+ }
+
+ info_levels[INFO_FLIST] = save_info_flist;
+ info_levels[INFO_PROGRESS] = save_info_progress;
+
+ if (delete_during == 2)
+ do_delayed_deletions(fbuf);
+ if (delete_after && !solo_file && file_total > 0)
+ do_delete_pass();
+
+ if (max_delete >= 0 && skipped_deletes) {
+ rprintf(FWARNING,
+ "Deletions stopped due to --max-delete limit (%d skipped)\n",
+ skipped_deletes);
+ io_error |= IOERR_DEL_LIMIT;
+ }
+
+ if (protocol_version >= 31) {
+ if (!EARLY_DELETE_DONE_MSG()) {
+ if (INFO_GTE(STATS, 2) || read_batch)
+ write_del_stats(f_out);
+ write_ndx(f_out, NDX_DONE);
+ }
+
+ /* Read MSG_DONE for late-delete phase & prior messages. */
+ while (msgdone_cnt == 3)
+ wait_for_receiver();
+ }
+
+ if ((need_retouch_dir_perms || need_retouch_dir_times)
+ && dir_tweaking && (!inc_recurse || delete_during == 2))
+ touch_up_dirs(dir_flist, -1);
+
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "generate_files finished\n");
+}
diff --git a/getfsdev.c b/getfsdev.c
new file mode 100644
index 0000000..bc8c65d
--- /dev/null
+++ b/getfsdev.c
@@ -0,0 +1,22 @@
+#include "rsync.h"
+
+ int main(int argc, char *argv[])
+{
+ STRUCT_STAT st;
+ int ret;
+
+ while (--argc > 0) {
+#ifdef USE_STAT64_FUNCS
+ ret = stat64(*++argv, &st);
+#else
+ ret = stat(*++argv, &st);
+#endif
+ if (ret < 0) {
+ fprintf(stderr, "Unable to stat `%s'\n", *argv);
+ exit(1);
+ }
+ printf("%ld/%ld\n", (long)major(st.st_dev), (long)minor(st.st_dev));
+ }
+
+ return 0;
+}
diff --git a/getgroups.c b/getgroups.c
new file mode 100644
index 0000000..8a37ed0
--- /dev/null
+++ b/getgroups.c
@@ -0,0 +1,61 @@
+/*
+ * Print out the gids of all groups for the current user. This is like
+ * `id -G` on Linux, but it's too hard to find a portable equivalent.
+ *
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2020 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+ int main(UNUSED(int argc), UNUSED(char *argv[]))
+{
+ int n, i;
+ gid_t *list;
+ gid_t gid = MY_GID();
+ int gid_in_list = 0;
+
+#ifdef HAVE_GETGROUPS
+ if ((n = getgroups(0, NULL)) < 0) {
+ perror("getgroups");
+ return 1;
+ }
+#else
+ n = 0;
+#endif
+
+ list = (gid_t*)malloc(sizeof (gid_t) * (n + 1));
+ if (!list) {
+ fprintf(stderr, "out of memory!\n");
+ exit(1);
+ }
+
+#ifdef HAVE_GETGROUPS
+ if (n > 0)
+ n = getgroups(n, list);
+#endif
+
+ for (i = 0; i < n; i++) {
+ printf("%lu ", (unsigned long)list[i]);
+ if (list[i] == gid)
+ gid_in_list = 1;
+ }
+ /* The default gid might not be in the list on some systems. */
+ if (!gid_in_list)
+ printf("%lu", (unsigned long)gid);
+ printf("\n");
+
+ return 0;
+}
diff --git a/hashtable.c b/hashtable.c
new file mode 100644
index 0000000..2cc4e55
--- /dev/null
+++ b/hashtable.c
@@ -0,0 +1,662 @@
+/*
+ * Routines to provide a memory-efficient hashtable.
+ *
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+#define HASH_LOAD_LIMIT(size) ((size)*3/4)
+
+struct hashtable *hashtable_create(int size, int key64)
+{
+ int req = size;
+ struct hashtable *tbl;
+ int node_size = key64 ? sizeof (struct ht_int64_node)
+ : sizeof (struct ht_int32_node);
+
+ /* Pick a power of 2 that can hold the requested size. */
+ if (size & (size-1) || size < 16) {
+ size = 16;
+ while (size < req)
+ size *= 2;
+ }
+
+ tbl = new(struct hashtable);
+ tbl->nodes = new_array0(char, size * node_size);
+ tbl->size = size;
+ tbl->entries = 0;
+ tbl->node_size = node_size;
+ tbl->key64 = key64 ? 1 : 0;
+
+ if (DEBUG_GTE(HASH, 1)) {
+ char buf[32];
+ if (req != size)
+ snprintf(buf, sizeof buf, "req: %d, ", req);
+ else
+ *buf = '\0';
+ rprintf(FINFO, "[%s] created hashtable %lx (%ssize: %d, keys: %d-bit)\n",
+ who_am_i(), (long)tbl, buf, size, key64 ? 64 : 32);
+ }
+
+ return tbl;
+}
+
+void hashtable_destroy(struct hashtable *tbl)
+{
+ if (DEBUG_GTE(HASH, 1)) {
+ rprintf(FINFO, "[%s] destroyed hashtable %lx (size: %d, keys: %d-bit)\n",
+ who_am_i(), (long)tbl, tbl->size, tbl->key64 ? 64 : 32);
+ }
+ free(tbl->nodes);
+ free(tbl);
+}
+
+/* Returns the node that holds the indicated key if it exists. When it does not
+ * exist, it returns either NULL (when data_when_new is NULL), or it returns a
+ * new node with its node->data set to the indicated value.
+ *
+ * If your code doesn't know the data value for a new node in advance (usually
+ * because it doesn't know if a node is new or not) you should pass in a unique
+ * (non-0) value that you can use to check if the returned node is new. You can
+ * then overwrite the data with any value you want (even 0) since it only needs
+ * to be different than whatever data_when_new value you use later on.
+ *
+ * This return is a void* just because it might be pointing at a ht_int32_node
+ * or a ht_int64_node, and that makes the caller's assignment a little easier. */
+void *hashtable_find(struct hashtable *tbl, int64 key, void *data_when_new)
+{
+ int key64 = tbl->key64;
+ struct ht_int32_node *node;
+ uint32 ndx;
+
+ if (key64 ? key == 0 : (int32)key == 0) {
+ rprintf(FERROR, "Internal hashtable error: illegal key supplied!\n");
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+
+ if (data_when_new && tbl->entries > HASH_LOAD_LIMIT(tbl->size)) {
+ void *old_nodes = tbl->nodes;
+ int size = tbl->size * 2;
+ int i;
+
+ tbl->nodes = new_array0(char, size * tbl->node_size);
+ tbl->size = size;
+ tbl->entries = 0;
+
+ if (DEBUG_GTE(HASH, 1)) {
+ rprintf(FINFO, "[%s] growing hashtable %lx (size: %d, keys: %d-bit)\n",
+ who_am_i(), (long)tbl, size, key64 ? 64 : 32);
+ }
+
+ for (i = size / 2; i-- > 0; ) {
+ struct ht_int32_node *move_node = HT_NODE(tbl, old_nodes, i);
+ int64 move_key = HT_KEY(move_node, key64);
+ if (move_key == 0)
+ continue;
+ if (move_node->data)
+ hashtable_find(tbl, move_key, move_node->data);
+ else {
+ node = hashtable_find(tbl, move_key, "");
+ node->data = 0;
+ }
+ }
+
+ free(old_nodes);
+ }
+
+ if (!key64) {
+ /* Based on Jenkins One-at-a-time hash. */
+ uchar buf[4], *keyp = buf;
+ int i;
+
+ SIVALu(buf, 0, key);
+ for (ndx = 0, i = 0; i < 4; i++) {
+ ndx += keyp[i];
+ ndx += (ndx << 10);
+ ndx ^= (ndx >> 6);
+ }
+ ndx += (ndx << 3);
+ ndx ^= (ndx >> 11);
+ ndx += (ndx << 15);
+ } else {
+ /* Based on Jenkins hashword() from lookup3.c. */
+ uint32 a, b, c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (8 << 2);
+
+#define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k))))
+#if SIZEOF_INT64 >= 8
+ b += (uint32)(key >> 32);
+#endif
+ a += (uint32)key;
+ c ^= b; c -= rot(b, 14);
+ a ^= c; a -= rot(c, 11);
+ b ^= a; b -= rot(a, 25);
+ c ^= b; c -= rot(b, 16);
+ a ^= c; a -= rot(c, 4);
+ b ^= a; b -= rot(a, 14);
+ c ^= b; c -= rot(b, 24);
+#undef rot
+ ndx = c;
+ }
+
+ /* If it already exists, return the node. If we're not
+ * allocating, return NULL if the key is not found. */
+ while (1) {
+ int64 nkey;
+
+ ndx &= tbl->size - 1;
+ node = HT_NODE(tbl, tbl->nodes, ndx);
+ nkey = HT_KEY(node, key64);
+
+ if (nkey == key)
+ return node;
+ if (nkey == 0) {
+ if (!data_when_new)
+ return NULL;
+ break;
+ }
+ ndx++;
+ }
+
+ /* Take over this empty spot and then return the node. */
+ if (key64)
+ ((struct ht_int64_node*)node)->key = key;
+ else
+ node->key = (int32)key;
+ node->data = data_when_new;
+ tbl->entries++;
+ return node;
+}
+
+#ifndef WORDS_BIGENDIAN
+# define HASH_LITTLE_ENDIAN 1
+# define HASH_BIG_ENDIAN 0
+#else
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 1
+#endif
+
+/*
+ -------------------------------------------------------------------------------
+ lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+ These are functions for producing 32-bit hashes for hash table lookup.
+ hash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+ are externally useful functions. Routines to test the hash are included
+ if SELF_TEST is defined. You can use this free for any purpose. It's in
+ the public domain. It has no warranty.
+
+ You probably want to use hashlittle(). hashlittle() and hashbig()
+ hash byte arrays. hashlittle() is is faster than hashbig() on
+ little-endian machines. Intel and AMD are little-endian machines.
+ On second thought, you probably want hashlittle2(), which is identical to
+ hashlittle() except it returns two 32-bit hashes for the price of one.
+ You could implement hashbig2() if you wanted but I haven't bothered here.
+
+ If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+ then use c as the hash value. If you have a variable length array of
+ 4-byte integers to hash, use hash_word(). If you have a byte array (like
+ a character string), use hashlittle(). If you have several byte arrays, or
+ a mix of things, see the comments above hashlittle().
+
+ Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+ then mix those integers. This is fast (you can do a lot more thorough
+ mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+ on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+*/
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+ -------------------------------------------------------------------------------
+ mix -- mix 3 32-bit values reversibly.
+
+ This is reversible, so any information in (a,b,c) before mix() is
+ still in (a,b,c) after mix().
+
+ If four pairs of (a,b,c) inputs are run through mix(), or through
+ mix() in reverse, there are at least 32 bits of the output that
+ are sometimes the same for one pair and different for another pair.
+ This was tested for:
+ * pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+ * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+ * the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+ Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+ satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+ Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+ for "differ" defined as + with a one-bit base and a two-bit delta. I
+ used http://burtleburtle.net/bob/hash/avalanche.html to choose
+ the operations, constants, and arrangements of the variables.
+
+ This does not achieve avalanche. There are input bits of (a,b,c)
+ that fail to affect some output bits of (a,b,c), especially of a. The
+ most thoroughly mixed value is c, but it doesn't really even achieve
+ avalanche in c.
+
+ This allows some parallelism. Read-after-writes are good at doubling
+ the number of bits affected, so the goal of mixing pulls in the opposite
+ direction as the goal of parallelism. I did what I could. Rotates
+ seem to cost as much as shifts on every machine I could lay my hands
+ on, and rotates are much kinder to the top and bottom bits, so I used
+ rotates.
+ -------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+ -------------------------------------------------------------------------------
+ final -- final mixing of 3 32-bit values (a,b,c) into c
+
+ Pairs of (a,b,c) values differing in only a few bits will usually
+ produce values of c that look totally different. This was tested for
+ * pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+ * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+ * the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+ These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+ and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+ -------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+
+/*
+ -------------------------------------------------------------------------------
+ hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ val2 : IN: can be any 4-byte value OUT: second 32 bit hash.
+ Returns a 32-bit value. Every bit of the key affects every bit of
+ the return value. Two keys differing by one or two bits will have
+ totally different hash values. Note that the return value is better
+ mixed than val2, so use that first.
+
+ The best hash table sizes are powers of 2. There is no need to do
+ mod a prime (mod is sooo slow!). If you need less than 32 bits,
+ use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+ In which case, the hash table should have hashsize(10) elements.
+
+ If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+
+ By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
+ code any way you wish, private, educational, or commercial. It's free.
+
+ Use for hash table lookup, or anything where one collision in 2^^32 is
+ acceptable. Do NOT use for cryptographic purposes.
+ -------------------------------------------------------------------------------
+*/
+
+#define NON_ZERO_32(x) ((x) ? (x) : (uint32_t)1)
+#define NON_ZERO_64(x, y) ((x) || (y) ? (y) | (int64)(x) << 32 | (y) : (int64)1)
+
+uint32_t hashlittle(const void *key, size_t length)
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length);
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint8_t *k8;
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return NON_ZERO_32(c);
+ }
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return NON_ZERO_32(c); /* zero length requires no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ /* FALLTHROUGH */
+ case 11: c+=((uint32_t)k[10])<<16;
+ /* FALLTHROUGH */
+ case 10: c+=((uint32_t)k[9])<<8;
+ /* FALLTHROUGH */
+ case 9 : c+=k[8];
+ /* FALLTHROUGH */
+ case 8 : b+=((uint32_t)k[7])<<24;
+ /* FALLTHROUGH */
+ case 7 : b+=((uint32_t)k[6])<<16;
+ /* FALLTHROUGH */
+ case 6 : b+=((uint32_t)k[5])<<8;
+ /* FALLTHROUGH */
+ case 5 : b+=k[4];
+ /* FALLTHROUGH */
+ case 4 : a+=((uint32_t)k[3])<<24;
+ /* FALLTHROUGH */
+ case 3 : a+=((uint32_t)k[2])<<16;
+ /* FALLTHROUGH */
+ case 2 : a+=((uint32_t)k[1])<<8;
+ /* FALLTHROUGH */
+ case 1 : a+=k[0];
+ break;
+ case 0 : return NON_ZERO_32(c);
+ }
+ }
+
+ final(a,b,c);
+ return NON_ZERO_32(c);
+}
+
+#if SIZEOF_INT64 >= 8
+/*
+ * hashlittle2: return 2 32-bit hash values joined into an int64.
+ *
+ * This is identical to hashlittle(), except it returns two 32-bit hash
+ * values instead of just one. This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key. *pc is better mixed than *pb, so use *pc first. If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ */
+int64 hashlittle2(const void *key, size_t length)
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length);
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint8_t *k8;
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return NON_ZERO_64(b, c);
+ }
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return NON_ZERO_64(b, c); /* zero length strings require no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ /* FALLTHROUGH */
+ case 11: c+=((uint32_t)k[10])<<16;
+ /* FALLTHROUGH */
+ case 10: c+=((uint32_t)k[9])<<8;
+ /* FALLTHROUGH */
+ case 9 : c+=k[8];
+ /* FALLTHROUGH */
+ case 8 : b+=((uint32_t)k[7])<<24;
+ /* FALLTHROUGH */
+ case 7 : b+=((uint32_t)k[6])<<16;
+ /* FALLTHROUGH */
+ case 6 : b+=((uint32_t)k[5])<<8;
+ /* FALLTHROUGH */
+ case 5 : b+=k[4];
+ /* FALLTHROUGH */
+ case 4 : a+=((uint32_t)k[3])<<24;
+ /* FALLTHROUGH */
+ case 3 : a+=((uint32_t)k[2])<<16;
+ /* FALLTHROUGH */
+ case 2 : a+=((uint32_t)k[1])<<8;
+ /* FALLTHROUGH */
+ case 1 : a+=k[0];
+ break;
+ case 0 : return NON_ZERO_64(b, c);
+ }
+ }
+
+ final(a,b,c);
+ return NON_ZERO_64(b, c);
+}
+#else
+#define hashlittle2(key, len) hashlittle(key, len)
+#endif
diff --git a/help-from-md.awk b/help-from-md.awk
new file mode 100755
index 0000000..1900797
--- /dev/null
+++ b/help-from-md.awk
@@ -0,0 +1,40 @@
+#!/usr/bin/awk -f
+
+# The caller must pass args: -v hfile=help-NAME.h NAME.NUM.md
+
+BEGIN {
+ heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from the option list in " ARGV[1] "! */"
+ findcomment = hfile
+ sub("\\.", "\\.", findcomment)
+ findcomment = "\\[comment\\].*" findcomment
+ backtick_cnt = 0
+ prints = ""
+}
+
+/^```/ {
+ backtick_cnt++
+ next
+}
+
+foundcomment {
+ if (backtick_cnt > 1) exit
+ if (backtick_cnt == 1) {
+ gsub(/"/, "\\\"")
+ prints = prints "\n rprintf(F,\"" $0 "\\n\");"
+ }
+ next
+}
+
+$0 ~ findcomment {
+ foundcomment = 1
+ backtick_cnt = 0
+}
+
+END {
+ if (foundcomment && backtick_cnt > 1)
+ print heading "\n" prints > hfile
+ else {
+ print "Failed to find " hfile " section in " ARGV[1]
+ exit 1
+ }
+}
diff --git a/hlink.c b/hlink.c
new file mode 100644
index 0000000..20291f2
--- /dev/null
+++ b/hlink.c
@@ -0,0 +1,566 @@
+/*
+ * Routines to support hard-linking.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2004-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+#include "ifuncs.h"
+
+extern int dry_run;
+extern int list_only;
+extern int am_sender;
+extern int inc_recurse;
+extern int do_xfers;
+extern int alt_dest_type;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int protocol_version;
+extern int remove_source_files;
+extern int stdout_format_has_i;
+extern int maybe_ATTRS_REPORT;
+extern int unsort_ndx;
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern struct file_list *cur_flist;
+
+#ifdef SUPPORT_HARD_LINKS
+
+/* Starting with protocol 30, we use a simple hashtable on the sending side
+ * for hashing the st_dev and st_ino info. The receiving side gets told
+ * (via flags and a "group index") which items are hard-linked together, so
+ * we can avoid the pool of dev+inode data. For incremental recursion mode,
+ * the receiver will use a ndx hash to remember old pathnames. */
+
+static void *data_when_new = "";
+
+static struct hashtable *dev_tbl;
+
+static struct hashtable *prior_hlinks;
+
+static struct file_list *hlink_flist;
+
+void init_hard_links(void)
+{
+ if (am_sender || protocol_version < 30)
+ dev_tbl = hashtable_create(16, HT_KEY64);
+ else if (inc_recurse)
+ prior_hlinks = hashtable_create(1024, HT_KEY32);
+}
+
+struct ht_int64_node *idev_find(int64 dev, int64 ino)
+{
+ static struct ht_int64_node *dev_node = NULL;
+
+ /* Note that some OSes have a dev == 0, so increment to avoid storing a 0. */
+ if (!dev_node || dev_node->key != dev+1) {
+ /* We keep a separate hash table of inodes for every device. */
+ dev_node = hashtable_find(dev_tbl, dev+1, data_when_new);
+ if (dev_node->data == data_when_new) {
+ dev_node->data = hashtable_create(512, HT_KEY64);
+ if (DEBUG_GTE(HLINK, 3)) {
+ rprintf(FINFO, "[%s] created hashtable for dev %s\n",
+ who_am_i(), big_num(dev));
+ }
+ }
+ }
+
+ return hashtable_find(dev_node->data, ino, (void*)-1L);
+}
+
+void idev_destroy(void)
+{
+ int i;
+
+ for (i = 0; i < dev_tbl->size; i++) {
+ struct ht_int32_node *node = HT_NODE(dev_tbl, dev_tbl->nodes, i);
+ if (node->data)
+ hashtable_destroy(node->data);
+ }
+
+ hashtable_destroy(dev_tbl);
+}
+
+static int hlink_compare_gnum(int *int1, int *int2)
+{
+ struct file_struct *f1 = hlink_flist->sorted[*int1];
+ struct file_struct *f2 = hlink_flist->sorted[*int2];
+ int32 gnum1 = F_HL_GNUM(f1);
+ int32 gnum2 = F_HL_GNUM(f2);
+
+ if (gnum1 != gnum2)
+ return gnum1 > gnum2 ? 1 : -1;
+
+ return *int1 > *int2 ? 1 : -1;
+}
+
+static void match_gnums(int32 *ndx_list, int ndx_count)
+{
+ int32 from, prev;
+ struct file_struct *file, *file_next;
+ struct ht_int32_node *node = NULL;
+ int32 gnum, gnum_next;
+
+ qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)()) hlink_compare_gnum);
+
+ for (from = 0; from < ndx_count; from++) {
+ file = hlink_flist->sorted[ndx_list[from]];
+ gnum = F_HL_GNUM(file);
+ if (inc_recurse) {
+ node = hashtable_find(prior_hlinks, gnum, data_when_new);
+ if (node->data == data_when_new) {
+ node->data = new_array0(char, 5);
+ assert(gnum >= hlink_flist->ndx_start);
+ file->flags |= FLAG_HLINK_FIRST;
+ prev = -1;
+ } else if (CVAL(node->data, 0) == 0) {
+ struct file_list *flist;
+ prev = IVAL(node->data, 1);
+ flist = flist_for_ndx(prev, NULL);
+ if (flist)
+ flist->files[prev - flist->ndx_start]->flags &= ~FLAG_HLINK_LAST;
+ else {
+ /* We skipped all prior files in this
+ * group, so mark this as a "first". */
+ file->flags |= FLAG_HLINK_FIRST;
+ prev = -1;
+ }
+ } else
+ prev = -1;
+ } else {
+ file->flags |= FLAG_HLINK_FIRST;
+ prev = -1;
+ }
+ for ( ; from < ndx_count-1; file = file_next, gnum = gnum_next, from++) { /*SHARED ITERATOR*/
+ file_next = hlink_flist->sorted[ndx_list[from+1]];
+ gnum_next = F_HL_GNUM(file_next);
+ if (gnum != gnum_next)
+ break;
+ F_HL_PREV(file) = prev;
+ /* The linked list uses over-the-wire ndx values. */
+ if (unsort_ndx)
+ prev = F_NDX(file);
+ else
+ prev = ndx_list[from] + hlink_flist->ndx_start;
+ }
+ if (prev < 0 && !inc_recurse) {
+ /* Disable hard-link bit and set DONE so that
+ * HLINK_BUMP()-dependent values are unaffected. */
+ file->flags &= ~(FLAG_HLINKED | FLAG_HLINK_FIRST);
+ file->flags |= FLAG_HLINK_DONE;
+ continue;
+ }
+
+ file->flags |= FLAG_HLINK_LAST;
+ F_HL_PREV(file) = prev;
+ if (inc_recurse && CVAL(node->data, 0) == 0) {
+ if (unsort_ndx)
+ prev = F_NDX(file);
+ else
+ prev = ndx_list[from] + hlink_flist->ndx_start;
+ SIVAL(node->data, 1, prev);
+ }
+ }
+}
+
+/* Analyze the hard-links in the file-list by creating a list of all the
+ * items that have hlink data, sorting them, and matching up identical
+ * values into clusters. These will be a single linked list from last
+ * to first when we're done. */
+void match_hard_links(struct file_list *flist)
+{
+ if (!list_only && flist->used) {
+ int i, ndx_count = 0;
+ int32 *ndx_list;
+
+ ndx_list = new_array(int32, flist->used);
+
+ for (i = 0; i < flist->used; i++) {
+ if (F_IS_HLINKED(flist->sorted[i]))
+ ndx_list[ndx_count++] = i;
+ }
+
+ hlink_flist = flist;
+
+ if (ndx_count)
+ match_gnums(ndx_list, ndx_count);
+
+ free(ndx_list);
+ }
+ if (protocol_version < 30)
+ idev_destroy();
+}
+
+static int maybe_hard_link(struct file_struct *file, int ndx,
+ char *fname, int statret, stat_x *sxp,
+ const char *oldname, STRUCT_STAT *old_stp,
+ const char *realname, int itemizing, enum logcode code)
+{
+ if (statret == 0) {
+ if (sxp->st.st_dev == old_stp->st_dev
+ && sxp->st.st_ino == old_stp->st_ino) {
+ if (itemizing) {
+ itemize(fname, file, ndx, statret, sxp,
+ ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
+ 0, "");
+ }
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ file->flags |= FLAG_HLINK_DONE;
+ return 0;
+ }
+ }
+
+ if (atomic_create(file, fname, NULL, oldname, MAKEDEV(0, 0), sxp, statret == 0 ? DEL_FOR_FILE : 0)) {
+ if (itemizing) {
+ itemize(fname, file, ndx, statret, sxp,
+ ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
+ realname);
+ }
+ if (code != FNONE && INFO_GTE(NAME, 1))
+ rprintf(code, "%s => %s\n", fname, realname);
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Figure out if a prior entry is still there or if we just have a
+ * cached name for it. */
+static char *check_prior(struct file_struct *file, int gnum,
+ int *prev_ndx_p, struct file_list **flist_p)
+{
+ struct file_struct *fp;
+ struct ht_int32_node *node;
+ int prev_ndx = F_HL_PREV(file);
+
+ while (1) {
+ struct file_list *flist;
+ if (prev_ndx < 0
+ || (flist = flist_for_ndx(prev_ndx, NULL)) == NULL)
+ break;
+ fp = flist->files[prev_ndx - flist->ndx_start];
+ if (!(fp->flags & FLAG_SKIP_HLINK)) {
+ *prev_ndx_p = prev_ndx;
+ *flist_p = flist;
+ return NULL;
+ }
+ F_HL_PREV(file) = prev_ndx = F_HL_PREV(fp);
+ }
+
+ if (inc_recurse
+ && (node = hashtable_find(prior_hlinks, gnum, NULL)) != NULL) {
+ assert(node->data != NULL);
+ if (CVAL(node->data, 0) != 0) {
+ *prev_ndx_p = -1;
+ *flist_p = NULL;
+ return node->data;
+ }
+ /* The prior file must have been skipped. */
+ F_HL_PREV(file) = -1;
+ }
+
+ *prev_ndx_p = -1;
+ *flist_p = NULL;
+ return NULL;
+}
+
+/* Only called if FLAG_HLINKED is set and FLAG_HLINK_FIRST is not. Returns:
+ * 0 = process the file, 1 = skip the file, -1 = error occurred. */
+int hard_link_check(struct file_struct *file, int ndx, char *fname,
+ int statret, stat_x *sxp, int itemizing,
+ enum logcode code)
+{
+ STRUCT_STAT prev_st;
+ char namebuf[MAXPATHLEN], altbuf[MAXPATHLEN];
+ char *realname, *prev_name;
+ struct file_list *flist;
+ int gnum = inc_recurse ? F_HL_GNUM(file) : -1;
+ int prev_ndx;
+
+ prev_name = realname = check_prior(file, gnum, &prev_ndx, &flist);
+
+ if (!prev_name) {
+ struct file_struct *prev_file;
+
+ if (!flist) {
+ /* The previous file was skipped, so this one is
+ * treated as if it were the first in its group. */
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): virtual first\n",
+ ndx, f_name(file, NULL), gnum);
+ }
+ return 0;
+ }
+
+ prev_file = flist->files[prev_ndx - flist->ndx_start];
+
+ /* Is the previous link not complete yet? */
+ if (!(prev_file->flags & FLAG_HLINK_DONE)) {
+ /* Is the previous link being transferred? */
+ if (prev_file->flags & FLAG_FILE_SENT) {
+ /* Add ourselves to the list of files that will
+ * be updated when the transfer completes, and
+ * mark ourself as waiting for the transfer. */
+ F_HL_PREV(file) = F_HL_PREV(prev_file);
+ F_HL_PREV(prev_file) = ndx;
+ file->flags |= FLAG_FILE_SENT;
+ cur_flist->in_progress++;
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): waiting for %d\n",
+ ndx, f_name(file, NULL), gnum, F_HL_PREV(file));
+ }
+ return 1;
+ }
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): looking for a leader\n",
+ ndx, f_name(file, NULL), gnum);
+ }
+ return 0;
+ }
+
+ /* There is a finished file to link with! */
+ if (!(prev_file->flags & FLAG_HLINK_FIRST)) {
+ /* The previous previous is FIRST when prev is not. */
+ prev_name = realname = check_prior(prev_file, gnum, &prev_ndx, &flist);
+ /* Update our previous pointer to point to the FIRST. */
+ F_HL_PREV(file) = prev_ndx;
+ }
+
+ if (!prev_name) {
+ int alt_dest;
+
+ assert(flist != NULL);
+ prev_file = flist->files[prev_ndx - flist->ndx_start];
+ /* F_HL_PREV() is alt_dest value when DONE && FIRST. */
+ alt_dest = F_HL_PREV(prev_file);
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): found flist match (alt %d)\n",
+ ndx, f_name(file, NULL), gnum, alt_dest);
+ }
+
+ if (alt_dest >= 0 && dry_run) {
+ pathjoin(namebuf, MAXPATHLEN, basis_dir[alt_dest],
+ f_name(prev_file, NULL));
+ prev_name = namebuf;
+ realname = f_name(prev_file, altbuf);
+ } else {
+ prev_name = f_name(prev_file, namebuf);
+ realname = prev_name;
+ }
+ }
+ }
+
+ if (DEBUG_GTE(HLINK, 2)) {
+ rprintf(FINFO, "hlink for %d (%s,%d): leader is %d (%s)\n",
+ ndx, f_name(file, NULL), gnum, prev_ndx, prev_name);
+ }
+
+ if (link_stat(prev_name, &prev_st, 0) < 0) {
+ if (!dry_run || errno != ENOENT) {
+ rsyserr(FERROR_XFER, errno, "stat %s failed", full_fname(prev_name));
+ return -1;
+ }
+ /* A new hard-link will get a new dev & inode, so approximate
+ * those values in dry-run mode by zeroing them. */
+ memset(&prev_st, 0, sizeof prev_st);
+ }
+
+ if (statret < 0 && basis_dir[0] != NULL) {
+ /* If we match an alt-dest item, we don't output this as a change. */
+ char cmpbuf[MAXPATHLEN];
+ stat_x alt_sx;
+ int j = 0;
+ init_stat_x(&alt_sx);
+ do {
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &alt_sx.st, 0) < 0)
+ continue;
+ if (alt_dest_type == LINK_DEST) {
+ if (prev_st.st_dev != alt_sx.st.st_dev
+ || prev_st.st_ino != alt_sx.st.st_ino)
+ continue;
+ statret = 1;
+ if (stdout_format_has_i == 0
+ || (!INFO_GTE(NAME, 2) && stdout_format_has_i < 2)) {
+ itemizing = 0;
+ code = FNONE;
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ }
+ break;
+ }
+ if (!quick_check_ok(FT_REG, cmpbuf, file, &alt_sx.st))
+ continue;
+ statret = 1;
+ if (unchanged_attrs(cmpbuf, file, &alt_sx))
+ break;
+ } while (basis_dir[++j] != NULL);
+ if (statret == 1) {
+ sxp->st = alt_sx.st;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ free_acl(sxp);
+ if (!ACL_READY(alt_sx))
+ get_acl(cmpbuf, sxp);
+ else {
+ sxp->acc_acl = alt_sx.acc_acl;
+ sxp->def_acl = alt_sx.def_acl;
+ alt_sx.acc_acl = alt_sx.def_acl = NULL;
+ }
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ free_xattr(sxp);
+ if (!XATTR_READY(alt_sx))
+ get_xattr(cmpbuf, sxp);
+ else {
+ sxp->xattr = alt_sx.xattr;
+ alt_sx.xattr = NULL;
+ }
+ }
+#endif
+ } else
+ free_stat_x(&alt_sx);
+ }
+
+ if (maybe_hard_link(file, ndx, fname, statret, sxp, prev_name, &prev_st,
+ realname, itemizing, code) < 0)
+ return -1;
+
+ if (remove_source_files == 1 && do_xfers)
+ send_msg_success(fname, ndx);
+
+ return 1;
+}
+
+int hard_link_one(struct file_struct *file, const char *fname,
+ const char *oldname, int terse)
+{
+ if (do_link(oldname, fname) < 0) {
+ enum logcode code;
+ if (terse) {
+ if (!INFO_GTE(NAME, 1))
+ return 0;
+ code = FINFO;
+ } else
+ code = FERROR_XFER;
+ rsyserr(code, errno, "link %s => %s failed",
+ full_fname(fname), oldname);
+ return 0;
+ }
+
+ file->flags |= FLAG_HLINK_DONE;
+
+ return 1;
+}
+
+void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
+ STRUCT_STAT *stp, int itemizing, enum logcode code,
+ int alt_dest)
+{
+ stat_x prev_sx;
+ STRUCT_STAT st;
+ char prev_name[MAXPATHLEN], alt_name[MAXPATHLEN];
+ const char *our_name;
+ struct file_list *flist;
+ int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
+
+ if (stp == NULL && prev_ndx >= 0) {
+ if (link_stat(fname, &st, 0) < 0 && !dry_run) {
+ rsyserr(FERROR_XFER, errno, "stat %s failed",
+ full_fname(fname));
+ return;
+ }
+ stp = &st;
+ }
+
+ /* FIRST combined with DONE means we were the first to get done. */
+ file->flags |= FLAG_HLINK_FIRST | FLAG_HLINK_DONE;
+ F_HL_PREV(file) = alt_dest;
+ if (alt_dest >= 0 && dry_run) {
+ pathjoin(alt_name, MAXPATHLEN, basis_dir[alt_dest],
+ f_name(file, NULL));
+ our_name = alt_name;
+ } else
+ our_name = fname;
+
+ init_stat_x(&prev_sx);
+
+ while ((ndx = prev_ndx) >= 0) {
+ int val;
+ flist = flist_for_ndx(ndx, "finish_hard_link");
+ file = flist->files[ndx - flist->ndx_start];
+ file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
+ prev_ndx = F_HL_PREV(file);
+ F_HL_PREV(file) = fin_ndx;
+ prev_statret = link_stat(f_name(file, prev_name), &prev_sx.st, 0);
+ val = maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_sx,
+ our_name, stp, fname, itemizing, code);
+ flist->in_progress--;
+ free_stat_x(&prev_sx);
+ if (val < 0)
+ continue;
+ if (remove_source_files == 1 && do_xfers)
+ send_msg_success(fname, ndx);
+ }
+
+ if (inc_recurse) {
+ int gnum = F_HL_GNUM(file);
+ struct ht_int32_node *node = hashtable_find(prior_hlinks, gnum, NULL);
+ if (node == NULL) {
+ rprintf(FERROR, "Unable to find a hlink node for %d (%s)\n", gnum, f_name(file, prev_name));
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ if (node->data == NULL) {
+ rprintf(FERROR, "Hlink node data for %d is NULL (%s)\n", gnum, f_name(file, prev_name));
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ if (CVAL(node->data, 0) != 0) {
+ rprintf(FERROR, "Hlink node data for %d already has path=%s (%s)\n",
+ gnum, (char*)node->data, f_name(file, prev_name));
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ free(node->data);
+ node->data = strdup(our_name);
+ }
+}
+
+int skip_hard_link(struct file_struct *file, struct file_list **flist_p)
+{
+ struct file_list *flist;
+ int prev_ndx;
+
+ file->flags |= FLAG_SKIP_HLINK;
+ if (!(file->flags & FLAG_HLINK_LAST))
+ return -1;
+
+ check_prior(file, F_HL_GNUM(file), &prev_ndx, &flist);
+ if (prev_ndx >= 0) {
+ file = flist->files[prev_ndx - flist->ndx_start];
+ if (file->flags & (FLAG_HLINK_DONE|FLAG_FILE_SENT))
+ return -1;
+ file->flags |= FLAG_HLINK_LAST;
+ *flist_p = flist;
+ }
+
+ return prev_ndx;
+}
+#endif
diff --git a/ifuncs.h b/ifuncs.h
new file mode 100644
index 0000000..956fc22
--- /dev/null
+++ b/ifuncs.h
@@ -0,0 +1,112 @@
+/* Inline functions for rsync.
+ *
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+static inline void
+alloc_xbuf(xbuf *xb, size_t sz)
+{
+ xb->buf = new_array(char, sz);
+ xb->size = sz;
+ xb->len = xb->pos = 0;
+}
+
+static inline void
+realloc_xbuf(xbuf *xb, size_t sz)
+{
+ char *bf = realloc_array(xb->buf, char, sz);
+ xb->buf = bf;
+ xb->size = sz;
+}
+
+static inline void
+free_xbuf(xbuf *xb)
+{
+ if (xb->buf)
+ free(xb->buf);
+ memset(xb, 0, sizeof (xbuf));
+}
+
+static inline int
+to_wire_mode(mode_t mode)
+{
+#ifdef SUPPORT_LINKS
+#if _S_IFLNK != 0120000
+ if (S_ISLNK(mode))
+ return (mode & ~(_S_IFMT)) | 0120000;
+#endif
+#endif
+ return mode;
+}
+
+static inline mode_t
+from_wire_mode(int mode)
+{
+#if _S_IFLNK != 0120000
+ if ((mode & (_S_IFMT)) == 0120000)
+ return (mode & ~(_S_IFMT)) | _S_IFLNK;
+#endif
+ return mode;
+}
+
+static inline char *
+d_name(struct dirent *di)
+{
+#ifdef HAVE_BROKEN_READDIR
+ return (di->d_name - 2);
+#else
+ return di->d_name;
+#endif
+}
+
+static inline void
+init_stat_x(stat_x *sx_p)
+{
+ sx_p->crtime = 0;
+#ifdef SUPPORT_ACLS
+ sx_p->acc_acl = sx_p->def_acl = NULL;
+#endif
+#ifdef SUPPORT_XATTRS
+ sx_p->xattr = NULL;
+#endif
+}
+
+static inline void
+free_stat_x(stat_x *sx_p)
+{
+#ifdef SUPPORT_ACLS
+ {
+ extern int preserve_acls;
+ if (preserve_acls)
+ free_acl(sx_p);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ {
+ extern int preserve_xattrs;
+ if (preserve_xattrs)
+ free_xattr(sx_p);
+ }
+#endif
+}
+
+static inline char *my_strdup(const char *str, const char *file, int line)
+{
+ int len = strlen(str)+1;
+ char *buf = my_alloc(NULL, len, 1, file, line);
+ memcpy(buf, str, len);
+ return buf;
+}
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..8c409fb
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src ] || [ -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/_inst.$$_
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/inums.h b/inums.h
new file mode 100644
index 0000000..b421d1a
--- /dev/null
+++ b/inums.h
@@ -0,0 +1,57 @@
+/* Inline functions for rsync.
+ *
+ * Copyright (C) 2008-2019 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+static inline char *
+big_num(int64 num)
+{
+ return do_big_num(num, 0, NULL);
+}
+
+static inline char *
+comma_num(int64 num)
+{
+ extern int human_readable;
+ return do_big_num(num, human_readable != 0, NULL);
+}
+
+static inline char *
+human_num(int64 num)
+{
+ extern int human_readable;
+ return do_big_num(num, human_readable, NULL);
+}
+
+static inline char *
+big_dnum(double dnum, int decimal_digits)
+{
+ return do_big_dnum(dnum, 0, decimal_digits);
+}
+
+static inline char *
+comma_dnum(double dnum, int decimal_digits)
+{
+ extern int human_readable;
+ return do_big_dnum(dnum, human_readable != 0, decimal_digits);
+}
+
+static inline char *
+human_dnum(double dnum, int decimal_digits)
+{
+ extern int human_readable;
+ return do_big_dnum(dnum, human_readable, decimal_digits);
+}
diff --git a/io.c b/io.c
new file mode 100644
index 0000000..a99ac0e
--- /dev/null
+++ b/io.c
@@ -0,0 +1,2460 @@
+/*
+ * Socket and pipe I/O utilities used in rsync.
+ *
+ * Copyright (C) 1996-2001 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/* Rsync provides its own multiplexing system, which is used to send
+ * stderr and stdout over a single socket.
+ *
+ * For historical reasons this is off during the start of the
+ * connection, but it's switched on quite early using
+ * io_start_multiplex_out() and io_start_multiplex_in(). */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "inums.h"
+
+/** If no timeout is specified then use a 60 second select timeout */
+#define SELECT_TIMEOUT 60
+
+extern int bwlimit;
+extern size_t bwlimit_writemax;
+extern int io_timeout;
+extern int am_server;
+extern int am_sender;
+extern int am_receiver;
+extern int am_generator;
+extern int local_server;
+extern int msgs2stderr;
+extern int inc_recurse;
+extern int io_error;
+extern int batch_fd;
+extern int eol_nulls;
+extern int flist_eof;
+extern int file_total;
+extern int file_old_total;
+extern int list_only;
+extern int read_batch;
+extern int compat_flags;
+extern int protect_args;
+extern int checksum_seed;
+extern int daemon_connection;
+extern int protocol_version;
+extern int remove_source_files;
+extern int preserve_hard_links;
+extern BOOL extra_flist_sending_enabled;
+extern BOOL flush_ok_after_signal;
+extern struct stats stats;
+extern time_t stop_at_utime;
+extern struct file_list *cur_flist;
+#ifdef ICONV_OPTION
+extern int filesfrom_convert;
+extern iconv_t ic_send, ic_recv;
+#endif
+
+int csum_length = SHORT_SUM_LENGTH; /* initial value */
+int allowed_lull = 0;
+int msgdone_cnt = 0;
+int forward_flist_data = 0;
+BOOL flist_receiving_enabled = False;
+
+/* Ignore an EOF error if non-zero. See whine_about_eof(). */
+int kluge_around_eof = 0;
+int got_kill_signal = -1; /* is set to 0 only after multiplexed I/O starts */
+
+int sock_f_in = -1;
+int sock_f_out = -1;
+
+int64 total_data_read = 0;
+int64 total_data_written = 0;
+
+char num_dev_ino_buf[4 + 8 + 8];
+
+static struct {
+ xbuf in, out, msg;
+ int in_fd;
+ int out_fd; /* Both "out" and "msg" go to this fd. */
+ int in_multiplexed;
+ unsigned out_empty_len;
+ size_t raw_data_header_pos; /* in the out xbuf */
+ size_t raw_flushing_ends_before; /* in the out xbuf */
+ size_t raw_input_ends_before; /* in the in xbuf */
+} iobuf = { .in_fd = -1, .out_fd = -1 };
+
+static time_t last_io_in;
+static time_t last_io_out;
+
+static int write_batch_monitor_in = -1;
+static int write_batch_monitor_out = -1;
+
+static int ff_forward_fd = -1;
+static int ff_reenable_multiplex = -1;
+static char ff_lastchar = '\0';
+static xbuf ff_xb = EMPTY_XBUF;
+#ifdef ICONV_OPTION
+static xbuf iconv_buf = EMPTY_XBUF;
+#endif
+static int select_timeout = SELECT_TIMEOUT;
+static int active_filecnt = 0;
+static OFF_T active_bytecnt = 0;
+static int first_message = 1;
+
+static char int_byte_extra[64] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (00 - 3F)/4 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (40 - 7F)/4 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (80 - BF)/4 */
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6, /* (C0 - FF)/4 */
+};
+
+/* Our I/O buffers are sized with no bits on in the lowest byte of the "size"
+ * (indeed, our rounding of sizes in 1024-byte units assures more than this).
+ * This allows the code that is storing bytes near the physical end of a
+ * circular buffer to temporarily reduce the buffer's size (in order to make
+ * some storing idioms easier), while also making it simple to restore the
+ * buffer's actual size when the buffer's "pos" wraps around to the start (we
+ * just round the buffer's size up again). */
+
+#define IOBUF_WAS_REDUCED(siz) ((siz) & 0xFF)
+#define IOBUF_RESTORE_SIZE(siz) (((siz) | 0xFF) + 1)
+
+#define IN_MULTIPLEXED (iobuf.in_multiplexed != 0)
+#define IN_MULTIPLEXED_AND_READY (iobuf.in_multiplexed > 0)
+#define OUT_MULTIPLEXED (iobuf.out_empty_len != 0)
+
+#define PIO_NEED_INPUT (1<<0) /* The *_NEED_* flags are mutually exclusive. */
+#define PIO_NEED_OUTROOM (1<<1)
+#define PIO_NEED_MSGROOM (1<<2)
+
+#define PIO_CONSUME_INPUT (1<<4) /* Must becombined with PIO_NEED_INPUT. */
+
+#define PIO_INPUT_AND_CONSUME (PIO_NEED_INPUT | PIO_CONSUME_INPUT)
+#define PIO_NEED_FLAGS (PIO_NEED_INPUT | PIO_NEED_OUTROOM | PIO_NEED_MSGROOM)
+
+#define REMOTE_OPTION_ERROR "rsync: on remote machine: -"
+#define REMOTE_OPTION_ERROR2 ": unknown option"
+
+#define FILESFROM_BUFLEN 2048
+
+enum festatus { FES_SUCCESS, FES_REDO, FES_NO_SEND };
+
+static flist_ndx_list redo_list, hlink_list;
+
+static void read_a_msg(void);
+static void drain_multiplex_messages(void);
+static void sleep_for_bwlimit(int bytes_written);
+
+static void check_timeout(BOOL allow_keepalive, int keepalive_flags)
+{
+ time_t t, chk;
+
+ /* On the receiving side, the generator is now the one that decides
+ * when a timeout has occurred. When it is sifting through a lot of
+ * files looking for work, it will be sending keep-alive messages to
+ * the sender, and even though the receiver won't be sending/receiving
+ * anything (not even keep-alive messages), the successful writes to
+ * the sender will keep things going. If the receiver is actively
+ * receiving data, it will ensure that the generator knows that it is
+ * not idle by sending the generator keep-alive messages (since the
+ * generator might be blocked trying to send checksums, it needs to
+ * know that the receiver is active). Thus, as long as one or the
+ * other is successfully doing work, the generator will not timeout. */
+ if (!io_timeout)
+ return;
+
+ t = time(NULL);
+
+ if (allow_keepalive) {
+ /* This may put data into iobuf.msg w/o flushing. */
+ maybe_send_keepalive(t, keepalive_flags);
+ }
+
+ if (!last_io_in)
+ last_io_in = t;
+
+ if (am_receiver)
+ return;
+
+ chk = MAX(last_io_out, last_io_in);
+ if (t - chk >= io_timeout) {
+ if (am_server)
+ msgs2stderr = 1;
+ rprintf(FERROR, "[%s] io timeout after %d seconds -- exiting\n",
+ who_am_i(), (int)(t-chk));
+ exit_cleanup(RERR_TIMEOUT);
+ }
+}
+
+/* It's almost always an error to get an EOF when we're trying to read from the
+ * network, because the protocol is (for the most part) self-terminating.
+ *
+ * There is one case for the receiver when it is at the end of the transfer
+ * (hanging around reading any keep-alive packets that might come its way): if
+ * the sender dies before the generator's kill-signal comes through, we can end
+ * up here needing to loop until the kill-signal arrives. In this situation,
+ * kluge_around_eof will be < 0.
+ *
+ * There is another case for older protocol versions (< 24) where the module
+ * listing was not terminated, so we must ignore an EOF error in that case and
+ * exit. In this situation, kluge_around_eof will be > 0. */
+static NORETURN void whine_about_eof(BOOL allow_kluge)
+{
+ if (kluge_around_eof && allow_kluge) {
+ int i;
+ if (kluge_around_eof > 0)
+ exit_cleanup(0);
+ /* If we're still here after 10 seconds, exit with an error. */
+ for (i = 10*1000/20; i--; )
+ msleep(20);
+ }
+
+ rprintf(FERROR, RSYNC_NAME ": connection unexpectedly closed "
+ "(%s bytes received so far) [%s]\n",
+ big_num(stats.total_read), who_am_i());
+
+ exit_cleanup(RERR_STREAMIO);
+}
+
+/* Do a safe read, handling any needed looping and error handling.
+ * Returns the count of the bytes read, which will only be different
+ * from "len" if we encountered an EOF. This routine is not used on
+ * the socket except very early in the transfer. */
+static size_t safe_read(int fd, char *buf, size_t len)
+{
+ size_t got = 0;
+
+ assert(fd != iobuf.in_fd);
+
+ while (1) {
+ struct timeval tv;
+ fd_set r_fds, e_fds;
+ int cnt;
+
+ FD_ZERO(&r_fds);
+ FD_SET(fd, &r_fds);
+ FD_ZERO(&e_fds);
+ FD_SET(fd, &e_fds);
+ tv.tv_sec = select_timeout;
+ tv.tv_usec = 0;
+
+ cnt = select(fd+1, &r_fds, NULL, &e_fds, &tv);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EBADF) {
+ rsyserr(FERROR, errno, "safe_read select failed");
+ exit_cleanup(RERR_FILEIO);
+ }
+ check_timeout(1, MSK_ALLOW_FLUSH);
+ continue;
+ }
+
+ /*if (FD_ISSET(fd, &e_fds))
+ rprintf(FINFO, "select exception on fd %d\n", fd); */
+
+ if (FD_ISSET(fd, &r_fds)) {
+ ssize_t n = read(fd, buf + got, len - got);
+ if (DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] safe_read(%d)=%" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), fd, (SIZE_T_FMT_CAST)n);
+ }
+ if (n == 0)
+ break;
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ rsyserr(FERROR, errno, "safe_read failed to read %" SIZE_T_FMT_MOD "d bytes",
+ (SIZE_T_FMT_CAST)len);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ if ((got += (size_t)n) == len)
+ break;
+ }
+ }
+
+ return got;
+}
+
+static const char *what_fd_is(int fd)
+{
+ static char buf[20];
+
+ if (fd == sock_f_out)
+ return "socket";
+ else if (fd == iobuf.out_fd)
+ return "message fd";
+ else if (fd == batch_fd)
+ return "batch file";
+ else {
+ snprintf(buf, sizeof buf, "fd %d", fd);
+ return buf;
+ }
+}
+
+/* Do a safe write, handling any needed looping and error handling.
+ * Returns only if everything was successfully written. This routine
+ * is not used on the socket except very early in the transfer. */
+static void safe_write(int fd, const char *buf, size_t len)
+{
+ ssize_t n;
+
+ assert(fd != iobuf.out_fd);
+
+ n = write(fd, buf, len);
+ if ((size_t)n == len)
+ return;
+ if (n < 0) {
+ if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
+ write_failed:
+ rsyserr(FERROR, errno,
+ "safe_write failed to write %" SIZE_T_FMT_MOD "d bytes to %s",
+ (SIZE_T_FMT_CAST)len, what_fd_is(fd));
+ exit_cleanup(RERR_STREAMIO);
+ }
+ } else {
+ buf += n;
+ len -= n;
+ }
+
+ while (len) {
+ struct timeval tv;
+ fd_set w_fds;
+ int cnt;
+
+ FD_ZERO(&w_fds);
+ FD_SET(fd, &w_fds);
+ tv.tv_sec = select_timeout;
+ tv.tv_usec = 0;
+
+ cnt = select(fd + 1, NULL, &w_fds, NULL, &tv);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EBADF) {
+ rsyserr(FERROR, errno, "safe_write select failed on %s", what_fd_is(fd));
+ exit_cleanup(RERR_FILEIO);
+ }
+ if (io_timeout)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ continue;
+ }
+
+ if (FD_ISSET(fd, &w_fds)) {
+ n = write(fd, buf, len);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ goto write_failed;
+ }
+ buf += n;
+ len -= n;
+ }
+ }
+}
+
+/* This is only called when files-from data is known to be available. We read
+ * a chunk of data and put it into the output buffer. */
+static void forward_filesfrom_data(void)
+{
+ ssize_t len;
+
+ len = read(ff_forward_fd, ff_xb.buf + ff_xb.len, ff_xb.size - ff_xb.len);
+ if (len <= 0) {
+ if (len == 0 || errno != EINTR) {
+ /* Send end-of-file marker */
+ ff_forward_fd = -1;
+ write_buf(iobuf.out_fd, "\0\0", ff_lastchar ? 2 : 1);
+ free_xbuf(&ff_xb);
+ if (ff_reenable_multiplex >= 0)
+ io_start_multiplex_out(ff_reenable_multiplex);
+ free_implied_include_partial_string();
+ }
+ return;
+ }
+
+ if (DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] files-from read=%" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), (SIZE_T_FMT_CAST)len);
+ }
+
+#ifdef ICONV_OPTION
+ len += ff_xb.len;
+#endif
+
+ if (!eol_nulls) {
+ char *s = ff_xb.buf + len;
+ /* Transform CR and/or LF into '\0' */
+ while (s-- > ff_xb.buf) {
+ if (*s == '\n' || *s == '\r')
+ *s = '\0';
+ }
+ }
+
+ if (ff_lastchar)
+ ff_xb.pos = 0;
+ else {
+ char *s = ff_xb.buf;
+ /* Last buf ended with a '\0', so don't let this buf start with one. */
+ while (len && *s == '\0')
+ s++, len--;
+ ff_xb.pos = s - ff_xb.buf;
+ }
+
+#ifdef ICONV_OPTION
+ if (filesfrom_convert && len) {
+ char *sob = ff_xb.buf + ff_xb.pos, *s = sob;
+ char *eob = sob + len;
+ int flags = ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_CIRCULAR_OUT;
+ if (ff_lastchar == '\0')
+ flags |= ICB_INIT;
+ /* Convert/send each null-terminated string separately, skipping empties. */
+ while (s != eob) {
+ if (*s++ == '\0') {
+ ff_xb.len = s - sob - 1;
+ add_implied_include(sob, 0);
+ if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
+ exit_cleanup(RERR_PROTOCOL); /* impossible? */
+ write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
+ while (s != eob && *s == '\0')
+ s++;
+ sob = s;
+ ff_xb.pos = sob - ff_xb.buf;
+ flags |= ICB_INIT;
+ }
+ }
+
+ if ((ff_xb.len = s - sob) == 0)
+ ff_lastchar = '\0';
+ else {
+ /* Handle a partial string specially, saving any incomplete chars. */
+ implied_include_partial_string(sob, s);
+ flags &= ~ICB_INCLUDE_INCOMPLETE;
+ if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0) {
+ if (errno == E2BIG)
+ exit_cleanup(RERR_PROTOCOL); /* impossible? */
+ if (ff_xb.pos)
+ memmove(ff_xb.buf, ff_xb.buf + ff_xb.pos, ff_xb.len);
+ }
+ ff_lastchar = 'x'; /* Anything non-zero. */
+ }
+ } else
+#endif
+
+ if (len) {
+ char *f = ff_xb.buf + ff_xb.pos;
+ char *t = ff_xb.buf;
+ char *eob = f + len;
+ char *cur = t;
+ /* Eliminate any multi-'\0' runs. */
+ while (f != eob) {
+ if (!(*t++ = *f++)) {
+ add_implied_include(cur, 0);
+ cur = t;
+ while (f != eob && *f == '\0')
+ f++;
+ }
+ }
+ implied_include_partial_string(cur, t);
+ ff_lastchar = f[-1];
+ if ((len = t - ff_xb.buf) != 0) {
+ /* This will not circle back to perform_io() because we only get
+ * called when there is plenty of room in the output buffer. */
+ write_buf(iobuf.out_fd, ff_xb.buf, len);
+ }
+ }
+}
+
+void reduce_iobuf_size(xbuf *out, size_t new_size)
+{
+ if (new_size < out->size) {
+ /* Avoid weird buffer interactions by only outputting this to stderr. */
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
+ const char *name = out == &iobuf.out ? "iobuf.out"
+ : out == &iobuf.msg ? "iobuf.msg"
+ : NULL;
+ if (name) {
+ rprintf(FINFO, "[%s] reduced size of %s (-%d)\n",
+ who_am_i(), name, (int)(out->size - new_size));
+ }
+ }
+ out->size = new_size;
+ }
+}
+
+void restore_iobuf_size(xbuf *out)
+{
+ if (IOBUF_WAS_REDUCED(out->size)) {
+ size_t new_size = IOBUF_RESTORE_SIZE(out->size);
+ /* Avoid weird buffer interactions by only outputting this to stderr. */
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
+ const char *name = out == &iobuf.out ? "iobuf.out"
+ : out == &iobuf.msg ? "iobuf.msg"
+ : NULL;
+ if (name) {
+ rprintf(FINFO, "[%s] restored size of %s (+%d)\n",
+ who_am_i(), name, (int)(new_size - out->size));
+ }
+ }
+ out->size = new_size;
+ }
+}
+
+static void handle_kill_signal(BOOL flush_ok)
+{
+ got_kill_signal = -1;
+ flush_ok_after_signal = flush_ok;
+ exit_cleanup(RERR_SIGNAL);
+}
+
+/* Perform buffered input and/or output until specified conditions are met.
+ * When given a "needed" read or write request, this returns without doing any
+ * I/O if the needed input bytes or write space is already available. Once I/O
+ * is needed, this will try to do whatever reading and/or writing is currently
+ * possible, up to the maximum buffer allowances, no matter if this is a read
+ * or write request. However, the I/O stops as soon as the required input
+ * bytes or output space is available. If this is not a read request, the
+ * routine may also do some advantageous reading of messages from a multiplexed
+ * input source (which ensures that we don't jam up with everyone in their
+ * "need to write" code and nobody reading the accumulated data that would make
+ * writing possible).
+ *
+ * The iobuf.in, .out and .msg buffers are all circular. Callers need to be
+ * aware that some data copies will need to be split when the bytes wrap around
+ * from the end to the start. In order to help make writing into the output
+ * buffers easier for some operations (such as the use of SIVAL() into the
+ * buffer) a buffer may be temporarily shortened by a small amount, but the
+ * original size will be automatically restored when the .pos wraps to the
+ * start. See also the 3 raw_* iobuf vars that are used in the handling of
+ * MSG_DATA bytes as they are read-from/written-into the buffers.
+ *
+ * When writing, we flush data in the following priority order:
+ *
+ * 1. Finish writing any in-progress MSG_DATA sequence from iobuf.out.
+ *
+ * 2. Write out all the messages from the message buf (if iobuf.msg is active).
+ * Yes, this means that a PIO_NEED_OUTROOM call will completely flush any
+ * messages before getting to the iobuf.out flushing (except for rule 1).
+ *
+ * 3. Write out the raw data from iobuf.out, possibly filling in the multiplexed
+ * MSG_DATA header that was pre-allocated (when output is multiplexed).
+ *
+ * TODO: items for possible future work:
+ *
+ * - Make this routine able to read the generator-to-receiver batch flow?
+ *
+ * Unlike the old routines that this replaces, it is OK to read ahead as far as
+ * we can because the read_a_msg() routine now reads its bytes out of the input
+ * buffer. In the old days, only raw data was in the input buffer, and any
+ * unused raw data in the buf would prevent the reading of socket data. */
+static char *perform_io(size_t needed, int flags)
+{
+ fd_set r_fds, e_fds, w_fds;
+ struct timeval tv;
+ int cnt, max_fd;
+ size_t empty_buf_len = 0;
+ xbuf *out;
+ char *data;
+
+ if (iobuf.in.len == 0 && iobuf.in.pos != 0) {
+ if (iobuf.raw_input_ends_before)
+ iobuf.raw_input_ends_before -= iobuf.in.pos;
+ iobuf.in.pos = 0;
+ }
+
+ switch (flags & PIO_NEED_FLAGS) {
+ case PIO_NEED_INPUT:
+ /* We never resize the circular input buffer. */
+ if (iobuf.in.size < needed) {
+ rprintf(FERROR, "need to read %" SIZE_T_FMT_MOD "d bytes,"
+ " iobuf.in.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
+ (SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.in.size);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
+ rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %sinput)\n",
+ who_am_i(), (SIZE_T_FMT_CAST)needed, flags & PIO_CONSUME_INPUT ? "consume&" : "");
+ }
+ break;
+
+ case PIO_NEED_OUTROOM:
+ /* We never resize the circular output buffer. */
+ if (iobuf.out.size - iobuf.out_empty_len < needed) {
+ fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
+ " iobuf.out.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
+ (SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)(iobuf.out.size - iobuf.out_empty_len));
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
+ rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
+ " outroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), (SIZE_T_FMT_CAST)needed,
+ iobuf.out.len + needed > iobuf.out.size
+ ? (SIZE_T_FMT_CAST)(iobuf.out.len + needed - iobuf.out.size) : (SIZE_T_FMT_CAST)0);
+ }
+ break;
+
+ case PIO_NEED_MSGROOM:
+ /* We never resize the circular message buffer. */
+ if (iobuf.msg.size < needed) {
+ fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
+ " iobuf.msg.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
+ (SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.msg.size);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
+ rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
+ " msgroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), (SIZE_T_FMT_CAST)needed,
+ iobuf.msg.len + needed > iobuf.msg.size
+ ? (SIZE_T_FMT_CAST)(iobuf.msg.len + needed - iobuf.msg.size) : (SIZE_T_FMT_CAST)0);
+ }
+ break;
+
+ case 0:
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
+ rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %d)\n",
+ who_am_i(), (SIZE_T_FMT_CAST)needed, flags);
+ }
+ break;
+
+ default:
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ while (1) {
+ switch (flags & PIO_NEED_FLAGS) {
+ case PIO_NEED_INPUT:
+ if (iobuf.in.len >= needed)
+ goto double_break;
+ break;
+ case PIO_NEED_OUTROOM:
+ /* Note that iobuf.out_empty_len doesn't factor into this check
+ * because iobuf.out.len already holds any needed header len. */
+ if (iobuf.out.len + needed <= iobuf.out.size)
+ goto double_break;
+ break;
+ case PIO_NEED_MSGROOM:
+ if (iobuf.msg.len + needed <= iobuf.msg.size)
+ goto double_break;
+ break;
+ }
+
+ max_fd = -1;
+
+ FD_ZERO(&r_fds);
+ FD_ZERO(&e_fds);
+ if (iobuf.in_fd >= 0 && iobuf.in.size - iobuf.in.len) {
+ if (!read_batch || batch_fd >= 0) {
+ FD_SET(iobuf.in_fd, &r_fds);
+ FD_SET(iobuf.in_fd, &e_fds);
+ }
+ if (iobuf.in_fd > max_fd)
+ max_fd = iobuf.in_fd;
+ }
+
+ /* Only do more filesfrom processing if there is enough room in the out buffer. */
+ if (ff_forward_fd >= 0 && iobuf.out.size - iobuf.out.len > FILESFROM_BUFLEN*2) {
+ FD_SET(ff_forward_fd, &r_fds);
+ if (ff_forward_fd > max_fd)
+ max_fd = ff_forward_fd;
+ }
+
+ FD_ZERO(&w_fds);
+ if (iobuf.out_fd >= 0) {
+ if (iobuf.raw_flushing_ends_before
+ || (!iobuf.msg.len && iobuf.out.len > iobuf.out_empty_len && !(flags & PIO_NEED_MSGROOM))) {
+ if (OUT_MULTIPLEXED && !iobuf.raw_flushing_ends_before) {
+ /* The iobuf.raw_flushing_ends_before value can point off the end
+ * of the iobuf.out buffer for a while, for easier subtracting. */
+ iobuf.raw_flushing_ends_before = iobuf.out.pos + iobuf.out.len;
+
+ SIVAL(iobuf.out.buf + iobuf.raw_data_header_pos, 0,
+ ((MPLEX_BASE + (int)MSG_DATA)<<24) + iobuf.out.len - 4);
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
+ rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
+ who_am_i(), (int)MSG_DATA, (SIZE_T_FMT_CAST)iobuf.out.len - 4);
+ }
+
+ /* reserve room for the next MSG_DATA header */
+ iobuf.raw_data_header_pos = iobuf.raw_flushing_ends_before;
+ if (iobuf.raw_data_header_pos >= iobuf.out.size)
+ iobuf.raw_data_header_pos -= iobuf.out.size;
+ else if (iobuf.raw_data_header_pos + 4 > iobuf.out.size) {
+ /* The 4-byte header won't fit at the end of the buffer,
+ * so we'll temporarily reduce the output buffer's size
+ * and put the header at the start of the buffer. */
+ reduce_iobuf_size(&iobuf.out, iobuf.raw_data_header_pos);
+ iobuf.raw_data_header_pos = 0;
+ }
+ /* Yes, it is possible for this to make len > size for a while. */
+ iobuf.out.len += 4;
+ }
+
+ empty_buf_len = iobuf.out_empty_len;
+ out = &iobuf.out;
+ } else if (iobuf.msg.len) {
+ empty_buf_len = 0;
+ out = &iobuf.msg;
+ } else
+ out = NULL;
+ if (out) {
+ FD_SET(iobuf.out_fd, &w_fds);
+ if (iobuf.out_fd > max_fd)
+ max_fd = iobuf.out_fd;
+ }
+ } else
+ out = NULL;
+
+ if (max_fd < 0) {
+ switch (flags & PIO_NEED_FLAGS) {
+ case PIO_NEED_INPUT:
+ iobuf.in.len = 0;
+ if (kluge_around_eof == 2)
+ exit_cleanup(0);
+ if (iobuf.in_fd == -2)
+ whine_about_eof(True);
+ rprintf(FERROR, "error in perform_io: no fd for input.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ case PIO_NEED_OUTROOM:
+ case PIO_NEED_MSGROOM:
+ msgs2stderr = 1;
+ drain_multiplex_messages();
+ if (iobuf.out_fd == -2)
+ whine_about_eof(True);
+ rprintf(FERROR, "error in perform_io: no fd for output.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ default:
+ /* No stated needs, so I guess this is OK. */
+ break;
+ }
+ break;
+ }
+
+ if (got_kill_signal > 0)
+ handle_kill_signal(True);
+
+ if (extra_flist_sending_enabled) {
+ if (file_total - file_old_total < MAX_FILECNT_LOOKAHEAD && IN_MULTIPLEXED_AND_READY)
+ tv.tv_sec = 0;
+ else {
+ extra_flist_sending_enabled = False;
+ tv.tv_sec = select_timeout;
+ }
+ } else
+ tv.tv_sec = select_timeout;
+ tv.tv_usec = 0;
+
+ cnt = select(max_fd + 1, &r_fds, &w_fds, &e_fds, &tv);
+
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EBADF) {
+ msgs2stderr = 1;
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ if (extra_flist_sending_enabled) {
+ extra_flist_sending_enabled = False;
+ send_extra_file_list(sock_f_out, -1);
+ extra_flist_sending_enabled = !flist_eof;
+ } else
+ check_timeout((flags & PIO_NEED_INPUT) != 0, 0);
+ FD_ZERO(&r_fds); /* Just in case... */
+ FD_ZERO(&w_fds);
+ }
+
+ if (iobuf.in_fd >= 0 && FD_ISSET(iobuf.in_fd, &r_fds)) {
+ size_t len, pos = iobuf.in.pos + iobuf.in.len;
+ ssize_t n;
+ if (pos >= iobuf.in.size) {
+ pos -= iobuf.in.size;
+ len = iobuf.in.size - iobuf.in.len;
+ } else
+ len = iobuf.in.size - pos;
+ if ((n = read(iobuf.in_fd, iobuf.in.buf + pos, len)) <= 0) {
+ if (n == 0) {
+ /* Signal that input has become invalid. */
+ if (!read_batch || batch_fd < 0 || am_generator)
+ iobuf.in_fd = -2;
+ batch_fd = -1;
+ continue;
+ }
+ if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
+ n = 0;
+ else {
+ /* Don't write errors on a dead socket. */
+ if (iobuf.in_fd == sock_f_in) {
+ if (am_sender)
+ msgs2stderr = 1;
+ rsyserr(FERROR_SOCKET, errno, "read error");
+ } else
+ rsyserr(FERROR, errno, "read error");
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ }
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] recv=%" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), (SIZE_T_FMT_CAST)n);
+ }
+
+ if (io_timeout) {
+ last_io_in = time(NULL);
+ if (io_timeout && flags & PIO_NEED_INPUT)
+ maybe_send_keepalive(last_io_in, 0);
+ }
+ stats.total_read += n;
+
+ iobuf.in.len += n;
+ }
+
+ if (stop_at_utime && time(NULL) >= stop_at_utime) {
+ rprintf(FERROR, "stopping at requested limit\n");
+ exit_cleanup(RERR_TIMEOUT);
+ }
+
+ if (out && FD_ISSET(iobuf.out_fd, &w_fds)) {
+ size_t len = iobuf.raw_flushing_ends_before ? iobuf.raw_flushing_ends_before - out->pos : out->len;
+ ssize_t n;
+
+ if (bwlimit_writemax && len > bwlimit_writemax)
+ len = bwlimit_writemax;
+
+ if (out->pos + len > out->size)
+ len = out->size - out->pos;
+ if ((n = write(iobuf.out_fd, out->buf + out->pos, len)) <= 0) {
+ if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
+ n = 0;
+ else {
+ /* Don't write errors on a dead socket. */
+ msgs2stderr = 1;
+ iobuf.out_fd = -2;
+ iobuf.out.len = iobuf.msg.len = iobuf.raw_flushing_ends_before = 0;
+ rsyserr(FERROR_SOCKET, errno, "write error");
+ drain_multiplex_messages();
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ }
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] %s sent=%" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), out == &iobuf.out ? "out" : "msg", (SIZE_T_FMT_CAST)n);
+ }
+
+ if (io_timeout)
+ last_io_out = time(NULL);
+ stats.total_written += n;
+
+ if (bwlimit_writemax)
+ sleep_for_bwlimit(n);
+
+ if ((out->pos += n) == out->size) {
+ if (iobuf.raw_flushing_ends_before)
+ iobuf.raw_flushing_ends_before -= out->size;
+ out->pos = 0;
+ restore_iobuf_size(out);
+ } else if (out->pos == iobuf.raw_flushing_ends_before)
+ iobuf.raw_flushing_ends_before = 0;
+ if ((out->len -= n) == empty_buf_len) {
+ out->pos = 0;
+ restore_iobuf_size(out);
+ if (empty_buf_len)
+ iobuf.raw_data_header_pos = 0;
+ }
+ }
+
+ if (got_kill_signal > 0)
+ handle_kill_signal(True);
+
+ /* We need to help prevent deadlock by doing what reading
+ * we can whenever we are here trying to write. */
+ if (IN_MULTIPLEXED_AND_READY && !(flags & PIO_NEED_INPUT)) {
+ while (!iobuf.raw_input_ends_before && iobuf.in.len > 512)
+ read_a_msg();
+ if (flist_receiving_enabled && iobuf.in.len > 512)
+ wait_for_receiver(); /* generator only */
+ }
+
+ if (ff_forward_fd >= 0 && FD_ISSET(ff_forward_fd, &r_fds)) {
+ /* This can potentially flush all output and enable
+ * multiplexed output, so keep this last in the loop
+ * and be sure to not cache anything that would break
+ * such a change. */
+ forward_filesfrom_data();
+ }
+ }
+ double_break:
+
+ if (got_kill_signal > 0)
+ handle_kill_signal(True);
+
+ data = iobuf.in.buf + iobuf.in.pos;
+
+ if (flags & PIO_CONSUME_INPUT) {
+ iobuf.in.len -= needed;
+ iobuf.in.pos += needed;
+ if (iobuf.in.pos == iobuf.raw_input_ends_before)
+ iobuf.raw_input_ends_before = 0;
+ if (iobuf.in.pos >= iobuf.in.size) {
+ iobuf.in.pos -= iobuf.in.size;
+ if (iobuf.raw_input_ends_before)
+ iobuf.raw_input_ends_before -= iobuf.in.size;
+ }
+ }
+
+ return data;
+}
+
+static void raw_read_buf(char *buf, size_t len)
+{
+ size_t pos = iobuf.in.pos;
+ char *data = perform_io(len, PIO_INPUT_AND_CONSUME);
+ if (iobuf.in.pos <= pos && len) {
+ size_t siz = len - iobuf.in.pos;
+ memcpy(buf, data, siz);
+ memcpy(buf + siz, iobuf.in.buf, iobuf.in.pos);
+ } else
+ memcpy(buf, data, len);
+}
+
+static int32 raw_read_int(void)
+{
+ char *data, buf[4];
+ if (iobuf.in.size - iobuf.in.pos >= 4)
+ data = perform_io(4, PIO_INPUT_AND_CONSUME);
+ else
+ raw_read_buf(data = buf, 4);
+ return IVAL(data, 0);
+}
+
+void noop_io_until_death(void)
+{
+ char buf[1024];
+
+ if (!iobuf.in.buf || !iobuf.out.buf || iobuf.in_fd < 0 || iobuf.out_fd < 0 || kluge_around_eof)
+ return;
+
+ /* If we're talking to a daemon over a socket, don't short-circuit this logic */
+ if (msgs2stderr && daemon_connection >= 0)
+ return;
+
+ kluge_around_eof = 2;
+ /* Setting an I/O timeout ensures that if something inexplicably weird
+ * happens, we won't hang around forever. */
+ if (!io_timeout)
+ set_io_timeout(60);
+
+ while (1)
+ read_buf(iobuf.in_fd, buf, sizeof buf);
+}
+
+/* Buffer a message for the multiplexed output stream. Is not used for (normal) MSG_DATA. */
+int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
+{
+ char *hdr;
+ size_t needed, pos;
+ BOOL want_debug = DEBUG_GTE(IO, 1) && convert >= 0 && (msgs2stderr == 1 || code != MSG_INFO);
+
+ if (!OUT_MULTIPLEXED)
+ return 0;
+
+ if (want_debug) {
+ rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
+ who_am_i(), (int)code, (SIZE_T_FMT_CAST)len);
+ }
+
+ /* When checking for enough free space for this message, we need to
+ * make sure that there is space for the 4-byte header, plus we'll
+ * assume that we may waste up to 3 bytes (if the header doesn't fit
+ * at the physical end of the buffer). */
+#ifdef ICONV_OPTION
+ if (convert > 0 && ic_send == (iconv_t)-1)
+ convert = 0;
+ if (convert > 0) {
+ /* Ensuring double-size room leaves space for maximal conversion expansion. */
+ needed = len*2 + 4 + 3;
+ } else
+#endif
+ needed = len + 4 + 3;
+ if (iobuf.msg.len + needed > iobuf.msg.size) {
+ if (am_sender)
+ perform_io(needed, PIO_NEED_MSGROOM);
+ else { /* We sometimes allow the iobuf.msg size to increase to avoid a deadlock. */
+ size_t old_size = iobuf.msg.size;
+ restore_iobuf_size(&iobuf.msg);
+ realloc_xbuf(&iobuf.msg, iobuf.msg.size * 2);
+ if (iobuf.msg.pos + iobuf.msg.len > old_size)
+ memcpy(iobuf.msg.buf + old_size, iobuf.msg.buf, iobuf.msg.pos + iobuf.msg.len - old_size);
+ }
+ }
+
+ pos = iobuf.msg.pos + iobuf.msg.len; /* Must be set after any flushing. */
+ if (pos >= iobuf.msg.size)
+ pos -= iobuf.msg.size;
+ else if (pos + 4 > iobuf.msg.size) {
+ /* The 4-byte header won't fit at the end of the buffer,
+ * so we'll temporarily reduce the message buffer's size
+ * and put the header at the start of the buffer. */
+ reduce_iobuf_size(&iobuf.msg, pos);
+ pos = 0;
+ }
+ hdr = iobuf.msg.buf + pos;
+
+ iobuf.msg.len += 4; /* Allocate room for the coming header bytes. */
+
+#ifdef ICONV_OPTION
+ if (convert > 0) {
+ xbuf inbuf;
+
+ INIT_XBUF(inbuf, (char*)buf, len, (size_t)-1);
+
+ len = iobuf.msg.len;
+ iconvbufs(ic_send, &inbuf, &iobuf.msg,
+ ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_CIRCULAR_OUT | ICB_INIT);
+ if (inbuf.len > 0) {
+ rprintf(FERROR, "overflowed iobuf.msg buffer in send_msg");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ len = iobuf.msg.len - len;
+ } else
+#endif
+ {
+ size_t siz;
+
+ if ((pos += 4) == iobuf.msg.size)
+ pos = 0;
+
+ /* Handle a split copy if we wrap around the end of the circular buffer. */
+ if (pos >= iobuf.msg.pos && (siz = iobuf.msg.size - pos) < len) {
+ memcpy(iobuf.msg.buf + pos, buf, siz);
+ memcpy(iobuf.msg.buf, buf + siz, len - siz);
+ } else
+ memcpy(iobuf.msg.buf + pos, buf, len);
+
+ iobuf.msg.len += len;
+ }
+
+ SIVAL(hdr, 0, ((MPLEX_BASE + (int)code)<<24) + len);
+
+ if (want_debug && convert > 0) {
+ rprintf(FINFO, "[%s] converted msg len=%" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), (SIZE_T_FMT_CAST)len);
+ }
+
+ return 1;
+}
+
+void send_msg_int(enum msgcode code, int num)
+{
+ char numbuf[4];
+
+ if (DEBUG_GTE(IO, 1))
+ rprintf(FINFO, "[%s] send_msg_int(%d, %d)\n", who_am_i(), (int)code, num);
+
+ SIVAL(numbuf, 0, num);
+ send_msg(code, numbuf, 4, -1);
+}
+
+void send_msg_success(const char *fname, int num)
+{
+ if (local_server) {
+ STRUCT_STAT st;
+
+ if (DEBUG_GTE(IO, 1))
+ rprintf(FINFO, "[%s] send_msg_success(%d)\n", who_am_i(), num);
+
+ if (stat(fname, &st) < 0)
+ memset(&st, 0, sizeof (STRUCT_STAT));
+ SIVAL(num_dev_ino_buf, 0, num);
+ SIVAL64(num_dev_ino_buf, 4, st.st_dev);
+ SIVAL64(num_dev_ino_buf, 4+8, st.st_ino);
+ send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
+ } else
+ send_msg_int(MSG_SUCCESS, num);
+}
+
+static void got_flist_entry_status(enum festatus status, int ndx)
+{
+ struct file_list *flist = flist_for_ndx(ndx, "got_flist_entry_status");
+
+ if (remove_source_files) {
+ active_filecnt--;
+ active_bytecnt -= F_LENGTH(flist->files[ndx - flist->ndx_start]);
+ }
+
+ if (inc_recurse)
+ flist->in_progress--;
+
+ switch (status) {
+ case FES_SUCCESS:
+ if (remove_source_files) {
+ if (local_server)
+ send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
+ else
+ send_msg_int(MSG_SUCCESS, ndx);
+ }
+ /* FALL THROUGH */
+ case FES_NO_SEND:
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links) {
+ struct file_struct *file = flist->files[ndx - flist->ndx_start];
+ if (F_IS_HLINKED(file)) {
+ if (status == FES_NO_SEND)
+ flist_ndx_push(&hlink_list, -2); /* indicates a failure follows */
+ flist_ndx_push(&hlink_list, ndx);
+ if (inc_recurse)
+ flist->in_progress++;
+ }
+ }
+#endif
+ break;
+ case FES_REDO:
+ if (read_batch) {
+ if (inc_recurse)
+ flist->in_progress++;
+ break;
+ }
+ if (inc_recurse)
+ flist->to_redo++;
+ flist_ndx_push(&redo_list, ndx);
+ break;
+ }
+}
+
+/* Note the fds used for the main socket (which might really be a pipe
+ * for a local transfer, but we can ignore that). */
+void io_set_sock_fds(int f_in, int f_out)
+{
+ sock_f_in = f_in;
+ sock_f_out = f_out;
+}
+
+void set_io_timeout(int secs)
+{
+ io_timeout = secs;
+ allowed_lull = (io_timeout + 1) / 2;
+
+ if (!io_timeout || allowed_lull > SELECT_TIMEOUT)
+ select_timeout = SELECT_TIMEOUT;
+ else
+ select_timeout = allowed_lull;
+
+ if (read_batch)
+ allowed_lull = 0;
+}
+
+static void check_for_d_option_error(const char *msg)
+{
+ static char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
+ char *colon;
+ int saw_d = 0;
+
+ if (*msg != 'r'
+ || strncmp(msg, REMOTE_OPTION_ERROR, sizeof REMOTE_OPTION_ERROR - 1) != 0)
+ return;
+
+ msg += sizeof REMOTE_OPTION_ERROR - 1;
+ if (*msg == '-' || (colon = strchr(msg, ':')) == NULL
+ || strncmp(colon, REMOTE_OPTION_ERROR2, sizeof REMOTE_OPTION_ERROR2 - 1) != 0)
+ return;
+
+ for ( ; *msg != ':'; msg++) {
+ if (*msg == 'd')
+ saw_d = 1;
+ else if (*msg == 'e')
+ break;
+ else if (strchr(rsync263_opts, *msg) == NULL)
+ return;
+ }
+
+ if (saw_d) {
+ rprintf(FWARNING, "*** Try using \"--old-d\" if remote rsync is <= 2.6.3 ***\n");
+ }
+}
+
+/* This is used by the generator to limit how many file transfers can
+ * be active at once when --remove-source-files is specified. Without
+ * this, sender-side deletions were mostly happening at the end. */
+void increment_active_files(int ndx, int itemizing, enum logcode code)
+{
+ while (1) {
+ /* TODO: tune these limits? */
+ int limit = active_bytecnt >= 128*1024 ? 10 : 50;
+ if (active_filecnt < limit)
+ break;
+ check_for_finished_files(itemizing, code, 0);
+ if (active_filecnt < limit)
+ break;
+ wait_for_receiver();
+ }
+
+ active_filecnt++;
+ active_bytecnt += F_LENGTH(cur_flist->files[ndx - cur_flist->ndx_start]);
+}
+
+int get_redo_num(void)
+{
+ return flist_ndx_pop(&redo_list);
+}
+
+int get_hlink_num(void)
+{
+ return flist_ndx_pop(&hlink_list);
+}
+
+/* When we're the receiver and we have a local --files-from list of names
+ * that needs to be sent over the socket to the sender, we have to do two
+ * things at the same time: send the sender a list of what files we're
+ * processing and read the incoming file+info list from the sender. We do
+ * this by making recv_file_list() call forward_filesfrom_data(), which
+ * will ensure that we forward data to the sender until we get some data
+ * for recv_file_list() to use. */
+void start_filesfrom_forwarding(int fd)
+{
+ if (protocol_version < 31 && OUT_MULTIPLEXED) {
+ /* Older protocols send the files-from data w/o packaging
+ * it in multiplexed I/O packets, so temporarily switch
+ * to buffered I/O to match this behavior. */
+ iobuf.msg.pos = iobuf.msg.len = 0; /* Be extra sure no messages go out. */
+ ff_reenable_multiplex = io_end_multiplex_out(MPLX_TO_BUFFERED);
+ }
+ ff_forward_fd = fd;
+
+ alloc_xbuf(&ff_xb, FILESFROM_BUFLEN);
+}
+
+/* Read a line into the "buf" buffer. */
+int read_line(int fd, char *buf, size_t bufsiz, int flags)
+{
+ char ch, *s, *eob;
+
+#ifdef ICONV_OPTION
+ if (flags & RL_CONVERT && iconv_buf.size < bufsiz)
+ realloc_xbuf(&iconv_buf, ROUND_UP_1024(bufsiz) + 1024);
+#endif
+
+ start:
+#ifdef ICONV_OPTION
+ s = flags & RL_CONVERT ? iconv_buf.buf : buf;
+#else
+ s = buf;
+#endif
+ eob = s + bufsiz - 1;
+ while (1) {
+ /* We avoid read_byte() for files because files can return an EOF. */
+ if (fd == iobuf.in_fd)
+ ch = read_byte(fd);
+ else if (safe_read(fd, &ch, 1) == 0)
+ break;
+ if (flags & RL_EOL_NULLS ? ch == '\0' : (ch == '\r' || ch == '\n')) {
+ /* Skip empty lines if dumping comments. */
+ if (flags & RL_DUMP_COMMENTS && s == buf)
+ continue;
+ break;
+ }
+ if (s < eob)
+ *s++ = ch;
+ }
+ *s = '\0';
+
+ if (flags & RL_DUMP_COMMENTS && (*buf == '#' || *buf == ';'))
+ goto start;
+
+#ifdef ICONV_OPTION
+ if (flags & RL_CONVERT) {
+ xbuf outbuf;
+ INIT_XBUF(outbuf, buf, 0, bufsiz);
+ iconv_buf.pos = 0;
+ iconv_buf.len = s - iconv_buf.buf;
+ iconvbufs(ic_recv, &iconv_buf, &outbuf,
+ ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT);
+ outbuf.buf[outbuf.len] = '\0';
+ return outbuf.len;
+ }
+#endif
+
+ return s - buf;
+}
+
+void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
+ char ***argv_p, int *argc_p, char **request_p)
+{
+ int maxargs = MAX_ARGS;
+ int dot_pos = 0, argc = 0, request_len = 0;
+ char **argv, *p;
+ int rl_flags = (rl_nulls ? RL_EOL_NULLS : 0);
+
+#ifdef ICONV_OPTION
+ rl_flags |= (protect_args && ic_recv != (iconv_t)-1 ? RL_CONVERT : 0);
+#endif
+
+ argv = new_array(char *, maxargs);
+ if (mod_name && !protect_args)
+ argv[argc++] = "rsyncd";
+
+ if (request_p)
+ *request_p = NULL;
+
+ while (1) {
+ if (read_line(f_in, buf, bufsiz, rl_flags) == 0)
+ break;
+
+ if (argc == maxargs-1) {
+ maxargs += MAX_ARGS;
+ argv = realloc_array(argv, char *, maxargs);
+ }
+
+ if (dot_pos) {
+ if (request_p && request_len < 1024) {
+ int len = strlen(buf);
+ if (request_len)
+ request_p[0][request_len++] = ' ';
+ *request_p = realloc_array(*request_p, char, request_len + len + 1);
+ memcpy(*request_p + request_len, buf, len + 1);
+ request_len += len;
+ }
+ if (mod_name)
+ glob_expand_module(mod_name, buf, &argv, &argc, &maxargs);
+ else
+ glob_expand(buf, &argv, &argc, &maxargs);
+ } else {
+ p = strdup(buf);
+ argv[argc++] = p;
+ if (*p == '.' && p[1] == '\0')
+ dot_pos = argc;
+ }
+ }
+ argv[argc] = NULL;
+
+ glob_expand(NULL, NULL, NULL, NULL);
+
+ *argc_p = argc;
+ *argv_p = argv;
+}
+
+BOOL io_start_buffering_out(int f_out)
+{
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_buffering_out(%d)\n", who_am_i(), f_out);
+
+ if (iobuf.out.buf) {
+ if (iobuf.out_fd == -1)
+ iobuf.out_fd = f_out;
+ else
+ assert(f_out == iobuf.out_fd);
+ return False;
+ }
+
+ alloc_xbuf(&iobuf.out, ROUND_UP_1024(IO_BUFFER_SIZE * 2));
+ iobuf.out_fd = f_out;
+
+ return True;
+}
+
+BOOL io_start_buffering_in(int f_in)
+{
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_buffering_in(%d)\n", who_am_i(), f_in);
+
+ if (iobuf.in.buf) {
+ if (iobuf.in_fd == -1)
+ iobuf.in_fd = f_in;
+ else
+ assert(f_in == iobuf.in_fd);
+ return False;
+ }
+
+ alloc_xbuf(&iobuf.in, ROUND_UP_1024(IO_BUFFER_SIZE));
+ iobuf.in_fd = f_in;
+
+ return True;
+}
+
+void io_end_buffering_in(BOOL free_buffers)
+{
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] io_end_buffering_in(IOBUF_%s_BUFS)\n",
+ who_am_i(), free_buffers ? "FREE" : "KEEP");
+ }
+
+ if (free_buffers)
+ free_xbuf(&iobuf.in);
+ else
+ iobuf.in.pos = iobuf.in.len = 0;
+
+ iobuf.in_fd = -1;
+}
+
+void io_end_buffering_out(BOOL free_buffers)
+{
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
+ rprintf(FINFO, "[%s] io_end_buffering_out(IOBUF_%s_BUFS)\n",
+ who_am_i(), free_buffers ? "FREE" : "KEEP");
+ }
+
+ io_flush(FULL_FLUSH);
+
+ if (free_buffers) {
+ free_xbuf(&iobuf.out);
+ free_xbuf(&iobuf.msg);
+ }
+
+ iobuf.out_fd = -1;
+}
+
+void maybe_flush_socket(int important)
+{
+ if (flist_eof && iobuf.out.buf && iobuf.out.len > iobuf.out_empty_len
+ && (important || time(NULL) - last_io_out >= 5))
+ io_flush(NORMAL_FLUSH);
+}
+
+/* Older rsync versions used to send either a MSG_NOOP (protocol 30) or a
+ * raw-data-based keep-alive (protocol 29), both of which implied forwarding of
+ * the message through the sender. Since the new timeout method does not need
+ * any forwarding, we just send an empty MSG_DATA message, which works with all
+ * rsync versions. This avoids any message forwarding, and leaves the raw-data
+ * stream alone (since we can never be quite sure if that stream is in the
+ * right state for a keep-alive message). */
+void maybe_send_keepalive(time_t now, int flags)
+{
+ if (flags & MSK_ACTIVE_RECEIVER)
+ last_io_in = now; /* Fudge things when we're working hard on the files. */
+
+ /* Early in the transfer (before the receiver forks) the receiving side doesn't
+ * care if it hasn't sent data in a while as long as it is receiving data (in
+ * fact, a pre-3.1.0 rsync would die if we tried to send it a keep alive during
+ * this time). So, if we're an early-receiving proc, just return and let the
+ * incoming data determine if we timeout. */
+ if (!am_sender && !am_receiver && !am_generator)
+ return;
+
+ if (now - last_io_out >= allowed_lull) {
+ /* The receiver is special: it only sends keep-alive messages if it is
+ * actively receiving data. Otherwise, it lets the generator timeout. */
+ if (am_receiver && now - last_io_in >= io_timeout)
+ return;
+
+ if (!iobuf.msg.len && iobuf.out.len == iobuf.out_empty_len)
+ send_msg(MSG_DATA, "", 0, 0);
+ if (!(flags & MSK_ALLOW_FLUSH)) {
+ /* Let the caller worry about writing out the data. */
+ } else if (iobuf.msg.len)
+ perform_io(iobuf.msg.size - iobuf.msg.len + 1, PIO_NEED_MSGROOM);
+ else if (iobuf.out.len > iobuf.out_empty_len)
+ io_flush(NORMAL_FLUSH);
+ }
+}
+
+void start_flist_forward(int ndx)
+{
+ write_int(iobuf.out_fd, ndx);
+ forward_flist_data = 1;
+}
+
+void stop_flist_forward(void)
+{
+ forward_flist_data = 0;
+}
+
+/* Read a message from a multiplexed source. */
+static void read_a_msg(void)
+{
+ char data[BIGPATHBUFLEN];
+ int tag, val;
+ size_t msg_bytes;
+
+ /* This ensures that perform_io() does not try to do any message reading
+ * until we've read all of the data for this message. We should also
+ * try to avoid calling things that will cause data to be written via
+ * perform_io() prior to this being reset to 1. */
+ iobuf.in_multiplexed = -1;
+
+ tag = raw_read_int();
+
+ msg_bytes = tag & 0xFFFFFF;
+ tag = (tag >> 24) - MPLEX_BASE;
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
+ rprintf(FINFO, "[%s] got msg=%d, len=%" SIZE_T_FMT_MOD "d\n",
+ who_am_i(), (int)tag, (SIZE_T_FMT_CAST)msg_bytes);
+ }
+
+ switch (tag) {
+ case MSG_DATA:
+ assert(iobuf.raw_input_ends_before == 0);
+ /* Though this does not yet read the data, we do mark where in
+ * the buffer the msg data will end once it is read. It is
+ * possible that this points off the end of the buffer, in
+ * which case the gradual reading of the input stream will
+ * cause this value to wrap around and eventually become real. */
+ if (msg_bytes)
+ iobuf.raw_input_ends_before = iobuf.in.pos + msg_bytes;
+ iobuf.in_multiplexed = 1;
+ break;
+ case MSG_STATS:
+ if (msg_bytes != sizeof stats.total_read || !am_generator)
+ goto invalid_msg;
+ raw_read_buf((char*)&stats.total_read, sizeof stats.total_read);
+ iobuf.in_multiplexed = 1;
+ break;
+ case MSG_REDO:
+ if (msg_bytes != 4 || !am_generator)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ got_flist_entry_status(FES_REDO, val);
+ break;
+ case MSG_IO_ERROR:
+ if (msg_bytes != 4)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ io_error |= val;
+ if (am_receiver)
+ send_msg_int(MSG_IO_ERROR, val);
+ break;
+ case MSG_IO_TIMEOUT:
+ if (msg_bytes != 4 || am_server || am_generator)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ if (!io_timeout || io_timeout > val) {
+ if (INFO_GTE(MISC, 2))
+ rprintf(FINFO, "Setting --timeout=%d to match server\n", val);
+ set_io_timeout(val);
+ }
+ break;
+ case MSG_NOOP:
+ /* Support protocol-30 keep-alive method. */
+ if (msg_bytes != 0)
+ goto invalid_msg;
+ iobuf.in_multiplexed = 1;
+ if (am_sender)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ break;
+ case MSG_DELETED:
+ if (msg_bytes >= sizeof data)
+ goto overflow;
+ if (am_generator) {
+ raw_read_buf(data, msg_bytes);
+ iobuf.in_multiplexed = 1;
+ send_msg(MSG_DELETED, data, msg_bytes, 1);
+ break;
+ }
+#ifdef ICONV_OPTION
+ if (ic_recv != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+ char ibuf[512];
+ int add_null = 0;
+ int flags = ICB_INCLUDE_BAD | ICB_INIT;
+
+ INIT_CONST_XBUF(outbuf, data);
+ INIT_XBUF(inbuf, ibuf, 0, (size_t)-1);
+
+ while (msg_bytes) {
+ size_t len = msg_bytes > sizeof ibuf - inbuf.len ? sizeof ibuf - inbuf.len : msg_bytes;
+ raw_read_buf(ibuf + inbuf.len, len);
+ inbuf.pos = 0;
+ inbuf.len += len;
+ if (!(msg_bytes -= len) && !ibuf[inbuf.len-1])
+ inbuf.len--, add_null = 1;
+ if (iconvbufs(ic_send, &inbuf, &outbuf, flags) < 0) {
+ if (errno == E2BIG)
+ goto overflow;
+ /* Buffer ended with an incomplete char, so move the
+ * bytes to the start of the buffer and continue. */
+ memmove(ibuf, ibuf + inbuf.pos, inbuf.len);
+ }
+ flags &= ~ICB_INIT;
+ }
+ if (add_null) {
+ if (outbuf.len == outbuf.size)
+ goto overflow;
+ outbuf.buf[outbuf.len++] = '\0';
+ }
+ msg_bytes = outbuf.len;
+ } else
+#endif
+ raw_read_buf(data, msg_bytes);
+ iobuf.in_multiplexed = 1;
+ /* A directory name was sent with the trailing null */
+ if (msg_bytes > 0 && !data[msg_bytes-1])
+ log_delete(data, S_IFDIR);
+ else {
+ data[msg_bytes] = '\0';
+ log_delete(data, S_IFREG);
+ }
+ break;
+ case MSG_SUCCESS:
+ if (msg_bytes != (local_server ? 4+8+8 : 4)) {
+ invalid_msg:
+ rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
+ tag, (unsigned long)msg_bytes, who_am_i(),
+ inc_recurse ? "/inc" : "");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ raw_read_buf(num_dev_ino_buf, msg_bytes);
+ val = IVAL(num_dev_ino_buf, 0);
+ iobuf.in_multiplexed = 1;
+ if (am_generator)
+ got_flist_entry_status(FES_SUCCESS, val);
+ else
+ successful_send(val);
+ break;
+ case MSG_NO_SEND:
+ if (msg_bytes != 4)
+ goto invalid_msg;
+ val = raw_read_int();
+ iobuf.in_multiplexed = 1;
+ if (am_generator)
+ got_flist_entry_status(FES_NO_SEND, val);
+ else
+ send_msg_int(MSG_NO_SEND, val);
+ break;
+ case MSG_ERROR_SOCKET:
+ case MSG_ERROR_UTF8:
+ case MSG_CLIENT:
+ case MSG_LOG:
+ if (!am_generator)
+ goto invalid_msg;
+ if (tag == MSG_ERROR_SOCKET)
+ msgs2stderr = 1;
+ /* FALL THROUGH */
+ case MSG_INFO:
+ case MSG_ERROR:
+ case MSG_ERROR_XFER:
+ case MSG_WARNING:
+ if (msg_bytes >= sizeof data) {
+ overflow:
+ rprintf(FERROR,
+ "multiplexing overflow %d:%lu [%s%s]\n",
+ tag, (unsigned long)msg_bytes, who_am_i(),
+ inc_recurse ? "/inc" : "");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ raw_read_buf(data, msg_bytes);
+ /* We don't set in_multiplexed value back to 1 before writing this message
+ * because the write might loop back and read yet another message, over and
+ * over again, while waiting for room to put the message in the msg buffer. */
+ rwrite((enum logcode)tag, data, msg_bytes, !am_generator);
+ iobuf.in_multiplexed = 1;
+ if (first_message) {
+ if (list_only && !am_sender && tag == 1 && msg_bytes < sizeof data) {
+ data[msg_bytes] = '\0';
+ check_for_d_option_error(data);
+ }
+ first_message = 0;
+ }
+ break;
+ case MSG_ERROR_EXIT:
+ if (msg_bytes == 4)
+ val = raw_read_int();
+ else if (msg_bytes == 0)
+ val = 0;
+ else
+ goto invalid_msg;
+ iobuf.in_multiplexed = 1;
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] got MSG_ERROR_EXIT with %" SIZE_T_FMT_MOD "d bytes\n",
+ who_am_i(), (SIZE_T_FMT_CAST)msg_bytes);
+ }
+ if (msg_bytes == 0) {
+ if (!am_sender && !am_generator) {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT (len 0)\n",
+ who_am_i());
+ }
+ send_msg(MSG_ERROR_EXIT, "", 0, 0);
+ io_flush(FULL_FLUSH);
+ }
+ } else if (protocol_version >= 31) {
+ if (am_generator || am_receiver) {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT with exit_code %d\n",
+ who_am_i(), val);
+ }
+ send_msg_int(MSG_ERROR_EXIT, val);
+ } else {
+ if (DEBUG_GTE(EXIT, 3)) {
+ rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT (len 0)\n",
+ who_am_i());
+ }
+ send_msg(MSG_ERROR_EXIT, "", 0, 0);
+ }
+ }
+ /* Send a negative linenum so that we don't end up
+ * with a duplicate exit message. */
+ _exit_cleanup(val, __FILE__, 0 - __LINE__);
+ default:
+ rprintf(FERROR, "unexpected tag %d [%s%s]\n",
+ tag, who_am_i(), inc_recurse ? "/inc" : "");
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ assert(iobuf.in_multiplexed > 0);
+}
+
+static void drain_multiplex_messages(void)
+{
+ while (IN_MULTIPLEXED_AND_READY && iobuf.in.len) {
+ if (iobuf.raw_input_ends_before) {
+ size_t raw_len = iobuf.raw_input_ends_before - iobuf.in.pos;
+ iobuf.raw_input_ends_before = 0;
+ if (raw_len >= iobuf.in.len) {
+ iobuf.in.len = 0;
+ break;
+ }
+ iobuf.in.len -= raw_len;
+ if ((iobuf.in.pos += raw_len) >= iobuf.in.size)
+ iobuf.in.pos -= iobuf.in.size;
+ }
+ read_a_msg();
+ }
+}
+
+void wait_for_receiver(void)
+{
+ if (!iobuf.raw_input_ends_before)
+ read_a_msg();
+
+ if (iobuf.raw_input_ends_before) {
+ int ndx = read_int(iobuf.in_fd);
+ if (ndx < 0) {
+ switch (ndx) {
+ case NDX_FLIST_EOF:
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ break;
+ case NDX_DONE:
+ msgdone_cnt++;
+ break;
+ default:
+ exit_cleanup(RERR_STREAMIO);
+ }
+ } else {
+ struct file_list *flist;
+ flist_receiving_enabled = False;
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ flist = recv_file_list(iobuf.in_fd, ndx);
+ flist->parent_ndx = ndx;
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links)
+ match_hard_links(flist);
+#endif
+ flist_receiving_enabled = True;
+ }
+ }
+}
+
+unsigned short read_shortint(int f)
+{
+ char b[2];
+ read_buf(f, b, 2);
+ return (UVAL(b, 1) << 8) + UVAL(b, 0);
+}
+
+int32 read_int(int f)
+{
+ char b[4];
+ int32 num;
+
+ read_buf(f, b, 4);
+ num = IVAL(b, 0);
+#if SIZEOF_INT32 > 4
+ if (num & (int32)0x80000000)
+ num |= ~(int32)0xffffffff;
+#endif
+ return num;
+}
+
+uint32 read_uint(int f)
+{
+ char b[4];
+ read_buf(f, b, 4);
+ return IVAL(b, 0);
+}
+
+int32 read_varint(int f)
+{
+ union {
+ char b[5];
+ int32 x;
+ } u;
+ uchar ch;
+ int extra;
+
+ u.x = 0;
+ ch = read_byte(f);
+ extra = int_byte_extra[ch / 4];
+ if (extra) {
+ uchar bit = ((uchar)1<<(8-extra));
+ if (extra >= (int)sizeof u.b) {
+ rprintf(FERROR, "Overflow in read_varint()\n");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ read_buf(f, u.b, extra);
+ u.b[extra] = ch & (bit-1);
+ } else
+ u.b[0] = ch;
+#if CAREFUL_ALIGNMENT
+ u.x = IVAL(u.b,0);
+#endif
+#if SIZEOF_INT32 > 4
+ if (u.x & (int32)0x80000000)
+ u.x |= ~(int32)0xffffffff;
+#endif
+ return u.x;
+}
+
+int64 read_varlong(int f, uchar min_bytes)
+{
+ union {
+ char b[9];
+ int64 x;
+ } u;
+ char b2[8];
+ int extra;
+
+#if SIZEOF_INT64 < 8
+ memset(u.b, 0, 8);
+#else
+ u.x = 0;
+#endif
+ read_buf(f, b2, min_bytes);
+ memcpy(u.b, b2+1, min_bytes-1);
+ extra = int_byte_extra[CVAL(b2, 0) / 4];
+ if (extra) {
+ uchar bit = ((uchar)1<<(8-extra));
+ if (min_bytes + extra > (int)sizeof u.b) {
+ rprintf(FERROR, "Overflow in read_varlong()\n");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ read_buf(f, u.b + min_bytes - 1, extra);
+ u.b[min_bytes + extra - 1] = CVAL(b2, 0) & (bit-1);
+#if SIZEOF_INT64 < 8
+ if (min_bytes + extra > 5 || u.b[4] || CVAL(u.b,3) & 0x80) {
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+#endif
+ } else
+ u.b[min_bytes + extra - 1] = CVAL(b2, 0);
+#if SIZEOF_INT64 < 8
+ u.x = IVAL(u.b,0);
+#elif CAREFUL_ALIGNMENT
+ u.x = IVAL64(u.b,0);
+#endif
+ return u.x;
+}
+
+int64 read_longint(int f)
+{
+#if SIZEOF_INT64 >= 8
+ char b[9];
+#endif
+ int32 num = read_int(f);
+
+ if (num != (int32)0xffffffff)
+ return num;
+
+#if SIZEOF_INT64 < 8
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+#else
+ read_buf(f, b, 8);
+ return IVAL(b,0) | (((int64)IVAL(b,4))<<32);
+#endif
+}
+
+/* Debugging note: this will be named read_buf_() when using an external zlib. */
+void read_buf(int f, char *buf, size_t len)
+{
+ if (f != iobuf.in_fd) {
+ if (safe_read(f, buf, len) != len)
+ whine_about_eof(False); /* Doesn't return. */
+ goto batch_copy;
+ }
+
+ if (!IN_MULTIPLEXED) {
+ raw_read_buf(buf, len);
+ total_data_read += len;
+ if (forward_flist_data)
+ write_buf(iobuf.out_fd, buf, len);
+ batch_copy:
+ if (f == write_batch_monitor_in)
+ safe_write(batch_fd, buf, len);
+ return;
+ }
+
+ while (1) {
+ size_t siz;
+
+ while (!iobuf.raw_input_ends_before)
+ read_a_msg();
+
+ siz = MIN(len, iobuf.raw_input_ends_before - iobuf.in.pos);
+ if (siz >= iobuf.in.size)
+ siz = iobuf.in.size;
+ raw_read_buf(buf, siz);
+ total_data_read += siz;
+
+ if (forward_flist_data)
+ write_buf(iobuf.out_fd, buf, siz);
+
+ if (f == write_batch_monitor_in)
+ safe_write(batch_fd, buf, siz);
+
+ if ((len -= siz) == 0)
+ break;
+ buf += siz;
+ }
+}
+
+void read_sbuf(int f, char *buf, size_t len)
+{
+ read_buf(f, buf, len);
+ buf[len] = '\0';
+}
+
+uchar read_byte(int f)
+{
+ uchar c;
+ read_buf(f, (char*)&c, 1);
+ return c;
+}
+
+int read_vstring(int f, char *buf, int bufsize)
+{
+ int len = read_byte(f);
+
+ if (len & 0x80)
+ len = (len & ~0x80) * 0x100 + read_byte(f);
+
+ if (len >= bufsize) {
+ rprintf(FERROR, "over-long vstring received (%d > %d)\n",
+ len, bufsize - 1);
+ return -1;
+ }
+
+ if (len)
+ read_buf(f, buf, len);
+ buf[len] = '\0';
+ return len;
+}
+
+/* Populate a sum_struct with values from the socket. This is
+ * called by both the sender and the receiver. */
+void read_sum_head(int f, struct sum_struct *sum)
+{
+ int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE;
+ sum->count = read_int(f);
+ if (sum->count < 0) {
+ rprintf(FERROR, "Invalid checksum count %ld [%s]\n",
+ (long)sum->count, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ sum->blength = read_int(f);
+ if (sum->blength < 0 || sum->blength > max_blength) {
+ rprintf(FERROR, "Invalid block length %ld [%s]\n",
+ (long)sum->blength, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
+ if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
+ rprintf(FERROR, "Invalid checksum length %d [%s]\n",
+ sum->s2length, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ sum->remainder = read_int(f);
+ if (sum->remainder < 0 || sum->remainder > sum->blength) {
+ rprintf(FERROR, "Invalid remainder length %ld [%s]\n",
+ (long)sum->remainder, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+}
+
+/* Send the values from a sum_struct over the socket. Set sum to
+ * NULL if there are no checksums to send. This is called by both
+ * the generator and the sender. */
+void write_sum_head(int f, struct sum_struct *sum)
+{
+ static struct sum_struct null_sum;
+
+ if (sum == NULL)
+ sum = &null_sum;
+
+ write_int(f, sum->count);
+ write_int(f, sum->blength);
+ if (protocol_version >= 27)
+ write_int(f, sum->s2length);
+ write_int(f, sum->remainder);
+}
+
+/* Sleep after writing to limit I/O bandwidth usage.
+ *
+ * @todo Rather than sleeping after each write, it might be better to
+ * use some kind of averaging. The current algorithm seems to always
+ * use a bit less bandwidth than specified, because it doesn't make up
+ * for slow periods. But arguably this is a feature. In addition, we
+ * ought to take the time used to write the data into account.
+ *
+ * During some phases of big transfers (file FOO is uptodate) this is
+ * called with a small bytes_written every time. As the kernel has to
+ * round small waits up to guarantee that we actually wait at least the
+ * requested number of microseconds, this can become grossly inaccurate.
+ * We therefore keep track of the bytes we've written over time and only
+ * sleep when the accumulated delay is at least 1 tenth of a second. */
+static void sleep_for_bwlimit(int bytes_written)
+{
+ static struct timeval prior_tv;
+ static long total_written = 0;
+ struct timeval tv, start_tv;
+ long elapsed_usec, sleep_usec;
+
+#define ONE_SEC 1000000L /* # of microseconds in a second */
+
+ total_written += bytes_written;
+
+ gettimeofday(&start_tv, NULL);
+ if (prior_tv.tv_sec) {
+ elapsed_usec = (start_tv.tv_sec - prior_tv.tv_sec) * ONE_SEC
+ + (start_tv.tv_usec - prior_tv.tv_usec);
+ total_written -= (int64)elapsed_usec * bwlimit / (ONE_SEC/1024);
+ if (total_written < 0)
+ total_written = 0;
+ }
+
+ sleep_usec = total_written * (ONE_SEC/1024) / bwlimit;
+ if (sleep_usec < ONE_SEC / 10) {
+ prior_tv = start_tv;
+ return;
+ }
+
+ tv.tv_sec = sleep_usec / ONE_SEC;
+ tv.tv_usec = sleep_usec % ONE_SEC;
+ select(0, NULL, NULL, NULL, &tv);
+
+ gettimeofday(&prior_tv, NULL);
+ elapsed_usec = (prior_tv.tv_sec - start_tv.tv_sec) * ONE_SEC
+ + (prior_tv.tv_usec - start_tv.tv_usec);
+ total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024);
+}
+
+void io_flush(int flush_type)
+{
+ if (iobuf.out.len > iobuf.out_empty_len) {
+ if (flush_type == FULL_FLUSH) /* flush everything in the output buffers */
+ perform_io(iobuf.out.size - iobuf.out_empty_len, PIO_NEED_OUTROOM);
+ else if (flush_type == NORMAL_FLUSH) /* flush at least 1 byte */
+ perform_io(iobuf.out.size - iobuf.out.len + 1, PIO_NEED_OUTROOM);
+ /* MSG_FLUSH: flush iobuf.msg only */
+ }
+ if (iobuf.msg.len)
+ perform_io(iobuf.msg.size, PIO_NEED_MSGROOM);
+}
+
+void write_shortint(int f, unsigned short x)
+{
+ char b[2];
+ b[0] = (char)x;
+ b[1] = (char)(x >> 8);
+ write_buf(f, b, 2);
+}
+
+void write_int(int f, int32 x)
+{
+ char b[4];
+ SIVAL(b, 0, x);
+ write_buf(f, b, 4);
+}
+
+void write_varint(int f, int32 x)
+{
+ char b[5];
+ uchar bit;
+ int cnt;
+
+ SIVAL(b, 1, x);
+
+ for (cnt = 4; cnt > 1 && b[cnt] == 0; cnt--) {}
+ bit = ((uchar)1<<(7-cnt+1));
+
+ if (CVAL(b, cnt) >= bit) {
+ cnt++;
+ *b = ~(bit-1);
+ } else if (cnt > 1)
+ *b = b[cnt] | ~(bit*2-1);
+ else
+ *b = b[1];
+
+ write_buf(f, b, cnt);
+}
+
+void write_varlong(int f, int64 x, uchar min_bytes)
+{
+ char b[9];
+ uchar bit;
+ int cnt = 8;
+
+#if SIZEOF_INT64 >= 8
+ SIVAL64(b, 1, x);
+#else
+ SIVAL(b, 1, x);
+ if (x <= 0x7FFFFFFF && x >= 0)
+ memset(b + 5, 0, 4);
+ else {
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+#endif
+
+ while (cnt > min_bytes && b[cnt] == 0)
+ cnt--;
+ bit = ((uchar)1<<(7-cnt+min_bytes));
+ if (CVAL(b, cnt) >= bit) {
+ cnt++;
+ *b = ~(bit-1);
+ } else if (cnt > min_bytes)
+ *b = b[cnt] | ~(bit*2-1);
+ else
+ *b = b[cnt];
+
+ write_buf(f, b, cnt);
+}
+
+/*
+ * Note: int64 may actually be a 32-bit type if ./configure couldn't find any
+ * 64-bit types on this platform.
+ */
+void write_longint(int f, int64 x)
+{
+ char b[12], * const s = b+4;
+
+ SIVAL(s, 0, x);
+ if (x <= 0x7FFFFFFF && x >= 0) {
+ write_buf(f, s, 4);
+ return;
+ }
+
+#if SIZEOF_INT64 < 8
+ rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+#else
+ memset(b, 0xFF, 4);
+ SIVAL(s, 4, x >> 32);
+ write_buf(f, b, 12);
+#endif
+}
+
+void write_bigbuf(int f, const char *buf, size_t len)
+{
+ size_t half_max = (iobuf.out.size - iobuf.out_empty_len) / 2;
+
+ while (len > half_max + 1024) {
+ write_buf(f, buf, half_max);
+ buf += half_max;
+ len -= half_max;
+ }
+
+ write_buf(f, buf, len);
+}
+
+void write_buf(int f, const char *buf, size_t len)
+{
+ size_t pos, siz;
+
+ if (f != iobuf.out_fd) {
+ safe_write(f, buf, len);
+ goto batch_copy;
+ }
+
+ if (iobuf.out.len + len > iobuf.out.size)
+ perform_io(len, PIO_NEED_OUTROOM);
+
+ pos = iobuf.out.pos + iobuf.out.len; /* Must be set after any flushing. */
+ if (pos >= iobuf.out.size)
+ pos -= iobuf.out.size;
+
+ /* Handle a split copy if we wrap around the end of the circular buffer. */
+ if (pos >= iobuf.out.pos && (siz = iobuf.out.size - pos) < len) {
+ memcpy(iobuf.out.buf + pos, buf, siz);
+ memcpy(iobuf.out.buf, buf + siz, len - siz);
+ } else
+ memcpy(iobuf.out.buf + pos, buf, len);
+
+ iobuf.out.len += len;
+ total_data_written += len;
+
+ batch_copy:
+ if (f == write_batch_monitor_out)
+ safe_write(batch_fd, buf, len);
+}
+
+/* Write a string to the connection */
+void write_sbuf(int f, const char *buf)
+{
+ write_buf(f, buf, strlen(buf));
+}
+
+void write_byte(int f, uchar c)
+{
+ write_buf(f, (char *)&c, 1);
+}
+
+void write_vstring(int f, const char *str, int len)
+{
+ uchar lenbuf[3], *lb = lenbuf;
+
+ if (len > 0x7F) {
+ if (len > 0x7FFF) {
+ rprintf(FERROR,
+ "attempting to send over-long vstring (%d > %d)\n",
+ len, 0x7FFF);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ *lb++ = len / 0x100 + 0x80;
+ }
+ *lb = len;
+
+ write_buf(f, (char*)lenbuf, lb - lenbuf + 1);
+ if (len)
+ write_buf(f, str, len);
+}
+
+/* Send a file-list index using a byte-reduction method. */
+void write_ndx(int f, int32 ndx)
+{
+ static int32 prev_positive = -1, prev_negative = 1;
+ int32 diff, cnt = 0;
+ char b[6];
+
+ if (protocol_version < 30 || read_batch) {
+ write_int(f, ndx);
+ return;
+ }
+
+ /* Send NDX_DONE as a single-byte 0 with no side effects. Send
+ * negative nums as a positive after sending a leading 0xFF. */
+ if (ndx >= 0) {
+ diff = ndx - prev_positive;
+ prev_positive = ndx;
+ } else if (ndx == NDX_DONE) {
+ *b = 0;
+ write_buf(f, b, 1);
+ return;
+ } else {
+ b[cnt++] = (char)0xFF;
+ ndx = -ndx;
+ diff = ndx - prev_negative;
+ prev_negative = ndx;
+ }
+
+ /* A diff of 1 - 253 is sent as a one-byte diff; a diff of 254 - 32767
+ * or 0 is sent as a 0xFE + a two-byte diff; otherwise we send 0xFE
+ * & all 4 bytes of the (non-negative) num with the high-bit set. */
+ if (diff < 0xFE && diff > 0)
+ b[cnt++] = (char)diff;
+ else if (diff < 0 || diff > 0x7FFF) {
+ b[cnt++] = (char)0xFE;
+ b[cnt++] = (char)((ndx >> 24) | 0x80);
+ b[cnt++] = (char)ndx;
+ b[cnt++] = (char)(ndx >> 8);
+ b[cnt++] = (char)(ndx >> 16);
+ } else {
+ b[cnt++] = (char)0xFE;
+ b[cnt++] = (char)(diff >> 8);
+ b[cnt++] = (char)diff;
+ }
+ write_buf(f, b, cnt);
+}
+
+/* Receive a file-list index using a byte-reduction method. */
+int32 read_ndx(int f)
+{
+ static int32 prev_positive = -1, prev_negative = 1;
+ int32 *prev_ptr, num;
+ char b[4];
+
+ if (protocol_version < 30)
+ return read_int(f);
+
+ read_buf(f, b, 1);
+ if (CVAL(b, 0) == 0xFF) {
+ read_buf(f, b, 1);
+ prev_ptr = &prev_negative;
+ } else if (CVAL(b, 0) == 0)
+ return NDX_DONE;
+ else
+ prev_ptr = &prev_positive;
+ if (CVAL(b, 0) == 0xFE) {
+ read_buf(f, b, 2);
+ if (CVAL(b, 0) & 0x80) {
+ b[3] = CVAL(b, 0) & ~0x80;
+ b[0] = b[1];
+ read_buf(f, b+1, 2);
+ num = IVAL(b, 0);
+ } else
+ num = (UVAL(b,0)<<8) + UVAL(b,1) + *prev_ptr;
+ } else
+ num = UVAL(b, 0) + *prev_ptr;
+ *prev_ptr = num;
+ if (prev_ptr == &prev_negative)
+ num = -num;
+ return num;
+}
+
+/* Read a line of up to bufsiz-1 characters into buf. Strips
+ * the (required) trailing newline and all carriage returns.
+ * Returns 1 for success; 0 for I/O error or truncation. */
+int read_line_old(int fd, char *buf, size_t bufsiz, int eof_ok)
+{
+ assert(fd != iobuf.in_fd);
+ bufsiz--; /* leave room for the null */
+ while (bufsiz > 0) {
+ if (safe_read(fd, buf, 1) == 0) {
+ if (eof_ok)
+ break;
+ return 0;
+ }
+ if (*buf == '\0')
+ return 0;
+ if (*buf == '\n')
+ break;
+ if (*buf != '\r') {
+ buf++;
+ bufsiz--;
+ }
+ }
+ *buf = '\0';
+ return bufsiz > 0;
+}
+
+void io_printf(int fd, const char *format, ...)
+{
+ va_list ap;
+ char buf[BIGPATHBUFLEN];
+ int len;
+
+ va_start(ap, format);
+ len = vsnprintf(buf, sizeof buf, format, ap);
+ va_end(ap);
+
+ if (len < 0)
+ exit_cleanup(RERR_PROTOCOL);
+
+ if (len >= (int)sizeof buf) {
+ rprintf(FERROR, "io_printf() was too long for the buffer.\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ write_sbuf(fd, buf);
+}
+
+/* Setup for multiplexing a MSG_* stream with the data stream. */
+void io_start_multiplex_out(int fd)
+{
+ io_flush(FULL_FLUSH);
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_multiplex_out(%d)\n", who_am_i(), fd);
+
+ if (!iobuf.msg.buf)
+ alloc_xbuf(&iobuf.msg, ROUND_UP_1024(IO_BUFFER_SIZE));
+
+ iobuf.out_empty_len = 4; /* See also OUT_MULTIPLEXED */
+ io_start_buffering_out(fd);
+ got_kill_signal = 0;
+
+ iobuf.raw_data_header_pos = iobuf.out.pos + iobuf.out.len;
+ iobuf.out.len += 4;
+}
+
+/* Setup for multiplexing a MSG_* stream with the data stream. */
+void io_start_multiplex_in(int fd)
+{
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_start_multiplex_in(%d)\n", who_am_i(), fd);
+
+ iobuf.in_multiplexed = 1; /* See also IN_MULTIPLEXED */
+ io_start_buffering_in(fd);
+}
+
+int io_end_multiplex_in(int mode)
+{
+ int ret = iobuf.in_multiplexed ? iobuf.in_fd : -1;
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_end_multiplex_in(mode=%d)\n", who_am_i(), mode);
+
+ iobuf.in_multiplexed = 0;
+ if (mode == MPLX_SWITCHING)
+ iobuf.raw_input_ends_before = 0;
+ else
+ assert(iobuf.raw_input_ends_before == 0);
+ if (mode != MPLX_TO_BUFFERED)
+ io_end_buffering_in(mode);
+
+ return ret;
+}
+
+int io_end_multiplex_out(int mode)
+{
+ int ret = iobuf.out_empty_len ? iobuf.out_fd : -1;
+
+ if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
+ rprintf(FINFO, "[%s] io_end_multiplex_out(mode=%d)\n", who_am_i(), mode);
+
+ if (mode != MPLX_TO_BUFFERED)
+ io_end_buffering_out(mode);
+ else
+ io_flush(FULL_FLUSH);
+
+ iobuf.out.len = 0;
+ iobuf.out_empty_len = 0;
+ if (got_kill_signal > 0) /* Just in case... */
+ handle_kill_signal(False);
+ got_kill_signal = -1;
+
+ return ret;
+}
+
+void start_write_batch(int fd)
+{
+ /* Some communication has already taken place, but we don't
+ * enable batch writing until here so that we can write a
+ * canonical record of the communication even though the
+ * actual communication so far depends on whether a daemon
+ * is involved. */
+ write_int(batch_fd, protocol_version);
+ if (protocol_version >= 30)
+ write_varint(batch_fd, compat_flags);
+ write_int(batch_fd, checksum_seed);
+
+ if (am_sender)
+ write_batch_monitor_out = fd;
+ else
+ write_batch_monitor_in = fd;
+}
+
+void stop_write_batch(void)
+{
+ write_batch_monitor_out = -1;
+ write_batch_monitor_in = -1;
+}
diff --git a/io.h b/io.h
new file mode 100644
index 0000000..fd3b752
--- /dev/null
+++ b/io.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007-2019 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+extern int protocol_version;
+
+static inline int32
+read_varint30(int f)
+{
+ if (protocol_version < 30)
+ return read_int(f);
+ return read_varint(f);
+}
+
+static inline int64
+read_varlong30(int f, uchar min_bytes)
+{
+ if (protocol_version < 30)
+ return read_longint(f);
+ return read_varlong(f, min_bytes);
+}
+
+static inline void
+write_varint30(int f, int32 x)
+{
+ if (protocol_version < 30)
+ write_int(f, x);
+ else
+ write_varint(f, x);
+}
+
+static inline void
+write_varlong30(int f, int64 x, uchar min_bytes)
+{
+ if (protocol_version < 30)
+ write_longint(f, x);
+ else
+ write_varlong(f, x, min_bytes);
+}
diff --git a/itypes.h b/itypes.h
new file mode 100644
index 0000000..0a7111f
--- /dev/null
+++ b/itypes.h
@@ -0,0 +1,71 @@
+/* Inline functions for rsync.
+ *
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+static inline int
+isDigit(const char *ptr)
+{
+ return isdigit(*(unsigned char *)ptr);
+}
+
+static inline int
+isHexDigit(const char *ptr)
+{
+ return isxdigit(*(unsigned char *)ptr);
+}
+
+static inline int
+isPrint(const char *ptr)
+{
+ return isprint(*(unsigned char *)ptr);
+}
+
+static inline int
+isSpace(const char *ptr)
+{
+ return isspace(*(unsigned char *)ptr);
+}
+
+static inline int
+isAlNum(const char *ptr)
+{
+ return isalnum(*(unsigned char *)ptr);
+}
+
+static inline int
+isLower(const char *ptr)
+{
+ return islower(*(unsigned char *)ptr);
+}
+
+static inline int
+isUpper(const char *ptr)
+{
+ return isupper(*(unsigned char *)ptr);
+}
+
+static inline int
+toLower(const char *ptr)
+{
+ return tolower(*(unsigned char *)ptr);
+}
+
+static inline int
+toUpper(const char *ptr)
+{
+ return toupper(*(unsigned char *)ptr);
+}
diff --git a/latest-year.h b/latest-year.h
new file mode 100644
index 0000000..37e8efb
--- /dev/null
+++ b/latest-year.h
@@ -0,0 +1 @@
+#define LATEST_YEAR "2022"
diff --git a/lib/addrinfo.h b/lib/addrinfo.h
new file mode 100644
index 0000000..ee9f672
--- /dev/null
+++ b/lib/addrinfo.h
@@ -0,0 +1,180 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.h
+ * Support getaddrinfo() on platforms that don't have it.
+ *
+ * Note: we use our own routines on platforms that don't HAVE_STRUCT_ADDRINFO,
+ * whether or not the library routine getaddrinfo() can be found. This
+ * policy is needed because on some platforms a manually installed libbind.a
+ * may provide getaddrinfo(), yet the system headers may not provide the
+ * struct definitions needed to call it. To avoid conflict with the libbind
+ * definition in such cases, we rename our routines to pg_xxx() via macros.
+ *
+ * This code will also work on platforms where struct addrinfo is defined
+ * in the system headers but no getaddrinfo() can be located.
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ADDRINFO_H
+#define ADDRINFO_H
+
+
+/* Various macros that ought to be in <netdb.h>, but might not be */
+
+#ifndef EAI_FAIL
+#define EAI_BADFLAGS (-1)
+#define EAI_NONAME (-2)
+#define EAI_AGAIN (-3)
+#define EAI_FAIL (-4)
+#define EAI_FAMILY (-6)
+#define EAI_SOCKTYPE (-7)
+#define EAI_SERVICE (-8)
+#define EAI_MEMORY (-10)
+#define EAI_SYSTEM (-11)
+#endif /* !EAI_FAIL */
+
+#ifndef AI_PASSIVE
+#define AI_PASSIVE 0x0001
+#endif
+
+#ifndef AI_NUMERICHOST
+/*
+ * some platforms don't support AI_NUMERICHOST; define as zero if using
+ * the system version of getaddrinfo...
+ */
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICHOST 0
+#else
+#define AI_NUMERICHOST 0x0004
+#endif
+#endif
+
+#ifndef AI_CANONNAME
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_CANONNAME 0
+#else
+#define AI_CANONNAME 0x0008
+#endif
+#endif
+
+#ifndef AI_NUMERICSERV
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICSERV 0
+#else
+#define AI_NUMERICSERV 0x0010
+#endif
+#endif
+
+#ifndef NI_NUMERICHOST
+#define NI_NUMERICHOST 1
+#endif
+
+#ifndef NI_NUMERICSERV
+#define NI_NUMERICSERV 2
+#endif
+
+#ifndef NI_NOFQDN
+#define NI_NOFQDN 4
+#endif
+
+#ifndef NI_NAMEREQD
+#define NI_NAMEREQD 8
+#endif
+
+#ifndef NI_DGRAM
+#define NI_DGRAM 16
+#endif
+
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ struct sockaddr *ai_addr;
+ char *ai_canonname;
+ struct addrinfo *ai_next;
+};
+#endif /* !HAVE_STRUCT_ADDRINFO */
+
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+struct sockaddr_storage {
+ unsigned short ss_family;
+ unsigned long ss_align;
+ char ss_padding[128 - sizeof (unsigned long)];
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
+
+#ifndef HAVE_GETADDRINFO
+
+/* Rename private copies per comments above */
+#ifdef getaddrinfo
+#undef getaddrinfo
+#endif
+#define getaddrinfo pg_getaddrinfo
+
+#ifdef freeaddrinfo
+#undef freeaddrinfo
+#endif
+#define freeaddrinfo pg_freeaddrinfo
+
+#ifdef gai_strerror
+#undef gai_strerror
+#endif
+#define gai_strerror pg_gai_strerror
+
+#ifdef getnameinfo
+#undef getnameinfo
+#endif
+#define getnameinfo pg_getnameinfo
+
+extern int getaddrinfo(const char *node, const char *service,
+ const struct addrinfo * hints, struct addrinfo ** res);
+extern void freeaddrinfo(struct addrinfo * res);
+extern const char *gai_strerror(int errcode);
+extern int getnameinfo(const struct sockaddr * sa, socklen_t salen,
+ char *node, size_t nodelen,
+ char *service, size_t servicelen, int flags);
+#endif /* !HAVE_GETADDRINFO */
+
+#endif /* ADDRINFO_H */
diff --git a/lib/compat.c b/lib/compat.c
new file mode 100644
index 0000000..513d79b
--- /dev/null
+++ b/lib/compat.c
@@ -0,0 +1,272 @@
+/*
+ * Reimplementations of standard functions for platforms that don't have them.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2004-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+
+static char number_separator;
+
+char get_number_separator(void)
+{
+ if (!number_separator) {
+ char buf[32];
+ snprintf(buf, sizeof buf, "%f", 3.14);
+ if (strchr(buf, '.') != NULL)
+ number_separator = ',';
+ else
+ number_separator = '.';
+ }
+
+ return number_separator;
+}
+
+char get_decimal_point(void)
+{
+ return get_number_separator() == ',' ? '.' : ',';
+}
+
+#ifndef HAVE_GETCWD
+ char *getcwd(char *buf, int size)
+{
+ return getwd(buf);
+}
+#endif
+
+
+#ifndef HAVE_WAITPID
+ pid_t waitpid(pid_t pid, int *statptr, int options)
+{
+#ifdef HAVE_WAIT4
+ return wait4(pid, statptr, options, NULL);
+#else
+ /* If wait4 is also not available, try wait3 for SVR3 variants */
+ /* Less ideal because can't actually request a specific pid */
+ /* At least the WNOHANG option is supported */
+ /* Code borrowed from apache fragment written by dwd@bell-labs.com */
+ int tmp_pid, dummystat;;
+ if (kill(pid, 0) == -1) {
+ errno = ECHILD;
+ return -1;
+ }
+ if (statptr == NULL)
+ statptr = &dummystat;
+ while (((tmp_pid = wait3(statptr, options, 0)) != pid) &&
+ (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1))
+ ;
+ return tmp_pid;
+#endif
+}
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+ void *memmove(void *dest, const void *src, size_t n)
+{
+ bcopy((char *) src, (char *) dest, n);
+ return dest;
+}
+#endif
+
+#ifndef HAVE_STRPBRK
+/**
+ * Find the first occurrence in @p s of any character in @p accept.
+ *
+ * Derived from glibc
+ **/
+ char *strpbrk(const char *s, const char *accept)
+{
+ while (*s != '\0') {
+ const char *a = accept;
+ while (*a != '\0') {
+ if (*a++ == *s) return (char *)s;
+ }
+ ++s;
+ }
+
+ return NULL;
+}
+#endif
+
+
+#ifndef HAVE_STRLCPY
+/**
+ * Like strncpy but does not 0 fill the buffer and always null
+ * terminates.
+ *
+ * @param bufsize is the size of the destination buffer.
+ *
+ * @return index of the terminating byte.
+ **/
+ size_t strlcpy(char *d, const char *s, size_t bufsize)
+{
+ size_t len = strlen(s);
+ size_t ret = len;
+ if (bufsize > 0) {
+ if (len >= bufsize)
+ len = bufsize-1;
+ memcpy(d, s, len);
+ d[len] = 0;
+ }
+ return ret;
+}
+#endif
+
+#ifndef HAVE_STRLCAT
+/**
+ * Like strncat() but does not 0 fill the buffer and always null
+ * terminates.
+ *
+ * @param bufsize length of the buffer, which should be one more than
+ * the maximum resulting string length.
+ **/
+ size_t strlcat(char *d, const char *s, size_t bufsize)
+{
+ size_t len1 = strlen(d);
+ size_t len2 = strlen(s);
+ size_t ret = len1 + len2;
+
+ if (len1 < bufsize - 1) {
+ if (len2 >= bufsize - len1)
+ len2 = bufsize - len1 - 1;
+ memcpy(d+len1, s, len2);
+ d[len1+len2] = 0;
+ }
+ return ret;
+}
+#endif
+
+/* some systems don't take the 2nd argument */
+int sys_gettimeofday(struct timeval *tv)
+{
+#ifdef HAVE_GETTIMEOFDAY_TZ
+ return gettimeofday(tv, NULL);
+#else
+ return gettimeofday(tv);
+#endif
+}
+
+/* Return the int64 number as a string. If the human_flag arg is non-zero,
+ * we may output the number in K, M, G, or T units. If we don't add a unit
+ * suffix, we will append the fract string, if it is non-NULL. We can
+ * return up to 4 buffers at a time. */
+char *do_big_num(int64 num, int human_flag, const char *fract)
+{
+ static char bufs[4][128]; /* more than enough room */
+ static unsigned int n;
+ char *s;
+ int len, negated;
+
+ if (human_flag && !number_separator)
+ (void)get_number_separator();
+
+ n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
+
+ if (human_flag > 1) {
+ int mult = human_flag == 2 ? 1000 : 1024;
+ if (num >= mult || num <= -mult) {
+ double dnum = (double)num / mult;
+ char units;
+ if (num < 0)
+ dnum = -dnum;
+ if (dnum < mult)
+ units = 'K';
+ else if ((dnum /= mult) < mult)
+ units = 'M';
+ else if ((dnum /= mult) < mult)
+ units = 'G';
+ else if ((dnum /= mult) < mult)
+ units = 'T';
+ else {
+ dnum /= mult;
+ units = 'P';
+ }
+ if (num < 0)
+ dnum = -dnum;
+ snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units);
+ return bufs[n];
+ }
+ }
+
+ s = bufs[n] + sizeof bufs[0] - 1;
+ if (fract) {
+ len = strlen(fract);
+ s -= len;
+ strlcpy(s, fract, len + 1);
+ } else
+ *s = '\0';
+
+ len = 0;
+
+ if (!num)
+ *--s = '0';
+ if (num < 0) {
+ /* A maximum-size negated number can't fit as a positive,
+ * so do one digit in negated form to start us off. */
+ *--s = (char)(-(num % 10)) + '0';
+ num = -(num / 10);
+ len++;
+ negated = 1;
+ } else
+ negated = 0;
+
+ while (num) {
+ if (human_flag) {
+ if (len == 3) {
+ *--s = number_separator;
+ len = 1;
+ } else
+ len++;
+ }
+ *--s = (char)(num % 10) + '0';
+ num /= 10;
+ }
+
+ if (negated)
+ *--s = '-';
+
+ return s;
+}
+
+/* Return the double number as a string. If the human_flag option is > 1,
+ * we may output the number in K, M, G, or T units. The buffer we use for
+ * our result is either a single static buffer defined here, or a buffer
+ * we get from do_big_num(). */
+char *do_big_dnum(double dnum, int human_flag, int decimal_digits)
+{
+ static char tmp_buf[128];
+#if SIZEOF_INT64 >= 8
+ char *fract;
+
+ snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
+
+ if (!human_flag || (dnum < 1000.0 && dnum > -1000.0))
+ return tmp_buf;
+
+ for (fract = tmp_buf+1; isDigit(fract); fract++) {}
+
+ return do_big_num((int64)dnum, human_flag, fract);
+#else
+ /* A big number might lose digits converting to a too-short int64,
+ * so let's just return the raw double conversion. */
+ snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
+ return tmp_buf;
+#endif
+}
diff --git a/lib/dummy.in b/lib/dummy.in
new file mode 100644
index 0000000..3b26a20
--- /dev/null
+++ b/lib/dummy.in
@@ -0,0 +1,2 @@
+This is a dummy file to ensure that the lib directory gets created
+by configure when a VPATH is used.
diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c
new file mode 100644
index 0000000..96d7a2b
--- /dev/null
+++ b/lib/getaddrinfo.c
@@ -0,0 +1,504 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.c
+ * Support getaddrinfo() on platforms that don't have it.
+ *
+ * We also supply getnameinfo() here, assuming that the platform will have
+ * it if and only if it has getaddrinfo(). If this proves false on some
+ * platform, we'll need to split this file and provide a separate configure
+ * test for getnameinfo().
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ * Copyright (C) 2007 Jeremy Allison.
+ * Modified to return multiple IPv4 addresses for Samba.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "rsync.h"
+
+#ifndef SMB_MALLOC
+#define SMB_MALLOC(s) malloc(s)
+#endif
+
+#ifndef SMB_STRDUP
+#define SMB_STRDUP(s) strdup(s)
+#endif
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+static int check_hostent_err(struct hostent *hp)
+{
+#ifndef INET6
+ extern int h_errno;
+#endif
+ if (!hp) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ return EAI_NONAME;
+ case TRY_AGAIN:
+ return EAI_AGAIN;
+ case NO_RECOVERY:
+ default:
+ return EAI_FAIL;
+ }
+ }
+ if (!hp->h_name || hp->h_addrtype != AF_INET) {
+ return EAI_FAIL;
+ }
+ return 0;
+}
+
+static char *canon_name_from_hostent(struct hostent *hp,
+ int *perr)
+{
+ char *ret = NULL;
+
+ *perr = check_hostent_err(hp);
+ if (*perr) {
+ return NULL;
+ }
+ ret = SMB_STRDUP(hp->h_name);
+ if (!ret) {
+ *perr = EAI_MEMORY;
+ }
+ return ret;
+}
+
+static char *get_my_canon_name(int *perr)
+{
+ char name[HOST_NAME_MAX+1];
+
+ if (gethostname(name, HOST_NAME_MAX) == -1) {
+ *perr = EAI_FAIL;
+ return NULL;
+ }
+ /* Ensure null termination. */
+ name[HOST_NAME_MAX] = '\0';
+ return canon_name_from_hostent(gethostbyname(name), perr);
+}
+
+static char *get_canon_name_from_addr(struct in_addr ip,
+ int *perr)
+{
+ return canon_name_from_hostent(
+ gethostbyaddr((void *)&ip, sizeof ip, AF_INET),
+ perr);
+}
+
+static struct addrinfo *alloc_entry(const struct addrinfo *hints,
+ struct in_addr ip,
+ unsigned short port)
+{
+ struct sockaddr_in *psin = NULL;
+ struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
+
+ if (!ai) {
+ return NULL;
+ }
+ memset(ai, '\0', sizeof(*ai));
+
+ psin = SMB_MALLOC(sizeof(*psin));
+ if (!psin) {
+ free(ai);
+ return NULL;
+ }
+
+ memset(psin, '\0', sizeof(*psin));
+
+ psin->sin_family = AF_INET;
+ psin->sin_port = htons(port);
+ psin->sin_addr = ip;
+
+ ai->ai_flags = 0;
+ ai->ai_family = AF_INET;
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ ai->ai_addrlen = sizeof(*psin);
+ ai->ai_addr = (struct sockaddr *) psin;
+ ai->ai_canonname = NULL;
+ ai->ai_next = NULL;
+
+ return ai;
+}
+
+/*
+ * get address info for a single ipv4 address.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+static int getaddr_info_single_addr(const char *service,
+ uint32 addr,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+
+ struct addrinfo *ai = NULL;
+ struct in_addr ip;
+ unsigned short port = 0;
+
+ if (service) {
+ port = (unsigned short)atoi(service);
+ }
+ ip.s_addr = htonl(addr);
+
+ ai = alloc_entry(hints, ip, port);
+ if (!ai) {
+ return EAI_MEMORY;
+ }
+
+ /* If we're asked for the canonical name,
+ * make sure it returns correctly. */
+ if (!(hints->ai_flags & AI_NUMERICSERV) &&
+ hints->ai_flags & AI_CANONNAME) {
+ int err;
+ if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
+ ai->ai_canonname = get_my_canon_name(&err);
+ } else {
+ ai->ai_canonname =
+ get_canon_name_from_addr(ip,&err);
+ }
+ if (ai->ai_canonname == NULL) {
+ freeaddrinfo(ai);
+ return err;
+ }
+ }
+
+ *res = ai;
+ return 0;
+}
+
+/*
+ * get address info for multiple ipv4 addresses.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+static int getaddr_info_name(const char *node,
+ const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ struct addrinfo *listp = NULL, *prevp = NULL;
+ char **pptr = NULL;
+ int err;
+ struct hostent *hp = NULL;
+ unsigned short port = 0;
+
+ if (service) {
+ port = (unsigned short)atoi(service);
+ }
+
+ hp = gethostbyname(node);
+ err = check_hostent_err(hp);
+ if (err) {
+ return err;
+ }
+
+ for(pptr = hp->h_addr_list; *pptr; pptr++) {
+ struct in_addr ip = *(struct in_addr *)*pptr;
+ struct addrinfo *ai = alloc_entry(hints, ip, port);
+
+ if (!ai) {
+ freeaddrinfo(listp);
+ return EAI_MEMORY;
+ }
+
+ if (!listp) {
+ listp = ai;
+ prevp = ai;
+ ai->ai_canonname = SMB_STRDUP(hp->h_name);
+ if (!ai->ai_canonname) {
+ freeaddrinfo(listp);
+ return EAI_MEMORY;
+ }
+ } else {
+ prevp->ai_next = ai;
+ prevp = ai;
+ }
+ }
+ *res = listp;
+ return 0;
+}
+
+/*
+ * get address info for ipv4 sockets.
+ *
+ * Bugs: - servname can only be a number, not text.
+ */
+
+int getaddrinfo(const char *node,
+ const char *service,
+ const struct addrinfo * hintp,
+ struct addrinfo ** res)
+{
+ struct addrinfo hints;
+
+ /* Setup the hints struct. */
+ if (hintp == NULL) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ } else {
+ memcpy(&hints, hintp, sizeof(hints));
+ }
+
+ if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
+ return EAI_FAMILY;
+ }
+
+ if (hints.ai_socktype == 0) {
+ hints.ai_socktype = SOCK_STREAM;
+ }
+
+ if (!node && !service) {
+ return EAI_NONAME;
+ }
+
+ if (node) {
+ if (node[0] == '\0') {
+ return getaddr_info_single_addr(service,
+ INADDR_ANY,
+ &hints,
+ res);
+ } else if (hints.ai_flags & AI_NUMERICHOST) {
+ struct in_addr ip;
+ if (inet_pton(AF_INET, node, &ip) <= 0)
+ return EAI_FAIL;
+ return getaddr_info_single_addr(service,
+ ntohl(ip.s_addr),
+ &hints,
+ res);
+ } else {
+ return getaddr_info_name(node,
+ service,
+ &hints,
+ res);
+ }
+ } else if (hints.ai_flags & AI_PASSIVE) {
+ return getaddr_info_single_addr(service,
+ INADDR_ANY,
+ &hints,
+ res);
+ }
+ return getaddr_info_single_addr(service,
+ INADDR_LOOPBACK,
+ &hints,
+ res);
+}
+
+
+void freeaddrinfo(struct addrinfo *res)
+{
+ struct addrinfo *next = NULL;
+
+ for (;res; res = next) {
+ next = res->ai_next;
+ if (res->ai_canonname) {
+ free(res->ai_canonname);
+ }
+ if (res->ai_addr) {
+ free(res->ai_addr);
+ }
+ free(res);
+ }
+}
+
+
+const char *gai_strerror(int errcode)
+{
+#ifdef HAVE_HSTRERROR
+ int hcode;
+
+ switch (errcode)
+ {
+ case EAI_NONAME:
+ hcode = HOST_NOT_FOUND;
+ break;
+ case EAI_AGAIN:
+ hcode = TRY_AGAIN;
+ break;
+ case EAI_FAIL:
+ default:
+ hcode = NO_RECOVERY;
+ break;
+ }
+
+ return hstrerror(hcode);
+#else /* !HAVE_HSTRERROR */
+
+ switch (errcode)
+ {
+ case EAI_NONAME:
+ return "Unknown host";
+ case EAI_AGAIN:
+ return "Host name lookup failure";
+#ifdef EAI_BADFLAGS
+ case EAI_BADFLAGS:
+ return "Invalid argument";
+#endif
+#ifdef EAI_FAMILY
+ case EAI_FAMILY:
+ return "Address family not supported";
+#endif
+#ifdef EAI_MEMORY
+ case EAI_MEMORY:
+ return "Not enough memory";
+#endif
+#ifdef EAI_NODATA
+ case EAI_NODATA:
+ return "No host data of that type was found";
+#endif
+#ifdef EAI_SERVICE
+ case EAI_SERVICE:
+ return "Class type not found";
+#endif
+#ifdef EAI_SOCKTYPE
+ case EAI_SOCKTYPE:
+ return "Socket type not supported";
+#endif
+ default:
+ return "Unknown server error";
+ }
+#endif /* HAVE_HSTRERROR */
+}
+
+static int gethostnameinfo(const struct sockaddr *sa,
+ char *node,
+ size_t nodelen,
+ int flags)
+{
+ int ret = -1;
+ char *p = NULL;
+
+ if (!(flags & NI_NUMERICHOST)) {
+ struct hostent *hp = gethostbyaddr(
+ (void *)&((struct sockaddr_in *)sa)->sin_addr,
+ sizeof (struct in_addr),
+ sa->sa_family);
+ ret = check_hostent_err(hp);
+ if (ret == 0) {
+ /* Name looked up successfully. */
+ ret = snprintf(node, nodelen, "%s", hp->h_name);
+ if (ret < 0 || (size_t)ret >= nodelen) {
+ return EAI_MEMORY;
+ }
+ if (flags & NI_NOFQDN) {
+ p = strchr(node,'.');
+ if (p) {
+ *p = '\0';
+ }
+ }
+ return 0;
+ }
+
+ if (flags & NI_NAMEREQD) {
+ /* If we require a name and didn't get one,
+ * automatically fail. */
+ return ret;
+ }
+ /* Otherwise just fall into the numeric host code... */
+ }
+ p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
+ ret = snprintf(node, nodelen, "%s", p);
+ if (ret < 0 || (size_t)ret >= nodelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+}
+
+static int getservicenameinfo(const struct sockaddr *sa,
+ char *service,
+ size_t servicelen,
+ int flags)
+{
+ int ret = -1;
+ int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+
+ if (!(flags & NI_NUMERICSERV)) {
+ struct servent *se = getservbyport(
+ port,
+ (flags & NI_DGRAM) ? "udp" : "tcp");
+ if (se && se->s_name) {
+ /* Service name looked up successfully. */
+ ret = snprintf(service, servicelen, "%s", se->s_name);
+ if (ret < 0 || (size_t)ret >= servicelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+ }
+ /* Otherwise just fall into the numeric service code... */
+ }
+ ret = snprintf(service, servicelen, "%d", port);
+ if (ret < 0 || (size_t)ret >= servicelen) {
+ return EAI_MEMORY;
+ }
+ return 0;
+}
+
+/*
+ * Convert an ipv4 address to a hostname.
+ *
+ * Bugs: - No IPv6 support.
+ */
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *node, size_t nodelen,
+ char *service, size_t servicelen, int flags)
+{
+
+ /* Invalid arguments. */
+ if (sa == NULL || (node == NULL && service == NULL)) {
+ return EAI_FAIL;
+ }
+
+ if (sa->sa_family != AF_INET) {
+ return EAI_FAIL;
+ }
+
+ if (salen < (socklen_t)sizeof (struct sockaddr_in)) {
+ return EAI_FAIL;
+ }
+
+ if (node) {
+ int ret = gethostnameinfo(sa, node, nodelen, flags);
+ if (ret)
+ return ret;
+ }
+
+ if (service) {
+ return getservicenameinfo(sa, service, servicelen, flags);
+ }
+ return 0;
+}
diff --git a/lib/getpass.c b/lib/getpass.c
new file mode 100644
index 0000000..dea3126
--- /dev/null
+++ b/lib/getpass.c
@@ -0,0 +1,72 @@
+/*
+ * An implementation of getpass for systems that lack one.
+ *
+ * Copyright (C) 2013 Roman Donchenko
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "rsync.h"
+
+char *getpass(const char *prompt)
+{
+ static char password[256];
+
+ BOOL tty_changed = False, read_success;
+ struct termios tty_old, tty_new;
+ FILE *in = stdin, *out = stderr;
+ FILE *tty = fopen("/dev/tty", "w+");
+
+ if (tty)
+ in = out = tty;
+
+ if (tcgetattr(fileno(in), &tty_old) == 0) {
+ tty_new = tty_old;
+ tty_new.c_lflag &= ~(ECHO | ISIG);
+
+ if (tcsetattr(fileno(in), TCSAFLUSH, &tty_new) == 0)
+ tty_changed = True;
+ }
+
+ if (!tty_changed)
+ fputs("(WARNING: will be visible) ", out);
+ fputs(prompt, out);
+ fflush(out);
+
+ read_success = fgets(password, sizeof password, in) != NULL;
+
+ /* Print the newline that hasn't been echoed. */
+ fputc('\n', out);
+
+ if (tty_changed)
+ tcsetattr(fileno(in), TCSAFLUSH, &tty_old);
+
+ if (tty)
+ fclose(tty);
+
+ if (read_success) {
+ /* Remove the trailing newline. */
+ size_t password_len = strlen(password);
+ if (password_len && password[password_len - 1] == '\n')
+ password[password_len - 1] = '\0';
+
+ return password;
+ }
+
+ return NULL;
+}
diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c
new file mode 100644
index 0000000..7be7368
--- /dev/null
+++ b/lib/inet_ntop.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include "rsync.h"
+
+#define NS_INT16SZ 2
+#define NS_IN6ADDRSZ 16
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4(const unsigned char *src, char *dst,
+ size_t size);
+
+#ifdef AF_INET6
+static const char *inet_ntop6(const unsigned char *src, char *dst,
+ size_t size);
+#endif
+
+/* char *
+ * isc_net_ntop(af, src, dst, size)
+ * convert a network format address to presentation format.
+ * return:
+ * pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ * Paul Vixie, 1996.
+ */
+const char *
+inet_ntop(int af, const void *src, char *dst, size_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_ntop4(src, dst, size));
+#ifdef AF_INET6
+ case AF_INET6:
+ return (inet_ntop6(src, dst, size));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+ /* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ * format an IPv4 address
+ * return:
+ * `dst' (as a const)
+ * notes:
+ * (1) uses no statics
+ * (2) takes a unsigned char* not an in_addr as input
+ * author:
+ * Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(const unsigned char *src, char *dst, size_t size)
+{
+ static const char *fmt = "%u.%u.%u.%u";
+ char tmp[sizeof "255.255.255.255"];
+ size_t len;
+
+ len = snprintf(tmp, sizeof tmp, fmt, src[0], src[1], src[2], src[3]);
+ if (len >= size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ memcpy(dst, tmp, len + 1);
+
+ return (dst);
+}
+
+/* const char *
+ * isc_inet_ntop6(src, dst, size)
+ * convert IPv6 binary address into presentation (printable) format
+ * author:
+ * Paul Vixie, 1996.
+ */
+#ifdef AF_INET6
+static const char *
+inet_ntop6(const unsigned char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+ struct { int base, len; } best, cur;
+ unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
+ int i, inc;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < NS_IN6ADDRSZ; i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ cur.base = -1;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base &&
+ i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0)
+ *tp++ = ':';
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+ if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
+ return (NULL);
+ tp += strlen(tp);
+ break;
+ }
+ inc = snprintf(tp, 5, "%x", words[i]);
+ assert(inc < 5);
+ tp += inc;
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) ==
+ (NS_IN6ADDRSZ / NS_INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ memcpy(dst, tmp, tp - tmp);
+ return (dst);
+}
+#endif /* AF_INET6 */
diff --git a/lib/inet_pton.c b/lib/inet_pton.c
new file mode 100644
index 0000000..4a0be88
--- /dev/null
+++ b/lib/inet_pton.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 1996-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "rsync.h"
+
+#define NS_INT16SZ 2
+#define NS_INADDRSZ 4
+#define NS_IN6ADDRSZ 16
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4(const char *src, unsigned char *dst);
+#ifdef INET6
+static int inet_pton6(const char *src, unsigned char *dst);
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+inet_pton(int af,
+ const char *src,
+ void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+#ifdef INET6
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(src, dst)
+ const char *src;
+ unsigned char *dst;
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[NS_INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int new = *tp * 10 + (pch - digits);
+
+ if (new > 255)
+ return (0);
+ *tp = new;
+ if (! saw_digit) {
+ if (++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return (0);
+ }
+ if (octets < 4)
+ return (0);
+ memcpy(dst, tmp, NS_INADDRSZ);
+ return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+#ifdef INET6
+static int
+inet_pton6(src, dst)
+ const char *src;
+ unsigned char *dst;
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ unsigned int val;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += NS_INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
+ return (1);
+}
+#endif
diff --git a/lib/md-defines.h b/lib/md-defines.h
new file mode 100644
index 0000000..6ef6a68
--- /dev/null
+++ b/lib/md-defines.h
@@ -0,0 +1,37 @@
+/* Keep this simple so both C and ASM can use it */
+
+/* These allow something like CFLAGS=-DDISABLE_SHA512_DIGEST */
+#ifdef DISABLE_SHA256_DIGEST
+#undef SHA256_DIGEST_LENGTH
+#endif
+#ifdef DISABLE_SHA512_DIGEST
+#undef SHA512_DIGEST_LENGTH
+#endif
+
+#define MD4_DIGEST_LEN 16
+#define MD5_DIGEST_LEN 16
+#if defined SHA512_DIGEST_LENGTH
+#define MAX_DIGEST_LEN SHA512_DIGEST_LENGTH
+#elif defined SHA256_DIGEST_LENGTH
+#define MAX_DIGEST_LEN SHA256_DIGEST_LENGTH
+#elif defined SHA_DIGEST_LENGTH
+#define MAX_DIGEST_LEN SHA_DIGEST_LENGTH
+#else
+#define MAX_DIGEST_LEN MD5_DIGEST_LEN
+#endif
+
+#define CSUM_CHUNK 64
+
+#define CSUM_gone -1
+#define CSUM_NONE 0
+#define CSUM_MD4_ARCHAIC 1
+#define CSUM_MD4_BUSTED 2
+#define CSUM_MD4_OLD 3
+#define CSUM_MD4 4
+#define CSUM_MD5 5
+#define CSUM_XXH64 6
+#define CSUM_XXH3_64 7
+#define CSUM_XXH3_128 8
+#define CSUM_SHA1 9
+#define CSUM_SHA256 10
+#define CSUM_SHA512 11
diff --git a/lib/md5-asm-x86_64.S b/lib/md5-asm-x86_64.S
new file mode 100644
index 0000000..3737058
--- /dev/null
+++ b/lib/md5-asm-x86_64.S
@@ -0,0 +1,701 @@
+/*
+ * x86-64 optimized assembler MD5 implementation
+ *
+ * Author: Marc Bevand, 2004
+ *
+ * This code was placed in the public domain by the author. The original
+ * publication can be found at:
+ *
+ * https://www.zorinaq.com/papers/md5-amd64.html
+ */
+/*
+ * No modifications were made aside from changing the function and file names.
+ * The MD5_CTX structure as expected here (from OpenSSL) is binary compatible
+ * with the md_context used by rsync, for the fields accessed.
+ *
+ * Benchmarks (in MB/s) C ASM
+ * - Intel Atom D2700 302 334
+ * - Intel i7-7700hq 351 376
+ * - AMD ThreadRipper 2950x 728 784
+ *
+ * The original code was also incorporated into OpenSSL. It has since been
+ * modified there. Those changes have not been made here due to licensing
+ * incompatibilities. Benchmarks of those changes on the above CPUs did not
+ * show any significant difference in performance, though.
+ */
+
+#include "config.h"
+#include "md-defines.h"
+
+#ifdef USE_MD5_ASM /* { */
+
+#ifdef __APPLE__
+#define md5_process_asm _md5_process_asm
+#endif
+
+.text
+.align 16
+
+.globl md5_process_asm
+md5_process_asm:
+ push %rbp
+ push %rbx
+ push %r12
+ push %r13 # not really useful (r13 is unused)
+ push %r14
+ push %r15
+
+ # rdi = arg #1 (ctx, MD5_CTX pointer)
+ # rsi = arg #2 (ptr, data pointer)
+ # rdx = arg #3 (nbr, number of 16-word blocks to process)
+ mov %rdi, %rbp # rbp = ctx
+ shl $6, %rdx # rdx = nbr in bytes
+ lea (%rsi,%rdx), %rdi # rdi = end
+ mov 0*4(%rbp), %eax # eax = ctx->A
+ mov 1*4(%rbp), %ebx # ebx = ctx->B
+ mov 2*4(%rbp), %ecx # ecx = ctx->C
+ mov 3*4(%rbp), %edx # edx = ctx->D
+ # end is 'rdi'
+ # ptr is 'rsi'
+ # A is 'eax'
+ # B is 'ebx'
+ # C is 'ecx'
+ # D is 'edx'
+
+ cmp %rdi, %rsi # cmp end with ptr
+ je 1f # jmp if ptr == end
+
+ # BEGIN of loop over 16-word blocks
+2: # save old values of A, B, C, D
+ mov %eax, %r8d
+ mov %ebx, %r9d
+ mov %ecx, %r14d
+ mov %edx, %r15d
+ mov 0*4(%rsi), %r10d /* (NEXT STEP) X[0] */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ xor %ecx, %r11d /* y ^ ... */
+ lea -680876936(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea -389564586(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea 606105819(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea -1044525330(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ xor %ecx, %r11d /* y ^ ... */
+ lea -176418897(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea 1200080426(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea -1473231341(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea -45705983(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ xor %ecx, %r11d /* y ^ ... */
+ lea 1770035416(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea -1958414417(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea -42063(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea -1990404162(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ xor %ecx, %r11d /* y ^ ... */
+ lea 1804603682(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r11d /* x & ... */
+ xor %edx, %r11d /* z ^ ... */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ add %r11d, %eax /* dst += ... */
+ rol $7, %eax /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %ebx, %eax /* dst += x */
+ xor %ebx, %r11d /* y ^ ... */
+ lea -40341101(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r11d /* x & ... */
+ xor %ecx, %r11d /* z ^ ... */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ add %r11d, %edx /* dst += ... */
+ rol $12, %edx /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %eax, %edx /* dst += x */
+ xor %eax, %r11d /* y ^ ... */
+ lea -1502002290(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r11d /* x & ... */
+ xor %ebx, %r11d /* z ^ ... */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ add %r11d, %ecx /* dst += ... */
+ rol $17, %ecx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %edx, %ecx /* dst += x */
+ xor %edx, %r11d /* y ^ ... */
+ lea 1236535329(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r11d /* x & ... */
+ xor %eax, %r11d /* z ^ ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ add %r11d, %ebx /* dst += ... */
+ rol $22, %ebx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %ecx, %ebx /* dst += x */
+ mov 1*4(%rsi), %r10d /* (NEXT STEP) X[1] */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ not %r11d /* not z */
+ lea -165796510(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea -1069501632(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea 643717713(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea -373897302(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ not %r11d /* not z */
+ lea -701558691(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea 38016083(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea -660478335(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea -405537848(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ not %r11d /* not z */
+ lea 568446438(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea -1019803690(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea -187363961(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea 1163531501(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ not %r11d /* not z */
+ lea -1444681467(%eax,%r10d),%eax /* Const + dst + ... */
+ and %ebx, %r12d /* x & z */
+ and %ecx, %r11d /* y & (not z) */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
+ add %r12d, %eax /* dst += ... */
+ mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
+ rol $5, %eax /* dst <<< s */
+ add %ebx, %eax /* dst += x */
+ not %r11d /* not z */
+ lea -51403784(%edx,%r10d),%edx /* Const + dst + ... */
+ and %eax, %r12d /* x & z */
+ and %ebx, %r11d /* y & (not z) */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
+ add %r12d, %edx /* dst += ... */
+ mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
+ rol $9, %edx /* dst <<< s */
+ add %eax, %edx /* dst += x */
+ not %r11d /* not z */
+ lea 1735328473(%ecx,%r10d),%ecx /* Const + dst + ... */
+ and %edx, %r12d /* x & z */
+ and %eax, %r11d /* y & (not z) */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %eax, %r11d /* (NEXT STEP) z' = %eax */
+ add %r12d, %ecx /* dst += ... */
+ mov %eax, %r12d /* (NEXT STEP) z' = %eax */
+ rol $14, %ecx /* dst <<< s */
+ add %edx, %ecx /* dst += x */
+ not %r11d /* not z */
+ lea -1926607734(%ebx,%r10d),%ebx /* Const + dst + ... */
+ and %ecx, %r12d /* x & z */
+ and %edx, %r11d /* y & (not z) */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ or %r11d, %r12d /* (y & (not z)) | (x & z) */
+ mov %edx, %r11d /* (NEXT STEP) z' = %edx */
+ add %r12d, %ebx /* dst += ... */
+ mov %edx, %r12d /* (NEXT STEP) z' = %edx */
+ rol $20, %ebx /* dst <<< s */
+ add %ecx, %ebx /* dst += x */
+ mov 5*4(%rsi), %r10d /* (NEXT STEP) X[5] */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ lea -378558(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea -2022574463(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea 1839030562(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea -35309556(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ lea -1530992060(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea 1272893353(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea -155497632(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea -1094730640(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ lea 681279174(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea -358537222(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea -722521979(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea 76029189(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ lea -640364487(%eax,%r10d),%eax /* Const + dst + ... */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ xor %edx, %r11d /* z ^ ... */
+ xor %ebx, %r11d /* x ^ ... */
+ add %r11d, %eax /* dst += ... */
+ rol $4, %eax /* dst <<< s */
+ mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
+ add %ebx, %eax /* dst += x */
+ lea -421815835(%edx,%r10d),%edx /* Const + dst + ... */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ xor %ecx, %r11d /* z ^ ... */
+ xor %eax, %r11d /* x ^ ... */
+ add %r11d, %edx /* dst += ... */
+ rol $11, %edx /* dst <<< s */
+ mov %eax, %r11d /* (NEXT STEP) y' = %eax */
+ add %eax, %edx /* dst += x */
+ lea 530742520(%ecx,%r10d),%ecx /* Const + dst + ... */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ xor %ebx, %r11d /* z ^ ... */
+ xor %edx, %r11d /* x ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ rol $16, %ecx /* dst <<< s */
+ mov %edx, %r11d /* (NEXT STEP) y' = %edx */
+ add %edx, %ecx /* dst += x */
+ lea -995338651(%ebx,%r10d),%ebx /* Const + dst + ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ xor %eax, %r11d /* z ^ ... */
+ xor %ecx, %r11d /* x ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ rol $23, %ebx /* dst <<< s */
+ mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
+ add %ecx, %ebx /* dst += x */
+ mov 0*4(%rsi), %r10d /* (NEXT STEP) X[0] */
+ mov $0xffffffff, %r11d
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx*/
+ lea -198630844(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea 1126891415(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea -1416354905(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea -57434055(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ lea 1700485571(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea -1894986606(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea -1051523(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea -2054922799(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ lea 1873313359(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea -30611744(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea -1560198380(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea 1309151649(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ lea -145523070(%eax,%r10d),%eax /* Const + dst + ... */
+ or %ebx, %r11d /* x | ... */
+ xor %ecx, %r11d /* y ^ ... */
+ add %r11d, %eax /* dst += ... */
+ mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
+ mov $0xffffffff, %r11d
+ rol $6, %eax /* dst <<< s */
+ xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
+ add %ebx, %eax /* dst += x */
+ lea -1120210379(%edx,%r10d),%edx /* Const + dst + ... */
+ or %eax, %r11d /* x | ... */
+ xor %ebx, %r11d /* y ^ ... */
+ add %r11d, %edx /* dst += ... */
+ mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
+ mov $0xffffffff, %r11d
+ rol $10, %edx /* dst <<< s */
+ xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
+ add %eax, %edx /* dst += x */
+ lea 718787259(%ecx,%r10d),%ecx /* Const + dst + ... */
+ or %edx, %r11d /* x | ... */
+ xor %eax, %r11d /* y ^ ... */
+ add %r11d, %ecx /* dst += ... */
+ mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
+ mov $0xffffffff, %r11d
+ rol $15, %ecx /* dst <<< s */
+ xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
+ add %edx, %ecx /* dst += x */
+ lea -343485551(%ebx,%r10d),%ebx /* Const + dst + ... */
+ or %ecx, %r11d /* x | ... */
+ xor %edx, %r11d /* y ^ ... */
+ add %r11d, %ebx /* dst += ... */
+ mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
+ mov $0xffffffff, %r11d
+ rol $21, %ebx /* dst <<< s */
+ xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
+ add %ecx, %ebx /* dst += x */
+ # add old values of A, B, C, D
+ add %r8d, %eax
+ add %r9d, %ebx
+ add %r14d, %ecx
+ add %r15d, %edx
+
+ # loop control
+ add $64, %rsi # ptr += 64
+ cmp %rdi, %rsi # cmp end with ptr
+ jb 2b # jmp if ptr < end
+ # END of loop over 16-word blocks
+1:
+ mov %eax, 0*4(%rbp) # ctx->A = A
+ mov %ebx, 1*4(%rbp) # ctx->B = B
+ mov %ecx, 2*4(%rbp) # ctx->C = C
+ mov %edx, 3*4(%rbp) # ctx->D = D
+
+ pop %r15
+ pop %r14
+ pop %r13 # not really useful (r13 is unused)
+ pop %r12
+ pop %rbx
+ pop %rbp
+ ret
+
+#endif /* } USE_MD5_ASM */
diff --git a/lib/md5.c b/lib/md5.c
new file mode 100644
index 0000000..f36c5ba
--- /dev/null
+++ b/lib/md5.c
@@ -0,0 +1,321 @@
+/*
+ * RFC 1321 compliant MD5 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+void md5_begin(md_context *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xEFCDAB89;
+ ctx->C = 0x98BADCFE;
+ ctx->D = 0x10325476;
+
+ ctx->totalN = ctx->totalN2 = 0;
+}
+
+static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
+{
+ uint32 X[16], A, B, C, D;
+
+ A = ctx->A;
+ B = ctx->B;
+ C = ctx->C;
+ D = ctx->D;
+
+ X[0] = IVALu(data, 0);
+ X[1] = IVALu(data, 4);
+ X[2] = IVALu(data, 8);
+ X[3] = IVALu(data, 12);
+ X[4] = IVALu(data, 16);
+ X[5] = IVALu(data, 20);
+ X[6] = IVALu(data, 24);
+ X[7] = IVALu(data, 28);
+ X[8] = IVALu(data, 32);
+ X[9] = IVALu(data, 36);
+ X[10] = IVALu(data, 40);
+ X[11] = IVALu(data, 44);
+ X[12] = IVALu(data, 48);
+ X[13] = IVALu(data, 52);
+ X[14] = IVALu(data, 56);
+ X[15] = IVALu(data, 60);
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t) a += F(b,c,d) + X[k] + t, a = S(a,s) + b
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+ P(A, B, C, D, 0, 7, 0xD76AA478);
+ P(D, A, B, C, 1, 12, 0xE8C7B756);
+ P(C, D, A, B, 2, 17, 0x242070DB);
+ P(B, C, D, A, 3, 22, 0xC1BDCEEE);
+ P(A, B, C, D, 4, 7, 0xF57C0FAF);
+ P(D, A, B, C, 5, 12, 0x4787C62A);
+ P(C, D, A, B, 6, 17, 0xA8304613);
+ P(B, C, D, A, 7, 22, 0xFD469501);
+ P(A, B, C, D, 8, 7, 0x698098D8);
+ P(D, A, B, C, 9, 12, 0x8B44F7AF);
+ P(C, D, A, B, 10, 17, 0xFFFF5BB1);
+ P(B, C, D, A, 11, 22, 0x895CD7BE);
+ P(A, B, C, D, 12, 7, 0x6B901122);
+ P(D, A, B, C, 13, 12, 0xFD987193);
+ P(C, D, A, B, 14, 17, 0xA679438E);
+ P(B, C, D, A, 15, 22, 0x49B40821);
+
+#undef F
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+ P(A, B, C, D, 1, 5, 0xF61E2562);
+ P(D, A, B, C, 6, 9, 0xC040B340);
+ P(C, D, A, B, 11, 14, 0x265E5A51);
+ P(B, C, D, A, 0, 20, 0xE9B6C7AA);
+ P(A, B, C, D, 5, 5, 0xD62F105D);
+ P(D, A, B, C, 10, 9, 0x02441453);
+ P(C, D, A, B, 15, 14, 0xD8A1E681);
+ P(B, C, D, A, 4, 20, 0xE7D3FBC8);
+ P(A, B, C, D, 9, 5, 0x21E1CDE6);
+ P(D, A, B, C, 14, 9, 0xC33707D6);
+ P(C, D, A, B, 3, 14, 0xF4D50D87);
+ P(B, C, D, A, 8, 20, 0x455A14ED);
+ P(A, B, C, D, 13, 5, 0xA9E3E905);
+ P(D, A, B, C, 2, 9, 0xFCEFA3F8);
+ P(C, D, A, B, 7, 14, 0x676F02D9);
+ P(B, C, D, A, 12, 20, 0x8D2A4C8A);
+
+#undef F
+#define F(x,y,z) (x ^ y ^ z)
+
+ P(A, B, C, D, 5, 4, 0xFFFA3942);
+ P(D, A, B, C, 8, 11, 0x8771F681);
+ P(C, D, A, B, 11, 16, 0x6D9D6122);
+ P(B, C, D, A, 14, 23, 0xFDE5380C);
+ P(A, B, C, D, 1, 4, 0xA4BEEA44);
+ P(D, A, B, C, 4, 11, 0x4BDECFA9);
+ P(C, D, A, B, 7, 16, 0xF6BB4B60);
+ P(B, C, D, A, 10, 23, 0xBEBFBC70);
+ P(A, B, C, D, 13, 4, 0x289B7EC6);
+ P(D, A, B, C, 0, 11, 0xEAA127FA);
+ P(C, D, A, B, 3, 16, 0xD4EF3085);
+ P(B, C, D, A, 6, 23, 0x04881D05);
+ P(A, B, C, D, 9, 4, 0xD9D4D039);
+ P(D, A, B, C, 12, 11, 0xE6DB99E5);
+ P(C, D, A, B, 15, 16, 0x1FA27CF8);
+ P(B, C, D, A, 2, 23, 0xC4AC5665);
+
+#undef F
+#define F(x,y,z) (y ^ (x | ~z))
+
+ P(A, B, C, D, 0, 6, 0xF4292244);
+ P(D, A, B, C, 7, 10, 0x432AFF97);
+ P(C, D, A, B, 14, 15, 0xAB9423A7);
+ P(B, C, D, A, 5, 21, 0xFC93A039);
+ P(A, B, C, D, 12, 6, 0x655B59C3);
+ P(D, A, B, C, 3, 10, 0x8F0CCC92);
+ P(C, D, A, B, 10, 15, 0xFFEFF47D);
+ P(B, C, D, A, 1, 21, 0x85845DD1);
+ P(A, B, C, D, 8, 6, 0x6FA87E4F);
+ P(D, A, B, C, 15, 10, 0xFE2CE6E0);
+ P(C, D, A, B, 6, 15, 0xA3014314);
+ P(B, C, D, A, 13, 21, 0x4E0811A1);
+ P(A, B, C, D, 4, 6, 0xF7537E82);
+ P(D, A, B, C, 11, 10, 0xBD3AF235);
+ P(C, D, A, B, 2, 15, 0x2AD7D2BB);
+ P(B, C, D, A, 9, 21, 0xEB86D391);
+
+#undef F
+
+ ctx->A += A;
+ ctx->B += B;
+ ctx->C += C;
+ ctx->D += D;
+}
+
+#ifdef USE_MD5_ASM
+#if CSUM_CHUNK != 64
+#error The MD5 ASM code does not support CSUM_CHUNK != 64
+#endif
+extern void md5_process_asm(md_context *ctx, const void *data, size_t num);
+#endif
+
+void md5_update(md_context *ctx, const uchar *input, uint32 length)
+{
+ uint32 left, fill;
+
+ if (!length)
+ return;
+
+ left = ctx->totalN & 0x3F;
+ fill = CSUM_CHUNK - left;
+
+ ctx->totalN += length;
+ ctx->totalN &= 0xFFFFFFFF;
+
+ if (ctx->totalN < length)
+ ctx->totalN2++;
+
+ if (left && length >= fill) {
+ memcpy(ctx->buffer + left, input, fill);
+ md5_process(ctx, ctx->buffer);
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+#ifdef USE_MD5_ASM /* { */
+ if (length >= CSUM_CHUNK) {
+ uint32 chunks = length / CSUM_CHUNK;
+ md5_process_asm(ctx, input, chunks);
+ length -= chunks * CSUM_CHUNK;
+ input += chunks * CSUM_CHUNK;
+ }
+#else /* } { */
+ while (length >= CSUM_CHUNK) {
+ md5_process(ctx, input);
+ length -= CSUM_CHUNK;
+ input += CSUM_CHUNK;
+ }
+#endif /* } */
+
+ if (length)
+ memcpy(ctx->buffer + left, input, length);
+}
+
+static uchar md5_padding[CSUM_CHUNK] = { 0x80 };
+
+void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
+{
+ uint32 last, padn;
+ uint32 high, low;
+ uchar msglen[8];
+
+ high = (ctx->totalN >> 29)
+ | (ctx->totalN2 << 3);
+ low = (ctx->totalN << 3);
+
+ SIVALu(msglen, 0, low);
+ SIVALu(msglen, 4, high);
+
+ last = ctx->totalN & 0x3F;
+ padn = last < 56 ? 56 - last : 120 - last;
+
+ md5_update(ctx, md5_padding, padn);
+ md5_update(ctx, msglen, 8);
+
+ SIVALu(digest, 0, ctx->A);
+ SIVALu(digest, 4, ctx->B);
+ SIVALu(digest, 8, ctx->C);
+ SIVALu(digest, 12, ctx->D);
+}
+
+#ifdef TEST_MD5 /* { */
+
+void get_md5(uchar *out, const uchar *input, int n)
+{
+ md_context ctx;
+ md5_begin(&ctx);
+ md5_update(&ctx, input, n);
+ md5_result(&ctx, out);
+}
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * those are the standard RFC 1321 test vectors
+ */
+
+static struct {
+ char *str, *md5;
+} tests[] = {
+ { "",
+ "d41d8cd98f00b204e9800998ecf8427e" },
+ { "a",
+ "0cc175b9c0f1b6a831c399e269772661" },
+ { "abc",
+ "900150983cd24fb0d6963f7d28e17f72" },
+ { "message digest",
+ "f96b697d7cb7938d525a2f31aaf161d0" },
+ { "abcdefghijklmnopqrstuvwxyz",
+ "c3fcd3d76192e4007dfb496cca67e13b" },
+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "d174ab98d277d9f5a5611c2c9f419d9f" },
+ { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "57edf4a22be3c955ac49da2e2107b67a" },
+ { NULL, NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ int i, j;
+ char output[33];
+ md_context ctx;
+ uchar buf[1000];
+ uchar md5sum[MD5_DIGEST_LEN];
+
+ if (argc < 2) {
+ printf("\nMD5 Validation Tests:\n\n");
+
+ for (i = 0; tests[i].str; i++) {
+ char *str = tests[i].str;
+ char *chk = tests[i].md5;
+
+ printf(" Test %d ", i + 1);
+
+ get_md5(md5sum, str, strlen(str));
+
+ for (j = 0; j < MD5_DIGEST_LEN; j++)
+ sprintf(output + j * 2, "%02x", md5sum[j]);
+
+ if (memcmp(output, chk, 32)) {
+ printf("failed!\n");
+ return 1;
+ }
+
+ printf("passed.\n");
+ }
+
+ printf("\n");
+ return 0;
+ }
+
+ while (--argc) {
+ if (!(f = fopen(*++argv, "rb"))) {
+ perror("fopen");
+ return 1;
+ }
+
+ md5_begin(&ctx);
+
+ while ((i = fread(buf, 1, sizeof buf, f)) > 0)
+ md5_update(&ctx, buf, i);
+
+ md5_result(&ctx, md5sum);
+
+ for (j = 0; j < MD5_DIGEST_LEN; j++)
+ printf("%02x", md5sum[j]);
+
+ printf(" %s\n", *argv);
+ }
+
+ return 0;
+}
+
+#endif /* } */
diff --git a/lib/mdfour.c b/lib/mdfour.c
new file mode 100644
index 0000000..6203658
--- /dev/null
+++ b/lib/mdfour.c
@@ -0,0 +1,247 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * An implementation of MD4 designed for use in the SMB authentication protocol.
+ *
+ * Copyright (C) 1997-1998 Andrew Tridgell
+ * Copyright (C) 2005-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* NOTE: This code makes no attempt to be fast!
+ *
+ * It assumes that a int is at least 32 bits long. */
+
+static md_context *m;
+
+#define MASK32 (0xffffffff)
+
+#define F(X,Y,Z) ((((X)&(Y)) | ((~(X))&(Z))))
+#define G(X,Y,Z) ((((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))))
+#define H(X,Y,Z) (((X)^(Y)^(Z)))
+#define lshift(x,s) (((((x)<<(s))&MASK32) | (((x)>>(32-(s)))&MASK32)))
+
+#define ROUND1(a,b,c,d,k,s) a = lshift((a + F(b,c,d) + M[k])&MASK32, s)
+#define ROUND2(a,b,c,d,k,s) a = lshift((a + G(b,c,d) + M[k] + 0x5A827999)&MASK32,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift((a + H(b,c,d) + M[k] + 0x6ED9EBA1)&MASK32,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+ uint32 AA, BB, CC, DD;
+ uint32 A,B,C,D;
+
+ A = m->A; B = m->B; C = m->C; D = m->D;
+ AA = A; BB = B; CC = C; DD = D;
+
+ ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
+ ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
+ ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
+ ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
+ ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
+ ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
+ ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
+ ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
+
+ ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
+ ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
+ ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
+ ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
+ ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
+ ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
+ ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
+ ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
+
+ ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
+ ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
+ ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
+ ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
+ ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
+ ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
+ ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
+ ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
+
+ A += AA; B += BB;
+ C += CC; D += DD;
+
+ A &= MASK32; B &= MASK32;
+ C &= MASK32; D &= MASK32;
+
+ m->A = A; m->B = B; m->C = C; m->D = D;
+}
+
+static void copy64(uint32 *M, const uchar *in)
+{
+ int i;
+
+ for (i = 0; i < MD4_DIGEST_LEN; i++) {
+ M[i] = (in[i*4+3] << 24) | (in[i*4+2] << 16)
+ | (in[i*4+1] << 8) | (in[i*4+0] << 0);
+ }
+}
+
+static void copy4(uchar *out,uint32 x)
+{
+ out[0] = x&0xFF;
+ out[1] = (x>>8)&0xFF;
+ out[2] = (x>>16)&0xFF;
+ out[3] = (x>>24)&0xFF;
+}
+
+void mdfour_begin(md_context *md)
+{
+ md->A = 0x67452301;
+ md->B = 0xefcdab89;
+ md->C = 0x98badcfe;
+ md->D = 0x10325476;
+ md->totalN = 0;
+ md->totalN2 = 0;
+}
+
+static void mdfour_tail(const uchar *in, uint32 length)
+{
+ uchar buf[128];
+ uint32 M[16];
+ extern int protocol_version;
+
+ /*
+ * Count total number of bits, modulo 2^64
+ */
+ m->totalN += length << 3;
+ if (m->totalN < (length << 3))
+ m->totalN2++;
+ m->totalN2 += length >> 29;
+
+ memset(buf, 0, 128);
+ if (length)
+ memcpy(buf, in, length);
+ buf[length] = 0x80;
+
+ if (length <= 55) {
+ copy4(buf+56, m->totalN);
+ /*
+ * Prior to protocol version 27 only the number of bits
+ * modulo 2^32 was included. MD4 requires the number
+ * of bits modulo 2^64, which was fixed starting with
+ * protocol version 27.
+ */
+ if (protocol_version >= 27)
+ copy4(buf+60, m->totalN2);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf+120, m->totalN);
+ /*
+ * Prior to protocol version 27 only the number of bits
+ * modulo 2^32 was included. MD4 requires the number
+ * of bits modulo 2^64, which was fixed starting with
+ * protocol version 27.
+ */
+ if (protocol_version >= 27)
+ copy4(buf+124, m->totalN2);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf+64);
+ mdfour64(M);
+ }
+}
+
+void mdfour_update(md_context *md, const uchar *in, uint32 length)
+{
+ uint32 M[16];
+
+ m = md;
+
+ if (length == 0)
+ mdfour_tail(in, length);
+
+ while (length >= 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ length -= 64;
+ m->totalN += 64 << 3;
+ if (m->totalN < 64 << 3)
+ m->totalN2++;
+ }
+
+ if (length)
+ mdfour_tail(in, length);
+}
+
+void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN])
+{
+ m = md;
+
+ copy4(digest, m->A);
+ copy4(digest+4, m->B);
+ copy4(digest+8, m->C);
+ copy4(digest+12, m->D);
+}
+
+#ifdef TEST_MDFOUR
+
+void mdfour(uchar digest[MD4_DIGEST_LEN], uchar *in, int length)
+{
+ md_context md;
+ mdfour_begin(&md);
+ mdfour_update(&md, in, length);
+ mdfour_result(&md, digest);
+}
+
+int protocol_version = 28;
+
+static void file_checksum1(char *fname)
+{
+ int fd, i, was_multiple_of_64 = 1;
+ md_context md;
+ uchar buf[64*1024], sum[MD4_DIGEST_LEN];
+
+ fd = open(fname,O_RDONLY);
+ if (fd == -1) {
+ perror("fname");
+ exit(1);
+ }
+
+ mdfour_begin(&md);
+
+ while (1) {
+ int n = read(fd, buf, sizeof buf);
+ if (n <= 0)
+ break;
+ was_multiple_of_64 = !(n % 64);
+ mdfour_update(&md, buf, n);
+ }
+ if (was_multiple_of_64 && protocol_version >= 27)
+ mdfour_update(&md, buf, 0);
+
+ close(fd);
+
+ mdfour_result(&md, sum);
+
+ for (i = 0; i < MD4_DIGEST_LEN; i++)
+ printf("%02X", sum[i]);
+ printf("\n");
+}
+
+ int main(int argc, char *argv[])
+{
+ while (--argc)
+ file_checksum1(*++argv);
+ return 0;
+}
+#endif
diff --git a/lib/mdigest.h b/lib/mdigest.h
new file mode 100644
index 0000000..9d52ef5
--- /dev/null
+++ b/lib/mdigest.h
@@ -0,0 +1,22 @@
+/* The include file for both the MD4 and MD5 routines. */
+
+#ifdef USE_OPENSSL
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#endif
+#include "md-defines.h"
+
+typedef struct {
+ uint32 A, B, C, D;
+ uint32 totalN; /* bit count, lower 32 bits */
+ uint32 totalN2; /* bit count, upper 32 bits */
+ uchar buffer[CSUM_CHUNK];
+} md_context;
+
+void mdfour_begin(md_context *md);
+void mdfour_update(md_context *md, const uchar *in, uint32 length);
+void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN]);
+
+void md5_begin(md_context *ctx);
+void md5_update(md_context *ctx, const uchar *input, uint32 length);
+void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN]);
diff --git a/lib/permstring.c b/lib/permstring.c
new file mode 100644
index 0000000..ab8e26c
--- /dev/null
+++ b/lib/permstring.c
@@ -0,0 +1,65 @@
+/*
+ * A single utility routine.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2019 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* Produce a string representation of Unix mode bits like that used by ls(1).
+ * The "buf" buffer must be at least 11 characters. */
+void permstring(char *perms, mode_t mode)
+{
+ static const char *perm_map = "rwxrwxrwx";
+ int i;
+
+ strlcpy(perms, "----------", 11);
+
+ for (i = 0; i < 9; i++) {
+ if (mode & (1 << i))
+ perms[9-i] = perm_map[8-i];
+ }
+
+ /* Handle setuid/sticky bits. You might think the indices are
+ * off by one, but remember there's a type char at the
+ * start. */
+ if (mode & S_ISUID)
+ perms[3] = (mode & S_IXUSR) ? 's' : 'S';
+
+ if (mode & S_ISGID)
+ perms[6] = (mode & S_IXGRP) ? 's' : 'S';
+
+#ifdef S_ISVTX
+ if (mode & S_ISVTX)
+ perms[9] = (mode & S_IXOTH) ? 't' : 'T';
+#endif
+
+ if (S_ISDIR(mode))
+ perms[0] = 'd';
+ else if (S_ISLNK(mode))
+ perms[0] = 'l';
+ else if (S_ISBLK(mode))
+ perms[0] = 'b';
+ else if (S_ISCHR(mode))
+ perms[0] = 'c';
+ else if (S_ISSOCK(mode))
+ perms[0] = 's';
+ else if (S_ISFIFO(mode))
+ perms[0] = 'p';
+}
diff --git a/lib/permstring.h b/lib/permstring.h
new file mode 100644
index 0000000..b996f2a
--- /dev/null
+++ b/lib/permstring.h
@@ -0,0 +1,3 @@
+#define PERMSTRING_SIZE 11
+
+void permstring(char *perms, mode_t mode);
diff --git a/lib/pool_alloc.3 b/lib/pool_alloc.3
new file mode 100644
index 0000000..128c1f7
--- /dev/null
+++ b/lib/pool_alloc.3
@@ -0,0 +1,268 @@
+.ds d \-\^\-
+.ds o \fR[\fP
+.ds c \fR]\fP
+.ds | \fR|\fP
+.de D
+\\.B \*d\\$1
+..
+.de DI
+\\.BI \*d\\$1 \\$2
+..
+.de DR
+\\.BR \*d\\$1 \\$2
+..
+.de Di
+\\.BI \*d\\$1 " \\$2"
+..
+.de Db
+\\.B \*d\\$1 " \\$2"
+..
+.de Df
+\\.B \*d\*ono\*c\\$1
+..
+.de See
+See \fB\\$1\fP for details.
+..
+.de SeeIn
+See \fB\\$1\fP in \fB\\$2\fP for details.
+..
+.TH POOL_ALLOC 3
+.SH NAME
+pool_alloc, pool_free, pool_free_old, pool_talloc, pool_tfree, pool_create, pool_destroy, pool_boundary
+\- Allocate and free memory in managed allocation pools.
+.SH SYNOPSIS
+.B #include "pool_alloc.h"
+
+\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char*,char*,int), int \fIflags\fB);
+
+\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);
+
+\fBvoid *pool_alloc(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, char *\fImsg\fB);
+
+\fBvoid pool_free(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, void *\fIaddr\fB);
+
+\fBvoid pool_free_old(struct alloc_pool *\fIpool\fB, void *\fIaddr\fB);
+
+\fBvoid *pool_talloc(struct alloc_pool *\fIpool\fB, \fItype\fB), int \fIcount\fB, char *\fImsg\fB);
+
+\fBvoid pool_tfree(struct alloc_pool *\fIpool\fB, \fItype\fB, int \fIcount\fB, void *\fIaddr\fB);
+
+\fBvoid pool_boundary(struct alloc_pool *\fIpool\fB, sise_t \fIsize\fB);
+.SH DESCRIPTION
+.P
+The pool allocation routines use
+.B malloc()
+for underlying memory management.
+What allocation pools do is cause memory within a given pool
+to be allocated in large contiguous blocks
+(called extents) that will be reusable when freed. Unlike
+.BR malloc() ,
+the allocations are not managed individually.
+Instead, each extent tracks the total free memory within the
+extent. Each extent can either be used to allocate memory
+or to manage the freeing of memory within that extent.
+When an extent has less free memory than a given
+allocation request, the current extent ceases to be used
+for allocation. See also the
+.B pool_boundary()
+function.
+.P
+This form of memory management is suited to large numbers of small
+related allocations that are held for a while
+and then freed as a group.
+Because the
+underlying allocations are done in large contiguous extents,
+when an extent is freed, it can release a large enough
+contiguous block of memory to allow the memory to be returned
+to the OS for use by whatever program needs it.
+You can allocate from one or more memory pools and/or
+.B malloc()
+all at the same time without interfering with how pools work.
+.P
+.B pool_create()
+Creates an allocation pool for subsequent calls to the pool
+allocation functions.
+When an extent is created for allocations it will be
+.I size
+bytes.
+Allocations from the pool have their sizes rounded up to a
+multiple of
+.I quantum
+bytes in length.
+Specifying
+.B 0
+for
+.I quantum
+will produce a quantum that should meet maximal alignment
+on most platforms.
+Unless
+.B POOL_NO_QALIGN
+is set in the
+.IR flags ,
+allocations will be aligned to addresses that are a
+multiple of
+.IR quantum .
+A
+.B NULL
+may be specified for the
+.I bomb
+function pointer if it is not needed. (See the
+.B pool_alloc()
+function for how it is used.)
+If
+.B POOL_CLEAR
+is set in the
+.IR flags ,
+all allocations from the pool will be initialized to zeros.
+If either
+.B POOL_PREPEND
+or
+.B POOL_INTERN
+is specified in the
+.IR flags ,
+each extent's data structure will be allocated at the start of the
+.IR size -length
+buffer (rather than as a separate, non-pool allocation), with the
+former extending the
+.I size
+to hold the structure, and the latter subtracting the structure's
+length from the indicated
+.IR size .
+.P
+.B pool_destroy()
+destroys an allocation
+.I pool
+and frees all its associated memory.
+.P
+.B pool_alloc()
+allocates
+.I size
+bytes from the specified
+.IR pool .
+If
+.I size
+is
+.BR 0 ,
+.I quantum
+bytes will be allocated.
+If the pool has been created without
+.BR POOL_NO_QALIGN ,
+every chunk of memory that is returned will be suitably aligned.
+You can use this with the default
+.I quantum
+size to ensure that all memory can store a variable of any type.
+If the requested memory cannot be allocated, the
+.I bomb()
+function will be called with
+.I msg
+as its sole argument (if the function was defined at the time
+the pool was created), and then a
+.B NULL
+address is returned (assuming that the bomb function didn't exit).
+.P
+.B pool_free()
+frees
+.I size
+bytes pointed to by an
+.I addr
+that was previously allocated in the specified
+.IR pool .
+If
+.I size
+is
+.BR 0 ,
+.I quantum
+bytes will be freed.
+The memory freed within an extent will not be reusable until
+all of the memory in that extent has been freed with one
+exception: the most recent pool allocation may be freed back
+into the pool prior to making any further allocations.
+If enough free calls are made to indicate that an extent has no
+remaining allocated objects (as computed by the total freed size for
+an extent), its memory will be completely freed back to the system.
+If
+.I addr
+is
+.BR NULL ,
+no memory will be freed, but subsequent allocations will come
+from a new extent.
+.P
+.B pool_free_old()
+takes a boundary
+.I addr
+value that was returned by
+.B pool_boundary()
+and frees up any extents in the
+.I pool
+that have data allocated from that point backward in time.
+NOTE: you must NOT mix calls to both
+.B pool_free
+and
+.B pool_free_old
+on the same pool!
+.P
+.B pool_boundary()
+asks for a boundary value that can be sent to
+.B pool_free_old()
+at a later time to free up all memory allocated prior to a particular
+moment in time.
+If the extent that holds the boundary point has allocations from after the
+boundary point, it will not be freed until a future
+.B pool_free_old()
+call encompasses the entirety of the extent's data.
+If
+.I len
+is non-zero, the call will also check if the active extent has at least
+that much free memory available in it, and if not, it will mark the
+extent as inactive, forcing a new extent to be used for future allocations.
+(You can specify -1 for
+.I len
+if you want to force a new extent to start.)
+.P
+.B pool_talloc()
+is a macro that takes a
+.I type
+and a
+.I count
+instead of a
+.IR size .
+It casts the return value to the correct pointer type.
+.P
+.B pool_tfree
+is a macro that calls
+.B pool_free
+on memory that was allocated by
+.BR pool_talloc() .
+.SH RETURN VALUE
+.B pool_create()
+returns a pointer to
+.BR "struct alloc_pool" .
+.P
+.B pool_alloc()
+and
+.B pool_talloc()
+return pointers to the allocated memory,
+or NULL if the request fails.
+The return type of
+.B pool_alloc()
+will normally require casting to the desired type but
+.B pool_talloc()
+will returns a pointer of the requested
+.IR type .
+.P
+.B pool_boundary()
+returns a pointer that should only be used in a call to
+.BR pool_free_old() .
+.P
+.BR pool_free() ,
+.BR pool_free_old() ,
+.B pool_tfree()
+and
+.B pool_destroy()
+return no value.
+.SH SEE ALSO
+.nf
+malloc(3)
+.SH AUTHOR
+pool_alloc was created by J.W. Schultz of Pegasystems Technologies.
+.SH BUGS AND ISSUES
diff --git a/lib/pool_alloc.c b/lib/pool_alloc.c
new file mode 100644
index 0000000..a1a7245
--- /dev/null
+++ b/lib/pool_alloc.c
@@ -0,0 +1,375 @@
+#include "rsync.h"
+
+#define POOL_DEF_EXTENT (32 * 1024)
+
+#define POOL_QALIGN_P2 (1<<16) /* power-of-2 qalign */
+
+struct alloc_pool
+{
+ size_t size; /* extent size */
+ size_t quantum; /* allocation quantum */
+ struct pool_extent *extents; /* top extent is "live" */
+ void (*bomb)(); /* called if malloc fails */
+ int flags;
+
+ /* statistical data */
+ unsigned long e_created; /* extents created */
+ unsigned long e_freed; /* extents destroyed */
+ int64 n_allocated; /* calls to alloc */
+ int64 n_freed; /* calls to free */
+ int64 b_allocated; /* cum. bytes allocated */
+ int64 b_freed; /* cum. bytes freed */
+};
+
+struct pool_extent
+{
+ struct pool_extent *next;
+ void *start; /* starting address */
+ size_t free; /* free bytecount */
+ size_t bound; /* trapped free bytes */
+};
+
+struct align_test {
+ uchar foo;
+ union {
+ int64 i;
+ void *p;
+ } bar;
+};
+
+#define MINALIGN offsetof(struct align_test, bar)
+
+/* Temporarily cast a void* var into a char* var when adding an offset (to
+ * keep some compilers from complaining about the pointer arithmetic). */
+#define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) )
+
+alloc_pool_t
+pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags)
+{
+ struct alloc_pool *pool;
+
+ if ((MINALIGN & (MINALIGN - 1)) != (0)) {
+ if (bomb)
+ (*bomb)("Compiler error: MINALIGN is not a power of 2", __FILE__, __LINE__);
+ return NULL;
+ }
+
+ if (!(pool = new0(struct alloc_pool)))
+ return NULL;
+
+ if (!size)
+ size = POOL_DEF_EXTENT;
+ if (!quantum)
+ quantum = MINALIGN;
+
+ if (flags & POOL_INTERN) {
+ if (size <= sizeof (struct pool_extent))
+ size = quantum;
+ else
+ size -= sizeof (struct pool_extent);
+ flags |= POOL_PREPEND;
+ }
+
+ if (quantum <= 1)
+ flags = (flags | POOL_NO_QALIGN) & ~POOL_QALIGN_P2;
+ else if (!(flags & POOL_NO_QALIGN)) {
+ if (size % quantum)
+ size += quantum - size % quantum;
+ /* If quantum is a power of 2, we'll avoid using modulus. */
+ if (!(quantum & (quantum - 1)))
+ flags |= POOL_QALIGN_P2;
+ }
+
+ pool->size = size;
+ pool->quantum = quantum;
+ pool->bomb = bomb;
+ pool->flags = flags;
+
+ return pool;
+}
+
+void
+pool_destroy(alloc_pool_t p)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ struct pool_extent *cur, *next;
+
+ if (!pool)
+ return;
+
+ for (cur = pool->extents; cur; cur = next) {
+ next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ }
+
+ free(pool);
+}
+
+void *
+pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ if (!pool)
+ return NULL;
+
+ if (!len)
+ len = pool->quantum;
+ else if (pool->flags & POOL_QALIGN_P2) {
+ if (len & (pool->quantum - 1))
+ len += pool->quantum - (len & (pool->quantum - 1));
+ } else if (!(pool->flags & POOL_NO_QALIGN)) {
+ if (len % pool->quantum)
+ len += pool->quantum - len % pool->quantum;
+ }
+
+ if (len > pool->size)
+ goto bomb_out;
+
+ if (!pool->extents || len > pool->extents->free) {
+ void *start;
+ size_t asize;
+ struct pool_extent *ext;
+
+ asize = pool->size;
+ if (pool->flags & POOL_PREPEND)
+ asize += sizeof (struct pool_extent);
+
+ if (!(start = new_array(char, asize)))
+ goto bomb_out;
+
+ if (pool->flags & POOL_CLEAR)
+ memset(start, 0, asize);
+
+ if (pool->flags & POOL_PREPEND) {
+ ext = start;
+ start = PTR_ADD(start, sizeof (struct pool_extent));
+ } else if (!(ext = new(struct pool_extent)))
+ goto bomb_out;
+ ext->start = start;
+ ext->free = pool->size;
+ ext->bound = 0;
+ ext->next = pool->extents;
+ pool->extents = ext;
+
+ pool->e_created++;
+ }
+
+ pool->n_allocated++;
+ pool->b_allocated += len;
+
+ pool->extents->free -= len;
+
+ return PTR_ADD(pool->extents->start, pool->extents->free);
+
+ bomb_out:
+ if (pool->bomb)
+ (*pool->bomb)(bomb_msg, __FILE__, __LINE__);
+ return NULL;
+}
+
+/* This function allows you to declare memory in the pool that you are done
+ * using. If you free all the memory in a pool's extent, that extent will
+ * be freed. */
+void
+pool_free(alloc_pool_t p, size_t len, void *addr)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur, *prev;
+
+ if (!pool)
+ return;
+
+ if (!addr) {
+ /* A NULL addr starts a fresh extent for new allocations. */
+ if ((cur = pool->extents) != NULL && cur->free != pool->size) {
+ cur->bound += cur->free;
+ cur->free = 0;
+ }
+ return;
+ }
+
+ if (!len)
+ len = pool->quantum;
+ else if (pool->flags & POOL_QALIGN_P2) {
+ if (len & (pool->quantum - 1))
+ len += pool->quantum - (len & (pool->quantum - 1));
+ } else if (!(pool->flags & POOL_NO_QALIGN)) {
+ if (len % pool->quantum)
+ len += pool->quantum - len % pool->quantum;
+ }
+
+ pool->n_freed++;
+ pool->b_freed += len;
+
+ for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
+ if (addr >= cur->start
+ && addr < PTR_ADD(cur->start, pool->size))
+ break;
+ }
+ if (!cur)
+ return;
+
+ if (!prev) {
+ /* The "live" extent is kept ready for more allocations. */
+ if (cur->free + cur->bound + len >= pool->size) {
+ if (pool->flags & POOL_CLEAR) {
+ memset(PTR_ADD(cur->start, cur->free), 0,
+ pool->size - cur->free);
+ }
+ cur->free = pool->size;
+ cur->bound = 0;
+ } else if (addr == PTR_ADD(cur->start, cur->free)) {
+ if (pool->flags & POOL_CLEAR)
+ memset(addr, 0, len);
+ cur->free += len;
+ } else
+ cur->bound += len;
+ } else {
+ cur->bound += len;
+
+ if (cur->free + cur->bound >= pool->size) {
+ prev->next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ pool->e_freed++;
+ } else if (prev != pool->extents) {
+ /* Move the extent to be the first non-live extent. */
+ prev->next = cur->next;
+ cur->next = pool->extents->next;
+ pool->extents->next = cur;
+ }
+ }
+}
+
+/* This allows you to declare that the given address marks the edge of some
+ * pool memory that is no longer needed. Any extents that hold only data
+ * older than the boundary address are freed. NOTE: You MUST NOT USE BOTH
+ * pool_free() and pool_free_old() on the same pool!! */
+void
+pool_free_old(alloc_pool_t p, void *addr)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur, *prev, *next;
+
+ if (!pool || !addr)
+ return;
+
+ for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
+ if (addr >= cur->start
+ && addr < PTR_ADD(cur->start, pool->size))
+ break;
+ }
+ if (!cur)
+ return;
+
+ if (addr == PTR_ADD(cur->start, cur->free)) {
+ if (prev) {
+ prev->next = NULL;
+ next = cur;
+ } else {
+ /* The most recent live extent can just be reset. */
+ if (pool->flags & POOL_CLEAR)
+ memset(addr, 0, pool->size - cur->free);
+ cur->free = pool->size;
+ cur->bound = 0;
+ next = cur->next;
+ cur->next = NULL;
+ }
+ } else {
+ next = cur->next;
+ cur->next = NULL;
+ }
+
+ while ((cur = next) != NULL) {
+ next = cur->next;
+ if (pool->flags & POOL_PREPEND)
+ free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
+ else {
+ free(cur->start);
+ free(cur);
+ }
+ pool->e_freed++;
+ }
+}
+
+/* If the current extent doesn't have "len" free space in it, mark it as full
+ * so that the next alloc will start a new extent. If len is (size_t)-1, this
+ * bump will always occur. The function returns a boundary address that can
+ * be used with pool_free_old(), or a NULL if no memory is allocated. */
+void *
+pool_boundary(alloc_pool_t p, size_t len)
+{
+ struct alloc_pool *pool = (struct alloc_pool *)p;
+ struct pool_extent *cur;
+
+ if (!pool || !pool->extents)
+ return NULL;
+
+ cur = pool->extents;
+
+ if (cur->free < len) {
+ cur->bound += cur->free;
+ cur->free = 0;
+ }
+
+ return PTR_ADD(cur->start, cur->free);
+}
+
+#define FDPRINT(label, value) \
+ do { \
+ int len = snprintf(buf, sizeof buf, label, value); \
+ if (write(fd, buf, len) != len) \
+ ret = -1; \
+ } while (0)
+
+#define FDEXTSTAT(ext) \
+ do { \
+ int len = snprintf(buf, sizeof buf, " %12ld %5ld\n", \
+ (long)ext->free, (long)ext->bound); \
+ if (write(fd, buf, len) != len) \
+ ret = -1; \
+ } while (0)
+
+int
+pool_stats(alloc_pool_t p, int fd, int summarize)
+{
+ struct alloc_pool *pool = (struct alloc_pool *) p;
+ struct pool_extent *cur;
+ char buf[BUFSIZ];
+ int ret = 0;
+
+ if (!pool)
+ return ret;
+
+ FDPRINT(" Extent size: %12ld\n", (long) pool->size);
+ FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum);
+ FDPRINT(" Extents created: %12ld\n", pool->e_created);
+ FDPRINT(" Extents freed: %12ld\n", pool->e_freed);
+ FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated);
+ FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed);
+ FDPRINT(" Bytes allocated: %12.0f\n", (double) pool->b_allocated);
+ FDPRINT(" Bytes freed: %12.0f\n", (double) pool->b_freed);
+
+ if (summarize)
+ return ret;
+
+ if (!pool->extents)
+ return ret;
+
+ if (write(fd, "\n", 1) != 1)
+ ret = -1;
+
+ for (cur = pool->extents; cur; cur = cur->next)
+ FDEXTSTAT(cur);
+
+ return ret;
+}
diff --git a/lib/pool_alloc.h b/lib/pool_alloc.h
new file mode 100644
index 0000000..2805921
--- /dev/null
+++ b/lib/pool_alloc.h
@@ -0,0 +1,21 @@
+#include <stddef.h>
+
+#define POOL_CLEAR (1<<0) /* zero fill allocations */
+#define POOL_NO_QALIGN (1<<1) /* don't align data to quanta */
+#define POOL_INTERN (1<<2) /* Allocate extent structures */
+#define POOL_PREPEND (1<<3) /* or prepend to extent data */
+
+typedef void *alloc_pool_t;
+
+alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags);
+void pool_destroy(alloc_pool_t pool);
+void *pool_alloc(alloc_pool_t pool, size_t size, const char *bomb_msg);
+void pool_free(alloc_pool_t pool, size_t size, void *addr);
+void pool_free_old(alloc_pool_t pool, void *addr);
+void *pool_boundary(alloc_pool_t pool, size_t size);
+
+#define pool_talloc(pool, type, count, bomb_msg) \
+ ((type *)pool_alloc(pool, sizeof(type) * count, bomb_msg))
+
+#define pool_tfree(pool, type, count, addr) \
+ (pool_free(pool, sizeof(type) * count, addr))
diff --git a/lib/snprintf.c b/lib/snprintf.c
new file mode 100644
index 0000000..15c0529
--- /dev/null
+++ b/lib/snprintf.c
@@ -0,0 +1,1512 @@
+/*
+ * NOTE: If you change this file, please merge it into rsync, samba, etc.
+ */
+
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nasty effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formatted the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -I.. -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Andrew Tridgell (tridge@samba.org) Oct 1998
+ * fixed handling of %.0f
+ * added test for HAVE_LONG_DOUBLE
+ *
+ * tridge@samba.org, idra@samba.org, April 2001
+ * got rid of fcvt code (twas buggy and made testing harder)
+ * added C99 semantics
+ *
+ * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0
+ * actually print args for %g and %e
+ *
+ * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0
+ * Since includes.h isn't included here, VA_COPY has to be defined here. I don't
+ * see any include file that is guaranteed to be here, so I'm defining it
+ * locally. Fixes AIX and Solaris builds.
+ *
+ * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13
+ * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
+ * functions
+ *
+ * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4
+ * Fix usage of va_list passed as an arg. Use __va_copy before using it
+ * when it exists.
+ *
+ * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14
+ * Fix incorrect zpadlen handling in fmtfp.
+ * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
+ * few mods to make it easier to compile the tests.
+ * added the "Ollie" test to the floating point ones.
+ *
+ * Martin Pool (mbp@samba.org) April 2003
+ * Remove NO_CONFIG_H so that the test case can be built within a source
+ * tree with less trouble.
+ * Remove unnecessary SAFE_FREE() definition.
+ *
+ * Martin Pool (mbp@samba.org) May 2003
+ * Put in a prototype for dummy_snprintf() to quiet compiler warnings.
+ *
+ * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
+ * if the C library has some snprintf functions already.
+ *
+ * Darren Tucker (dtucker@zip.com.au) 2005
+ * Fix bug allowing read overruns of the source string with "%.*s"
+ * Usually harmless unless the read runs outside the process' allocation
+ * (eg if your malloc does guard pages) in which case it will segfault.
+ * From OpenSSH. Also added test for same.
+ *
+ * Simo Sorce (idra@samba.org) Jan 2006
+ *
+ * Add support for position independent parameters
+ * fix fmtstr now it conforms to sprintf wrt min.max
+ *
+ **************************************************************/
+
+#include "config.h"
+
+#ifdef TEST_SNPRINTF /* need math library headers for testing */
+
+/* In test mode, we pretend that this system doesn't have any snprintf
+ * functions, regardless of what config.h says. */
+# undef HAVE_SNPRINTF
+# undef HAVE_VSNPRINTF
+# undef HAVE_C99_VSNPRINTF
+# undef HAVE_ASPRINTF
+# undef HAVE_VASPRINTF
+# include <math.h>
+#endif /* TEST_SNPRINTF */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#include <sys/types.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
+/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
+#include <stdio.h>
+ /* make the compiler happy with an empty file */
+ void dummy_snprintf(void);
+ void dummy_snprintf(void) {}
+#endif /* HAVE_SNPRINTF, etc */
+
+#ifdef STDC_HEADERS
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#if !defined HAVE_LONG_LONG && SIZEOF_LONG_LONG
+#define HAVE_LONG_LONG 1
+#endif
+#ifdef HAVE_LONG_LONG
+#define LLONG long long
+#else
+#define LLONG long
+#endif
+
+#ifndef VA_COPY
+#if defined HAVE_VA_COPY || defined va_copy
+#define VA_COPY(dest, src) va_copy(dest, src)
+#else
+#ifdef HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge) */
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_CHAR 1
+#define DP_C_SHORT 2
+#define DP_C_LONG 3
+#define DP_C_LDOUBLE 4
+#define DP_C_LLONG 5
+#define DP_C_SIZET 6
+
+/* Chunk types */
+#define CNK_FMT_STR 0
+#define CNK_INT 1
+#define CNK_OCTAL 2
+#define CNK_UINT 3
+#define CNK_HEX 4
+#define CNK_FLOAT 5
+#define CNK_CHAR 6
+#define CNK_STRING 7
+#define CNK_PTR 8
+#define CNK_NUM 9
+#define CNK_PRCNT 10
+
+#define char_to_int(p) ((p)- '0')
+#ifndef MAX
+#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
+#endif
+
+struct pr_chunk {
+ int type; /* chunk type */
+ int num; /* parameter number */
+ int min;
+ int max;
+ int flags;
+ int cflags;
+ int start;
+ int len;
+ LLONG value;
+ LDOUBLE fvalue;
+ char *strvalue;
+ void *pnum;
+ struct pr_chunk *min_star;
+ struct pr_chunk *max_star;
+ struct pr_chunk *next;
+};
+
+struct pr_chunk_x {
+ struct pr_chunk **chunks;
+ int num;
+};
+
+static int dopr(char *buffer, size_t maxlen, const char *format,
+ va_list args_in);
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags);
+static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags);
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
+static struct pr_chunk *new_chunk(void);
+static int add_cnk_list_entry(struct pr_chunk_x **list,
+ int max_num, struct pr_chunk *chunk);
+
+static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
+{
+ char ch;
+ int state;
+ int pflag;
+ int pnum;
+ int pfirst;
+ size_t currlen;
+ va_list args;
+ const char *base;
+ struct pr_chunk *chunks = NULL;
+ struct pr_chunk *cnk = NULL;
+ struct pr_chunk_x *clist = NULL;
+ int max_pos;
+ int ret = -1;
+
+ VA_COPY(args, args_in);
+
+ state = DP_S_DEFAULT;
+ pfirst = 1;
+ pflag = 0;
+ pnum = 0;
+
+ max_pos = 0;
+ base = format;
+ ch = *format++;
+
+ /* retrieve the string structure as chunks */
+ while (state != DP_S_DONE) {
+ if (ch == '\0')
+ state = DP_S_DONE;
+
+ switch(state) {
+ case DP_S_DEFAULT:
+
+ if (cnk) {
+ cnk->next = new_chunk();
+ cnk = cnk->next;
+ } else {
+ cnk = new_chunk();
+ }
+ if (!cnk) goto done;
+ if (!chunks) chunks = cnk;
+
+ if (ch == '%') {
+ state = DP_S_FLAGS;
+ ch = *format++;
+ } else {
+ cnk->type = CNK_FMT_STR;
+ cnk->start = format - base -1;
+ while ((ch != '\0') && (ch != '%')) ch = *format++;
+ cnk->len = format - base - cnk->start -1;
+ }
+ break;
+ case DP_S_FLAGS:
+ switch (ch) {
+ case '-':
+ cnk->flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ cnk->flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ cnk->flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ cnk->flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ cnk->flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ case 'I':
+ /* internationalization not supported yet */
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch)) {
+ cnk->min = 10 * cnk->min + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '$') {
+ if (!pfirst && !pflag) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ if (pfirst) {
+ pfirst = 0;
+ pflag = 1;
+ }
+ if (cnk->min == 0) /* what ?? */
+ goto done;
+ cnk->num = cnk->min;
+ cnk->min = 0;
+ ch = *format++;
+ } else if (ch == '*') {
+ if (pfirst) pfirst = 0;
+ cnk->min_star = new_chunk();
+ if (!cnk->min_star) /* out of memory :-( */
+ goto done;
+ cnk->min_star->type = CNK_INT;
+ if (pflag) {
+ int num;
+ ch = *format++;
+ if (!isdigit((unsigned char)ch)) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
+ num = 10 * num + char_to_int(ch);
+ }
+ cnk->min_star->num = num;
+ if (ch != '$') /* what ?? */
+ goto done;
+ } else {
+ cnk->min_star->num = ++pnum;
+ }
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+ ch = *format++;
+ state = DP_S_DOT;
+ } else {
+ if (pfirst) pfirst = 0;
+ state = DP_S_DOT;
+ }
+ break;
+ case DP_S_DOT:
+ if (ch == '.') {
+ state = DP_S_MAX;
+ ch = *format++;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch)) {
+ if (cnk->max < 0)
+ cnk->max = 0;
+ cnk->max = 10 * cnk->max + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '$') {
+ if (!pfirst && !pflag) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ if (cnk->max <= 0) /* what ?? */
+ goto done;
+ cnk->num = cnk->max;
+ cnk->max = -1;
+ ch = *format++;
+ } else if (ch == '*') {
+ cnk->max_star = new_chunk();
+ if (!cnk->max_star) /* out of memory :-( */
+ goto done;
+ cnk->max_star->type = CNK_INT;
+ if (pflag) {
+ int num;
+ ch = *format++;
+ if (!isdigit((unsigned char)ch)) {
+ /* parameters must be all positioned or none */
+ goto done;
+ }
+ for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
+ num = 10 * num + char_to_int(ch);
+ }
+ cnk->max_star->num = num;
+ if (ch != '$') /* what ?? */
+ goto done;
+ } else {
+ cnk->max_star->num = ++pnum;
+ }
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+
+ ch = *format++;
+ state = DP_S_MOD;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MOD:
+ switch (ch) {
+ case 'h':
+ cnk->cflags = DP_C_SHORT;
+ ch = *format++;
+ if (ch == 'h') {
+ cnk->cflags = DP_C_CHAR;
+ ch = *format++;
+ }
+ break;
+ case 'l':
+ cnk->cflags = DP_C_LONG;
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long */
+ cnk->cflags = DP_C_LLONG;
+ ch = *format++;
+ }
+ break;
+ case 'L':
+ cnk->cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ case 'z':
+ cnk->cflags = DP_C_SIZET;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ if (cnk->num == 0) cnk->num = ++pnum;
+ max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
+ if (max_pos == 0) /* out of memory :-( */
+ goto done;
+
+ switch (ch) {
+ case 'd':
+ case 'i':
+ cnk->type = CNK_INT;
+ break;
+ case 'o':
+ cnk->type = CNK_OCTAL;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'u':
+ cnk->type = CNK_UINT;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'X':
+ cnk->flags |= DP_F_UP;
+ case 'x':
+ cnk->type = CNK_HEX;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'A':
+ /* hex float not supported yet */
+ case 'E':
+ case 'G':
+ case 'F':
+ cnk->flags |= DP_F_UP;
+ case 'a':
+ /* hex float not supported yet */
+ case 'e':
+ case 'f':
+ case 'g':
+ cnk->type = CNK_FLOAT;
+ break;
+ case 'c':
+ cnk->type = CNK_CHAR;
+ break;
+ case 's':
+ cnk->type = CNK_STRING;
+ break;
+ case 'p':
+ cnk->type = CNK_PTR;
+ cnk->flags |= DP_F_UNSIGNED;
+ break;
+ case 'n':
+ cnk->type = CNK_NUM;
+ break;
+ case '%':
+ cnk->type = CNK_PRCNT;
+ break;
+ default:
+ /* Unknown, bail out*/
+ goto done;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+
+ /* retrieve the format arguments */
+ for (pnum = 0; pnum < max_pos; pnum++) {
+ int i;
+
+ if (clist[pnum].num == 0) {
+ /* ignoring a parameter should not be permitted
+ * all parameters must be matched at least once
+ * BUT seem some system ignore this rule ...
+ * at least my glibc based system does --SSS
+ */
+#ifdef DEBUG_SNPRINTF
+ printf("parameter at position %d not used\n", pnum+1);
+#endif
+ /* eat the parameter */
+ va_arg (args, int);
+ continue;
+ }
+ for (i = 1; i < clist[pnum].num; i++) {
+ if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
+ /* nooo noo no!
+ * all the references to a parameter
+ * must be of the same type
+ */
+ goto done;
+ }
+ }
+ cnk = clist[pnum].chunks[0];
+ switch (cnk->type) {
+ case CNK_INT:
+ if (cnk->cflags == DP_C_SHORT)
+ cnk->value = va_arg (args, int);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->value = va_arg (args, long int);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->value = va_arg (args, LLONG);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->value = va_arg (args, ssize_t);
+ else
+ cnk->value = va_arg (args, int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_OCTAL:
+ case CNK_UINT:
+ case CNK_HEX:
+ if (cnk->cflags == DP_C_SHORT)
+ cnk->value = va_arg (args, unsigned int);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->value = (unsigned long int)va_arg (args, unsigned long int);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->value = (LLONG)va_arg (args, unsigned LLONG);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->value = (size_t)va_arg (args, size_t);
+ else
+ cnk->value = (unsigned int)va_arg (args, unsigned int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_FLOAT:
+ if (cnk->cflags == DP_C_LDOUBLE)
+ cnk->fvalue = va_arg (args, LDOUBLE);
+ else
+ cnk->fvalue = va_arg (args, double);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->fvalue = cnk->fvalue;
+ }
+ break;
+
+ case CNK_CHAR:
+ cnk->value = va_arg (args, int);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->value = cnk->value;
+ }
+ break;
+
+ case CNK_STRING:
+ cnk->strvalue = va_arg (args, char *);
+ if (!cnk->strvalue) cnk->strvalue = "(NULL)";
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->strvalue = cnk->strvalue;
+ }
+ break;
+
+ case CNK_PTR:
+ cnk->strvalue = va_arg (args, void *);
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->strvalue = cnk->strvalue;
+ }
+ break;
+
+ case CNK_NUM:
+ if (cnk->cflags == DP_C_CHAR)
+ cnk->pnum = va_arg (args, char *);
+ else if (cnk->cflags == DP_C_SHORT)
+ cnk->pnum = va_arg (args, short int *);
+ else if (cnk->cflags == DP_C_LONG)
+ cnk->pnum = va_arg (args, long int *);
+ else if (cnk->cflags == DP_C_LLONG)
+ cnk->pnum = va_arg (args, LLONG *);
+ else if (cnk->cflags == DP_C_SIZET)
+ cnk->pnum = va_arg (args, ssize_t *);
+ else
+ cnk->pnum = va_arg (args, int *);
+
+ for (i = 1; i < clist[pnum].num; i++) {
+ clist[pnum].chunks[i]->pnum = cnk->pnum;
+ }
+ break;
+
+ case CNK_PRCNT:
+ break;
+
+ default:
+ /* what ?? */
+ goto done;
+ }
+ }
+ /* print out the actual string from chunks */
+ currlen = 0;
+ cnk = chunks;
+ while (cnk) {
+ int len, min, max;
+
+ if (cnk->min_star) min = cnk->min_star->value;
+ else min = cnk->min;
+ if (cnk->max_star) max = cnk->max_star->value;
+ else max = cnk->max;
+
+ switch (cnk->type) {
+
+ case CNK_FMT_STR:
+ if (maxlen != 0 && maxlen > currlen) {
+ if (maxlen > (currlen + cnk->len)) len = cnk->len;
+ else len = maxlen - currlen;
+
+ memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
+ }
+ currlen += cnk->len;
+
+ break;
+
+ case CNK_INT:
+ case CNK_UINT:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
+ break;
+
+ case CNK_OCTAL:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
+ break;
+
+ case CNK_HEX:
+ fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
+ break;
+
+ case CNK_FLOAT:
+ fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
+ break;
+
+ case CNK_CHAR:
+ dopr_outch (buffer, &currlen, maxlen, cnk->value);
+ break;
+
+ case CNK_STRING:
+ if (max == -1) {
+ max = strlen(cnk->strvalue);
+ }
+ fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
+ break;
+
+ case CNK_PTR:
+ fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
+ break;
+
+ case CNK_NUM:
+ if (cnk->cflags == DP_C_CHAR)
+ *((char *)(cnk->pnum)) = (char)currlen;
+ else if (cnk->cflags == DP_C_SHORT)
+ *((short int *)(cnk->pnum)) = (short int)currlen;
+ else if (cnk->cflags == DP_C_LONG)
+ *((long int *)(cnk->pnum)) = (long int)currlen;
+ else if (cnk->cflags == DP_C_LLONG)
+ *((LLONG *)(cnk->pnum)) = (LLONG)currlen;
+ else if (cnk->cflags == DP_C_SIZET)
+ *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen;
+ else
+ *((int *)(cnk->pnum)) = (int)currlen;
+ break;
+
+ case CNK_PRCNT:
+ dopr_outch (buffer, &currlen, maxlen, '%');
+ break;
+
+ default:
+ /* what ?? */
+ goto done;
+ }
+ cnk = cnk->next;
+ }
+ if (maxlen != 0) {
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else if (maxlen > 0)
+ buffer[maxlen - 1] = '\0';
+ }
+ ret = currlen;
+
+done:
+ va_end(args);
+
+ while (chunks) {
+ cnk = chunks->next;
+ free(chunks);
+ chunks = cnk;
+ }
+ if (clist) {
+ for (pnum = 0; pnum < max_pos; pnum++) {
+ if (clist[pnum].chunks) free(clist[pnum].chunks);
+ }
+ free(clist);
+ }
+ return ret;
+}
+
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
+#endif
+ if (value == 0) {
+ value = "<NULL>";
+ }
+
+ for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ while (*value && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ LLONG value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned LLONG uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED)) {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ } else {
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place);
+#endif
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0) {
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+ LDOUBLE result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static LDOUBLE POW10(int exp)
+{
+ LDOUBLE result = 1;
+
+ while (exp) {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static LLONG ROUND(LDOUBLE value)
+{
+ LLONG intpart;
+
+ intpart = (LLONG)value;
+ value = value - intpart;
+ if (value >= 0.5) intpart++;
+
+ return intpart;
+}
+
+/* a replacement for modf that doesn't need the math library. Should
+ be portable, but slow */
+static double my_modf(double x0, double *iptr)
+{
+ int i;
+ LLONG l=0;
+ double x = x0;
+ double f = 1.0;
+
+ for (i=0;i<100;i++) {
+ l = (long)x;
+ if (l <= (x+1) && l >= (x-1)) break;
+ x *= 0.1;
+ f *= 10.0;
+ }
+
+ if (i == 100) {
+ /* yikes! the number is beyond what we can handle. What do we do? */
+ (*iptr) = 0;
+ return 0;
+ }
+
+ if (i != 0) {
+ double i2;
+ double ret;
+
+ ret = my_modf(x0-l*f, &i2);
+ (*iptr) = l*f + i2;
+ return ret;
+ }
+
+ (*iptr) = l;
+ return x - (*iptr);
+}
+
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ double ufvalue;
+ char iconvert[311];
+ char fconvert[311];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ int idx;
+ double intpart;
+ double fracpart;
+ double temp;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0) {
+ signvalue = '-';
+ } else {
+ if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
+ signvalue = '+';
+ } else {
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+#if 0
+ if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
+#endif
+
+ /*
+ * Sorry, we only support 9 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 9)
+ max = 9;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+
+ temp = ufvalue;
+ my_modf(temp, &intpart);
+
+ fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
+
+ if (fracpart >= POW10(max)) {
+ intpart++;
+ fracpart -= POW10(max);
+ }
+
+
+ /* Convert integer part */
+ do {
+ temp = intpart*0.1;
+ my_modf(temp, &intpart);
+ idx = (int) ((temp -intpart +0.05)* 10.0);
+ /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
+ /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while (intpart && (iplace < 311));
+ if (iplace == 311) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ if (fracpart)
+ {
+ do {
+ temp = fracpart*0.1;
+ my_modf(temp, &fracpart);
+ idx = (int) ((temp -fracpart +0.05)* 10.0);
+ /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
+ /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while(fracpart && (fplace < 311));
+ if (fplace == 311) fplace--;
+ }
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0) zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0)) {
+ if (signvalue) {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
+#endif
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ if (max > 0) {
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+ }
+
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen) {
+ buffer[(*currlen)] = c;
+ }
+ (*currlen)++;
+}
+
+static struct pr_chunk *new_chunk(void) {
+ struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk));
+
+ if (!new_c)
+ return NULL;
+
+ new_c->type = 0;
+ new_c->num = 0;
+ new_c->min = 0;
+ new_c->min_star = NULL;
+ new_c->max = -1;
+ new_c->max_star = NULL;
+ new_c->flags = 0;
+ new_c->cflags = 0;
+ new_c->start = 0;
+ new_c->len = 0;
+ new_c->value = 0;
+ new_c->fvalue = 0;
+ new_c->strvalue = NULL;
+ new_c->pnum = NULL;
+ new_c->next = NULL;
+
+ return new_c;
+}
+
+static int add_cnk_list_entry(struct pr_chunk_x **list,
+ int max_num, struct pr_chunk *chunk) {
+ struct pr_chunk_x *l;
+ struct pr_chunk **c;
+ int max;
+ int cnum;
+ int i, pos;
+
+ if (chunk->num > max_num) {
+ max = chunk->num;
+
+ if (*list == NULL) {
+ l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max);
+ pos = 0;
+ } else {
+ l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max);
+ pos = max_num;
+ }
+ if (l == NULL) {
+ for (i = 0; i < max; i++) {
+ if ((*list)[i].chunks) free((*list)[i].chunks);
+ }
+ return 0;
+ }
+ for (i = pos; i < max; i++) {
+ l[i].chunks = NULL;
+ l[i].num = 0;
+ }
+ } else {
+ l = *list;
+ max = max_num;
+ }
+
+ i = chunk->num - 1;
+ cnum = l[i].num + 1;
+ if (l[i].chunks == NULL) {
+ c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum);
+ } else {
+ c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum);
+ }
+ if (c == NULL) {
+ for (i = 0; i < max; i++) {
+ if (l[i].chunks) free(l[i].chunks);
+ }
+ return 0;
+ }
+ c[l[i].num] = chunk;
+ l[i].chunks = c;
+ l[i].num = cnum;
+
+ *list = l;
+ return max;
+}
+
+ int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ return dopr(str, count, fmt, args);
+}
+#define vsnprintf rsync_vsnprintf
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge)
+ *
+ * The logic for these two is that we need our own definition if the
+ * OS *either* has no definition of *sprintf, or if it does have one
+ * that doesn't work properly according to the autoconf test.
+ */
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+int rsync_snprintf(char *str,size_t count,const char *fmt,...)
+{
+ size_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(str, count, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#define snprintf rsync_snprintf
+#endif
+
+#ifndef HAVE_VASPRINTF
+ int vasprintf(char **ptr, const char *format, va_list ap)
+{
+ int ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+ ret = vsnprintf(NULL, 0, format, ap2);
+ va_end(ap2);
+ if (ret < 0) return ret;
+
+ (*ptr) = (char *)malloc(ret+1);
+ if (!*ptr) return -1;
+
+ VA_COPY(ap2, ap);
+ ret = vsnprintf(*ptr, ret+1, format, ap2);
+ va_end(ap2);
+
+ return ret;
+}
+#endif
+
+
+#ifndef HAVE_ASPRINTF
+ int asprintf(char **ptr, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ *ptr = NULL;
+ va_start(ap, format);
+ ret = vasprintf(ptr, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif
+
+#ifdef TEST_SNPRINTF
+
+ int sprintf(char *str,const char *fmt,...);
+ int printf(const char *fmt,...);
+
+ int main (void)
+{
+ char buf1[1024];
+ char buf2[1024];
+ char *buf3;
+ char *fp_fmt[] = {
+ "%1.1f",
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ "%.0f",
+ "%f",
+ "%-8.8f",
+ "%-9.9f",
+ NULL
+ };
+ double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 5.030201, 0.00205,
+ /* END LIST */ 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ "%d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
+ char *str_fmt[] = {
+ "%10.5s",
+ "%-10.5s",
+ "%5.10s",
+ "%-5.10s",
+ "%10.1s",
+ "%0.10s",
+ "%10.0s",
+ "%1.10s",
+ "%s",
+ "%.1s",
+ "%.10s",
+ "%10s",
+ NULL
+ };
+ char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
+#ifdef HAVE_LONG_LONG
+ char *ll_fmt[] = {
+ "%llu",
+ NULL
+ };
+ LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
+#endif
+ int x, y;
+ int fail = 0;
+ int num = 0;
+ int l1, l2;
+ char *ss_fmt[] = {
+ "%zd",
+ "%zu",
+ NULL
+ };
+ size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0};
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] ; x++) {
+ for (y = 0; fp_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
+ l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ fp_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; int_fmt[x] ; x++) {
+ for (y = 0; int_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
+ l2 = sprintf (buf2, int_fmt[x], int_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ int_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; str_fmt[x] ; x++) {
+ for (y = 0; str_vals[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
+ l2 = sprintf (buf2, str_fmt[x], str_vals[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ str_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+#ifdef HAVE_LONG_LONG
+ for (x = 0; ll_fmt[x] ; x++) {
+ for (y = 0; ll_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
+ l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ ll_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+#endif
+
+#define BUFSZ 2048
+
+ buf1[0] = buf2[0] = '\0';
+ if ((buf3 = malloc(BUFSZ)) == NULL) {
+ fail++;
+ } else {
+ num++;
+ memset(buf3, 'a', BUFSZ);
+ snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
+ buf1[1023] = '\0';
+ if (strcmp(buf1, "a") != 0) {
+ printf("length limit buf1 '%s' expected 'a'\n", buf1);
+ fail++;
+ }
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
+ l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
+ l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ for (x = 0; ss_fmt[x] ; x++) {
+ for (y = 0; ss_nums[y] != 0 ; y++) {
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]);
+ l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp (buf1, buf2) || (l1 != l2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ ss_fmt[x], l1, buf1, l2, buf2);
+ fail++;
+ }
+ num++;
+ }
+ }
+#if 0
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
+ l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%lld", l1, buf1, l2, buf2);
+ fail++;
+ }
+
+ buf1[0] = buf2[0] = '\0';
+ l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
+ l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
+ buf1[1023] = buf2[1023] = '\0';
+ if (strcmp(buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
+ "%Lf", l1, buf1, l2, buf2);
+ fail++;
+ }
+#endif
+ printf ("%d tests failed out of %d.\n", fail, num);
+
+ printf("seeing how many digits we support\n");
+ {
+ double v0 = 0.12345678901234567890123456789012345678901;
+ for (x=0; x<100; x++) {
+ double p = pow(10, x);
+ double r = v0*p;
+ snprintf(buf1, sizeof(buf1), "%1.1f", r);
+ sprintf(buf2, "%1.1f", r);
+ if (strcmp(buf1, buf2)) {
+ printf("we seem to support %d digits\n", x-1);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* TEST_SNPRINTF */
diff --git a/lib/sysacls.c b/lib/sysacls.c
new file mode 100644
index 0000000..a5abe40
--- /dev/null
+++ b/lib/sysacls.c
@@ -0,0 +1,2802 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Based on the Samba ACL support code.
+ * Copyright (C) Jeremy Allison 2000.
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * The permission functions have been changed to get/set all bits via
+ * one call. Some functions that rsync doesn't need were also removed.
+ *
+ * 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 3 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
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "sysacls.h"
+
+#ifdef SUPPORT_ACLS
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG(x, y)
+
+void SAFE_FREE(void *mem)
+{
+ if (mem)
+ free(mem);
+}
+
+/*
+ This file wraps all differing system ACL interfaces into a consistent
+ one based on the POSIX interface. It also returns the correct errors
+ for older UNIX systems that don't support ACLs.
+
+ The interfaces that each ACL implementation must support are as follows :
+
+ int sys_acl_get_entry(SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+ int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+ SMB_ACL_T sys_acl_get_fd(int fd)
+ SMB_ACL_T sys_acl_init(int count)
+ int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+ int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+ int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+ int sys_acl_valid(SMB_ACL_T theacl)
+ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+ int sys_acl_delete_def_file(const char *path)
+ int sys_acl_free_acl(SMB_ACL_T posix_acl)
+
+*/
+
+#if defined(HAVE_POSIX_ACLS) /*--------------------------------------------*/
+
+/* Identity mapping - easy. */
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return acl_get_entry(the_acl, entry_id, entry_p);
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type(entry_d, tag_type_p);
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file(path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+#endif
+
+#if defined(HAVE_ACL_GET_PERM_NP)
+#define acl_get_perm(p, b) acl_get_perm_np(p, b)
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ acl_permset_t permset;
+
+ if (acl_get_tag_type(entry, tag_type_p) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ *bits_p = (acl_get_perm(permset, ACL_READ) ? 4 : 0)
+ | (acl_get_perm(permset, ACL_WRITE) ? 2 : 0)
+ | (acl_get_perm(permset, ACL_EXECUTE) ? 1 : 0);
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP) {
+ void *qual;
+ if ((qual = acl_get_qualifier(entry)) == NULL)
+ return -1;
+ *u_g_id_p = *(id_t*)qual;
+ acl_free(qual);
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ if (acl_set_tag_type(entry, tag_type) != 0)
+ return -1;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP) {
+ if (acl_set_qualifier(entry, (void*)&u_g_id) != 0)
+ return -1;
+ }
+
+ return sys_acl_set_access_bits(entry, bits);
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ acl_permset_t permset;
+ int rc;
+ if ((rc = acl_get_permset(entry, &permset)) != 0)
+ return rc;
+ acl_clear_perms(permset);
+ if (bits & 4)
+ acl_add_perm(permset, ACL_READ);
+ if (bits & 2)
+ acl_add_perm(permset, ACL_WRITE);
+ if (bits & 1)
+ acl_add_perm(permset, ACL_EXECUTE);
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#elif defined(HAVE_TRU64_ACLS) /*--------------------------------------------*/
+/*
+ * The interface to DEC/Compaq Tru64 UNIX ACLs
+ * is based on Draft 13 of the POSIX spec which is
+ * slightly different from the Draft 16 interface.
+ *
+ * Also, some of the permset manipulation functions
+ * such as acl_clear_perm() and acl_add_perm() appear
+ * to be broken on Tru64 so we have to manipulate
+ * the permission bits in the permset directly.
+ */
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) {
+ return -1;
+ }
+
+ errno = 0;
+ if ((entry = acl_get_entry(the_acl)) != NULL) {
+ *entry_p = entry;
+ return 1;
+ }
+
+ return errno ? -1 : 0;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type(entry_d, tag_type_p);
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file((char *)path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd, ACL_TYPE_ACCESS);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ acl_permset_t permset;
+
+ if (acl_get_tag_type(entry, tag_type_p) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ *bits_p = *permset & 7; /* Tru64 doesn't have acl_get_perm() */
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP) {
+ void *qual;
+ if ((qual = acl_get_qualifier(entry)) == NULL)
+ return -1;
+ *u_g_id_p = *(id_t*)qual;
+ acl_free_qualifier(qual, *tag_type_p);
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if ((entry = acl_create_entry(pacl)) == NULL) {
+ return -1;
+ }
+
+ *pentry = entry;
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ if (acl_set_tag_type(entry, tag_type) != 0)
+ return -1;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP) {
+ if (acl_set_qualifier(entry, (void*)&u_g_id) != 0)
+ return -1;
+ }
+
+ return sys_acl_set_access_bits(entry, bits);
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ acl_permset_t permset;
+ int rc;
+ if ((rc = acl_get_permset(entry, &permset)) != 0)
+ return rc;
+ *permset = bits & 7;
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ acl_entry_t entry;
+
+ return acl_valid(theacl, &entry);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file((char *)name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file((char *)name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS) /*-----------*/
+
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/*
+ * The only difference between Solaris and UnixWare / OpenUNIX is
+ * that the #defines for the ACL operations have different names
+ */
+#if defined(HAVE_UNIXWARE_ACLS)
+
+#define SETACL ACL_SET
+#define GETACL ACL_GET
+#define GETACLCNT ACL_CNT
+
+#endif
+
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * GETACL will be unless you first call GETACLCNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use GETACLCNT to find out the actual
+ * size, reallocate the ACL buffer, and then call GETACL again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * GETACLCNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * GETACLCNT and the call to GETACL.
+ */
+ while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries precede any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+
+ acl_d->count = naccess;
+
+ return acl_d;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->a_type;
+
+ *bits_p = entry->a_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->a_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + count * sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->a_type = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->a_id = u_g_id;
+
+ entry->a_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->a_perm = bits;
+ return 0;
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof acl_buf[0]);
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof acl_buf[0]);
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, SETACL, acl_count, acl_p);
+
+ SAFE_FREE(acl_buf);
+
+ return ret;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, SETACL, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ SAFE_FREE(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_HPUX_ACLS) /*---------------------------------------------*/
+
+#ifdef HAVE_DL_H
+#include <dl.h>
+#endif
+
+/*
+ * Based on the Solaris/SCO code - with modifications.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/* This checks if the POSIX ACL system call is defined */
+/* which basically corresponds to whether JFS 3.3 or */
+/* higher is installed. If acl() was called when it */
+/* isn't defined, it causes the process to core dump */
+/* so it is important to check this and avoid acl() */
+/* calls if it isn't there. */
+
+#ifdef __TANDEM
+inline int do_acl(const char *path_p, int cmd, int nentries, struct acl *aclbufp)
+{
+ return acl((char*)path_p, cmd, nentries, aclbufp);
+}
+#define acl(p,c,n,a) do_acl(p,c,n,a)
+#endif
+
+static BOOL hpux_acl_call_presence(void)
+{
+#ifndef __TANDEM
+ shl_t handle = NULL;
+ void *value;
+ int ret_val=0;
+ static BOOL already_checked=0;
+
+ if (already_checked)
+ return True;
+
+ ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value);
+
+ if (ret_val != 0) {
+ DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n",
+ ret_val, errno, strerror(errno)));
+ DEBUG(5, ("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n"));
+ return False;
+ }
+
+ DEBUG(10, ("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n"));
+
+ already_checked = True;
+#endif
+ return True;
+}
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * ACL_GET will be unless you first call ACL_CNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use ACL_CNT to find out the actual
+ * size, reallocate the ACL buffer, and then call ACL_GET again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+#ifndef NACLENTRIES
+#define NACLENTRIES 0
+#endif
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if (hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ return NULL;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * ACL_CNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * ACL_CNT and the call to ACL_GET.
+ */
+ while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, ACL_CNT, NACLENTRIES, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries precede any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->a_type;
+
+ *bits_p = entry->a_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->a_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + count * sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->a_type = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->a_id = u_g_id;
+
+ entry->a_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->a_perm = bits;
+
+ return 0;
+}
+
+/* Structure to capture the count for each type of ACE. */
+
+struct hpux_acl_types {
+ int n_user;
+ int n_def_user;
+ int n_user_obj;
+ int n_def_user_obj;
+
+ int n_group;
+ int n_def_group;
+ int n_group_obj;
+ int n_def_group_obj;
+
+ int n_other;
+ int n_other_obj;
+ int n_def_other_obj;
+
+ int n_class_obj;
+ int n_def_class_obj;
+
+ int n_illegal_obj;
+};
+
+/* count_obj:
+ * Counts the different number of objects in a given array of ACL
+ * structures.
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * aclp - Array of ACL structures.
+ * acl_type_count - Pointer to acl_types structure. Should already be
+ * allocated.
+ * Output:
+ *
+ * acl_type_count - This structure is filled up with counts of various
+ * acl types.
+ */
+
+static void hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count)
+{
+ int i;
+
+ memset(acl_type_count, 0, sizeof (struct hpux_acl_types));
+
+ for (i = 0; i < acl_count; i++) {
+ switch (aclp[i].a_type) {
+ case USER:
+ acl_type_count->n_user++;
+ break;
+ case USER_OBJ:
+ acl_type_count->n_user_obj++;
+ break;
+ case DEF_USER_OBJ:
+ acl_type_count->n_def_user_obj++;
+ break;
+ case GROUP:
+ acl_type_count->n_group++;
+ break;
+ case GROUP_OBJ:
+ acl_type_count->n_group_obj++;
+ break;
+ case DEF_GROUP_OBJ:
+ acl_type_count->n_def_group_obj++;
+ break;
+ case OTHER_OBJ:
+ acl_type_count->n_other_obj++;
+ break;
+ case DEF_OTHER_OBJ:
+ acl_type_count->n_def_other_obj++;
+ break;
+ case CLASS_OBJ:
+ acl_type_count->n_class_obj++;
+ break;
+ case DEF_CLASS_OBJ:
+ acl_type_count->n_def_class_obj++;
+ break;
+ case DEF_USER:
+ acl_type_count->n_def_user++;
+ break;
+ case DEF_GROUP:
+ acl_type_count->n_def_group++;
+ break;
+ default:
+ acl_type_count->n_illegal_obj++;
+ break;
+ }
+ }
+}
+
+/* swap_acl_entries: Swaps two ACL entries.
+ *
+ * Inputs: aclp0, aclp1 - ACL entries to be swapped.
+ */
+
+static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
+{
+ struct acl temp_acl;
+
+ temp_acl.a_type = aclp0->a_type;
+ temp_acl.a_id = aclp0->a_id;
+ temp_acl.a_perm = aclp0->a_perm;
+
+ aclp0->a_type = aclp1->a_type;
+ aclp0->a_id = aclp1->a_id;
+ aclp0->a_perm = aclp1->a_perm;
+
+ aclp1->a_type = temp_acl.a_type;
+ aclp1->a_id = temp_acl.a_id;
+ aclp1->a_perm = temp_acl.a_perm;
+}
+
+/* prohibited_duplicate_type
+ * Identifies if given ACL type can have duplicate entries or
+ * not.
+ *
+ * Inputs: acl_type - ACL Type.
+ *
+ * Outputs:
+ *
+ * Return..
+ *
+ * True - If the ACL type matches any of the prohibited types.
+ * False - If the ACL type doesn't match any of the prohibited types.
+ */
+
+static BOOL hpux_prohibited_duplicate_type(int acl_type)
+{
+ switch (acl_type) {
+ case USER:
+ case GROUP:
+ case DEF_USER:
+ case DEF_GROUP:
+ return True;
+ default:
+ return False;
+ }
+}
+
+/* get_needed_class_perm
+ * Returns the permissions of a ACL structure only if the ACL
+ * type matches one of the pre-determined types for computing
+ * CLASS_OBJ permissions.
+ *
+ * Inputs: aclp - Pointer to ACL structure.
+ */
+
+static int hpux_get_needed_class_perm(struct acl *aclp)
+{
+ switch (aclp->a_type) {
+ case USER:
+ case GROUP_OBJ:
+ case GROUP:
+ case DEF_USER_OBJ:
+ case DEF_USER:
+ case DEF_GROUP_OBJ:
+ case DEF_GROUP:
+ case DEF_CLASS_OBJ:
+ case DEF_OTHER_OBJ:
+ return aclp->a_perm;
+ default:
+ return 0;
+ }
+}
+
+/* acl_sort for HPUX.
+ * Sorts the array of ACL structures as per the description in
+ * aclsort man page. Refer to aclsort man page for more details
+ *
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * calclass - If this is not zero, then we compute the CLASS_OBJ
+ * permissions.
+ * aclp - Array of ACL structures.
+ *
+ * Outputs:
+ *
+ * aclp - Sorted array of ACL structures.
+ *
+ * Outputs:
+ *
+ * Returns 0 for success -1 for failure. Prints a message to the Samba
+ * debug log in case of failure.
+ */
+
+static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
+{
+#if !defined(HAVE_HPUX_ACLSORT)
+ /*
+ * The aclsort() system call is available on the latest HPUX General
+ * Patch Bundles. So for HPUX, we developed our version of acl_sort
+ * function. Because, we don't want to update to a new
+ * HPUX GR bundle just for aclsort() call.
+ */
+
+ struct hpux_acl_types acl_obj_count;
+ int n_class_obj_perm = 0;
+ int i, j;
+
+ if (!acl_count) {
+ DEBUG(10, ("Zero acl count passed. Returning Success\n"));
+ return 0;
+ }
+
+ if (aclp == NULL) {
+ DEBUG(0, ("Null ACL pointer in hpux_acl_sort. Returning Failure. \n"));
+ return -1;
+ }
+
+ /* Count different types of ACLs in the ACLs array */
+
+ hpux_count_obj(acl_count, aclp, &acl_obj_count);
+
+ /* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
+ * CLASS_OBJ and OTHER_OBJ
+ */
+
+ if (acl_obj_count.n_user_obj != 1
+ || acl_obj_count.n_group_obj != 1
+ || acl_obj_count.n_class_obj != 1
+ || acl_obj_count.n_other_obj != 1) {
+ DEBUG(0, ("hpux_acl_sort: More than one entry or no entries for \
+USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n"));
+ return -1;
+ }
+
+ /* If any of the default objects are present, there should be only
+ * one of them each.
+ */
+ if (acl_obj_count.n_def_user_obj > 1 || acl_obj_count.n_def_group_obj > 1
+ || acl_obj_count.n_def_other_obj > 1 || acl_obj_count.n_def_class_obj > 1) {
+ DEBUG(0, ("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \
+or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
+ return -1;
+ }
+
+ /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
+ * structures.
+ *
+ * Sorting crieteria - First sort by ACL type. If there are multiple entries of
+ * same ACL type, sort by ACL id.
+ *
+ * I am using the trivial kind of sorting method here because, performance isn't
+ * really effected by the ACLs feature. More over there aren't going to be more
+ * than 17 entries on HPUX.
+ */
+
+ for (i = 0; i < acl_count; i++) {
+ for (j = i+1; j < acl_count; j++) {
+ if (aclp[i].a_type > aclp[j].a_type) {
+ /* ACL entries out of order, swap them */
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+ } else if (aclp[i].a_type == aclp[j].a_type) {
+ /* ACL entries of same type, sort by id */
+ if (aclp[i].a_id > aclp[j].a_id) {
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+ } else if (aclp[i].a_id == aclp[j].a_id) {
+ /* We have a duplicate entry. */
+ if (hpux_prohibited_duplicate_type(aclp[i].a_type)) {
+ DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n",
+ aclp[i].a_type, aclp[i].a_id));
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ /* set the class obj permissions to the computed one. */
+ if (calclass) {
+ int n_class_obj_index = -1;
+
+ for (i = 0;i < acl_count; i++) {
+ n_class_obj_perm |= hpux_get_needed_class_perm((aclp+i));
+ if (aclp[i].a_type == CLASS_OBJ)
+ n_class_obj_index = i;
+ }
+ aclp[n_class_obj_index].a_perm = n_class_obj_perm;
+ }
+
+ return 0;
+#else
+ return aclsort(acl_count, calclass, aclp);
+#endif
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if (hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ errno=ENOSYS;
+ return -1;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof acl_buf[0]);
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof acl_buf[0]);
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, ACL_SET, acl_count, acl_p);
+
+ if (acl_buf) {
+ free(acl_buf);
+ }
+
+ return ret;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ free(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_IRIX_ACLS) /*---------------------------------------------*/
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->aclp->acl_cnt) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->aclp->acl_entry[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->ae_tag;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T a;
+
+ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_file(path_p, type)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T a;
+
+ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_fd(fd)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ *tag_type_p = entry->ae_tag;
+
+ *bits_p = entry->ae_perm;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ *u_g_id_p = entry->ae_id;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof a[0] + sizeof (struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->next = -1;
+ a->freeaclp = False;
+ a->aclp = (struct acl *)((char *)a + sizeof a[0]);
+ a->aclp->acl_cnt = 0;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++];
+ entry_d->ae_tag = 0;
+ entry_d->ae_id = 0;
+ entry_d->ae_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->ae_tag = tag_type;
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ entry->ae_id = u_g_id;
+
+ entry->ae_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry_d, uint32 bits)
+{
+ entry_d->ae_perm = bits;
+
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_valid(acl_d->aclp);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ return acl_set_file(name, type, acl_d->aclp);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ return acl_set_fd(fd, acl_d->aclp);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ if (acl_d->freeaclp) {
+ acl_free(acl_d->aclp);
+ }
+ acl_free(acl_d);
+ return 0;
+}
+
+#elif defined(HAVE_AIX_ACLS) /*----------------------------------------------*/
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+int sys_acl_get_entry(SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ struct acl_entry_link *link;
+ int keep_going;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ theacl->count = 0;
+ else if (entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ DEBUG(10, ("This is the count: %d\n", theacl->count));
+
+ /* Check if count was previously set to -1. *
+ * If it was, that means we reached the end *
+ * of the acl last time. */
+ if (theacl->count == -1)
+ return 0;
+
+ link = theacl;
+ /* To get to the next acl, traverse linked list until index *
+ * of acl matches the count we are keeping. This count is *
+ * incremented each time we return an acl entry. */
+
+ for (keep_going = 0; keep_going < theacl->count; keep_going++)
+ link = link->nextp;
+
+ *entry_p = link->entryp;
+
+#if 0
+ {
+ struct new_acl_entry *entry = *entry_p;
+ DEBUG(10, ("*entry_p is %lx\n", (long)entry));
+ DEBUG(10, ("*entry_p->ace_access is %d\n", entry->ace_access));
+ }
+#endif
+
+ /* Increment count */
+ theacl->count++;
+ if (link->nextp == NULL)
+ theacl->count = -1;
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ /* Initialize tag type */
+
+ *tag_type_p = -1;
+ DEBUG(10, ("the tagtype is %d\n", entry_d->ace_id->id_type));
+
+ /* Depending on what type of entry we have, *
+ * return tag type. */
+ switch (entry_d->ace_id->id_type) {
+ case ACEID_USER:
+ *tag_type_p = SMB_ACL_USER;
+ break;
+ case ACEID_GROUP:
+ *tag_type_p = SMB_ACL_GROUP;
+ break;
+
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ *tag_type_p = entry_d->ace_id->id_type;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+
+ /* AIX has no DEFAULT */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+#ifdef ENOTSUP
+ errno = ENOTSUP;
+#else
+ errno = ENOSYS;
+#endif
+ return NULL;
+ }
+
+ /* Get the acl using statacl */
+
+ DEBUG(10, ("Entering sys_acl_get_file\n"));
+ DEBUG(10, ("path_p is %s\n", path_p));
+
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file: %d\n", errno));
+ return NULL;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ rc = statacl((char *)path_p, 0, file_acl, BUFSIZ);
+ if (rc == -1) {
+ DEBUG(0, ("statacl returned %d with errno %d\n", rc, errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ DEBUG(10, ("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+ acl_entry = file_acl->acl_ext;
+
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if (acl_entry_link_head == NULL)
+ return NULL;
+
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+
+ DEBUG(10, ("acl_entry is %d\n", acl_entry));
+ DEBUG(10, ("acl_last(file_acl) id %d\n", acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if (file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while (acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+ idp = id_nxt(acl_entry->ace_id);
+ if ((acl_entry->ace_type == ACC_SPECIFY || acl_entry->ace_type == ACC_PERMIT)
+ && idp != id_last(acl_entry)) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if (acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+
+ if (acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id, idp, sizeof (struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch (acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10, ("acl_entry->ace_access is %d\n", acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10, ("acl_entry_link->entryp->ace_access is %d\n", acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return 0;
+ }
+
+ DEBUG(10, ("acl_entry = %d\n", acl_entry));
+ DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for (i = 1; i < 4; i++) {
+ DEBUG(10, ("i is %d\n", i));
+ if (acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in AIX sys_acl_get_file is %d\n", errno));
+ return NULL;
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof (struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof (struct ace_id);
+ DEBUG(10, ("idp->id_len = %d\n", idp->id_len));
+ memset(idp->id_data, 0, sizeof (uid_t));
+
+ switch (i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return NULL;
+
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10, ("new_acl_entry->ace_access = %d\n", new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return acl_entry_link_head;
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+
+ /* Get the acl using fstatacl */
+
+ DEBUG(10, ("Entering sys_acl_get_fd\n"));
+ DEBUG(10, ("fd is %d\n", fd));
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ return NULL;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ rc = fstatacl(fd, 0, file_acl, BUFSIZ);
+ if (rc == -1) {
+ DEBUG(0, ("The fstatacl call returned %d with errno %d\n", rc, errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ DEBUG(10, ("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+
+ acl_entry = file_acl->acl_ext;
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if (acl_entry_link_head == NULL){
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+
+ if (acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ DEBUG(10, ("acl_entry is %d\n", acl_entry));
+ DEBUG(10, ("acl_last(file_acl) id %d\n", acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if (file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while (acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+
+ idp = id_nxt(acl_entry->ace_id);
+ if ((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if (acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id, idp, sizeof (struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch (acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10, ("acl_entry->ace_access is %d\n", acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10, ("acl_entry_link->entryp->ace_access is %d\n", acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return 0;
+ }
+
+ DEBUG(10, ("acl_entry = %d\n", acl_entry));
+ DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for (i = 1; i < 4; i++) {
+ DEBUG(10, ("i is %d\n", i));
+ if (acl_entry_link_head->count != 0){
+ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ SAFE_FREE(file_acl);
+ return NULL;
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+
+ if (acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_get_fd is %d\n", errno));
+ return NULL;
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof (struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof (struct ace_id);
+ DEBUG(10, ("idp->id_len = %d\n", idp->id_len));
+ memset(idp->id_data, 0, sizeof (uid_t));
+
+ switch (i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10, ("new_acl_entry->ace_access = %d\n", new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return acl_entry_link_head;
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ uint *permset;
+
+ if (sys_acl_get_tag_type(entry, tag_type_p) != 0)
+ return -1;
+
+ if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
+ memcpy(u_g_id_p, entry->ace_id->id_data, sizeof (id_t));
+
+ permset = &entry->ace_access;
+
+ DEBUG(10, ("*permset is %d\n", *permset));
+ *bits_p = (*permset & S_IRUSR ? 4 : 0)
+ | (*permset & S_IWUSR ? 2 : 0)
+ | (*permset & S_IXUSR ? 1 : 0);
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ struct acl_entry_link *theacl = NULL;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ DEBUG(10, ("Entering sys_acl_init\n"));
+
+ theacl = SMB_MALLOC_P(struct acl_entry_link);
+ if (theacl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_init is %d\n", errno));
+ return NULL;
+ }
+
+ theacl->count = 0;
+ theacl->nextp = NULL;
+ theacl->prevp = NULL;
+ theacl->entryp = NULL;
+ DEBUG(10, ("Exiting sys_acl_init\n"));
+ return theacl;
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ struct acl_entry_link *theacl;
+ struct acl_entry_link *acl_entryp;
+ struct acl_entry_link *temp_entry = NULL;
+ int counting;
+
+ DEBUG(10, ("Entering the sys_acl_create_entry\n"));
+
+ theacl = acl_entryp = *pacl;
+
+ /* Get to the end of the acl before adding entry */
+
+ for (counting = 0; counting < theacl->count; counting++){
+ DEBUG(10, ("The acl_entryp is %d\n", acl_entryp));
+ temp_entry = acl_entryp;
+ acl_entryp = acl_entryp->nextp;
+ }
+
+ if (theacl->count != 0){
+ temp_entry->nextp = acl_entryp = SMB_MALLOC_P(struct acl_entry_link);
+ if (acl_entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_create_entry is %d\n", errno));
+ return -1;
+ }
+
+ DEBUG(10, ("The acl_entryp is %d\n", acl_entryp));
+ acl_entryp->prevp = temp_entry;
+ DEBUG(10, ("The acl_entryp->prevp is %d\n", acl_entryp->prevp));
+ }
+
+ *pentry = acl_entryp->entryp = SMB_MALLOC_P(struct new_acl_entry);
+ if (*pentry == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_create_entry is %d\n", errno));
+ return -1;
+ }
+
+ memset(*pentry, 0, sizeof (struct new_acl_entry));
+ acl_entryp->entryp->ace_len = sizeof (struct acl_entry);
+ acl_entryp->entryp->ace_type = ACC_PERMIT;
+ acl_entryp->entryp->ace_id->id_len = sizeof (struct ace_id);
+ acl_entryp->nextp = NULL;
+ theacl->count++;
+ DEBUG(10, ("Exiting sys_acl_create_entry\n"));
+ return 0;
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ entry->ace_id->id_type = tag_type;
+ DEBUG(10, ("The tag type is %d\n", entry->ace_id->id_type));
+
+ if (tag_type == SMB_ACL_USER || tag_type == SMB_ACL_GROUP)
+ memcpy(entry->ace_id->id_data, &u_g_id, sizeof (id_t));
+
+ entry->ace_access = bits;
+ DEBUG(10, ("entry->ace_access = %d\n", entry->ace_access));
+
+ return 0;
+}
+
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ DEBUG(10, ("Starting AIX sys_acl_set_permset\n"));
+ entry->ace_access = bits;
+ DEBUG(10, ("entry->ace_access = %d\n", entry->ace_access));
+ DEBUG(10, ("Ending AIX sys_acl_set_permset\n"));
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ int user_obj = 0;
+ int group_obj = 0;
+ int other_obj = 0;
+ struct acl_entry_link *acl_entry;
+
+ for (acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) {
+ user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ);
+ group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ);
+ other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER);
+ }
+
+ DEBUG(10, ("user_obj=%d, group_obj=%d, other_obj=%d\n", user_obj, group_obj, other_obj));
+
+ if (user_obj != 1 || group_obj != 1 || other_obj != 1)
+ return -1;
+
+ return 0;
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10, ("Entering sys_acl_set_file\n"));
+ DEBUG(10, ("File name is %s\n", name));
+
+ /* AIX has no default ACL */
+ if (acltype == SMB_ACL_TYPE_DEFAULT)
+ return 0;
+
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_file is %d\n", errno));
+ return -1;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for (acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+
+ switch (id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if ((file_acl->acl_len + sizeof (struct acl_entry)) > acl_length) {
+ acl_length += sizeof (struct acl_entry);
+ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+ if (file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_file is %d\n", errno));
+ return -1;
+ }
+
+ memcpy(file_acl_temp, file_acl, file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof (struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10, ("The id type is %d\n", ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof (uid_t));
+ memcpy(acl_entry->ace_id->id_data, &user_id, sizeof (uid_t));
+ }
+
+ rc = chacl((char*)name, file_acl, file_acl->acl_len);
+ DEBUG(10, ("errno is %d\n", errno));
+ DEBUG(10, ("return code is %d\n", rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10, ("Exiting the sys_acl_set_file\n"));
+ return rc;
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10, ("Entering sys_acl_set_fd\n"));
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+ if (file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_fd is %d\n", errno));
+ return -1;
+ }
+
+ memset(file_acl, 0, BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for (acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10, ("The id_type is %d\n", id_type));
+
+ switch (id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if ((file_acl->acl_len + sizeof (struct acl_entry)) > acl_length) {
+ acl_length += sizeof (struct acl_entry);
+ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+ if (file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0, ("Error in sys_acl_set_fd is %d\n", errno));
+ return -1;
+ }
+
+ memcpy(file_acl_temp, file_acl, file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof (struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10, ("The id type is %d\n", ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof (uid_t));
+ memcpy(ace_id->id_data, &user_id, sizeof (uid_t));
+ }
+
+ rc = fchacl(fd, file_acl, file_acl->acl_len);
+ DEBUG(10, ("errno is %d\n", errno));
+ DEBUG(10, ("return code is %d\n", rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10, ("Exiting sys_acl_set_fd\n"));
+ return rc;
+}
+#endif
+
+int sys_acl_delete_def_file(UNUSED(const char *name))
+{
+ /* AIX has no default ACL */
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T posix_acl)
+{
+ struct acl_entry_link *acl_entry_link;
+
+ for (acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) {
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ }
+
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ SAFE_FREE(acl_entry_link->entryp);
+ SAFE_FREE(acl_entry_link);
+
+ return 0;
+}
+
+#elif defined(HAVE_OSX_ACLS) /*----------------------------------------------*/
+
+#define OSX_BROKEN_GETENTRY /* returns 0 instead of 1 */
+
+#include <membership.h>
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ int ret = acl_get_entry(the_acl, entry_id, entry_p);
+#ifdef OSX_BROKEN_GETENTRY
+ if (ret == 0)
+ ret = 1;
+ else if (ret == -1 && errno == 22)
+ ret = 0;
+#endif
+ return ret;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ if (type == ACL_TYPE_DEFAULT) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+ errno = 0;
+ return acl_get_file(path_p, type);
+}
+
+#if 0
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+#endif
+
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p)
+{
+ uuid_t *uup;
+ acl_tag_t tag;
+ acl_flagset_t flagset;
+ acl_permset_t permset;
+ uint32 bits, fb, bb, pb;
+ int id_type = -1;
+ int rc;
+
+ if (acl_get_tag_type(entry, &tag) != 0
+ || acl_get_flagset_np(entry, &flagset) != 0
+ || acl_get_permset(entry, &permset) != 0
+ || (uup = acl_get_qualifier(entry)) == NULL)
+ return -1;
+
+ rc = mbr_uuid_to_id(*uup, u_g_id_p, &id_type);
+ acl_free(uup);
+ if (rc != 0)
+ return rc;
+
+ if (id_type == ID_TYPE_UID)
+ *tag_type_p = SMB_ACL_USER;
+ else
+ *tag_type_p = SMB_ACL_GROUP;
+
+ bits = tag == ACL_EXTENDED_ALLOW ? 1 : 0;
+
+ for (fb = (1u<<4), bb = (1u<<1); bb < (1u<<12); fb *= 2, bb *= 2) {
+ if (acl_get_flag_np(flagset, fb) == 1)
+ bits |= bb;
+ }
+
+ for (pb = (1u<<1), bb = (1u<<12); bb < (1u<<25); pb *= 2, bb *= 2) {
+ if (acl_get_perm_np(permset, pb) == 1)
+ bits |= bb;
+ }
+
+ *bits_p = bits;
+
+ return 0;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tag_type, uint32 bits, id_t u_g_id)
+{
+ acl_flagset_t flagset;
+ acl_permset_t permset;
+ uint32 fb, bb, pb;
+ int is_user = tag_type == SMB_ACL_USER;
+ uuid_t uu;
+ int rc;
+
+ tag_type = bits & 1 ? ACL_EXTENDED_ALLOW : ACL_EXTENDED_DENY;
+
+ if (acl_get_flagset_np(entry, &flagset) != 0
+ || acl_get_permset(entry, &permset) != 0)
+ return -1;
+
+ acl_clear_flags_np(flagset);
+ acl_clear_perms(permset);
+
+ for (fb = (1u<<4), bb = (1u<<1); bb < (1u<<12); fb *= 2, bb *= 2) {
+ if (bits & bb)
+ acl_add_flag_np(flagset, fb);
+ }
+
+ for (pb = (1u<<1), bb = (1u<<12); bb < (1u<<25); pb *= 2, bb *= 2) {
+ if (bits & bb)
+ acl_add_perm(permset, pb);
+ }
+
+ if (is_user)
+ rc = mbr_uid_to_uuid(u_g_id, uu);
+ else
+ rc = mbr_gid_to_uuid(u_g_id, uu);
+ if (rc != 0)
+ return rc;
+
+ if (acl_set_tag_type(entry, tag_type) != 0
+ || acl_set_qualifier(entry, &uu) != 0
+ || acl_set_permset(entry, permset) != 0
+ || acl_set_flagset_np(entry, flagset) != 0)
+ return -1;
+
+ return 0;
+}
+
+#if 0
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits)
+{
+ return -1; /* Not needed for OS X. */
+}
+#endif
+
+int sys_acl_valid(SMB_ACL_T theacl)
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+#if 0
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+#endif
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+#else /* No ACLs. */
+
+#error No ACL functions defined for this platform!
+
+#endif
+
+/************************************************************************
+ Deliberately outside the ACL defines. Return 1 if this is a "no acls"
+ errno, 0 if not.
+************************************************************************/
+
+int no_acl_syscall_error(int err)
+{
+#ifdef HAVE_OSX_ACLS
+ if (err == ENOENT)
+ return 1; /* Weird problem with directory ACLs. */
+#endif
+#if defined(ENOSYS)
+ if (err == ENOSYS) {
+ return 1;
+ }
+#endif
+#if defined(ENOTSUP)
+ if (err == ENOTSUP) {
+ return 1;
+ }
+#endif
+ if (err == EINVAL) {
+ /* If the type of SMB_ACL_TYPE_ACCESS or SMB_ACL_TYPE_DEFAULT
+ * isn't valid, then the ACLs must be non-POSIX. */
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* SUPPORT_ACLS */
diff --git a/lib/sysacls.h b/lib/sysacls.h
new file mode 100644
index 0000000..c069597
--- /dev/null
+++ b/lib/sysacls.h
@@ -0,0 +1,307 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 2.2.x
+ * Portable SMB ACL interface
+ * Copyright (C) Jeremy Allison 2000
+ * Copyright (C) 2007-2022 Wayne Davison
+ *
+ * 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 3 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
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+#ifdef SUPPORT_ACLS
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+
+#define SMB_MALLOC(cnt) new_array(char, cnt)
+#define SMB_MALLOC_P(obj) new_array(obj, 1)
+#define SMB_MALLOC_ARRAY(obj, cnt) new_array(obj, cnt)
+#define SMB_REALLOC(mem, cnt) realloc_array(mem, char, cnt)
+#define slprintf snprintf
+
+#if defined HAVE_POSIX_ACLS /*-----------------------------------------------*/
+
+/* This is an identity mapping (just remove the SMB_). */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_TRU64_ACLS /*---------------------------------------------*/
+
+/* This is for DEC/Compaq Tru64 UNIX */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_UNIXWARE_ACLS || defined HAVE_SOLARIS_ACLS /*-------------*/
+
+/* Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris. */
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#ifdef __CYGWIN__
+#define SMB_ACL_LOSES_SPECIAL_MODE_BITS
+#endif
+
+#elif defined HAVE_HPUX_ACLS /*----------------------------------------------*/
+
+/* Based on the Solaris & UnixWare code. */
+
+#ifndef __TANDEM
+#undef GROUP
+#endif
+#include <sys/aclv.h>
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_IRIX_ACLS /*----------------------------------------------*/
+
+/* IRIX ACLs */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER_OBJ
+#define SMB_ACL_MASK ACL_MASK
+
+typedef struct SMB_ACL_T {
+ int next;
+ BOOL freeaclp;
+ struct acl *aclp;
+} *SMB_ACL_T;
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined HAVE_AIX_ACLS /*-----------------------------------------------*/
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+#include "/usr/include/acl.h"
+
+struct acl_entry_link{
+ struct acl_entry_link *prevp;
+ struct new_acl_entry *entryp;
+ struct acl_entry_link *nextp;
+ int count;
+};
+
+struct new_acl_entry{
+ unsigned short ace_len;
+ unsigned short ace_type;
+ unsigned int ace_access;
+ struct ace_id ace_id[1];
+};
+
+#define SMB_ACL_ENTRY_T struct new_acl_entry*
+#define SMB_ACL_T struct acl_entry_link*
+
+#define SMB_ACL_TAG_T unsigned short
+#define SMB_ACL_TYPE_T int
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACEID_USER
+#define SMB_ACL_USER_OBJ 3
+#define SMB_ACL_GROUP ACEID_GROUP
+#define SMB_ACL_GROUP_OBJ 4
+#define SMB_ACL_OTHER 5
+#define SMB_ACL_MASK 6
+
+#define SMB_ACL_FIRST_ENTRY 1
+#define SMB_ACL_NEXT_ENTRY 2
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1)
+#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1)
+
+#define SMB_ACL_NEED_SORT
+
+#elif defined(HAVE_OSX_ACLS) /*----------------------------------------------*/
+
+/* Special handling for OS X ACLs */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_USER 1
+#define SMB_ACL_GROUP 2
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_EXTENDED
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#define SMB_ACL_VALID_NAME_BITS ((1<<25)-1)
+#define SMB_ACL_VALID_OBJ_BITS 0
+
+/*#undef SMB_ACL_NEED_SORT*/
+
+#else /*---------------------------------------------------------------------*/
+
+/* Unknown platform. */
+
+#error Cannot handle ACLs on this platform!
+
+#endif
+
+int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p);
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p);
+int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *bits_p, id_t *u_g_id_p);
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type);
+SMB_ACL_T sys_acl_get_fd(int fd);
+SMB_ACL_T sys_acl_init(int count);
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry);
+int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype, uint32 bits, id_t u_g_id);
+int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits);
+int sys_acl_valid(SMB_ACL_T theacl);
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl);
+int sys_acl_delete_def_file(const char *name);
+int sys_acl_free_acl(SMB_ACL_T the_acl);
+int no_acl_syscall_error(int err);
+
+#endif /* SUPPORT_ACLS */
diff --git a/lib/sysxattrs.c b/lib/sysxattrs.c
new file mode 100644
index 0000000..ca08d13
--- /dev/null
+++ b/lib/sysxattrs.c
@@ -0,0 +1,301 @@
+/*
+ * Extended attribute support for rsync.
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2003-2022 Wayne Davison
+ * Written by Jay Fenlason.
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "sysxattrs.h"
+
+#ifdef SUPPORT_XATTRS
+
+#ifdef HAVE_OSX_XATTRS
+#define GETXATTR_FETCH_LIMIT (64*1024*1024)
+#endif
+
+#if defined HAVE_LINUX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ return lgetxattr(path, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return fgetxattr(filedes, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return lsetxattr(path, name, value, size, 0);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return lremovexattr(path, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ return llistxattr(path, list, size);
+}
+
+#elif HAVE_OSX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ ssize_t len = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+
+ /* If we're retrieving data, handle resource forks > 64MB specially */
+ if (value != NULL && len == GETXATTR_FETCH_LIMIT && (size_t)len < size) {
+ /* getxattr will only return 64MB of data at a time, need to call again with a new offset */
+ u_int32_t offset = len;
+ size_t data_retrieved = len;
+ while (data_retrieved < size) {
+ len = getxattr(path, name, (char*)value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
+ if (len <= 0)
+ break;
+ data_retrieved += len;
+ offset += (u_int32_t)len;
+ }
+ len = data_retrieved;
+ }
+
+ return len;
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return fgetxattr(filedes, name, value, size, 0, 0);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return removexattr(path, name, XATTR_NOFOLLOW);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ return listxattr(path, list, size, XATTR_NOFOLLOW);
+}
+
+#elif HAVE_FREEBSD_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ unsigned char keylen;
+ ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
+
+ if (len <= 0 || (size_t)len > size)
+ return len;
+
+ /* FreeBSD puts a single-byte length before each string, with no '\0'
+ * terminator. We need to change this into a series of null-terminted
+ * strings. Since the size is the same, we can simply transform the
+ * output in place. */
+ for (off = 0; off < len; off += keylen + 1) {
+ keylen = ((unsigned char*)list)[off];
+ if (off + keylen >= len) {
+ /* Should be impossible, but kernel bugs happen! */
+ errno = EINVAL;
+ return -1;
+ }
+ memmove(list+off, list+off+1, keylen);
+ list[off+keylen] = '\0';
+ }
+
+ return len;
+}
+
+#elif HAVE_SOLARIS_XATTRS
+
+static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
+{
+ STRUCT_STAT sb;
+ ssize_t ret;
+
+ if (fstat(attrfd, &sb) < 0)
+ ret = -1;
+ else if (sb.st_size > SSIZE_MAX) {
+ errno = ERANGE;
+ ret = -1;
+ } else if (buflen == 0)
+ ret = sb.st_size;
+ else if (sb.st_size > buflen) {
+ errno = ERANGE;
+ ret = -1;
+ } else {
+ size_t bufpos;
+ for (bufpos = 0; bufpos < sb.st_size; ) {
+ ssize_t cnt = read(attrfd, (char*)buf + bufpos, sb.st_size - bufpos);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EINTR)
+ continue;
+ bufpos = -1;
+ break;
+ }
+ bufpos += cnt;
+ }
+ ret = bufpos;
+ }
+
+ close(attrfd);
+
+ return ret;
+}
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+ int attrfd;
+
+ if ((attrfd = attropen(path, name, O_RDONLY)) < 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return read_xattr(attrfd, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+ int attrfd;
+
+ if ((attrfd = openat(filedes, name, O_RDONLY|O_XATTR, 0)) < 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return read_xattr(attrfd, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+ int attrfd;
+ size_t bufpos;
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+ if ((attrfd = attropen(path, name, O_CREAT|O_TRUNC|O_WRONLY, mode)) < 0)
+ return -1;
+
+ for (bufpos = 0; bufpos < size; ) {
+ ssize_t cnt = write(attrfd, (char*)value + bufpos, size);
+ if (cnt <= 0) {
+ if (cnt < 0 && errno == EINTR)
+ continue;
+ bufpos = -1;
+ break;
+ }
+ bufpos += cnt;
+ }
+
+ close(attrfd);
+
+ return bufpos > 0 ? 0 : -1;
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+ int attrdirfd;
+ int ret;
+
+ if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0)
+ return -1;
+
+ ret = unlinkat(attrdirfd, name, 0);
+
+ close(attrdirfd);
+
+ return ret;
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+ int attrdirfd;
+ DIR *dirp;
+ struct dirent *dp;
+ ssize_t ret = 0;
+
+ if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ if ((dirp = fdopendir(attrdirfd)) == NULL) {
+ close(attrdirfd);
+ return -1;
+ }
+
+ while ((dp = readdir(dirp))) {
+ int len = strlen(dp->d_name);
+
+ if (dp->d_name[0] == '.' && (len == 1 || (len == 2 && dp->d_name[1] == '.')))
+ continue;
+ if (len == 11 && dp->d_name[0] == 'S' && strncmp(dp->d_name, "SUNWattr_r", 10) == 0
+ && (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
+ continue;
+
+ ret += len + 1;
+ if ((size_t)ret > size) {
+ if (size == 0)
+ continue;
+ ret = -1;
+ errno = ERANGE;
+ break;
+ }
+ memcpy(list, dp->d_name, len+1);
+ list += len+1;
+ }
+
+ closedir(dirp);
+ close(attrdirfd);
+
+ return ret;
+}
+
+#else
+
+#error You need to create xattr compatibility functions.
+
+#endif
+
+#endif /* SUPPORT_XATTRS */
diff --git a/lib/sysxattrs.h b/lib/sysxattrs.h
new file mode 100644
index 0000000..024bbd1
--- /dev/null
+++ b/lib/sysxattrs.h
@@ -0,0 +1,26 @@
+#ifdef SUPPORT_XATTRS
+
+#if defined HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#elif defined HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif defined HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+
+/* Linux 2.4 does not define this as a distinct errno value: */
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
+int sys_lremovexattr(const char *path, const char *name);
+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
+
+#else
+
+/* No xattrs available */
+
+#endif
diff --git a/lib/wildmatch.c b/lib/wildmatch.c
new file mode 100644
index 0000000..f3a1731
--- /dev/null
+++ b/lib/wildmatch.c
@@ -0,0 +1,368 @@
+/*
+** Do shell-style pattern matching for ?, \, [], and * characters.
+** It is 8bit clean.
+**
+** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+** Rich $alz is now <rsalz@bbn.com>.
+**
+** Modified by Wayne Davison to special-case '/' matching, to make '**'
+** work differently than '*', and to fix the character-class code.
+*/
+
+#include "rsync.h"
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS '!'
+#define NEGATE_CLASS2 '^'
+
+#define FALSE 0
+#define TRUE 1
+#define ABORT_ALL -1
+#define ABORT_TO_STARSTAR -2
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+ && *(class) == *(litmatch) \
+ && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+#ifdef WILD_TEST_ITERATIONS
+int wildmatch_iteration_count;
+#endif
+
+static int force_lower_case = 0;
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+{
+ uchar p_ch;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count++;
+#endif
+
+ for ( ; (p_ch = *p) != '\0'; text++, p++) {
+ int matched, special;
+ uchar t_ch, prev_ch;
+ while ((t_ch = *text) == '\0') {
+ if (*a == NULL) {
+ if (p_ch != '*')
+ return ABORT_ALL;
+ break;
+ }
+ text = *a++;
+ }
+ if (force_lower_case && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ switch (p_ch) {
+ case '\\':
+ /* Literal match with following character. Note that the test
+ * in "default" handles the p[1] == '\0' failure case. */
+ p_ch = *++p;
+ /* FALLTHROUGH */
+ default:
+ if (t_ch != p_ch)
+ return FALSE;
+ continue;
+ case '?':
+ /* Match anything but '/'. */
+ if (t_ch == '/')
+ return FALSE;
+ continue;
+ case '*':
+ if (*++p == '*') {
+ while (*++p == '*') {}
+ special = TRUE;
+ } else
+ special = FALSE;
+ if (*p == '\0') {
+ /* Trailing "**" matches everything. Trailing "*" matches
+ * only if there are no more slash characters. */
+ if (!special) {
+ do {
+ if (strchr((char*)text, '/') != NULL)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+ }
+ return TRUE;
+ }
+ while (1) {
+ if (t_ch == '\0') {
+ if ((text = *a++) == NULL)
+ break;
+ t_ch = *text;
+ continue;
+ }
+ if ((matched = dowild(p, text, a)) != FALSE) {
+ if (!special || matched != ABORT_TO_STARSTAR)
+ return matched;
+ } else if (!special && t_ch == '/')
+ return ABORT_TO_STARSTAR;
+ t_ch = *++text;
+ }
+ return ABORT_ALL;
+ case '[':
+ p_ch = *++p;
+#ifdef NEGATE_CLASS2
+ if (p_ch == NEGATE_CLASS2)
+ p_ch = NEGATE_CLASS;
+#endif
+ /* Assign literal TRUE/FALSE because of "matched" comparison. */
+ special = p_ch == NEGATE_CLASS? TRUE : FALSE;
+ if (special) {
+ /* Inverted character class. */
+ p_ch = *++p;
+ }
+ prev_ch = 0;
+ matched = FALSE;
+ do {
+ if (!p_ch)
+ return ABORT_ALL;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ if (t_ch == p_ch)
+ matched = TRUE;
+ } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+ p_ch = *++p;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ }
+ if (t_ch <= p_ch && t_ch >= prev_ch)
+ matched = TRUE;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (p_ch == '[' && p[1] == ':') {
+ const uchar *s;
+ int i;
+ for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+ if (!p_ch)
+ return ABORT_ALL;
+ i = p - s - 1;
+ if (i < 0 || p[-1] != ':') {
+ /* Didn't find ":]", so treat like a normal set. */
+ p = s - 2;
+ p_ch = '[';
+ if (t_ch == p_ch)
+ matched = TRUE;
+ continue;
+ }
+ if (CC_EQ(s,i, "alnum")) {
+ if (ISALNUM(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "alpha")) {
+ if (ISALPHA(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "blank")) {
+ if (ISBLANK(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "cntrl")) {
+ if (ISCNTRL(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "digit")) {
+ if (ISDIGIT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "graph")) {
+ if (ISGRAPH(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "lower")) {
+ if (ISLOWER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "print")) {
+ if (ISPRINT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "punct")) {
+ if (ISPUNCT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "space")) {
+ if (ISSPACE(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "upper")) {
+ if (ISUPPER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "xdigit")) {
+ if (ISXDIGIT(t_ch))
+ matched = TRUE;
+ } else /* malformed [:class:] string */
+ return ABORT_ALL;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (t_ch == p_ch)
+ matched = TRUE;
+ } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+ if (matched == special || t_ch == '/')
+ return FALSE;
+ continue;
+ }
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
+{
+ for ( ; *s != '\0'; text++, s++) {
+ while (*text == '\0') {
+ if ((text = *a++) == NULL)
+ return FALSE;
+ }
+ if (*text != *s)
+ return FALSE;
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Return the last "count" path elements from the concatenated string.
+ * We return a string pointer to the start of the string, and update the
+ * array pointer-pointer to point to any remaining string elements. */
+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
+{
+ const uchar*const *a = *a_ptr;
+ const uchar*const *first_a = a;
+
+ while (*a)
+ a++;
+
+ while (a != first_a) {
+ const uchar *s = *--a;
+ s += strlen((char*)s);
+ while (--s >= *a) {
+ if (*s == '/' && !--count) {
+ *a_ptr = a+1;
+ return s+1;
+ }
+ }
+ }
+
+ if (count == 1) {
+ *a_ptr = a+1;
+ return *a;
+ }
+
+ return NULL;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+}
+
+/* Match the "pattern" against the forced-to-lower-case "text" string. */
+int iwildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+ int ret;
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ force_lower_case = 1;
+ ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+ force_lower_case = 0;
+ return ret;
+}
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), > 0 (match only
+ * the trailing N slash-separated filename components of "texts"), or < 0
+ * (match the "pattern" at the start or after any slash in "texts"). */
+int wildmatch_array(const char *pattern, const char*const *texts, int where)
+{
+ const uchar *p = (const uchar*)pattern;
+ const uchar*const *a = (const uchar*const*)texts;
+ const uchar *text;
+ int matched;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ if ((matched = dowild(p, text, a)) != TRUE && where < 0
+ && matched != ABORT_ALL) {
+ while (1) {
+ if (*text == '\0') {
+ if ((text = (uchar*)*a++) == NULL)
+ return FALSE;
+ continue;
+ }
+ if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+ && matched != ABORT_TO_STARSTAR)
+ break;
+ }
+ }
+ return matched == TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), or > 0 (match
+ * only the trailing N slash-separated filename components of "texts"). */
+int litmatch_array(const char *string, const char*const *texts, int where)
+{
+ const uchar *s = (const uchar*)string;
+ const uchar*const *a = (const uchar* const*)texts;
+ const uchar *text;
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ return doliteral(s, text, a) == TRUE;
+}
diff --git a/lib/wildmatch.h b/lib/wildmatch.h
new file mode 100644
index 0000000..e7f1a35
--- /dev/null
+++ b/lib/wildmatch.h
@@ -0,0 +1,6 @@
+/* wildmatch.h */
+
+int wildmatch(const char *pattern, const char *text);
+int iwildmatch(const char *pattern, const char *text);
+int wildmatch_array(const char *pattern, const char*const *texts, int where);
+int litmatch_array(const char *string, const char*const *texts, int where);
diff --git a/loadparm.c b/loadparm.c
new file mode 100644
index 0000000..3906bc0
--- /dev/null
+++ b/loadparm.c
@@ -0,0 +1,592 @@
+/*
+ * 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 3 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, visit the http://fsf.org website.
+ *
+ * This is based on loadparm.c from Samba, written by Andrew Tridgell
+ * and Karl Auer. Some of the changes are:
+ *
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2020 Wayne Davison
+ */
+
+/* Load parameters.
+ *
+ * This module provides suitable callback functions for the params
+ * module. It builds the internal table of section details which is
+ * then used by the rest of the server.
+ *
+ * To add a parameter:
+ *
+ * 1) add it to the global_vars or local_vars structure definition
+ * 2) add it to the parm_table
+ * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING())
+ * 4) initialise it in the Defaults static structure
+ *
+ * Notes:
+ * The configuration file is processed sequentially for speed. For this
+ * reason, there is a fair bit of sequence-dependent code here - ie., code
+ * which assumes that certain things happen before others. In particular, the
+ * code which happens at the boundary between sections is delicately poised,
+ * so be careful!
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "ifuncs.h"
+#include "default-dont-compress.h"
+
+extern item_list dparam_list;
+
+#define strequal(a, b) (strcasecmp(a, b)==0)
+
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+
+/* the following are used by loadparm for option lists */
+typedef enum {
+ P_BOOL, P_BOOLREV, P_BOOL3, P_CHAR, P_INTEGER,
+ P_OCTAL, P_PATH, P_STRING, P_ENUM
+} parm_type;
+
+typedef enum {
+ P_LOCAL, P_GLOBAL, P_NONE
+} parm_class;
+
+struct enum_list {
+ int value;
+ char *name;
+};
+
+struct parm_struct {
+ char *label;
+ parm_type type;
+ parm_class class;
+ void *ptr;
+ struct enum_list *enum_list;
+ unsigned flags;
+};
+
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#endif
+
+/* some helpful bits */
+#define iSECTION(i) ((local_vars*)section_list.items)[i]
+#define LP_SNUM_OK(i) ((i) >= 0 && (i) < (int)section_list.count)
+#define SECTION_PTR(s, p) (((char*)(s)) + (ptrdiff_t)(((char*)(p))-(char*)&Vars.l))
+
+/* Stack of "Vars" values used by the &include directive. */
+static item_list Vars_stack = EMPTY_ITEM_LIST;
+
+/* The array of section values that holds all the defined modules. */
+static item_list section_list = EMPTY_ITEM_LIST;
+
+static int iSectionIndex = -1;
+static BOOL bInGlobalSection = True;
+
+static struct enum_list enum_syslog_facility[] = {
+#ifdef LOG_AUTH
+ { LOG_AUTH, "auth" },
+#endif
+#ifdef LOG_AUTHPRIV
+ { LOG_AUTHPRIV, "authpriv" },
+#endif
+#ifdef LOG_CRON
+ { LOG_CRON, "cron" },
+#endif
+#ifdef LOG_DAEMON
+ { LOG_DAEMON, "daemon" },
+#endif
+#ifdef LOG_FTP
+ { LOG_FTP, "ftp" },
+#endif
+#ifdef LOG_KERN
+ { LOG_KERN, "kern" },
+#endif
+#ifdef LOG_LPR
+ { LOG_LPR, "lpr" },
+#endif
+#ifdef LOG_MAIL
+ { LOG_MAIL, "mail" },
+#endif
+#ifdef LOG_NEWS
+ { LOG_NEWS, "news" },
+#endif
+#ifdef LOG_AUTH
+ { LOG_AUTH, "security" },
+#endif
+#ifdef LOG_SYSLOG
+ { LOG_SYSLOG, "syslog" },
+#endif
+#ifdef LOG_USER
+ { LOG_USER, "user" },
+#endif
+#ifdef LOG_UUCP
+ { LOG_UUCP, "uucp" },
+#endif
+#ifdef LOG_LOCAL0
+ { LOG_LOCAL0, "local0" },
+#endif
+#ifdef LOG_LOCAL1
+ { LOG_LOCAL1, "local1" },
+#endif
+#ifdef LOG_LOCAL2
+ { LOG_LOCAL2, "local2" },
+#endif
+#ifdef LOG_LOCAL3
+ { LOG_LOCAL3, "local3" },
+#endif
+#ifdef LOG_LOCAL4
+ { LOG_LOCAL4, "local4" },
+#endif
+#ifdef LOG_LOCAL5
+ { LOG_LOCAL5, "local5" },
+#endif
+#ifdef LOG_LOCAL6
+ { LOG_LOCAL6, "local6" },
+#endif
+#ifdef LOG_LOCAL7
+ { LOG_LOCAL7, "local7" },
+#endif
+ { -1, NULL }
+};
+
+/* Expand %VAR% references. Any unknown vars or unrecognized
+ * syntax leaves the raw chars unchanged. */
+static char *expand_vars(const char *str)
+{
+ char *buf, *t;
+ const char *f;
+ int bufsize;
+
+ if (!str || !strchr(str, '%'))
+ return (char *)str; /* TODO change return value to const char* at some point. */
+
+ bufsize = strlen(str) + 2048;
+ buf = new_array(char, bufsize+1); /* +1 for trailing '\0' */
+
+ for (t = buf, f = str; bufsize && *f; ) {
+ if (*f == '%' && isUpper(f+1)) {
+ char *percent = strchr(f+1, '%');
+ if (percent && percent - f < bufsize) {
+ char *val;
+ strlcpy(t, f+1, percent - f);
+ val = getenv(t);
+ if (val) {
+ int len = strlcpy(t, val, bufsize+1);
+ if (len > bufsize)
+ break;
+ bufsize -= len;
+ t += len;
+ f = percent + 1;
+ continue;
+ }
+ }
+ }
+ *t++ = *f++;
+ bufsize--;
+ }
+ *t = '\0';
+
+ if (*f) {
+ rprintf(FLOG, "Overflowed buf in expand_vars() trying to expand: %s\n", str);
+ exit_cleanup(RERR_MALLOC);
+ }
+
+ if (bufsize && (buf = realloc(buf, t - buf + 1)) == NULL)
+ out_of_memory("expand_vars");
+
+ return buf;
+}
+
+/* Each "char* foo" has an associated "BOOL foo_EXP" that tracks if the string has been expanded yet or not. */
+
+/* NOTE: use this function and all the FN_{GLOBAL,LOCAL} ones WITHOUT a trailing semicolon! */
+#define RETURN_EXPANDED(val) {if (!val ## _EXP) {val = expand_vars(val); val ## _EXP = True;} return val ? val : "";}
+
+/* In this section all the functions that are used to access the
+ * parameters from the rest of the program are defined. */
+
+#define FN_GLOBAL_STRING(fn_name, val) \
+ char *fn_name(void) RETURN_EXPANDED(Vars.g.val)
+#define FN_GLOBAL_BOOL(fn_name, val) \
+ BOOL fn_name(void) {return Vars.g.val;}
+#define FN_GLOBAL_CHAR(fn_name, val) \
+ char fn_name(void) {return Vars.g.val;}
+#define FN_GLOBAL_INTEGER(fn_name, val) \
+ int fn_name(void) {return Vars.g.val;}
+
+#define FN_LOCAL_STRING(fn_name, val) \
+ char *fn_name(int i) {if (LP_SNUM_OK(i) && iSECTION(i).val) RETURN_EXPANDED(iSECTION(i).val) else RETURN_EXPANDED(Vars.l.val)}
+#define FN_LOCAL_BOOL(fn_name, val) \
+ BOOL fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
+#define FN_LOCAL_CHAR(fn_name, val) \
+ char fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
+#define FN_LOCAL_INTEGER(fn_name, val) \
+ int fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
+
+/* The following include file contains:
+ *
+ * typedef global_vars - describes global (ie., server-wide) parameters.
+ * typedef local_vars - describes a single section.
+ * typedef all_vars - a combination of global_vars & local_vars.
+ * all_vars Defaults - the default values for all the variables.
+ * all_vars Vars - the currently configured values for all the variables.
+ * struct parm_struct parm_table - the strings & variables for the parser.
+ * FN_{LOCAL,GLOBAL}_{TYPE}() definition for all the lp_var_name() accessors.
+ */
+
+#include "daemon-parm.h"
+
+/* Initialise the Default all_vars structure. */
+void reset_daemon_vars(void)
+{
+ memcpy(&Vars, &Defaults, sizeof Vars);
+}
+
+/* Assign a copy of v to *s. Handles NULL strings. We don't worry
+ * about overwriting a malloc'd string because the long-running
+ * (port-listening) daemon only loads the config file once, and the
+ * per-job (forked or xinitd-ran) daemon only re-reads the file at
+ * the start, so any lost memory is inconsequential. */
+static inline void string_set(char **s, const char *v)
+{
+ *s = v ? strdup(v) : NULL;
+}
+
+/* Copy local_vars into a new section. No need to strdup since we don't free. */
+static void copy_section(local_vars *psectionDest, local_vars *psectionSource)
+{
+ memcpy(psectionDest, psectionSource, sizeof psectionDest[0]);
+}
+
+/* Initialise a section to the defaults. */
+static void init_section(local_vars *psection)
+{
+ memset(psection, 0, sizeof (local_vars));
+ copy_section(psection, &Vars.l);
+}
+
+/* Do a case-insensitive, whitespace-ignoring string equality check. */
+static int strwiEQ(char *psz1, char *psz2)
+{
+ /* If one or both strings are NULL, we return equality right away. */
+ if (psz1 == psz2)
+ return 1;
+ if (psz1 == NULL || psz2 == NULL)
+ return 0;
+
+ /* sync the strings on first non-whitespace */
+ while (1) {
+ while (isSpace(psz1))
+ psz1++;
+ while (isSpace(psz2))
+ psz2++;
+ if (*psz1 == '\0' || *psz2 == '\0')
+ break;
+ if (toUpper(psz1) != toUpper(psz2))
+ break;
+ psz1++;
+ psz2++;
+ }
+ return *psz1 == *psz2;
+}
+
+/* Find a section by name. Otherwise works like get_section. */
+static int getsectionbyname(char *name)
+{
+ int i;
+
+ for (i = section_list.count - 1; i >= 0; i--) {
+ if (strwiEQ(iSECTION(i).name, name))
+ break;
+ }
+
+ return i;
+}
+
+/* Add a new section to the sections array w/the default values. */
+static int add_a_section(char *name)
+{
+ int i;
+ local_vars *s;
+
+ /* it might already exist */
+ if (name) {
+ i = getsectionbyname(name);
+ if (i >= 0)
+ return i;
+ }
+
+ i = section_list.count;
+ s = EXPAND_ITEM_LIST(&section_list, local_vars, 2);
+
+ init_section(s);
+ if (name)
+ string_set(&s->name, name);
+
+ return i;
+}
+
+/* Map a parameter's string representation to something we can use.
+ * Returns False if the parameter string is not recognised, else TRUE. */
+static int map_parameter(char *parmname)
+{
+ int iIndex;
+
+ if (*parmname == '-')
+ return -1;
+
+ for (iIndex = 0; parm_table[iIndex].label; iIndex++) {
+ if (strwiEQ(parm_table[iIndex].label, parmname))
+ return iIndex;
+ }
+
+ rprintf(FLOG, "Unknown Parameter encountered: \"%s\"\n", parmname);
+ return -1;
+}
+
+/* Set a boolean variable from the text value stored in the passed string.
+ * Returns True in success, False if the passed string does not correctly
+ * represent a boolean. */
+static BOOL set_boolean(BOOL *pb, char *parmvalue, int allow_unset)
+{
+ if (strwiEQ(parmvalue, "yes") || strwiEQ(parmvalue, "true") || strwiEQ(parmvalue, "1"))
+ *pb = True;
+ else if (strwiEQ(parmvalue, "no") || strwiEQ(parmvalue, "false") || strwiEQ(parmvalue, "0"))
+ *pb = False;
+ else if (allow_unset && (strwiEQ(parmvalue, "unset") || strwiEQ(parmvalue, "-1")))
+ *pb = Unset;
+ else {
+ rprintf(FLOG, "Badly formed boolean in configuration file: \"%s\".\n", parmvalue);
+ return False;
+ }
+ return True;
+}
+
+/* Process a parameter. */
+static BOOL do_parameter(char *parmname, char *parmvalue)
+{
+ int parmnum, i;
+ void *parm_ptr; /* where we are going to store the result */
+ void *def_ptr;
+ char *cp;
+
+ parmnum = map_parameter(parmname);
+
+ if (parmnum < 0) {
+ rprintf(FLOG, "IGNORING unknown parameter \"%s\"\n", parmname);
+ return True;
+ }
+
+ def_ptr = parm_table[parmnum].ptr;
+
+ if (bInGlobalSection)
+ parm_ptr = def_ptr;
+ else {
+ if (parm_table[parmnum].class == P_GLOBAL) {
+ rprintf(FLOG, "Global parameter %s found in module section!\n", parmname);
+ return True;
+ }
+ parm_ptr = SECTION_PTR(&iSECTION(iSectionIndex), def_ptr);
+ }
+
+ /* now switch on the type of variable it is */
+ switch (parm_table[parmnum].type) {
+ case P_PATH:
+ case P_STRING:
+ /* delay expansion of %VAR% strings */
+ break;
+ default:
+ /* expand any %VAR% strings now */
+ parmvalue = expand_vars(parmvalue);
+ break;
+ }
+
+ switch (parm_table[parmnum].type) {
+ case P_BOOL:
+ set_boolean(parm_ptr, parmvalue, False);
+ break;
+
+ case P_BOOL3:
+ set_boolean(parm_ptr, parmvalue, True);
+ break;
+
+ case P_BOOLREV:
+ set_boolean(parm_ptr, parmvalue, False);
+ *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
+ break;
+
+ case P_INTEGER:
+ *(int *)parm_ptr = atoi(parmvalue);
+ break;
+
+ case P_CHAR:
+ *(char *)parm_ptr = *parmvalue;
+ break;
+
+ case P_OCTAL:
+ sscanf(parmvalue, "%o", (unsigned int *)parm_ptr);
+ break;
+
+ case P_PATH:
+ string_set(parm_ptr, parmvalue);
+ if ((cp = *(char**)parm_ptr) != NULL) {
+ int len = strlen(cp);
+ while (len > 1 && cp[len-1] == '/') len--;
+ cp[len] = '\0';
+ }
+ break;
+
+ case P_STRING:
+ string_set(parm_ptr, parmvalue);
+ break;
+
+ case P_ENUM:
+ for (i=0; parm_table[parmnum].enum_list[i].name; i++) {
+ if (strequal(parmvalue, parm_table[parmnum].enum_list[i].name)) {
+ *(int *)parm_ptr = parm_table[parmnum].enum_list[i].value;
+ break;
+ }
+ }
+ if (!parm_table[parmnum].enum_list[i].name) {
+ if (atoi(parmvalue) > 0)
+ *(int *)parm_ptr = atoi(parmvalue);
+ }
+ break;
+ }
+
+ return True;
+}
+
+/* Process a new section (rsync module).
+ * Returns True on success, False on failure. */
+static BOOL do_section(char *sectionname)
+{
+ BOOL isglobal;
+
+ if (*sectionname == ']') { /* A special push/pop/reset directive from params.c */
+ bInGlobalSection = 1;
+ if (strcmp(sectionname+1, "push") == 0) {
+ all_vars *vp = EXPAND_ITEM_LIST(&Vars_stack, all_vars, 2);
+ memcpy(vp, &Vars, sizeof Vars);
+ } else if (strcmp(sectionname+1, "pop") == 0
+ || strcmp(sectionname+1, "reset") == 0) {
+ all_vars *vp = ((all_vars*)Vars_stack.items) + Vars_stack.count - 1;
+ if (!Vars_stack.count)
+ return False;
+ memcpy(&Vars, vp, sizeof Vars);
+ if (sectionname[1] == 'p')
+ Vars_stack.count--;
+ } else
+ return False;
+ return True;
+ }
+
+ isglobal = strwiEQ(sectionname, GLOBAL_NAME);
+
+ /* At the end of the global section, add any --dparam items. */
+ if (bInGlobalSection && !isglobal) {
+ if (!section_list.count)
+ set_dparams(0);
+ }
+
+ /* if we've just struck a global section, note the fact. */
+ bInGlobalSection = isglobal;
+
+ /* check for multiple global sections */
+ if (bInGlobalSection)
+ return True;
+
+#if 0
+ /* If we have a current section, tidy it up before moving on. */
+ if (iSectionIndex >= 0) {
+ /* Add any tidy work as needed ... */
+ if (problem)
+ return False;
+ }
+#endif
+
+ if (strchr(sectionname, '/') != NULL) {
+ rprintf(FLOG, "Warning: invalid section name in configuration file: %s\n", sectionname);
+ return False;
+ }
+
+ if ((iSectionIndex = add_a_section(sectionname)) < 0) {
+ rprintf(FLOG, "Failed to add a new module\n");
+ bInGlobalSection = True;
+ return False;
+ }
+
+ return True;
+}
+
+/* Load the modules from the config file. Return True on success,
+ * False on failure. */
+int lp_load(char *pszFname, int globals_only)
+{
+ bInGlobalSection = True;
+
+ reset_daemon_vars();
+
+ /* We get sections first, so have to start 'behind' to make up. */
+ iSectionIndex = -1;
+ return pm_process(pszFname, globals_only ? NULL : do_section, do_parameter);
+}
+
+BOOL set_dparams(int syntax_check_only)
+{
+ char *equal, *val, **params = dparam_list.items;
+ unsigned j;
+
+ for (j = 0; j < dparam_list.count; j++) {
+ equal = strchr(params[j], '='); /* options.c verified this */
+ *equal = '\0';
+ if (syntax_check_only) {
+ if (map_parameter(params[j]) < 0) {
+ rprintf(FERROR, "Unknown parameter \"%s\"\n", params[j]);
+ *equal = '=';
+ return False;
+ }
+ } else {
+ for (val = equal+1; isSpace(val); val++) {}
+ do_parameter(params[j], val);
+ }
+ *equal = '=';
+ }
+
+ return True;
+}
+
+/* Return the max number of modules (sections). */
+int lp_num_modules(void)
+{
+ return section_list.count;
+}
+
+/* Return the number of the module with the given name, or -1 if it doesn't
+ * exist. Note that this is a DIFFERENT ANIMAL from the internal function
+ * getsectionbyname()! This works ONLY if all sections have been loaded,
+ * and does not copy the found section. */
+int lp_number(char *name)
+{
+ int i;
+
+ for (i = section_list.count - 1; i >= 0; i--) {
+ if (strcmp(lp_name(i), name) == 0)
+ break;
+ }
+
+ return i;
+}
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..e4ba1cc
--- /dev/null
+++ b/log.c
@@ -0,0 +1,910 @@
+/*
+ * Logging and utility functions.
+ *
+ * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 2000-2001 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "inums.h"
+
+extern int dry_run;
+extern int am_daemon;
+extern int am_server;
+extern int am_sender;
+extern int am_generator;
+extern int local_server;
+extern int quiet;
+extern int module_id;
+extern int allow_8bit_chars;
+extern int protocol_version;
+extern int always_checksum;
+extern int preserve_mtimes;
+extern int msgs2stderr;
+extern int stdout_format_has_i;
+extern int stdout_format_has_o_or_i;
+extern int logfile_format_has_i;
+extern int logfile_format_has_o_or_i;
+extern int receiver_symlink_times;
+extern int64 total_data_written;
+extern int64 total_data_read;
+extern mode_t orig_umask;
+extern char *auth_user;
+extern char *stdout_format;
+extern char *logfile_format;
+extern char *logfile_name;
+#ifdef ICONV_CONST
+extern iconv_t ic_chck;
+#endif
+#ifdef ICONV_OPTION
+extern iconv_t ic_recv;
+#endif
+extern char curr_dir[MAXPATHLEN];
+extern char *full_module_path;
+extern unsigned int module_dirlen;
+extern char sender_file_sum[MAX_DIGEST_LEN];
+extern const char undetermined_hostname[];
+
+extern struct name_num_item *xfer_sum_nni, *file_sum_nni;
+
+static int log_initialised;
+static int logfile_was_closed;
+static FILE *logfile_fp;
+struct stats stats;
+
+int got_xfer_error = 0;
+int output_needs_newline = 0;
+int send_msgs_to_gen = 0;
+
+static int64 initial_data_written;
+static int64 initial_data_read;
+
+struct {
+ int code;
+ char const *name;
+} const rerr_names[] = {
+ { RERR_SYNTAX , "syntax or usage error" },
+ { RERR_PROTOCOL , "protocol incompatibility" },
+ { RERR_FILESELECT , "errors selecting input/output files, dirs" },
+ { RERR_UNSUPPORTED, "requested action not supported" },
+ { RERR_STARTCLIENT, "error starting client-server protocol" },
+ { RERR_SOCKETIO , "error in socket IO" },
+ { RERR_FILEIO , "error in file IO" },
+ { RERR_STREAMIO , "error in rsync protocol data stream" },
+ { RERR_MESSAGEIO , "errors with program diagnostics" },
+ { RERR_IPC , "error in IPC code" },
+ { RERR_CRASHED , "sibling process crashed" },
+ { RERR_TERMINATED , "sibling process terminated abnormally" },
+ { RERR_SIGNAL1 , "received SIGUSR1" },
+ { RERR_SIGNAL , "received SIGINT, SIGTERM, or SIGHUP" },
+ { RERR_WAITCHILD , "waitpid() failed" },
+ { RERR_MALLOC , "error allocating core memory buffers" },
+ { RERR_PARTIAL , "some files/attrs were not transferred (see previous errors)" },
+ { RERR_VANISHED , "some files vanished before they could be transferred" },
+ { RERR_DEL_LIMIT , "the --max-delete limit stopped deletions" },
+ { RERR_TIMEOUT , "timeout in data send/receive" },
+ { RERR_CONTIMEOUT , "timeout waiting for daemon connection" },
+ { RERR_CMD_FAILED , "remote shell failed" },
+ { RERR_CMD_KILLED , "remote shell killed" },
+ { RERR_CMD_RUN , "remote command could not be run" },
+ { RERR_CMD_NOTFOUND,"remote command not found" },
+ { 0, NULL }
+};
+
+/*
+ * Map from rsync error code to name, or return NULL.
+ */
+static char const *rerr_name(int code)
+{
+ int i;
+ for (i = 0; rerr_names[i].name; i++) {
+ if (rerr_names[i].code == code)
+ return rerr_names[i].name;
+ }
+ return NULL;
+}
+
+static void logit(int priority, const char *buf)
+{
+ if (logfile_was_closed)
+ logfile_reopen();
+ if (logfile_fp) {
+ fprintf(logfile_fp, "%s [%d] %s", timestring(time(NULL)), (int)getpid(), buf);
+ fflush(logfile_fp);
+ } else {
+ syslog(priority, "%s", buf);
+ }
+}
+
+static void syslog_init()
+{
+ int options = LOG_PID;
+
+#ifdef LOG_NDELAY
+ options |= LOG_NDELAY;
+#endif
+
+#ifdef LOG_DAEMON
+ openlog(lp_syslog_tag(module_id), options, lp_syslog_facility(module_id));
+#else
+ openlog(lp_syslog_tag(module_id), options);
+#endif
+
+#ifndef LOG_NDELAY
+ logit(LOG_INFO, "rsyncd started\n");
+#endif
+}
+
+static void logfile_open(void)
+{
+ mode_t old_umask = umask(022 | orig_umask);
+ logfile_fp = fopen(logfile_name, "a");
+ umask(old_umask);
+ if (!logfile_fp) {
+ int fopen_errno = errno;
+ /* Rsync falls back to using syslog on failure. */
+ syslog_init();
+ rsyserr(FERROR, fopen_errno,
+ "failed to open log-file %s", logfile_name);
+ rprintf(FINFO, "Ignoring \"log file\" setting.\n");
+ logfile_name = "";
+ }
+}
+
+void log_init(int restart)
+{
+ if (log_initialised) {
+ if (!restart) /* Note: a restart only happens with am_daemon */
+ return;
+ assert(logfile_name); /* all am_daemon procs got at least an empty string */
+ if (strcmp(logfile_name, lp_log_file(module_id)) != 0) {
+ if (logfile_fp) {
+ fclose(logfile_fp);
+ logfile_fp = NULL;
+ } else
+ closelog();
+ logfile_name = NULL;
+ } else if (*logfile_name)
+ return; /* unchanged, non-empty "log file" names */
+ else if (lp_syslog_facility(-1) != lp_syslog_facility(module_id)
+ || strcmp(lp_syslog_tag(-1), lp_syslog_tag(module_id)) != 0)
+ closelog();
+ else
+ return; /* unchanged syslog settings */
+ } else
+ log_initialised = 1;
+
+ /* This looks pointless, but it is needed in order for the
+ * C library on some systems to fetch the timezone info
+ * before the chroot. */
+ timestring(time(NULL));
+
+ /* Optionally use a log file instead of syslog. (Non-daemon
+ * rsyncs will have already set logfile_name, as needed.) */
+ if (am_daemon && !logfile_name)
+ logfile_name = lp_log_file(module_id);
+ if (logfile_name && *logfile_name)
+ logfile_open();
+ else
+ syslog_init();
+}
+
+/* Note that this close & reopen idiom intentionally ignores syslog logging. */
+void logfile_close(void)
+{
+ if (logfile_fp) {
+ logfile_was_closed = 1;
+ fclose(logfile_fp);
+ logfile_fp = NULL;
+ }
+}
+
+void logfile_reopen(void)
+{
+ if (logfile_was_closed) {
+ logfile_was_closed = 0;
+ logfile_open();
+ }
+}
+
+static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isprint, char end_char)
+{
+ char outbuf[1024], *ob = outbuf;
+ const char *end = in_buf + in_len;
+ while (in_buf < end) {
+ if (ob - outbuf >= (int)sizeof outbuf - 10) {
+ if (fwrite(outbuf, ob - outbuf, 1, f) != 1)
+ exit_cleanup(RERR_MESSAGEIO);
+ ob = outbuf;
+ }
+ if ((in_buf < end - 4 && *in_buf == '\\' && in_buf[1] == '#'
+ && isDigit(in_buf + 2) && isDigit(in_buf + 3) && isDigit(in_buf + 4))
+ || (*in_buf != '\t' && ((use_isprint && !isPrint(in_buf)) || *(uchar*)in_buf < ' ')))
+ ob += snprintf(ob, 6, "\\#%03o", *(uchar*)in_buf++);
+ else
+ *ob++ = *in_buf++;
+ }
+ if (end_char) /* The "- 10" above means that there is always room for one more char here. */
+ *ob++ = end_char;
+ if (ob != outbuf && fwrite(outbuf, ob - outbuf, 1, f) != 1)
+ exit_cleanup(RERR_MESSAGEIO);
+}
+
+/* this is the underlying (unformatted) rsync debugging function. Call
+ * it with FINFO, FERROR_*, FWARNING, FLOG, or FCLIENT. Note: recursion
+ * can happen with certain fatal conditions. */
+void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
+{
+ char trailing_CR_or_NL;
+ FILE *f = msgs2stderr == 1 ? stderr : stdout;
+#ifdef ICONV_OPTION
+ iconv_t ic = is_utf8 && ic_recv != (iconv_t)-1 ? ic_recv : ic_chck;
+#else
+#ifdef ICONV_CONST
+ iconv_t ic = ic_chck;
+#endif
+#endif
+
+ if (len < 0)
+ exit_cleanup(RERR_MESSAGEIO);
+
+ if (msgs2stderr == 1) {
+ /* A normal daemon can get msgs2stderr set if the socket is busted, so we
+ * change the message destination into an FLOG message in order to try to
+ * get some info about an abnormal-exit into the log file. An rsh daemon
+ * can have this set via user request, so we'll leave the code alone so
+ * that the msg gets logged and then sent to stderr after that. */
+ if (am_daemon > 0 && code != FCLIENT)
+ code = FLOG;
+ } else if (send_msgs_to_gen) {
+ assert(!is_utf8);
+ /* Pass the message to our sibling in native charset. */
+ send_msg((enum msgcode)code, buf, len, 0);
+ return;
+ }
+
+ if (code == FERROR_SOCKET) /* This gets simplified for a non-sibling. */
+ code = FERROR;
+ else if (code == FERROR_UTF8) {
+ is_utf8 = 1;
+ code = FERROR;
+ }
+
+ if (code == FCLIENT)
+ code = FINFO;
+ else if (am_daemon || logfile_name) {
+ static int in_block;
+ char msg[2048];
+ int priority = code == FINFO || code == FLOG ? LOG_INFO : LOG_WARNING;
+
+ if (in_block)
+ return;
+ in_block = 1;
+ if (!log_initialised)
+ log_init(0);
+ strlcpy(msg, buf, MIN((int)sizeof msg, len + 1));
+ logit(priority, msg);
+ in_block = 0;
+
+ if (code == FLOG || (am_daemon && !am_server))
+ return;
+ } else if (code == FLOG)
+ return;
+
+ switch (code) {
+ case FERROR_XFER:
+ got_xfer_error = 1;
+ /* FALL THROUGH */
+ case FERROR:
+ case FWARNING:
+ f = stderr;
+ break;
+ case FINFO:
+ if (quiet)
+ return;
+ break;
+ /*case FLOG:*/
+ /*case FCLIENT:*/
+ /*case FERROR_UTF8:*/
+ /*case FERROR_SOCKET:*/
+ default:
+ fprintf(stderr, "Bad logcode in rwrite(): %d [%s]\n", (int)code, who_am_i());
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+
+ if (am_server && msgs2stderr != 1 && (msgs2stderr != 2 || f != stderr)) {
+ enum msgcode msg = (enum msgcode)code;
+ if (protocol_version < 30) {
+ if (msg == MSG_ERROR)
+ msg = MSG_ERROR_XFER;
+ else if (msg == MSG_WARNING)
+ msg = MSG_INFO;
+ }
+ /* Pass the message to the non-server side. */
+ if (send_msg(msg, buf, len, !is_utf8))
+ return;
+ if (am_daemon > 0) {
+ /* TODO: can we send the error to the user somehow? */
+ return;
+ }
+ f = stderr;
+ }
+
+ if (output_needs_newline) {
+ fputc('\n', f);
+ output_needs_newline = 0;
+ }
+
+ trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r') ? buf[--len] : '\0';
+
+ if (len && buf[0] == '\r') {
+ fputc('\r', f);
+ buf++;
+ len--;
+ }
+
+#ifdef ICONV_CONST
+ if (ic != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+ char convbuf[1024];
+ int ierrno;
+
+ INIT_CONST_XBUF(outbuf, convbuf);
+ INIT_XBUF(inbuf, (char*)buf, len, (size_t)-1);
+
+ while (inbuf.len) {
+ iconvbufs(ic, &inbuf, &outbuf, inbuf.pos ? 0 : ICB_INIT);
+ ierrno = errno;
+ if (outbuf.len) {
+ char trailing = inbuf.len ? '\0' : trailing_CR_or_NL;
+ filtered_fwrite(f, convbuf, outbuf.len, 0, trailing);
+ if (trailing) {
+ trailing_CR_or_NL = '\0';
+ fflush(f);
+ }
+ outbuf.len = 0;
+ }
+ /* Log one byte of illegal/incomplete sequence and continue with
+ * the next character. Check that the buffer is non-empty for the
+ * sake of robustness. */
+ if ((ierrno == EILSEQ || ierrno == EINVAL) && inbuf.len) {
+ fprintf(f, "\\#%03o", CVAL(inbuf.buf, inbuf.pos++));
+ inbuf.len--;
+ }
+ }
+
+ if (trailing_CR_or_NL) {
+ fputc(trailing_CR_or_NL, f);
+ fflush(f);
+ }
+ } else
+#endif
+ {
+ filtered_fwrite(f, buf, len, !allow_8bit_chars, trailing_CR_or_NL);
+ if (trailing_CR_or_NL)
+ fflush(f);
+ }
+}
+
+/* This is the rsync debugging function. Call it with FINFO, FERROR_*,
+ * FWARNING, FLOG, or FCLIENT. */
+void rprintf(enum logcode code, const char *format, ...)
+{
+ va_list ap;
+ char buf[BIGPATHBUFLEN];
+ size_t len;
+
+ va_start(ap, format);
+ len = vsnprintf(buf, sizeof buf, format, ap);
+ va_end(ap);
+
+ /* Deal with buffer overruns. Instead of panicking, just
+ * truncate the resulting string. (Note that configure ensures
+ * that we have a vsnprintf() that doesn't ever return -1.) */
+ if (len > sizeof buf - 1) {
+ static const char ellipsis[] = "[...]";
+
+ /* Reset length, and zero-terminate the end of our buffer */
+ len = sizeof buf - 1;
+ buf[len] = '\0';
+
+ /* Copy the ellipsis to the end of the string, but give
+ * us one extra character:
+ *
+ * v--- null byte at buf[sizeof buf - 1]
+ * abcdefghij0
+ * -> abcd[...]00 <-- now two null bytes at end
+ *
+ * If the input format string has a trailing newline,
+ * we copy it into that extra null; if it doesn't, well,
+ * all we lose is one byte. */
+ memcpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis);
+ if (format[strlen(format)-1] == '\n') {
+ buf[len-1] = '\n';
+ }
+ }
+
+ rwrite(code, buf, len, 0);
+}
+
+/* This is like rprintf, but it also tries to print some
+ * representation of the error code. Normally errcode = errno.
+ *
+ * Unlike rprintf, this always adds a newline and there should not be
+ * one in the format string.
+ *
+ * Note that since strerror might involve dynamically loading a
+ * message catalog we need to call it once before chroot-ing. */
+void rsyserr(enum logcode code, int errcode, const char *format, ...)
+{
+ va_list ap;
+ char buf[BIGPATHBUFLEN];
+ size_t len;
+
+ len = snprintf(buf, sizeof buf, RSYNC_NAME ": [%s] ", who_am_i());
+
+ va_start(ap, format);
+ len += vsnprintf(buf + len, sizeof buf - len, format, ap);
+ va_end(ap);
+
+ if (len < sizeof buf) {
+ len += snprintf(buf + len, sizeof buf - len,
+ ": %s (%d)\n", strerror(errcode), errcode);
+ }
+ if (len >= sizeof buf)
+ exit_cleanup(RERR_MESSAGEIO);
+
+ rwrite(code, buf, len, 0);
+}
+
+void rflush(enum logcode code)
+{
+ FILE *f;
+
+ if (am_daemon || code == FLOG)
+ return;
+
+ if (!am_server && (code == FINFO || code == FCLIENT))
+ f = stdout;
+ else
+ f = stderr;
+
+ fflush(f);
+}
+
+void remember_initial_stats(void)
+{
+ initial_data_read = total_data_read;
+ initial_data_written = total_data_written;
+}
+
+/* A generic logging routine for send/recv, with parameter substitiution. */
+static void log_formatted(enum logcode code, const char *format, const char *op,
+ struct file_struct *file, const char *fname, int iflags,
+ const char *hlink)
+{
+ char buf[MAXPATHLEN+1024], buf2[MAXPATHLEN], fmt[32];
+ char *p, *s, *c;
+ const char *n;
+ size_t len, total;
+ int64 b;
+
+ *fmt = '%';
+
+ /* We expand % codes one by one in place in buf. We don't
+ * copy in the terminating null of the inserted strings, but
+ * rather keep going until we reach the null of the format. */
+ total = strlcpy(buf, format, sizeof buf);
+ if (total > MAXPATHLEN) {
+ rprintf(FERROR, "log-format string is WAY too long!\n");
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+ buf[total++] = '\n';
+ buf[total] = '\0';
+
+ for (p = buf; (p = strchr(p, '%')) != NULL; ) {
+ int humanize = 0;
+ s = p++;
+ c = fmt + 1;
+ while (*p == '\'') {
+ humanize++;
+ p++;
+ }
+ if (*p == '-')
+ *c++ = *p++;
+ while (isDigit(p) && c - fmt < (int)(sizeof fmt) - 8)
+ *c++ = *p++;
+ while (*p == '\'') {
+ humanize++;
+ p++;
+ }
+ if (!*p)
+ break;
+ *c = '\0';
+ n = NULL;
+
+ /* Note for %h and %a: it doesn't matter what fd we pass to
+ * client_{name,addr} because rsync_module will already have
+ * forced the answer to be cached (assuming, of course, for %h
+ * that lp_reverse_lookup(module_id) is true). */
+ switch (*p) {
+ case 'h':
+ if (am_daemon) {
+ n = lp_reverse_lookup(module_id)
+ ? client_name(0) : undetermined_hostname;
+ }
+ break;
+ case 'a':
+ if (am_daemon)
+ n = client_addr(0);
+ break;
+ case 'l':
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ do_big_num(F_LENGTH(file), humanize, NULL));
+ n = buf2;
+ break;
+ case 'U':
+ strlcat(fmt, "u", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ uid_ndx ? F_OWNER(file) : 0);
+ n = buf2;
+ break;
+ case 'G':
+ if (!gid_ndx || file->flags & FLAG_SKIP_GROUP)
+ n = "DEFAULT";
+ else {
+ strlcat(fmt, "u", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ F_GROUP(file));
+ n = buf2;
+ }
+ break;
+ case 'p':
+ strlcat(fmt, "d", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt, (int)getpid());
+ n = buf2;
+ break;
+ case 'M':
+ n = c = timestring(file->modtime);
+ while ((c = strchr(c, ' ')) != NULL)
+ *c = '-';
+ break;
+ case 'B':
+ c = buf2 + MAXPATHLEN - PERMSTRING_SIZE - 1;
+ permstring(c, file->mode);
+ n = c + 1; /* skip the type char */
+ break;
+ case 'o':
+ n = op;
+ break;
+ case 'f':
+ if (fname) {
+ c = f_name_buf();
+ strlcpy(c, fname, MAXPATHLEN);
+ } else
+ c = f_name(file, NULL);
+ if (am_sender && F_PATHNAME(file)) {
+ pathjoin(buf2, sizeof buf2,
+ F_PATHNAME(file), c);
+ clean_fname(buf2, 0);
+ if (fmt[1]) {
+ strlcpy(c, buf2, MAXPATHLEN);
+ n = c;
+ } else
+ n = buf2;
+ } else if (am_daemon && *c != '/') {
+ pathjoin(buf2, sizeof buf2,
+ curr_dir + module_dirlen, c);
+ clean_fname(buf2, 0);
+ if (fmt[1]) {
+ strlcpy(c, buf2, MAXPATHLEN);
+ n = c;
+ } else
+ n = buf2;
+ } else {
+ clean_fname(c, 0);
+ n = c;
+ }
+ if (*n == '/')
+ n++;
+ break;
+ case 'n':
+ if (fname) {
+ c = f_name_buf();
+ strlcpy(c, fname, MAXPATHLEN);
+ } else
+ c = f_name(file, NULL);
+ if (S_ISDIR(file->mode))
+ strlcat(c, "/", MAXPATHLEN);
+ n = c;
+ break;
+ case 'L':
+ if (hlink && *hlink) {
+ n = hlink;
+ strlcpy(buf2, " => ", sizeof buf2);
+ } else if (S_ISLNK(file->mode) && !fname) {
+ n = F_SYMLINK(file);
+ strlcpy(buf2, " -> ", sizeof buf2);
+ } else {
+ n = "";
+ if (!fmt[1])
+ break;
+ strlcpy(buf2, " ", sizeof buf2);
+ }
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2 + 4, sizeof buf2 - 4, fmt, n);
+ n = buf2;
+ break;
+ case 'm':
+ n = lp_name(module_id);
+ break;
+ case 't':
+ n = timestring(time(NULL));
+ break;
+ case 'P':
+ n = full_module_path;
+ break;
+ case 'u':
+ n = auth_user;
+ break;
+ case 'b':
+ case 'c':
+ if (!(iflags & ITEM_TRANSFER))
+ b = 0;
+ else if ((!!am_sender) ^ (*p == 'c'))
+ b = total_data_written - initial_data_written;
+ else
+ b = total_data_read - initial_data_read;
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt,
+ do_big_num(b, humanize, NULL));
+ n = buf2;
+ break;
+ case 'C':
+ n = NULL;
+ if (S_ISREG(file->mode)) {
+ if (always_checksum)
+ n = sum_as_hex(file_sum_nni->num, F_SUM(file), 1);
+ else if (iflags & ITEM_TRANSFER)
+ n = sum_as_hex(xfer_sum_nni->num, sender_file_sum, 0);
+ }
+ if (!n) {
+ int sum_len = csum_len_for_type(always_checksum ? file_sum_nni->num : xfer_sum_nni->num,
+ always_checksum);
+ memset(buf2, ' ', sum_len*2);
+ buf2[sum_len*2] = '\0';
+ n = buf2;
+ }
+ break;
+ case 'i':
+ if (iflags & ITEM_DELETED) {
+ n = "*deleting ";
+ break;
+ }
+ n = c = buf2 + MAXPATHLEN - 32;
+ c[0] = iflags & ITEM_LOCAL_CHANGE
+ ? iflags & ITEM_XNAME_FOLLOWS ? 'h' : 'c'
+ : !(iflags & ITEM_TRANSFER) ? '.'
+ : !local_server && *op == 's' ? '<' : '>';
+ if (S_ISLNK(file->mode)) {
+ c[1] = 'L';
+ c[3] = '.';
+ c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
+ : !preserve_mtimes || !receiver_symlink_times
+ || (iflags & ITEM_REPORT_TIMEFAIL) ? 'T' : 't';
+ } else {
+ c[1] = S_ISDIR(file->mode) ? 'd'
+ : IS_SPECIAL(file->mode) ? 'S'
+ : IS_DEVICE(file->mode) ? 'D' : 'f';
+ c[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's';
+ c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
+ : !preserve_mtimes ? 'T' : 't';
+ }
+ c[2] = !(iflags & ITEM_REPORT_CHANGE) ? '.' : 'c';
+ c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
+ c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
+ c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
+ c[8] = !(iflags & (ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME)) ? '.'
+ : BITS_SET(iflags, ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME) ? 'b'
+ : iflags & ITEM_REPORT_ATIME ? 'u' : 'n';
+ c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
+ c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
+ c[11] = '\0';
+
+ if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
+ char ch = iflags & ITEM_IS_NEW ? '+' : '?';
+ int i;
+ for (i = 2; c[i]; i++)
+ c[i] = ch;
+ } else if (c[0] == '.' || c[0] == 'h' || c[0] == 'c') {
+ int i;
+ for (i = 2; c[i]; i++) {
+ if (c[i] != '.')
+ break;
+ }
+ if (!c[i]) {
+ for (i = 2; c[i]; i++)
+ c[i] = ' ';
+ }
+ }
+ break;
+ }
+
+ /* "n" is the string to be inserted in place of this % code. */
+ if (!n)
+ continue;
+ if (n != buf2 && fmt[1]) {
+ strlcat(fmt, "s", sizeof fmt);
+ snprintf(buf2, sizeof buf2, fmt, n);
+ n = buf2;
+ }
+ len = strlen(n);
+
+ /* Subtract the length of the escape from the string's size. */
+ total -= p - s + 1;
+
+ if (len + total >= (size_t)sizeof buf) {
+ rprintf(FERROR,
+ "buffer overflow expanding %%%c -- exiting\n",
+ p[0]);
+ exit_cleanup(RERR_MESSAGEIO);
+ }
+
+ /* Shuffle the rest of the string along to make space for n */
+ if (len != (size_t)(p - s + 1))
+ memmove(s + len, p + 1, total - (s - buf) + 1);
+ total += len;
+
+ /* Insert the contents of string "n", but NOT its null. */
+ if (len)
+ memcpy(s, n, len);
+
+ /* Skip over inserted string; continue looking */
+ p = s + len;
+ }
+
+ rwrite(code, buf, total, 0);
+}
+
+/* Return 1 if the format escape is in the log-format string (e.g. look for
+ * the 'b' in the "%9b" format escape). */
+int log_format_has(const char *format, char esc)
+{
+ const char *p;
+
+ if (!format)
+ return 0;
+
+ for (p = format; (p = strchr(p, '%')) != NULL; ) {
+ for (p++; *p == '\''; p++) {} /*SHARED ITERATOR*/
+ if (*p == '-')
+ p++;
+ while (isDigit(p))
+ p++;
+ while (*p == '\'') p++;
+ if (!*p)
+ break;
+ if (*p == esc)
+ return 1;
+ }
+ return 0;
+}
+
+/* Log the transfer of a file. If the code is FCLIENT, the output just goes
+ * to stdout. If it is FLOG, it just goes to the log file. Otherwise we
+ * output to both. */
+void log_item(enum logcode code, struct file_struct *file, int iflags, const char *hlink)
+{
+ const char *s_or_r = am_sender ? "send" : "recv";
+
+ if (code != FLOG && stdout_format && !am_server)
+ log_formatted(FCLIENT, stdout_format, s_or_r, file, NULL, iflags, hlink);
+ if (code != FCLIENT && logfile_format && *logfile_format)
+ log_formatted(FLOG, logfile_format, s_or_r, file, NULL, iflags, hlink);
+}
+
+void maybe_log_item(struct file_struct *file, int iflags, int itemizing, const char *buf)
+{
+ int significant_flags = iflags & SIGNIFICANT_ITEM_FLAGS;
+ int see_item = itemizing && (significant_flags || *buf
+ || stdout_format_has_i > 1 || (INFO_GTE(NAME, 2) && stdout_format_has_i));
+ int local_change = iflags & ITEM_LOCAL_CHANGE && significant_flags;
+ if (am_server) {
+ if (logfile_name && !dry_run && see_item
+ && (significant_flags || logfile_format_has_i))
+ log_item(FLOG, file, iflags, buf);
+ } else if (see_item || local_change || *buf
+ || (S_ISDIR(file->mode) && significant_flags)) {
+ enum logcode code = significant_flags || logfile_format_has_i ? FINFO : FCLIENT;
+ log_item(code, file, iflags, buf);
+ }
+}
+
+void log_delete(const char *fname, int mode)
+{
+ static struct file_struct *file = NULL;
+ int len = strlen(fname);
+ const char *fmt;
+
+ if (!file) {
+ int extra_len = (file_extra_cnt + 2) * EXTRA_LEN;
+ char *bp;
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ bp = new_array0(char, FILE_STRUCT_LEN + extra_len + 1);
+ bp += extra_len;
+ file = (struct file_struct *)bp;
+ }
+
+ file->mode = mode;
+
+ if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
+ if (S_ISDIR(mode))
+ len++; /* directories include trailing null */
+ send_msg(MSG_DELETED, fname, len, am_generator);
+ } else if (!INFO_GTE(DEL, 1) && !stdout_format)
+ ;
+ else {
+ fmt = stdout_format_has_o_or_i ? stdout_format : "deleting %n";
+ log_formatted(FCLIENT, fmt, "del.", file, fname, ITEM_DELETED, NULL);
+ }
+
+ if (!logfile_name || dry_run || !logfile_format)
+ return;
+
+ fmt = logfile_format_has_o_or_i ? logfile_format : "deleting %n";
+ log_formatted(FLOG, fmt, "del.", file, fname, ITEM_DELETED, NULL);
+}
+
+/*
+ * Called when the transfer is interrupted for some reason.
+ *
+ * Code is one of the RERR_* codes from errcode.h, or terminating
+ * successfully.
+ */
+void log_exit(int code, const char *file, int line)
+{
+ /* The receiving side's stats are split between 2 procs until the
+ * end of the run, so only the sender can output non-final info. */
+ if (code == 0 || am_sender) {
+ rprintf(FLOG,"sent %s bytes received %s bytes total size %s\n",
+ big_num(stats.total_written),
+ big_num(stats.total_read),
+ big_num(stats.total_size));
+ }
+ if (code != 0 && am_server != 2) {
+ const char *name;
+
+ name = rerr_name(code);
+ if (!name)
+ name = "unexplained error";
+
+ /* VANISHED is not an error, only a warning */
+ if (code == RERR_VANISHED) {
+ rprintf(FWARNING, "rsync warning: %s (code %d) at %s(%d) [%s=%s]\n",
+ name, code, src_file(file), line, who_am_i(), rsync_version());
+ } else {
+ rprintf(FERROR, "rsync error: %s (code %d) at %s(%d) [%s=%s]\n",
+ name, code, src_file(file), line, who_am_i(), rsync_version());
+ }
+ }
+}
diff --git a/m4/have_type.m4 b/m4/have_type.m4
new file mode 100644
index 0000000..12fc719
--- /dev/null
+++ b/m4/have_type.m4
@@ -0,0 +1,21 @@
+dnl AC_HAVE_TYPE(TYPE,INCLUDES)
+AC_DEFUN([AC_HAVE_TYPE], [
+cv=`echo "$1" | sed 'y%./+- %__p__%'`
+AC_MSG_CHECKING(for $1)
+AC_CACHE_VAL([ac_cv_type_$cv],
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+AC_INCLUDES_DEFAULT
+$2]],
+[[$1 foo;]])],
+[eval "ac_cv_type_$cv=yes"],
+[eval "ac_cv_type_$cv=no"]))dnl
+ac_foo=`eval echo \\$ac_cv_type_$cv`
+AC_MSG_RESULT($ac_foo)
+if test "$ac_foo" = yes; then
+ ac_tr_hdr=HAVE_`echo $1 | sed 'y%abcdefghijklmnopqrstuvwxyz./- %ABCDEFGHIJKLMNOPQRSTUVWXYZ____%'`
+if false; then
+ AC_CHECK_TYPES($1)
+fi
+ AC_DEFINE_UNQUOTED($ac_tr_hdr, 1, [Define if you have type `$1'])
+fi
+])
diff --git a/m4/header_major_fixed.m4 b/m4/header_major_fixed.m4
new file mode 100644
index 0000000..0f156aa
--- /dev/null
+++ b/m4/header_major_fixed.m4
@@ -0,0 +1,27 @@
+AC_DEFUN([AC_HEADER_MAJOR_FIXED],
+[AC_CACHE_CHECK(whether sys/types.h defines makedev,
+ ac_cv_header_sys_types_h_makedev,
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <sys/types.h>]],
+ [[return makedev(0, 0);]])],
+ [if grep sys/sysmacros.h conftest.err >/dev/null; then
+ ac_cv_header_sys_types_h_makedev=no
+ else
+ ac_cv_header_sys_types_h_makedev=yes
+ fi],
+ [ac_cv_header_sys_types_h_makedev=no])
+])
+
+if test $ac_cv_header_sys_types_h_makedev = no; then
+AC_CHECK_HEADER(sys/mkdev.h,
+ [AC_DEFINE(MAJOR_IN_MKDEV, 1,
+ [Define to 1 if `major', `minor', and `makedev' are
+ declared in <mkdev.h>.])])
+
+ if test $ac_cv_header_sys_mkdev_h = no; then
+ AC_CHECK_HEADER(sys/sysmacros.h,
+ [AC_DEFINE(MAJOR_IN_SYSMACROS, 1,
+ [Define to 1 if `major', `minor', and `makedev'
+ are declared in <sysmacros.h>.])])
+ fi
+fi
+])
diff --git a/m4/socklen_t.m4 b/m4/socklen_t.m4
new file mode 100644
index 0000000..99ca6d4
--- /dev/null
+++ b/m4/socklen_t.m4
@@ -0,0 +1,45 @@
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc. So we
+dnl have to test to find something that will work.
+
+dnl This is no good, because passing the wrong pointer on C compilers is
+dnl likely to only generate a warning, not an error. We don't call this at
+dnl the moment.
+
+AC_DEFUN([TYPE_SOCKLEN_T],
+[
+ AC_CHECK_TYPE([socklen_t], ,[
+ AC_MSG_CHECKING([for socklen_t equivalent])
+ AC_CACHE_VAL([rsync_cv_socklen_t_equiv],
+ [
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ rsync_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+
+ int getpeername (int, $arg2 *, $t *);
+ ]],[[
+ $t len;
+ getpeername(0,0,&len);
+ ]])],[
+ rsync_cv_socklen_t_equiv="$t"
+ break
+ ])
+ done
+ done
+
+ if test "x$rsync_cv_socklen_t_equiv" = x; then
+ AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+ fi
+ ])
+ AC_MSG_RESULT($rsync_cv_socklen_t_equiv)
+ AC_DEFINE_UNQUOTED(socklen_t, $rsync_cv_socklen_t_equiv,
+ [type to use in place of socklen_t if not defined])],
+ [#include <sys/types.h>
+#include <sys/socket.h>])
+])
diff --git a/m4/validate_cache_system_type.m4 b/m4/validate_cache_system_type.m4
new file mode 100644
index 0000000..dcb3b54
--- /dev/null
+++ b/m4/validate_cache_system_type.m4
@@ -0,0 +1,23 @@
+dnl AC_VALIDATE_CACHE_SYSTEM_TYPE[(cmd)]
+dnl if the cache file is inconsistent with the current host,
+dnl target and build system types, execute CMD or print a default
+dnl error message.
+AC_DEFUN([AC_VALIDATE_CACHE_SYSTEM_TYPE], [
+ AC_REQUIRE([AC_CANONICAL_SYSTEM])
+ AC_MSG_CHECKING([config.cache system type])
+ if { test x"${ac_cv_host_system_type+set}" = x"set" &&
+ test x"$ac_cv_host_system_type" != x"$host"; } ||
+ { test x"${ac_cv_build_system_type+set}" = x"set" &&
+ test x"$ac_cv_build_system_type" != x"$build"; } ||
+ { test x"${ac_cv_target_system_type+set}" = x"set" &&
+ test x"$ac_cv_target_system_type" != x"$target"; }; then
+ AC_MSG_RESULT([different])
+ ifelse($#, 1, [$1],
+ [AC_MSG_ERROR(["you must remove config.cache and restart configure"])])
+ else
+ AC_MSG_RESULT([same])
+ fi
+ ac_cv_host_system_type="$host"
+ ac_cv_build_system_type="$build"
+ ac_cv_target_system_type="$target"
+])
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..d2a7b9b
--- /dev/null
+++ b/main.c
@@ -0,0 +1,1864 @@
+/*
+ * The startup routines, including main(), for rsync.
+ *
+ * Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+#include "ifuncs.h"
+#include "io.h"
+#if defined CONFIG_LOCALE && defined HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <popt.h>
+#ifdef __TANDEM
+#include <floss.h(floss_execlp)>
+#endif
+
+extern int dry_run;
+extern int list_only;
+extern int io_timeout;
+extern int am_root;
+extern int am_server;
+extern int am_sender;
+extern int am_daemon;
+extern int inc_recurse;
+extern int blocking_io;
+extern int always_checksum;
+extern int remove_source_files;
+extern int output_needs_newline;
+extern int called_from_signal_handler;
+extern int need_messages_from_generator;
+extern int kluge_around_eof;
+extern int got_xfer_error;
+extern int old_style_args;
+extern int msgs2stderr;
+extern int module_id;
+extern int read_only;
+extern int copy_links;
+extern int copy_dirlinks;
+extern int copy_unsafe_links;
+extern int keep_dirlinks;
+extern int preserve_hard_links;
+extern int protocol_version;
+extern int mkpath_dest_arg;
+extern int file_total;
+extern int recurse;
+extern int xfer_dirs;
+extern int protect_args;
+extern int relative_paths;
+extern int sanitize_paths;
+extern int curr_dir_depth;
+extern int curr_dir_len;
+extern int module_id;
+extern int rsync_port;
+extern int whole_file;
+extern int read_batch;
+extern int write_batch;
+extern int batch_fd;
+extern int sock_f_in;
+extern int sock_f_out;
+extern int filesfrom_fd;
+extern int connect_timeout;
+extern int send_msgs_to_gen;
+extern dev_t filesystem_dev;
+extern pid_t cleanup_child_pid;
+extern size_t bwlimit_writemax;
+extern unsigned int module_dirlen;
+extern BOOL flist_receiving_enabled;
+extern BOOL want_progress_now;
+extern BOOL shutting_down;
+extern int backup_dir_len;
+extern int basis_dir_cnt;
+extern int default_af_hint;
+extern int stdout_format_has_i;
+extern struct stats stats;
+extern char *stdout_format;
+extern char *logfile_format;
+extern char *filesfrom_host;
+extern char *partial_dir;
+extern char *rsync_path;
+extern char *shell_cmd;
+extern char *password_file;
+extern char *backup_dir;
+extern char *copy_as;
+extern char *tmpdir;
+extern char curr_dir[MAXPATHLEN];
+extern char backup_dir_buf[MAXPATHLEN];
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern struct file_list *first_flist;
+extern filter_rule_list daemon_filter_list, implied_filter_list;
+
+uid_t our_uid;
+gid_t our_gid;
+int am_receiver = 0; /* Only set to 1 after the receiver/generator fork. */
+int am_generator = 0; /* Only set to 1 after the receiver/generator fork. */
+int local_server = 0;
+int daemon_connection = 0; /* 0 = no daemon, 1 = daemon via remote shell, -1 = daemon via socket */
+mode_t orig_umask = 0;
+int batch_gen_fd = -1;
+int sender_keeps_checksum = 0;
+int raw_argc, cooked_argc;
+char **raw_argv, **cooked_argv;
+
+/* There's probably never more than at most 2 outstanding child processes,
+ * but set it higher, just in case. */
+#define MAXCHILDPROCS 7
+
+#ifdef HAVE_SIGACTION
+# ifdef HAVE_SIGPROCMASK
+# define SIGACTMASK(n,h) SIGACTION(n,h), sigaddset(&sigmask,(n))
+# else
+# define SIGACTMASK(n,h) SIGACTION(n,h)
+# endif
+static struct sigaction sigact;
+#endif
+
+struct pid_status {
+ pid_t pid;
+ int status;
+} pid_stat_table[MAXCHILDPROCS];
+
+static time_t starttime, endtime;
+static int64 total_read, total_written;
+
+static void show_malloc_stats(void);
+
+/* Works like waitpid(), but if we already harvested the child pid in our
+ * remember_children(), we succeed instead of returning an error. */
+pid_t wait_process(pid_t pid, int *status_ptr, int flags)
+{
+ pid_t waited_pid;
+
+ do {
+ waited_pid = waitpid(pid, status_ptr, flags);
+ } while (waited_pid == -1 && errno == EINTR);
+
+ if (waited_pid == -1 && errno == ECHILD) {
+ /* Status of requested child no longer available: check to
+ * see if it was processed by remember_children(). */
+ int cnt;
+ for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) {
+ if (pid == pid_stat_table[cnt].pid) {
+ *status_ptr = pid_stat_table[cnt].status;
+ pid_stat_table[cnt].pid = 0;
+ return pid;
+ }
+ }
+ }
+
+ return waited_pid;
+}
+
+int shell_exec(const char *cmd)
+{
+ char *shell = getenv("RSYNC_SHELL");
+ int status;
+ pid_t pid;
+
+ if (!shell)
+ return system(cmd);
+
+ if ((pid = fork()) < 0)
+ return -1;
+
+ if (pid == 0) {
+ execlp(shell, shell, "-c", cmd, NULL);
+ _exit(1);
+ }
+
+ int ret = wait_process(pid, &status, 0);
+ return ret < 0 ? -1 : status;
+}
+
+/* Wait for a process to exit, calling io_flush while waiting. */
+static void wait_process_with_flush(pid_t pid, int *exit_code_ptr)
+{
+ pid_t waited_pid;
+ int status;
+
+ while ((waited_pid = wait_process(pid, &status, WNOHANG)) == 0) {
+ msleep(20);
+ io_flush(FULL_FLUSH);
+ }
+
+ /* TODO: If the child exited on a signal, then log an
+ * appropriate error message. Perhaps we should also accept a
+ * message describing the purpose of the child. Also indicate
+ * this to the caller so that they know something went wrong. */
+ if (waited_pid < 0) {
+ rsyserr(FERROR, errno, "waitpid");
+ *exit_code_ptr = RERR_WAITCHILD;
+ } else if (!WIFEXITED(status)) {
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status))
+ *exit_code_ptr = RERR_CRASHED;
+ else
+#endif
+ if (WIFSIGNALED(status))
+ *exit_code_ptr = RERR_TERMINATED;
+ else
+ *exit_code_ptr = RERR_WAITCHILD;
+ } else
+ *exit_code_ptr = WEXITSTATUS(status);
+}
+
+void write_del_stats(int f)
+{
+ if (read_batch)
+ write_int(f, NDX_DEL_STATS);
+ else
+ write_ndx(f, NDX_DEL_STATS);
+ write_varint(f, stats.deleted_files - stats.deleted_dirs
+ - stats.deleted_symlinks - stats.deleted_devices
+ - stats.deleted_specials);
+ write_varint(f, stats.deleted_dirs);
+ write_varint(f, stats.deleted_symlinks);
+ write_varint(f, stats.deleted_devices);
+ write_varint(f, stats.deleted_specials);
+}
+
+void read_del_stats(int f)
+{
+ stats.deleted_files = read_varint(f);
+ stats.deleted_files += stats.deleted_dirs = read_varint(f);
+ stats.deleted_files += stats.deleted_symlinks = read_varint(f);
+ stats.deleted_files += stats.deleted_devices = read_varint(f);
+ stats.deleted_files += stats.deleted_specials = read_varint(f);
+}
+
+static void become_copy_as_user()
+{
+ char *gname;
+ uid_t uid;
+ gid_t gid;
+
+ if (!copy_as)
+ return;
+
+ if (DEBUG_GTE(CMD, 2))
+ rprintf(FINFO, "[%s] copy_as=%s\n", who_am_i(), copy_as);
+
+ if ((gname = strchr(copy_as, ':')) != NULL)
+ *gname++ = '\0';
+
+ if (!user_to_uid(copy_as, &uid, True)) {
+ rprintf(FERROR, "Invalid copy-as user: %s\n", copy_as);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (gname) {
+ if (!group_to_gid(gname, &gid, True)) {
+ rprintf(FERROR, "Invalid copy-as group: %s\n", gname);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ } else {
+ struct passwd *pw;
+ if ((pw = getpwuid(uid)) == NULL) {
+ rsyserr(FERROR, errno, "getpwuid failed");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ gid = pw->pw_gid;
+ }
+
+ if (setgid(gid) < 0) {
+ rsyserr(FERROR, errno, "setgid failed");
+ exit_cleanup(RERR_SYNTAX);
+ }
+#ifdef HAVE_SETGROUPS
+ if (setgroups(1, &gid)) {
+ rsyserr(FERROR, errno, "setgroups failed");
+ exit_cleanup(RERR_SYNTAX);
+ }
+#endif
+#ifdef HAVE_INITGROUPS
+ if (!gname && initgroups(copy_as, gid) < 0) {
+ rsyserr(FERROR, errno, "initgroups failed");
+ exit_cleanup(RERR_SYNTAX);
+ }
+#endif
+
+ if (setuid(uid) < 0
+#ifdef HAVE_SETEUID
+ || seteuid(uid) < 0
+#endif
+ ) {
+ rsyserr(FERROR, errno, "setuid failed");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ our_uid = MY_UID();
+ our_gid = MY_GID();
+ am_root = (our_uid == ROOT_UID);
+
+ if (gname)
+ gname[-1] = ':';
+}
+
+/* This function gets called from all 3 processes. We want the client side
+ * to actually output the text, but the sender is the only process that has
+ * all the stats we need. So, if we're a client sender, we do the report.
+ * If we're a server sender, we write the stats on the supplied fd. If
+ * we're the client receiver we read the stats from the supplied fd and do
+ * the report. All processes might also generate a set of debug stats, if
+ * the verbose level is high enough (this is the only thing that the
+ * generator process and the server receiver ever do here). */
+static void handle_stats(int f)
+{
+ endtime = time(NULL);
+
+ /* Cache two stats because the read/write code can change it. */
+ total_read = stats.total_read;
+ total_written = stats.total_written;
+
+ if (INFO_GTE(STATS, 3)) {
+ /* These come out from every process */
+ show_malloc_stats();
+ show_flist_stats();
+ }
+
+ if (am_generator)
+ return;
+
+ if (am_daemon) {
+ if (f == -1 || !am_sender)
+ return;
+ }
+
+ if (am_server) {
+ if (am_sender) {
+ write_varlong30(f, total_read, 3);
+ write_varlong30(f, total_written, 3);
+ write_varlong30(f, stats.total_size, 3);
+ if (protocol_version >= 29) {
+ write_varlong30(f, stats.flist_buildtime, 3);
+ write_varlong30(f, stats.flist_xfertime, 3);
+ }
+ }
+ return;
+ }
+
+ /* this is the client */
+
+ if (f < 0 && !am_sender) /* e.g. when we got an empty file list. */
+ ;
+ else if (!am_sender) {
+ /* Read the first two in opposite order because the meaning of
+ * read/write swaps when switching from sender to receiver. */
+ total_written = read_varlong30(f, 3);
+ total_read = read_varlong30(f, 3);
+ stats.total_size = read_varlong30(f, 3);
+ if (protocol_version >= 29) {
+ stats.flist_buildtime = read_varlong30(f, 3);
+ stats.flist_xfertime = read_varlong30(f, 3);
+ }
+ } else if (write_batch) {
+ /* The --read-batch process is going to be a client
+ * receiver, so we need to give it the stats. */
+ write_varlong30(batch_fd, total_read, 3);
+ write_varlong30(batch_fd, total_written, 3);
+ write_varlong30(batch_fd, stats.total_size, 3);
+ if (protocol_version >= 29) {
+ write_varlong30(batch_fd, stats.flist_buildtime, 3);
+ write_varlong30(batch_fd, stats.flist_xfertime, 3);
+ }
+ }
+}
+
+static void output_itemized_counts(const char *prefix, int *counts)
+{
+ static char *labels[] = { "reg", "dir", "link", "dev", "special" };
+ char buf[1024], *pre = " (";
+ int j, len = 0;
+ int total = counts[0];
+ if (total) {
+ counts[0] -= counts[1] + counts[2] + counts[3] + counts[4];
+ for (j = 0; j < 5; j++) {
+ if (counts[j]) {
+ len += snprintf(buf+len, sizeof buf - len - 2,
+ "%s%s: %s",
+ pre, labels[j], comma_num(counts[j]));
+ pre = ", ";
+ }
+ }
+ buf[len++] = ')';
+ }
+ buf[len] = '\0';
+ rprintf(FINFO, "%s: %s%s\n", prefix, comma_num(total), buf);
+}
+
+static const char *bytes_per_sec_human_dnum(void)
+{
+ if (starttime == (time_t)-1 || endtime == (time_t)-1)
+ return "UNKNOWN";
+ return human_dnum((total_written + total_read) / (0.5 + (endtime - starttime)), 2);
+}
+
+static void output_summary(void)
+{
+ if (INFO_GTE(STATS, 2)) {
+ rprintf(FCLIENT, "\n");
+ output_itemized_counts("Number of files", &stats.num_files);
+ if (protocol_version >= 29)
+ output_itemized_counts("Number of created files", &stats.created_files);
+ if (protocol_version >= 31)
+ output_itemized_counts("Number of deleted files", &stats.deleted_files);
+ rprintf(FINFO,"Number of regular files transferred: %s\n",
+ comma_num(stats.xferred_files));
+ rprintf(FINFO,"Total file size: %s bytes\n",
+ human_num(stats.total_size));
+ rprintf(FINFO,"Total transferred file size: %s bytes\n",
+ human_num(stats.total_transferred_size));
+ rprintf(FINFO,"Literal data: %s bytes\n",
+ human_num(stats.literal_data));
+ rprintf(FINFO,"Matched data: %s bytes\n",
+ human_num(stats.matched_data));
+ rprintf(FINFO,"File list size: %s\n",
+ human_num(stats.flist_size));
+ if (stats.flist_buildtime) {
+ rprintf(FINFO,
+ "File list generation time: %s seconds\n",
+ comma_dnum((double)stats.flist_buildtime / 1000, 3));
+ rprintf(FINFO,
+ "File list transfer time: %s seconds\n",
+ comma_dnum((double)stats.flist_xfertime / 1000, 3));
+ }
+ rprintf(FINFO,"Total bytes sent: %s\n",
+ human_num(total_written));
+ rprintf(FINFO,"Total bytes received: %s\n",
+ human_num(total_read));
+ }
+
+ if (INFO_GTE(STATS, 1)) {
+ rprintf(FCLIENT, "\n");
+ rprintf(FINFO,
+ "sent %s bytes received %s bytes %s bytes/sec\n",
+ human_num(total_written), human_num(total_read),
+ bytes_per_sec_human_dnum());
+ rprintf(FINFO, "total size is %s speedup is %s%s\n",
+ human_num(stats.total_size),
+ comma_dnum((double)stats.total_size / (total_written+total_read), 2),
+ write_batch < 0 ? " (BATCH ONLY)" : dry_run ? " (DRY RUN)" : "");
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+}
+
+
+/**
+ * If our C library can get malloc statistics, then show them to FINFO
+ **/
+static void show_malloc_stats(void)
+{
+#ifdef MEM_ALLOC_INFO
+ struct MEM_ALLOC_INFO mi = MEM_ALLOC_INFO(); /* mallinfo or mallinfo2 */
+
+ rprintf(FCLIENT, "\n");
+ rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n",
+ (int)getpid(), am_server ? "server " : "",
+ am_daemon ? "daemon " : "", who_am_i());
+
+#define PRINT_ALLOC_NUM(title, descr, num) \
+ rprintf(FINFO, " %-11s%10" SIZE_T_FMT_MOD "d (" descr ")\n", \
+ title ":", (SIZE_T_FMT_CAST)(num));
+
+ PRINT_ALLOC_NUM("arena", "bytes from sbrk", mi.arena);
+ PRINT_ALLOC_NUM("ordblks", "chunks not in use", mi.ordblks);
+ PRINT_ALLOC_NUM("smblks", "free fastbin blocks", mi.smblks);
+ PRINT_ALLOC_NUM("hblks", "chunks from mmap", mi.hblks);
+ PRINT_ALLOC_NUM("hblkhd", "bytes from mmap", mi.hblkhd);
+ PRINT_ALLOC_NUM("allmem", "bytes from sbrk + mmap", mi.arena + mi.hblkhd);
+ PRINT_ALLOC_NUM("usmblks", "always 0", mi.usmblks);
+ PRINT_ALLOC_NUM("fsmblks", "bytes in freed fastbin blocks", mi.fsmblks);
+ PRINT_ALLOC_NUM("uordblks", "bytes used", mi.uordblks);
+ PRINT_ALLOC_NUM("fordblks", "bytes free", mi.fordblks);
+ PRINT_ALLOC_NUM("keepcost", "bytes in releasable chunk", mi.keepcost);
+
+#undef PRINT_ALLOC_NUM
+
+#endif /* MEM_ALLOC_INFO */
+}
+
+
+/* Start the remote shell. cmd may be NULL to use the default. */
+static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, int remote_argc,
+ int *f_in_p, int *f_out_p)
+{
+ int i, argc = 0;
+ char *args[MAX_ARGS], *need_to_free = NULL;
+ pid_t pid;
+ int dash_l_set = 0;
+
+ if (!read_batch && !local_server) {
+ char *t, *f, in_quote = '\0';
+ char *rsh_env = getenv(RSYNC_RSH_ENV);
+ if (!cmd)
+ cmd = rsh_env;
+ if (!cmd)
+ cmd = RSYNC_RSH;
+ cmd = need_to_free = strdup(cmd);
+
+ for (t = f = cmd; *f; f++) {
+ if (*f == ' ')
+ continue;
+ /* Comparison leaves rooms for server_options(). */
+ if (argc >= MAX_ARGS - MAX_SERVER_ARGS)
+ goto arg_overflow;
+ args[argc++] = t;
+ while (*f != ' ' || in_quote) {
+ if (!*f) {
+ if (in_quote) {
+ rprintf(FERROR,
+ "Missing trailing-%c in remote-shell command.\n",
+ in_quote);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ f--;
+ break;
+ }
+ if (*f == '\'' || *f == '"') {
+ if (!in_quote) {
+ in_quote = *f++;
+ continue;
+ }
+ if (*f == in_quote && *++f != in_quote) {
+ in_quote = '\0';
+ continue;
+ }
+ }
+ *t++ = *f++;
+ }
+ *t++ = '\0';
+ }
+
+ /* NOTE: must preserve t == start of command name until the end of the args handling! */
+ if ((t = strrchr(cmd, '/')) != NULL)
+ t++;
+ else
+ t = cmd;
+
+ /* Check to see if we've already been given '-l user' in the remote-shell command. */
+ for (i = 0; i < argc-1; i++) {
+ if (!strcmp(args[i], "-l") && args[i+1][0] != '-')
+ dash_l_set = 1;
+ }
+
+#ifdef HAVE_REMSH
+ /* remsh (on HPUX) takes the arguments the other way around */
+ args[argc++] = machine;
+ if (user && !(daemon_connection && dash_l_set)) {
+ args[argc++] = "-l";
+ args[argc++] = user;
+ }
+#else
+ if (user && !(daemon_connection && dash_l_set)) {
+ args[argc++] = "-l";
+ args[argc++] = user;
+ }
+#ifdef AF_INET
+ if (default_af_hint == AF_INET && strcmp(t, "ssh") == 0)
+ args[argc++] = "-4"; /* we're using ssh so we can add a -4 option */
+#endif
+#ifdef AF_INET6
+ if (default_af_hint == AF_INET6 && strcmp(t, "ssh") == 0)
+ args[argc++] = "-6"; /* we're using ssh so we can add a -6 option */
+#endif
+ args[argc++] = machine;
+#endif
+
+ args[argc++] = rsync_path;
+
+ if (blocking_io < 0 && (strcmp(t, "rsh") == 0 || strcmp(t, "remsh") == 0))
+ blocking_io = 1;
+
+ if (daemon_connection > 0) {
+ args[argc++] = "--server";
+ args[argc++] = "--daemon";
+ } else
+ server_options(args, &argc);
+
+ if (argc >= MAX_ARGS - 2)
+ goto arg_overflow;
+ }
+
+ args[argc++] = ".";
+
+ if (!daemon_connection) {
+ while (remote_argc > 0) {
+ if (argc >= MAX_ARGS - 1) {
+ arg_overflow:
+ rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ args[argc++] = safe_arg(NULL, *remote_argv++);
+ remote_argc--;
+ }
+ }
+
+ args[argc] = NULL;
+
+ if (DEBUG_GTE(CMD, 2)) {
+ for (i = 0; i < argc; i++)
+ rprintf(FCLIENT, "cmd[%d]=%s ", i, args[i]);
+ rprintf(FCLIENT, "\n");
+ }
+
+ if (read_batch) {
+ int from_gen_pipe[2];
+ set_allow_inc_recurse();
+ if (fd_pair(from_gen_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe");
+ exit_cleanup(RERR_IPC);
+ }
+ batch_gen_fd = from_gen_pipe[0];
+ *f_out_p = from_gen_pipe[1];
+ *f_in_p = batch_fd;
+ pid = (pid_t)-1; /* no child pid */
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else if (local_server) {
+ /* If the user didn't request --[no-]whole-file, force
+ * it on, but only if we're not batch processing. */
+ if (whole_file < 0 && !write_batch)
+ whole_file = 1;
+ set_allow_inc_recurse();
+ pid = local_child(argc, args, f_in_p, f_out_p, child_main);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else {
+ pid = piped_child(args, f_in_p, f_out_p);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ if (protect_args && !daemon_connection)
+ send_protected_args(*f_out_p, args);
+ }
+
+ if (need_to_free)
+ free(need_to_free);
+
+ return pid;
+}
+
+/* Older versions turn an empty string as a reference to the current directory.
+ * We now treat this as an error unless --old-args was used. */
+static char *dot_dir_or_error()
+{
+ if (old_style_args || am_server)
+ return ".";
+ rprintf(FERROR, "Empty destination arg specified (use \".\" or see --old-args).\n");
+ exit_cleanup(RERR_SYNTAX);
+}
+
+/* The receiving side operates in one of two modes:
+ *
+ * 1. it receives any number of files into a destination directory,
+ * placing them according to their names in the file-list.
+ *
+ * 2. it receives a single file and saves it using the name in the
+ * destination path instead of its file-list name. This requires a
+ * "local name" for writing out the destination file.
+ *
+ * So, our task is to figure out what mode/local-name we need.
+ * For mode 1, we change into the destination directory and return NULL.
+ * For mode 2, we change into the directory containing the destination
+ * file (if we aren't already there) and return the local-name. */
+static char *get_local_name(struct file_list *flist, char *dest_path)
+{
+ STRUCT_STAT st;
+ int statret, trailing_slash;
+ char *cp;
+
+ if (DEBUG_GTE(RECV, 1)) {
+ rprintf(FINFO, "get_local_name count=%d %s\n",
+ file_total, NS(dest_path));
+ }
+
+ if (!dest_path || list_only)
+ return NULL;
+
+ if (!*dest_path)
+ dest_path = dot_dir_or_error();
+
+ if (daemon_filter_list.head) {
+ char *slash = strrchr(dest_path, '/');
+ if (slash && (slash[1] == '\0' || (slash[1] == '.' && slash[2] == '\0')))
+ *slash = '\0';
+ else
+ slash = NULL;
+ if ((*dest_path != '.' || dest_path[1] != '\0')
+ && (check_filter(&daemon_filter_list, FLOG, dest_path, 0) < 0
+ || check_filter(&daemon_filter_list, FLOG, dest_path, 1) < 0)) {
+ rprintf(FERROR, "ERROR: daemon has excluded destination \"%s\"\n",
+ dest_path);
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (slash)
+ *slash = '/';
+ }
+
+ /* See what currently exists at the destination. */
+ statret = do_stat(dest_path, &st);
+ cp = strrchr(dest_path, '/');
+ trailing_slash = cp && !cp[1];
+
+ if (mkpath_dest_arg && statret < 0 && (cp || file_total > 1)) {
+ int save_errno = errno;
+ int ret = make_path(dest_path, file_total > 1 && !trailing_slash ? 0 : MKP_DROP_NAME);
+ if (ret < 0)
+ goto mkdir_error;
+ if (ret && (INFO_GTE(NAME, 1) || stdout_format_has_i)) {
+ if (file_total == 1 || trailing_slash)
+ *cp = '\0';
+ rprintf(FINFO, "created %d director%s for %s\n", ret, ret == 1 ? "y" : "ies", dest_path);
+ if (file_total == 1 || trailing_slash)
+ *cp = '/';
+ }
+ if (ret)
+ statret = do_stat(dest_path, &st);
+ else
+ errno = save_errno;
+ }
+
+ if (statret == 0) {
+ /* If the destination is a dir, enter it and use mode 1. */
+ if (S_ISDIR(st.st_mode)) {
+ if (!change_dir(dest_path, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#1 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ filesystem_dev = st.st_dev; /* ensures --force works right w/-x */
+ return NULL;
+ }
+ if (file_total > 1) {
+ rprintf(FERROR,
+ "ERROR: destination must be a directory when"
+ " copying more than 1 file\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (file_total == 1 && S_ISDIR(flist->files[0]->mode)) {
+ rprintf(FERROR,
+ "ERROR: cannot overwrite non-directory"
+ " with a directory\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ } else if (errno != ENOENT) {
+ /* If we don't know what's at the destination, fail. */
+ rsyserr(FERROR, errno, "ERROR: cannot stat destination %s",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ /* If we need a destination directory because the transfer is not
+ * of a single non-directory or the user has requested one via a
+ * destination path ending in a slash, create one and use mode 1. */
+ if (file_total > 1 || trailing_slash) {
+ if (trailing_slash)
+ *cp = '\0'; /* Lop off the final slash (if any). */
+
+ if (statret == 0) {
+ rprintf(FERROR, "ERROR: destination path is not a directory\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (do_mkdir(dest_path, ACCESSPERMS) != 0) {
+ mkdir_error:
+ rsyserr(FERROR, errno, "mkdir %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (flist->high >= flist->low
+ && strcmp(flist->files[flist->low]->basename, ".") == 0)
+ flist->files[0]->flags |= FLAG_DIR_CREATED;
+
+ if (INFO_GTE(NAME, 1) || stdout_format_has_i)
+ rprintf(FINFO, "created directory %s\n", dest_path);
+
+ if (dry_run) {
+ /* Indicate that dest dir doesn't really exist. */
+ dry_run++;
+ }
+
+ if (!change_dir(dest_path, dry_run > 1 ? CD_SKIP_CHDIR : CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#2 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ return NULL;
+ }
+
+ /* Otherwise, we are writing a single file, possibly on top of an
+ * existing non-directory. Change to the item's parent directory
+ * (if it has a path component), return the basename of the
+ * destination file as the local name, and use mode 2. */
+ if (!cp)
+ return dest_path;
+
+ if (cp == dest_path)
+ dest_path = "/";
+
+ *cp = '\0';
+ if (!change_dir(dest_path, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#3 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ *cp = '/';
+
+ return cp + 1;
+}
+
+/* This function checks on our alternate-basis directories. If we're in
+ * dry-run mode and the destination dir does not yet exist, we'll try to
+ * tweak any dest-relative paths to make them work for a dry-run (the
+ * destination dir must be in curr_dir[] when this function is called).
+ * We also warn about any arg that is non-existent or not a directory. */
+static void check_alt_basis_dirs(void)
+{
+ STRUCT_STAT st;
+ char *slash = strrchr(curr_dir, '/');
+ int j;
+
+ for (j = 0; j < basis_dir_cnt; j++) {
+ char *bdir = basis_dir[j];
+ int bd_len = strlen(bdir);
+ if (bd_len > 1 && bdir[bd_len-1] == '/')
+ bdir[--bd_len] = '\0';
+ if (dry_run > 1 && *bdir != '/') {
+ int len = curr_dir_len + 1 + bd_len + 1;
+ char *new = new_array(char, len);
+ if (slash && strncmp(bdir, "../", 3) == 0) {
+ /* We want to remove only one leading "../" prefix for
+ * the directory we couldn't create in dry-run mode:
+ * this ensures that any other ".." references get
+ * evaluated the same as they would for a live copy. */
+ *slash = '\0';
+ pathjoin(new, len, curr_dir, bdir + 3);
+ *slash = '/';
+ } else
+ pathjoin(new, len, curr_dir, bdir);
+ basis_dir[j] = bdir = new;
+ }
+ if (do_stat(bdir, &st) < 0)
+ rprintf(FWARNING, "%s arg does not exist: %s\n", alt_dest_opt(0), bdir);
+ else if (!S_ISDIR(st.st_mode))
+ rprintf(FWARNING, "%s arg is not a dir: %s\n", alt_dest_opt(0), bdir);
+ }
+}
+
+/* This is only called by the sender. */
+static void read_final_goodbye(int f_in, int f_out)
+{
+ int i, iflags, xlen;
+ uchar fnamecmp_type;
+ char xname[MAXPATHLEN];
+
+ shutting_down = True;
+
+ if (protocol_version < 29)
+ i = read_int(f_in);
+ else {
+ i = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type, xname, &xlen);
+ if (protocol_version >= 31 && i == NDX_DONE) {
+ if (am_sender)
+ write_ndx(f_out, NDX_DONE);
+ else {
+ if (batch_gen_fd >= 0) {
+ while (read_int(batch_gen_fd) != NDX_DEL_STATS) {}
+ read_del_stats(batch_gen_fd);
+ }
+ write_int(f_out, NDX_DONE);
+ }
+ i = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type, xname, &xlen);
+ }
+ }
+
+ if (i != NDX_DONE) {
+ rprintf(FERROR, "Invalid packet at end of run (%d) [%s]\n",
+ i, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+}
+
+static void do_server_sender(int f_in, int f_out, int argc, char *argv[])
+{
+ struct file_list *flist;
+ char *dir;
+
+ if (DEBUG_GTE(SEND, 1))
+ rprintf(FINFO, "server_sender starting pid=%d\n", (int)getpid());
+
+ if (am_daemon && lp_write_only(module_id)) {
+ rprintf(FERROR, "ERROR: module is write only\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (am_daemon && read_only && remove_source_files) {
+ rprintf(FERROR,
+ "ERROR: --remove-%s-files cannot be used with a read-only module\n",
+ remove_source_files == 1 ? "source" : "sent");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (argc < 1) {
+ rprintf(FERROR, "ERROR: do_server_sender called without args\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ become_copy_as_user();
+
+ dir = argv[0];
+ if (!relative_paths) {
+ if (!change_dir(dir, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#3 %s failed",
+ full_fname(dir));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ }
+ argc--;
+ argv++;
+
+ if (argc == 0 && (recurse || xfer_dirs || list_only)) {
+ argc = 1;
+ argv--;
+ argv[0] = ".";
+ }
+
+ flist = send_file_list(f_out,argc,argv);
+ if (!flist || flist->used == 0) {
+ /* Make sure input buffering is off so we can't hang in noop_io_until_death(). */
+ io_end_buffering_in(0);
+ /* TODO: we should really exit in a more controlled manner. */
+ exit_cleanup(0);
+ }
+
+ io_start_buffering_in(f_in);
+
+ send_files(f_in, f_out);
+ io_flush(FULL_FLUSH);
+ handle_stats(f_out);
+ if (protocol_version >= 24)
+ read_final_goodbye(f_in, f_out);
+ io_flush(FULL_FLUSH);
+ exit_cleanup(0);
+}
+
+
+static int do_recv(int f_in, int f_out, char *local_name)
+{
+ int pid;
+ int exit_code = 0;
+ int error_pipe[2];
+
+ /* The receiving side mustn't obey this, or an existing symlink that
+ * points to an identical file won't be replaced by the referent. */
+ copy_links = copy_dirlinks = copy_unsafe_links = 0;
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && !inc_recurse)
+ match_hard_links(first_flist);
+#endif
+
+ if (fd_pair(error_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe failed in do_recv");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (backup_dir) {
+ STRUCT_STAT st;
+ int ret;
+ if (backup_dir_len > 1)
+ backup_dir_buf[backup_dir_len-1] = '\0';
+ ret = do_stat(backup_dir_buf, &st);
+ if (ret != 0 || !S_ISDIR(st.st_mode)) {
+ if (ret == 0) {
+ rprintf(FERROR, "The backup-dir is not a directory: %s\n", backup_dir_buf);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (errno != ENOENT) {
+ rprintf(FERROR, "Failed to stat %s: %s\n", backup_dir_buf, strerror(errno));
+ exit_cleanup(RERR_FILEIO);
+ }
+ if (INFO_GTE(BACKUP, 1))
+ rprintf(FINFO, "(new) backup_dir is %s\n", backup_dir_buf);
+ } else if (INFO_GTE(BACKUP, 1))
+ rprintf(FINFO, "backup_dir is %s\n", backup_dir_buf);
+ if (backup_dir_len > 1)
+ backup_dir_buf[backup_dir_len-1] = '/';
+ }
+
+ if (tmpdir) {
+ STRUCT_STAT st;
+ int ret = do_stat(tmpdir, &st);
+ if (ret < 0 || !S_ISDIR(st.st_mode)) {
+ if (ret == 0) {
+ rprintf(FERROR, "The temp-dir is not a directory: %s\n", tmpdir);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (errno == ENOENT) {
+ rprintf(FERROR, "The temp-dir does not exist: %s\n", tmpdir);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ rprintf(FERROR, "Failed to stat temp-dir %s: %s\n", tmpdir, strerror(errno));
+ exit_cleanup(RERR_FILEIO);
+ }
+ }
+
+ io_flush(FULL_FLUSH);
+
+ if ((pid = do_fork()) == -1) {
+ rsyserr(FERROR, errno, "fork failed in do_recv");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (pid == 0) {
+ am_receiver = 1;
+ send_msgs_to_gen = am_server;
+
+ close(error_pipe[0]);
+
+ /* We can't let two processes write to the socket at one time. */
+ io_end_multiplex_out(MPLX_SWITCHING);
+ if (f_in != f_out)
+ close(f_out);
+ sock_f_out = -1;
+ f_out = error_pipe[1];
+
+ bwlimit_writemax = 0; /* receiver doesn't need to do this */
+
+ if (read_batch)
+ io_start_buffering_in(f_in);
+ io_start_multiplex_out(f_out);
+
+ recv_files(f_in, f_out, local_name);
+ io_flush(FULL_FLUSH);
+ handle_stats(f_in);
+
+ if (output_needs_newline) {
+ fputc('\n', stdout);
+ output_needs_newline = 0;
+ }
+
+ write_int(f_out, NDX_DONE);
+ send_msg(MSG_STATS, (char*)&stats.total_read, sizeof stats.total_read, 0);
+ io_flush(FULL_FLUSH);
+
+ /* Handle any keep-alive packets from the post-processing work
+ * that the generator does. */
+ if (protocol_version >= 29) {
+ kluge_around_eof = -1;
+
+ /* This should only get stopped via a USR2 signal. */
+ read_final_goodbye(f_in, f_out);
+
+ rprintf(FERROR, "Invalid packet at end of run [%s]\n",
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ /* Finally, we go to sleep until our parent kills us with a
+ * USR2 signal. We sleep for a short time, as on some OSes
+ * a signal won't interrupt a sleep! */
+ while (1)
+ msleep(20);
+ }
+
+ am_generator = 1;
+ implied_filter_list.head = implied_filter_list.tail = NULL;
+ flist_receiving_enabled = True;
+
+ io_end_multiplex_in(MPLX_SWITCHING);
+ if (write_batch && !am_server)
+ stop_write_batch();
+
+ close(error_pipe[1]);
+ if (f_in != f_out)
+ close(f_in);
+ sock_f_in = -1;
+ f_in = error_pipe[0];
+
+ io_start_buffering_out(f_out);
+ io_start_multiplex_in(f_in);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && inc_recurse) {
+ struct file_list *flist;
+ for (flist = first_flist; flist; flist = flist->next)
+ match_hard_links(flist);
+ }
+#endif
+
+ generate_files(f_out, local_name);
+
+ handle_stats(-1);
+ io_flush(FULL_FLUSH);
+ shutting_down = True;
+ if (protocol_version >= 24) {
+ /* send a final goodbye message */
+ write_ndx(f_out, NDX_DONE);
+ }
+ io_flush(FULL_FLUSH);
+
+ kill(pid, SIGUSR2);
+ wait_process_with_flush(pid, &exit_code);
+ return exit_code;
+}
+
+static void do_server_recv(int f_in, int f_out, int argc, char *argv[])
+{
+ int exit_code;
+ struct file_list *flist;
+ char *local_name = NULL;
+ int negated_levels;
+
+ if (filesfrom_fd >= 0 && msgs2stderr != 1 && protocol_version < 31) {
+ /* We can't mix messages with files-from data on the socket,
+ * so temporarily turn off info/debug messages. */
+ negate_output_levels();
+ negated_levels = 1;
+ } else
+ negated_levels = 0;
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "server_recv(%d) starting pid=%d\n", argc, (int)getpid());
+
+ if (am_daemon && read_only) {
+ rprintf(FERROR,"ERROR: module is read only\n");
+ exit_cleanup(RERR_SYNTAX);
+ return;
+ }
+
+ become_copy_as_user();
+
+ if (argc > 0) {
+ char *dir = argv[0];
+ argc--;
+ argv++;
+ if (!am_daemon && !change_dir(dir, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#4 %s failed",
+ full_fname(dir));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ }
+
+ if (protocol_version >= 30)
+ io_start_multiplex_in(f_in);
+ else
+ io_start_buffering_in(f_in);
+ recv_filter_list(f_in);
+
+ if (filesfrom_fd >= 0) {
+ /* We need to send the files-from names to the sender at the
+ * same time that we receive the file-list from them, so we
+ * need the IO routines to automatically write out the names
+ * onto our f_out socket as we read the file-list. This
+ * avoids both deadlock and extra delays/buffers. */
+ start_filesfrom_forwarding(filesfrom_fd);
+ filesfrom_fd = -1;
+ }
+
+ flist = recv_file_list(f_in, -1);
+ if (!flist) {
+ rprintf(FERROR,"server_recv: recv_file_list error\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (inc_recurse && file_total == 1)
+ recv_additional_file_list(f_in);
+
+ if (negated_levels)
+ negate_output_levels();
+
+ if (argc > 0)
+ local_name = get_local_name(flist,argv[0]);
+
+ /* Now that we know what our destination directory turned out to be,
+ * we can sanitize the --link-/copy-/compare-dest args correctly. */
+ if (sanitize_paths) {
+ char **dir_p;
+ for (dir_p = basis_dir; *dir_p; dir_p++)
+ *dir_p = sanitize_path(NULL, *dir_p, NULL, curr_dir_depth, SP_DEFAULT);
+ if (partial_dir)
+ partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth, SP_DEFAULT);
+ }
+ check_alt_basis_dirs();
+
+ if (daemon_filter_list.head) {
+ char **dir_p;
+ filter_rule_list *elp = &daemon_filter_list;
+
+ for (dir_p = basis_dir; *dir_p; dir_p++) {
+ char *dir = *dir_p;
+ if (*dir == '/')
+ dir += module_dirlen;
+ if (check_filter(elp, FLOG, dir, 1) < 0)
+ goto options_rejected;
+ }
+ if (partial_dir && *partial_dir == '/'
+ && check_filter(elp, FLOG, partial_dir + module_dirlen, 1) < 0) {
+ options_rejected:
+ rprintf(FERROR, "Your options have been rejected by the server.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+
+ exit_code = do_recv(f_in, f_out, local_name);
+ exit_cleanup(exit_code);
+}
+
+
+int child_main(int argc, char *argv[])
+{
+ start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
+ return 0;
+}
+
+
+void start_server(int f_in, int f_out, int argc, char *argv[])
+{
+ set_nonblocking(f_in);
+ set_nonblocking(f_out);
+
+ io_set_sock_fds(f_in, f_out);
+ setup_protocol(f_out, f_in);
+
+ if (protocol_version >= 23)
+ io_start_multiplex_out(f_out);
+ if (am_daemon && io_timeout && protocol_version >= 31)
+ send_msg_int(MSG_IO_TIMEOUT, io_timeout);
+
+ if (am_sender) {
+ keep_dirlinks = 0; /* Must be disabled on the sender. */
+ if (need_messages_from_generator)
+ io_start_multiplex_in(f_in);
+ else
+ io_start_buffering_in(f_in);
+ recv_filter_list(f_in);
+ do_server_sender(f_in, f_out, argc, argv);
+ } else
+ do_server_recv(f_in, f_out, argc, argv);
+ exit_cleanup(0);
+}
+
+/* This is called once the connection has been negotiated. It is used
+ * for rsyncd, remote-shell, and local connections. */
+int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
+{
+ struct file_list *flist = NULL;
+ int exit_code = 0, exit_code2 = 0;
+ char *local_name = NULL;
+
+ cleanup_child_pid = pid;
+ if (!read_batch) {
+ set_nonblocking(f_in);
+ set_nonblocking(f_out);
+ }
+
+ io_set_sock_fds(f_in, f_out);
+ setup_protocol(f_out,f_in);
+
+ /* We set our stderr file handle to blocking because ssh might have
+ * set it to non-blocking. This can be particularly troublesome if
+ * stderr is a clone of stdout, because ssh would have set our stdout
+ * to non-blocking at the same time (which can easily cause us to lose
+ * output from our print statements). This kluge shouldn't cause ssh
+ * any problems for how we use it. Note also that we delayed setting
+ * this until after the above protocol setup so that we know for sure
+ * that ssh is done twiddling its file descriptors. */
+ set_blocking(STDERR_FILENO);
+
+ if (am_sender) {
+ keep_dirlinks = 0; /* Must be disabled on the sender. */
+
+ if (always_checksum
+ && (log_format_has(stdout_format, 'C')
+ || log_format_has(logfile_format, 'C')))
+ sender_keeps_checksum = 1;
+
+ if (protocol_version >= 30)
+ io_start_multiplex_out(f_out);
+ else
+ io_start_buffering_out(f_out);
+ if (protocol_version >= 31 || (!filesfrom_host && protocol_version >= 23))
+ io_start_multiplex_in(f_in);
+ else
+ io_start_buffering_in(f_in);
+ send_filter_list(f_out);
+ if (filesfrom_host)
+ filesfrom_fd = f_in;
+
+ if (write_batch && !am_server)
+ start_write_batch(f_out);
+
+ become_copy_as_user();
+
+ flist = send_file_list(f_out, argc, argv);
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO,"file list sent\n");
+
+ if (protocol_version < 31 && filesfrom_host && protocol_version >= 23)
+ io_start_multiplex_in(f_in);
+
+ io_flush(NORMAL_FLUSH);
+ send_files(f_in, f_out);
+ io_flush(FULL_FLUSH);
+ handle_stats(-1);
+ if (protocol_version >= 24)
+ read_final_goodbye(f_in, f_out);
+ if (pid != -1) {
+ if (DEBUG_GTE(EXIT, 2))
+ rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
+ io_flush(FULL_FLUSH);
+ wait_process_with_flush(pid, &exit_code);
+ }
+ output_summary();
+ io_flush(FULL_FLUSH);
+ exit_cleanup(exit_code);
+ }
+
+ if (!read_batch) {
+ if (protocol_version >= 23)
+ io_start_multiplex_in(f_in);
+ if (need_messages_from_generator)
+ io_start_multiplex_out(f_out);
+ else
+ io_start_buffering_out(f_out);
+ }
+
+ become_copy_as_user();
+
+ send_filter_list(read_batch ? -1 : f_out);
+
+ if (filesfrom_fd >= 0) {
+ start_filesfrom_forwarding(filesfrom_fd);
+ filesfrom_fd = -1;
+ }
+
+ if (write_batch && !am_server)
+ start_write_batch(f_in);
+ flist = recv_file_list(f_in, -1);
+ if (inc_recurse && file_total == 1)
+ recv_additional_file_list(f_in);
+
+ if (flist && flist->used > 0) {
+ local_name = get_local_name(flist, argv[0]);
+
+ check_alt_basis_dirs();
+
+ exit_code2 = do_recv(f_in, f_out, local_name);
+ } else {
+ handle_stats(-1);
+ output_summary();
+ }
+
+ if (pid != -1) {
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO,"client_run2 waiting on %d\n", (int) pid);
+ io_flush(FULL_FLUSH);
+ wait_process_with_flush(pid, &exit_code);
+ }
+
+ return MAX(exit_code, exit_code2);
+}
+
+static void dup_argv(char *argv[])
+{
+ int i;
+
+ for (i = 0; argv[i]; i++)
+ argv[i] = strdup(argv[i]);
+}
+
+
+/* Start a client for either type of remote connection. Work out
+ * whether the arguments request a remote shell or rsyncd connection,
+ * and call the appropriate connection function, then run_client.
+ *
+ * Calls either start_socket_client (for sockets) or do_cmd and
+ * client_run (for ssh). */
+static int start_client(int argc, char *argv[])
+{
+ char *p, *shell_machine = NULL, *shell_user = NULL;
+ char **remote_argv;
+ int remote_argc, env_port = rsync_port;
+ int f_in, f_out;
+ int ret;
+ pid_t pid;
+
+ /* Don't clobber argv[] so that ps(1) can still show the right
+ * command line. */
+ dup_argv(argv);
+
+ if (!read_batch) { /* for read_batch, NO source is specified */
+ char *path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
+ if (path) { /* source is remote */
+ char *dummy_host;
+ int dummy_port = 0;
+ *argv = path;
+ remote_argv = argv;
+ remote_argc = argc;
+ argv += argc - 1;
+ if (argc == 1 || **argv == ':')
+ argc = 0; /* no dest arg */
+ else if (check_for_hostspec(*argv, &dummy_host, &dummy_port)) {
+ rprintf(FERROR,
+ "The source and destination cannot both be remote.\n");
+ exit_cleanup(RERR_SYNTAX);
+ } else {
+ remote_argc--; /* don't count dest */
+ argc = 1;
+ }
+ if (filesfrom_host && *filesfrom_host && strcmp(filesfrom_host, shell_machine) != 0) {
+ rprintf(FERROR,
+ "--files-from hostname is not the same as the transfer hostname\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ am_sender = 0;
+ if (rsync_port)
+ daemon_connection = shell_cmd ? 1 : -1;
+ } else { /* source is local, check dest arg */
+ am_sender = 1;
+
+ if (argc > 1) {
+ p = argv[--argc];
+ if (!*p)
+ p = dot_dir_or_error();
+ remote_argv = argv + argc;
+ } else {
+ static char *dotarg[1] = { "." };
+ p = dotarg[0];
+ remote_argv = dotarg;
+ }
+ remote_argc = 1;
+
+ path = check_for_hostspec(p, &shell_machine, &rsync_port);
+ if (path && filesfrom_host && *filesfrom_host && strcmp(filesfrom_host, shell_machine) != 0) {
+ rprintf(FERROR,
+ "--files-from hostname is not the same as the transfer hostname\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (!path) { /* no hostspec found, so src & dest are local */
+ local_server = 1;
+ if (filesfrom_host) {
+ rprintf(FERROR,
+ "--files-from cannot be remote when the transfer is local\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ shell_machine = NULL;
+ rsync_port = 0;
+ } else { /* hostspec was found, so dest is remote */
+ argv[argc] = path;
+ if (rsync_port)
+ daemon_connection = shell_cmd ? 1 : -1;
+ }
+ }
+ } else { /* read_batch */
+ local_server = 1;
+ if (check_for_hostspec(argv[argc-1], &shell_machine, &rsync_port)) {
+ rprintf(FERROR, "remote destination is not allowed with --read-batch\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ remote_argv = argv += argc - 1;
+ remote_argc = argc = 1;
+ rsync_port = 0;
+ }
+
+ /* A local transfer doesn't unbackslash anything, so leave the args alone. */
+ if (local_server)
+ old_style_args = 2;
+
+ if (!rsync_port && remote_argc && !**remote_argv) /* Turn an empty arg into a dot dir. */
+ *remote_argv = ".";
+
+ if (am_sender) {
+ char *dummy_host;
+ int dummy_port = rsync_port;
+ int i;
+ if (!argv[0][0])
+ goto invalid_empty;
+ /* For local source, extra source args must not have hostspec. */
+ for (i = 1; i < argc; i++) {
+ if (!argv[i][0]) {
+ invalid_empty:
+ rprintf(FERROR, "Empty source arg specified.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (check_for_hostspec(argv[i], &dummy_host, &dummy_port)) {
+ rprintf(FERROR, "Unexpected remote arg: %s\n", argv[i]);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+ } else {
+ char *dummy_host;
+ int dummy_port = rsync_port;
+ int i;
+ if (filesfrom_fd < 0)
+ add_implied_include(remote_argv[0], daemon_connection);
+ /* For remote source, any extra source args must have either
+ * the same hostname or an empty hostname. */
+ for (i = 1; i < remote_argc; i++) {
+ char *arg = check_for_hostspec(remote_argv[i], &dummy_host, &dummy_port);
+ if (!arg) {
+ rprintf(FERROR, "Unexpected local arg: %s\n", remote_argv[i]);
+ rprintf(FERROR, "If arg is a remote file/dir, prefix it with a colon (:).\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (*dummy_host && strcmp(dummy_host, shell_machine) != 0) {
+ rprintf(FERROR, "All source args must come from the same machine.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (rsync_port != dummy_port) {
+ if (!rsync_port || !dummy_port)
+ rprintf(FERROR, "All source args must use the same hostspec format.\n");
+ else
+ rprintf(FERROR, "All source args must use the same port number.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (!rsync_port && !*arg) /* Turn an empty arg into a dot dir. */
+ arg = ".";
+ remote_argv[i] = arg;
+ add_implied_include(arg, daemon_connection);
+ }
+ }
+
+ if (rsync_port < 0)
+ rsync_port = RSYNC_PORT;
+ else
+ env_port = rsync_port;
+
+ if (daemon_connection < 0)
+ return start_socket_client(shell_machine, remote_argc, remote_argv, argc, argv);
+
+ if (password_file && !daemon_connection) {
+ rprintf(FERROR, "The --password-file option may only be "
+ "used when accessing an rsync daemon.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (connect_timeout) {
+ rprintf(FERROR, "The --contimeout option may only be "
+ "used when connecting to an rsync daemon.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (shell_machine) {
+ p = strrchr(shell_machine,'@');
+ if (p) {
+ *p = 0;
+ shell_user = shell_machine;
+ shell_machine = p+1;
+ }
+ }
+
+ if (DEBUG_GTE(CMD, 2)) {
+ rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
+ NS(shell_cmd), NS(shell_machine), NS(shell_user),
+ NS(remote_argv[0]));
+ }
+
+#ifdef HAVE_PUTENV
+ if (daemon_connection)
+ set_env_num("RSYNC_PORT", env_port);
+#else
+ (void)env_port;
+#endif
+
+ pid = do_cmd(shell_cmd, shell_machine, shell_user, remote_argv, remote_argc, &f_in, &f_out);
+
+ /* if we're running an rsync server on the remote host over a
+ * remote shell command, we need to do the RSYNCD protocol first */
+ if (daemon_connection) {
+ int tmpret;
+ tmpret = start_inband_exchange(f_in, f_out, shell_user, remote_argc, remote_argv);
+ if (tmpret < 0)
+ return tmpret;
+ }
+
+ ret = client_run(f_in, f_out, pid, argc, argv);
+
+ fflush(stdout);
+ fflush(stderr);
+
+ return ret;
+}
+
+
+static void sigusr1_handler(UNUSED(int val))
+{
+ called_from_signal_handler = 1;
+ exit_cleanup(RERR_SIGNAL1);
+}
+
+static void sigusr2_handler(UNUSED(int val))
+{
+ if (!am_server)
+ output_summary();
+ close_all();
+ if (got_xfer_error)
+ _exit(RERR_PARTIAL);
+ _exit(0);
+}
+
+#if defined SIGINFO || defined SIGVTALRM
+static void siginfo_handler(UNUSED(int val))
+{
+ if (!am_server && !INFO_GTE(PROGRESS, 1))
+ want_progress_now = True;
+}
+#endif
+
+void remember_children(UNUSED(int val))
+{
+#ifdef WNOHANG
+ int cnt, status;
+ pid_t pid;
+ /* An empty waitpid() loop was put here by Tridge and we could never
+ * get him to explain why he put it in, so rather than taking it
+ * out we're instead saving the child exit statuses for later use.
+ * The waitpid() loop presumably eliminates all possibility of leaving
+ * zombie children, maybe that's why he did it. */
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ /* save the child's exit status */
+ for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) {
+ if (pid_stat_table[cnt].pid == 0) {
+ pid_stat_table[cnt].pid = pid;
+ pid_stat_table[cnt].status = status;
+ break;
+ }
+ }
+ }
+#endif
+#ifndef HAVE_SIGACTION
+ signal(SIGCHLD, remember_children);
+#endif
+}
+
+/**
+ * This routine catches signals and tries to send them to gdb.
+ *
+ * Because it's called from inside a signal handler it ought not to
+ * use too many library routines.
+ *
+ * @todo Perhaps use "screen -X" instead/as well, to help people
+ * debugging without easy access to X. Perhaps use an environment
+ * variable, or just call a script?
+ *
+ * @todo The /proc/ magic probably only works on Linux (and
+ * Solaris?) Can we be more portable?
+ **/
+#ifdef MAINTAINER_MODE
+const char *get_panic_action(void)
+{
+ const char *cmd_fmt = getenv("RSYNC_PANIC_ACTION");
+
+ if (cmd_fmt)
+ return cmd_fmt;
+ return "xterm -display :0 -T Panic -n Panic -e gdb /proc/%d/exe %d";
+}
+
+/**
+ * Handle a fatal signal by launching a debugger, controlled by $RSYNC_PANIC_ACTION.
+ *
+ * This signal handler is only installed if we were configured with
+ * --enable-maintainer-mode. Perhaps it should always be on and we
+ * should just look at the environment variable, but I'm a bit leery
+ * of a signal sending us into a busy loop.
+ **/
+static void rsync_panic_handler(UNUSED(int whatsig))
+{
+ char cmd_buf[300];
+ int ret, pid_int = getpid();
+
+ snprintf(cmd_buf, sizeof cmd_buf, get_panic_action(), pid_int, pid_int);
+
+ /* Unless we failed to execute gdb, we allow the process to
+ * continue. I'm not sure if that's right. */
+ ret = shell_exec(cmd_buf);
+ if (ret)
+ _exit(ret);
+}
+#endif
+
+static void unset_env_var(const char *var)
+{
+#ifdef HAVE_UNSETENV
+ unsetenv(var);
+#else
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s=", var) < 0)
+ out_of_memory("unset_env_var");
+ putenv(mem);
+#else
+ (void)var;
+#endif
+#endif
+}
+
+
+int main(int argc,char *argv[])
+{
+ int ret;
+
+ raw_argc = argc;
+ raw_argv = argv;
+
+#ifdef HAVE_SIGACTION
+# ifdef HAVE_SIGPROCMASK
+ sigset_t sigmask;
+
+ sigemptyset(&sigmask);
+# endif
+ sigact.sa_flags = SA_NOCLDSTOP;
+#endif
+ SIGACTMASK(SIGUSR1, sigusr1_handler);
+ SIGACTMASK(SIGUSR2, sigusr2_handler);
+ SIGACTMASK(SIGCHLD, remember_children);
+#ifdef MAINTAINER_MODE
+ SIGACTMASK(SIGSEGV, rsync_panic_handler);
+ SIGACTMASK(SIGFPE, rsync_panic_handler);
+ SIGACTMASK(SIGABRT, rsync_panic_handler);
+ SIGACTMASK(SIGBUS, rsync_panic_handler);
+#endif
+#ifdef SIGINFO
+ SIGACTMASK(SIGINFO, siginfo_handler);
+#endif
+#ifdef SIGVTALRM
+ SIGACTMASK(SIGVTALRM, siginfo_handler);
+#endif
+
+ starttime = time(NULL);
+ our_uid = MY_UID();
+ our_gid = MY_GID();
+ am_root = our_uid == ROOT_UID;
+
+ unset_env_var("DISPLAY");
+
+#if defined USE_OPENSSL && defined SET_OPENSSL_CONF
+#define TO_STR2(x) #x
+#define TO_STR(x) TO_STR2(x)
+ /* ./configure --with-openssl-conf=/etc/ssl/openssl-rsync.cnf
+ * defines SET_OPENSSL_CONF as that unquoted pathname. */
+ if (!getenv("OPENSSL_CONF")) /* Don't override it if it's already set. */
+ set_env_str("OPENSSL_CONF", TO_STR(SET_OPENSSL_CONF));
+#undef TO_STR
+#undef TO_STR2
+#endif
+
+ memset(&stats, 0, sizeof(stats));
+
+ /* Even a non-daemon runs needs the default config values to be set, e.g.
+ * lp_dont_compress() is queried when no --skip-compress option is set. */
+ reset_daemon_vars();
+
+ if (argc < 2) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ /* Get the umask for use in permission calculations. We no longer set
+ * it to zero; that is ugly and pointless now that all the callers that
+ * relied on it have been reeducated to work with default ACLs. */
+ umask(orig_umask = umask(0));
+
+#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_NUMERIC, "");
+#endif
+
+ if (!parse_arguments(&argc, (const char ***) &argv)) {
+ option_error();
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (write_batch
+ && poptDupArgv(argc, (const char **)argv, &cooked_argc, (const char ***)&cooked_argv) != 0)
+ out_of_memory("main");
+
+ SIGACTMASK(SIGINT, sig_int);
+ SIGACTMASK(SIGHUP, sig_int);
+ SIGACTMASK(SIGTERM, sig_int);
+#if defined HAVE_SIGACTION && HAVE_SIGPROCMASK
+ sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
+#endif
+
+ /* Ignore SIGPIPE; we consistently check error codes and will
+ * see the EPIPE. */
+ SIGACTION(SIGPIPE, SIG_IGN);
+#ifdef SIGXFSZ
+ SIGACTION(SIGXFSZ, SIG_IGN);
+#endif
+
+ /* Initialize change_dir() here because on some old systems getcwd
+ * (implemented by forking "pwd" and reading its output) doesn't
+ * work when there are other child processes. Also, on all systems
+ * that implement getcwd that way "pwd" can't be found after chroot. */
+ change_dir(NULL, CD_NORMAL);
+
+ if ((write_batch || read_batch) && !am_server) {
+ open_batch_files(); /* sets batch_fd */
+ if (read_batch)
+ read_stream_flags(batch_fd);
+ else
+ write_stream_flags(batch_fd);
+ }
+ if (write_batch < 0)
+ dry_run = 1;
+
+ if (am_server) {
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else if (am_daemon)
+ return daemon_main();
+
+ if (am_server && protect_args) {
+ char buf[MAXPATHLEN];
+ protect_args = 2;
+ read_args(STDIN_FILENO, NULL, buf, sizeof buf, 1, &argv, &argc, NULL);
+ if (!parse_arguments(&argc, (const char ***) &argv)) {
+ option_error();
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+
+ if (argc < 1) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (am_server) {
+ set_nonblocking(STDIN_FILENO);
+ set_nonblocking(STDOUT_FILENO);
+ if (am_daemon)
+ return start_daemon(STDIN_FILENO, STDOUT_FILENO);
+ start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
+ }
+
+ ret = start_client(argc, argv);
+ if (ret == -1)
+ exit_cleanup(RERR_STARTCLIENT);
+ else
+ exit_cleanup(ret);
+
+ return ret;
+}
diff --git a/match.c b/match.c
new file mode 100644
index 0000000..6243994
--- /dev/null
+++ b/match.c
@@ -0,0 +1,445 @@
+/*
+ * Block matching used by the file-transfer code.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+extern int checksum_seed;
+extern int append_mode;
+
+extern struct name_num_item *xfer_sum_nni;
+extern int xfer_sum_len;
+
+int updating_basis_file;
+char sender_file_sum[MAX_DIGEST_LEN];
+
+static int false_alarms;
+static int hash_hits;
+static int matches;
+static int64 data_transfer;
+
+static int total_false_alarms;
+static int total_hash_hits;
+static int total_matches;
+
+extern struct stats stats;
+
+#define TRADITIONAL_TABLESIZE (1<<16)
+
+static uint32 tablesize;
+static int32 *hash_table;
+
+#define SUM2HASH2(s1,s2) (((s1) + (s2)) & 0xFFFF)
+#define SUM2HASH(sum) SUM2HASH2((sum)&0xFFFF,(sum)>>16)
+
+#define BIG_SUM2HASH(sum) ((sum)%tablesize)
+
+static void build_hash_table(struct sum_struct *s)
+{
+ static uint32 alloc_size;
+ int32 i;
+
+ /* Dynamically calculate the hash table size so that the hash load
+ * for big files is about 80%. A number greater than the traditional
+ * size must be odd or s2 will not be able to span the entire set. */
+ tablesize = (uint32)(s->count/8) * 10 + 11;
+ if (tablesize < TRADITIONAL_TABLESIZE)
+ tablesize = TRADITIONAL_TABLESIZE;
+ if (tablesize > alloc_size || tablesize < alloc_size - 16*1024) {
+ if (hash_table)
+ free(hash_table);
+ hash_table = new_array(int32, tablesize);
+ alloc_size = tablesize;
+ }
+
+ memset(hash_table, 0xFF, tablesize * sizeof hash_table[0]);
+
+ if (tablesize == TRADITIONAL_TABLESIZE) {
+ for (i = 0; i < s->count; i++) {
+ uint32 t = SUM2HASH(s->sums[i].sum1);
+ s->sums[i].chain = hash_table[t];
+ hash_table[t] = i;
+ }
+ } else {
+ for (i = 0; i < s->count; i++) {
+ uint32 t = BIG_SUM2HASH(s->sums[i].sum1);
+ s->sums[i].chain = hash_table[t];
+ hash_table[t] = i;
+ }
+ }
+}
+
+
+static OFF_T last_match;
+
+
+/* Transmit a literal and/or match token.
+ *
+ * This delightfully-named function is called either when we find a
+ * match and need to transmit all the unmatched data leading up to it,
+ * or when we get bored of accumulating literal data and just need to
+ * transmit it. As a result of this second case, it is called even if
+ * we have not matched at all!
+ *
+ * If i >= 0, the number of a matched token. If < 0, indicates we have
+ * only literal data. A -1 will send a 0-token-int too, and a -2 sends
+ * only literal data, w/o any token-int. */
+static void matched(int f, struct sum_struct *s, struct map_struct *buf, OFF_T offset, int32 i)
+{
+ int32 n = (int32)(offset - last_match); /* max value: block_size (int32) */
+ int32 j;
+
+ if (DEBUG_GTE(DELTASUM, 2) && i >= 0) {
+ rprintf(FINFO,
+ "match at %s last_match=%s j=%d len=%ld n=%ld\n",
+ big_num(offset), big_num(last_match), i,
+ (long)s->sums[i].len, (long)n);
+ }
+
+ send_token(f, i, buf, last_match, n, i < 0 ? 0 : s->sums[i].len);
+ data_transfer += n;
+
+ if (i >= 0) {
+ stats.matched_data += s->sums[i].len;
+ n += s->sums[i].len;
+ }
+
+ for (j = 0; j < n; j += CHUNK_SIZE) {
+ int32 n1 = MIN(CHUNK_SIZE, n - j);
+ sum_update(map_ptr(buf, last_match + j, n1), n1);
+ }
+
+ if (i >= 0)
+ last_match = offset + s->sums[i].len;
+ else
+ last_match = offset;
+
+ if (buf && INFO_GTE(PROGRESS, 1))
+ show_progress(last_match, buf->file_size);
+}
+
+
+static void hash_search(int f,struct sum_struct *s,
+ struct map_struct *buf, OFF_T len)
+{
+ OFF_T offset, aligned_offset, end;
+ int32 k, want_i, aligned_i, backup;
+ char sum2[SUM_LENGTH];
+ uint32 s1, s2, sum;
+ int more;
+ schar *map;
+
+ /* want_i is used to encourage adjacent matches, allowing the RLL
+ * coding of the output to work more efficiently. */
+ want_i = 0;
+
+ if (DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO, "hash search b=%ld len=%s\n",
+ (long)s->blength, big_num(len));
+ }
+
+ k = (int32)MIN(len, (OFF_T)s->blength);
+
+ map = (schar *)map_ptr(buf, 0, k);
+
+ sum = get_checksum1((char *)map, k);
+ s1 = sum & 0xFFFF;
+ s2 = sum >> 16;
+ if (DEBUG_GTE(DELTASUM, 3))
+ rprintf(FINFO, "sum=%.8x k=%ld\n", sum, (long)k);
+
+ offset = aligned_offset = aligned_i = 0;
+
+ end = len + 1 - s->sums[s->count-1].len;
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO, "hash search s->blength=%ld len=%s count=%s\n",
+ (long)s->blength, big_num(len), big_num(s->count));
+ }
+
+ do {
+ int done_csum2 = 0;
+ uint32 hash_entry;
+ int32 i, *prev;
+
+ if (DEBUG_GTE(DELTASUM, 4)) {
+ rprintf(FINFO, "offset=%s sum=%04x%04x\n",
+ big_num(offset), s2 & 0xFFFF, s1 & 0xFFFF);
+ }
+
+ if (tablesize == TRADITIONAL_TABLESIZE) {
+ hash_entry = SUM2HASH2(s1,s2);
+ if ((i = hash_table[hash_entry]) < 0)
+ goto null_hash;
+ sum = (s1 & 0xffff) | (s2 << 16);
+ } else {
+ sum = (s1 & 0xffff) | (s2 << 16);
+ hash_entry = BIG_SUM2HASH(sum);
+ if ((i = hash_table[hash_entry]) < 0)
+ goto null_hash;
+ }
+ prev = &hash_table[hash_entry];
+
+ hash_hits++;
+ do {
+ int32 l;
+
+ /* When updating in-place, the chunk's offset must be
+ * either >= our offset or identical data at that offset.
+ * Remove any bypassed entries that we can never use. */
+ if (updating_basis_file && s->sums[i].offset < offset
+ && !(s->sums[i].flags & SUMFLG_SAME_OFFSET)) {
+ *prev = s->sums[i].chain;
+ continue;
+ }
+ prev = &s->sums[i].chain;
+
+ if (sum != s->sums[i].sum1)
+ continue;
+
+ /* also make sure the two blocks are the same length */
+ l = (int32)MIN((OFF_T)s->blength, len-offset);
+ if (l != s->sums[i].len)
+ continue;
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,
+ "potential match at %s i=%ld sum=%08x\n",
+ big_num(offset), (long)i, sum);
+ }
+
+ if (!done_csum2) {
+ map = (schar *)map_ptr(buf,offset,l);
+ get_checksum2((char *)map,l,sum2);
+ done_csum2 = 1;
+ }
+
+ if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) {
+ false_alarms++;
+ continue;
+ }
+
+ /* When updating in-place, the best possible match is
+ * one with an identical offset, so we prefer that over
+ * the adjacent want_i optimization. */
+ if (updating_basis_file) {
+ /* All the generator's chunks start at blength boundaries. */
+ while (aligned_offset < offset) {
+ aligned_offset += s->blength;
+ aligned_i++;
+ }
+ if ((offset == aligned_offset
+ || (sum == 0 && l == s->blength && aligned_offset + l <= len))
+ && aligned_i < s->count) {
+ if (i != aligned_i) {
+ if (sum != s->sums[aligned_i].sum1
+ || l != s->sums[aligned_i].len
+ || memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0)
+ goto check_want_i;
+ i = aligned_i;
+ }
+ if (offset != aligned_offset) {
+ /* We've matched some zeros in a spot that is also zeros
+ * further along in the basis file, if we find zeros ahead
+ * in the sender's file, we'll output enough literal data
+ * to re-align with the basis file, and get back to seeking
+ * instead of writing. */
+ backup = (int32)(aligned_offset - last_match);
+ if (backup < 0)
+ backup = 0;
+ map = (schar *)map_ptr(buf, aligned_offset - backup, l + backup)
+ + backup;
+ sum = get_checksum1((char *)map, l);
+ if (sum != s->sums[i].sum1)
+ goto check_want_i;
+ get_checksum2((char *)map, l, sum2);
+ if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0)
+ goto check_want_i;
+ /* OK, we have a re-alignment match. Bump the offset
+ * forward to the new match point. */
+ offset = aligned_offset;
+ }
+ /* This identical chunk is in the same spot in the old and new file. */
+ s->sums[i].flags |= SUMFLG_SAME_OFFSET;
+ want_i = i;
+ }
+ }
+
+ check_want_i:
+ /* we've found a match, but now check to see
+ * if want_i can hint at a better match. */
+ if (i != want_i && want_i < s->count
+ && (!updating_basis_file || s->sums[want_i].offset >= offset
+ || s->sums[want_i].flags & SUMFLG_SAME_OFFSET)
+ && sum == s->sums[want_i].sum1
+ && memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) {
+ /* we've found an adjacent match - the RLL coder
+ * will be happy */
+ i = want_i;
+ }
+ want_i = i + 1;
+
+ matched(f,s,buf,offset,i);
+ offset += s->sums[i].len - 1;
+ k = (int32)MIN((OFF_T)s->blength, len-offset);
+ map = (schar *)map_ptr(buf, offset, k);
+ sum = get_checksum1((char *)map, k);
+ s1 = sum & 0xFFFF;
+ s2 = sum >> 16;
+ matches++;
+ break;
+ } while ((i = s->sums[i].chain) >= 0);
+
+ null_hash:
+ backup = (int32)(offset - last_match);
+ /* We sometimes read 1 byte prior to last_match... */
+ if (backup < 0)
+ backup = 0;
+
+ /* Trim off the first byte from the checksum */
+ more = offset + k < len;
+ map = (schar *)map_ptr(buf, offset - backup, k + more + backup) + backup;
+ s1 -= map[0] + CHAR_OFFSET;
+ s2 -= k * (map[0]+CHAR_OFFSET);
+
+ /* Add on the next byte (if there is one) to the checksum */
+ if (more) {
+ s1 += map[k] + CHAR_OFFSET;
+ s2 += s1;
+ } else
+ --k;
+
+ /* By matching early we avoid re-reading the
+ data 3 times in the case where a token
+ match comes a long way after last
+ match. The 3 reads are caused by the
+ running match, the checksum update and the
+ literal send. */
+ if (backup >= s->blength+CHUNK_SIZE && end-offset > CHUNK_SIZE)
+ matched(f, s, buf, offset - s->blength, -2);
+ } while (++offset < end);
+
+ matched(f, s, buf, len, -1);
+ map_ptr(buf, len-1, 1);
+}
+
+
+/**
+ * Scan through a origin file, looking for sections that match
+ * checksums from the generator, and transmit either literal or token
+ * data.
+ *
+ * Also calculates the MD4 checksum of the whole file, using the md
+ * accumulator. This is transmitted with the file as protection
+ * against corruption on the wire.
+ *
+ * @param s Checksums received from the generator. If <tt>s->count ==
+ * 0</tt>, then there are actually no checksums for this file.
+ *
+ * @param len Length of the file to send.
+ **/
+void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
+{
+ last_match = 0;
+ false_alarms = 0;
+ hash_hits = 0;
+ matches = 0;
+ data_transfer = 0;
+
+ sum_init(xfer_sum_nni, checksum_seed);
+
+ if (append_mode > 0) {
+ if (append_mode == 2) {
+ OFF_T j = 0;
+ for (j = CHUNK_SIZE; j < s->flength; j += CHUNK_SIZE) {
+ if (buf && INFO_GTE(PROGRESS, 1))
+ show_progress(last_match, buf->file_size);
+ sum_update(map_ptr(buf, last_match, CHUNK_SIZE),
+ CHUNK_SIZE);
+ last_match = j;
+ }
+ if (last_match < s->flength) {
+ int32 n = (int32)(s->flength - last_match);
+ if (buf && INFO_GTE(PROGRESS, 1))
+ show_progress(last_match, buf->file_size);
+ sum_update(map_ptr(buf, last_match, n), n);
+ }
+ }
+ last_match = s->flength;
+ s->count = 0;
+ }
+
+ if (len > 0 && s->count > 0) {
+ build_hash_table(s);
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"built hash table\n");
+
+ hash_search(f, s, buf, len);
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"done hash search\n");
+ } else {
+ OFF_T j;
+ /* by doing this in pieces we avoid too many seeks */
+ for (j = last_match + CHUNK_SIZE; j < len; j += CHUNK_SIZE)
+ matched(f, s, buf, j, -2);
+ matched(f, s, buf, len, -1);
+ }
+
+ sum_end(sender_file_sum);
+
+ /* If we had a read error, send a bad checksum. We use all bits
+ * off as long as the checksum doesn't happen to be that, in
+ * which case we turn the last 0 bit into a 1. */
+ if (buf && buf->status != 0) {
+ int i;
+ for (i = 0; i < xfer_sum_len && sender_file_sum[i] == 0; i++) {}
+ memset(sender_file_sum, 0, xfer_sum_len);
+ if (i == xfer_sum_len)
+ sender_file_sum[i-1]++;
+ }
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"sending file_sum\n");
+ write_buf(f, sender_file_sum, xfer_sum_len);
+
+ if (DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n",
+ false_alarms, hash_hits, matches);
+ }
+
+ total_hash_hits += hash_hits;
+ total_false_alarms += false_alarms;
+ total_matches += matches;
+ stats.literal_data += data_transfer;
+}
+
+void match_report(void)
+{
+ if (!DEBUG_GTE(DELTASUM, 1))
+ return;
+
+ rprintf(FINFO,
+ "total: matches=%d hash_hits=%d false_alarms=%d data=%s\n",
+ total_matches, total_hash_hits, total_false_alarms,
+ big_num(stats.literal_data));
+}
diff --git a/maybe-make-man b/maybe-make-man
new file mode 100755
index 0000000..0dc1730
--- /dev/null
+++ b/maybe-make-man
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+if [ $# != 1 ]; then
+ echo "Usage: $0 NAME.NUM.md" 1>&2
+ exit 1
+fi
+
+inname="$1"
+srcdir=`dirname "$0"`
+flagfile="$srcdir/.md2man-works"
+
+if [ ! -f "$flagfile" ]; then
+ # We test our smallest manpage just to see if the python setup works.
+ if "$srcdir/md-convert" --test "$srcdir/rsync-ssl.1.md" >/dev/null 2>&1; then
+ touch $flagfile
+ else
+ outname=`echo "$inname" | sed 's/\.md$//'`
+ if [ -f "$outname" ]; then
+ exit 0
+ elif [ -f "$srcdir/$outname" ]; then
+ echo "Copying $srcdir/$outname"
+ cp -p "$srcdir/$outname" .
+ exit 0
+ else
+ echo "ERROR: $outname cannot be created."
+ if [ -f "$HOME/build_farm/build_test.fns" ]; then
+ exit 0 # No exit errorno to avoid a build failure in the samba build farm
+ else
+ exit 1
+ fi
+ fi
+ fi
+fi
+
+"$srcdir/md-convert" "$srcdir/$inname"
diff --git a/md-convert b/md-convert
new file mode 100755
index 0000000..a48689a
--- /dev/null
+++ b/md-convert
@@ -0,0 +1,634 @@
+#!/usr/bin/env python3
+
+# This script transforms markdown files into html and (optionally) nroff. The
+# output files are written into the current directory named for the input file
+# without the .md suffix and either the .html suffix or no suffix.
+#
+# If the input .md file has a section number at the end of the name (e.g.,
+# rsync.1.md) a nroff file is also output (PROJ.NUM.md -> PROJ.NUM).
+#
+# The markdown input format has one extra extension: if a numbered list starts
+# at 0, it is turned into a description list. The dl's dt tag is taken from the
+# contents of the first tag inside the li, which is usually a p, code, or
+# strong tag.
+#
+# The cmarkgfm or commonmark lib is used to transforms the input file into
+# html. Then, the html.parser is used as a state machine that lets us tweak
+# the html and (optionally) output nroff data based on the html tags.
+#
+# If the string @USE_GFM_PARSER@ exists in the file, the string is removed and
+# a github-flavored-markup parser is used to parse the file.
+#
+# The man-page .md files also get the vars @VERSION@, @BINDIR@, and @LIBDIR@
+# substituted. Some of these values depend on the Makefile $(prefix) (see the
+# generated Makefile). If the maintainer wants to build files for /usr/local
+# while creating release-ready man-page files for /usr, use the environment to
+# set RSYNC_OVERRIDE_PREFIX=/usr.
+
+# Copyright (C) 2020 - 2021 Wayne Davison
+#
+# This program is freely redistributable.
+
+import os, sys, re, argparse, subprocess, time
+from html.parser import HTMLParser
+
+VALID_PAGES = 'README INSTALL COPYING rsync.1 rrsync.1 rsync-ssl.1 rsyncd.conf.5'.split()
+
+CONSUMES_TXT = set('h1 h2 h3 p li pre'.split())
+
+HTML_START = """\
+<html><head>
+<title>%TITLE%</title>
+<meta charset="UTF-8"/>
+<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
+<style>
+body {
+ max-width: 50em;
+ margin: auto;
+}
+body, b, strong, u {
+ font-family: 'Roboto', sans-serif;
+}
+a.tgt { font-face: symbol; font-weight: 400; font-size: 70%; visibility: hidden; text-decoration: none; color: #ddd; padding: 0 4px; border: 0; }
+a.tgt:after { content: '🔗'; }
+a.tgt:hover { color: #444; background-color: #eaeaea; }
+h1:hover > a.tgt, h2:hover > a.tgt, h3:hover > a.tgt, dt:hover > a.tgt { visibility: visible; }
+code {
+ font-family: 'Roboto Mono', monospace;
+ font-weight: bold;
+ white-space: pre;
+}
+pre code {
+ display: block;
+ font-weight: normal;
+}
+blockquote pre code {
+ background: #f1f1f1;
+}
+dd p:first-of-type {
+ margin-block-start: 0em;
+}
+</style>
+</head><body>
+"""
+
+TABLE_STYLE = """\
+table {
+ border-color: grey;
+ border-spacing: 0;
+}
+tr {
+ border-top: 1px solid grey;
+}
+tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+th, td {
+ border: 1px solid #dfe2e5;
+ text-align: center;
+ padding-left: 1em;
+ padding-right: 1em;
+}
+"""
+
+MAN_HTML_END = """\
+<div style="float: right"><p><i>%s</i></p></div>
+"""
+
+HTML_END = """\
+</body></html>
+"""
+
+MAN_START = r"""
+.TH "%s" "%s" "%s" "%s" "User Commands"
+.\" prefix=%s
+""".lstrip()
+
+MAN_END = """\
+"""
+
+NORM_FONT = ('\1', r"\fP")
+BOLD_FONT = ('\2', r"\fB")
+UNDR_FONT = ('\3', r"\fI")
+NBR_DASH = ('\4', r"\-")
+NBR_SPACE = ('\xa0', r"\ ")
+
+FILENAME_RE = re.compile(r'^(?P<fn>(?P<srcdir>.+/)?(?P<name>(?P<prog>[^/]+?)(\.(?P<sect>\d+))?)\.md)$')
+ASSIGNMENT_RE = re.compile(r'^(\w+)=(.+)')
+VER_RE = re.compile(r'^#define\s+RSYNC_VERSION\s+"(\d.+?)"', re.M)
+TZ_RE = re.compile(r'^#define\s+MAINTAINER_TZ_OFFSET\s+(-?\d+(\.\d+)?)', re.M)
+VAR_REF_RE = re.compile(r'\$\{(\w+)\}')
+VERSION_RE = re.compile(r' (\d[.\d]+)[, ]')
+BIN_CHARS_RE = re.compile(r'[\1-\7]+')
+SPACE_DOUBLE_DASH_RE = re.compile(r'\s--(\s)')
+NON_SPACE_SINGLE_DASH_RE = re.compile(r'(^|\W)-')
+WHITESPACE_RE = re.compile(r'\s')
+CODE_BLOCK_RE = re.compile(r'[%s]([^=%s]+)[=%s]' % (BOLD_FONT[0], NORM_FONT[0], NORM_FONT[0]))
+NBR_DASH_RE = re.compile(r'[%s]' % NBR_DASH[0])
+INVALID_TARGET_CHARS_RE = re.compile(r'[^-A-Za-z0-9._]')
+INVALID_START_CHAR_RE = re.compile(r'^([^A-Za-z0-9])')
+MANIFY_LINESTART_RE = re.compile(r"^(['.])", flags=re.M)
+
+md_parser = None
+env_subs = { }
+
+warning_count = 0
+
+def main():
+ for mdfn in args.mdfiles:
+ parse_md_file(mdfn)
+
+ if args.test:
+ print("The test was successful.")
+
+
+def parse_md_file(mdfn):
+ fi = FILENAME_RE.match(mdfn)
+ if not fi:
+ die('Failed to parse a md input file name:', mdfn)
+ fi = argparse.Namespace(**fi.groupdict())
+ fi.want_manpage = not not fi.sect
+ if fi.want_manpage:
+ fi.title = fi.prog + '(' + fi.sect + ') manpage'
+ else:
+ fi.title = fi.prog + ' for rsync'
+
+ if fi.want_manpage:
+ if not env_subs:
+ find_man_substitutions()
+ prog_ver = 'rsync ' + env_subs['VERSION']
+ if fi.prog != 'rsync':
+ prog_ver = fi.prog + ' from ' + prog_ver
+ fi.man_headings = (fi.prog, fi.sect, env_subs['date'], prog_ver, env_subs['prefix'])
+
+ with open(mdfn, 'r', encoding='utf-8') as fh:
+ txt = fh.read()
+
+ use_gfm_parser = '@USE_GFM_PARSER@' in txt
+ if use_gfm_parser:
+ txt = txt.replace('@USE_GFM_PARSER@', '')
+
+ if fi.want_manpage:
+ txt = (txt.replace('@VERSION@', env_subs['VERSION'])
+ .replace('@BINDIR@', env_subs['bindir'])
+ .replace('@LIBDIR@', env_subs['libdir']))
+
+ if use_gfm_parser:
+ if not gfm_parser:
+ die('Input file requires cmarkgfm parser:', mdfn)
+ fi.html_in = gfm_parser(txt)
+ else:
+ fi.html_in = md_parser(txt)
+ txt = None
+
+ TransformHtml(fi)
+
+ if args.test:
+ return
+
+ output_list = [ (fi.name + '.html', fi.html_out) ]
+ if fi.want_manpage:
+ output_list += [ (fi.name, fi.man_out) ]
+ for fn, txt in output_list:
+ if args.dest and args.dest != '.':
+ fn = os.path.join(args.dest, fn)
+ if os.path.lexists(fn):
+ os.unlink(fn)
+ print("Wrote:", fn)
+ with open(fn, 'w', encoding='utf-8') as fh:
+ fh.write(txt)
+
+
+def find_man_substitutions():
+ srcdir = os.path.dirname(sys.argv[0]) + '/'
+ mtime = 0
+
+ git_dir = srcdir + '.git'
+ if os.path.lexists(git_dir):
+ mtime = int(subprocess.check_output(['git', '--git-dir', git_dir, 'log', '-1', '--format=%at']))
+
+ # Allow "prefix" to be overridden via the environment:
+ env_subs['prefix'] = os.environ.get('RSYNC_OVERRIDE_PREFIX', None)
+
+ if args.test:
+ env_subs['VERSION'] = '1.0.0'
+ env_subs['bindir'] = '/usr/bin'
+ env_subs['libdir'] = '/usr/lib/rsync'
+ tz_offset = 0
+ else:
+ for fn in (srcdir + 'version.h', 'Makefile'):
+ try:
+ st = os.lstat(fn)
+ except OSError:
+ die('Failed to find', srcdir + fn)
+ if not mtime:
+ mtime = st.st_mtime
+
+ with open(srcdir + 'version.h', 'r', encoding='utf-8') as fh:
+ txt = fh.read()
+ m = VER_RE.search(txt)
+ env_subs['VERSION'] = m.group(1)
+ m = TZ_RE.search(txt) # the tzdata lib may not be installed, so we use a simple hour offset
+ tz_offset = float(m.group(1)) * 60 * 60
+
+ with open('Makefile', 'r', encoding='utf-8') as fh:
+ for line in fh:
+ m = ASSIGNMENT_RE.match(line)
+ if not m:
+ continue
+ var, val = (m.group(1), m.group(2))
+ if var == 'prefix' and env_subs[var] is not None:
+ continue
+ while VAR_REF_RE.search(val):
+ val = VAR_REF_RE.sub(lambda m: env_subs[m.group(1)], val)
+ env_subs[var] = val
+ if var == 'srcdir':
+ break
+
+ env_subs['date'] = time.strftime('%d %b %Y', time.gmtime(mtime + tz_offset)).lstrip('0')
+
+
+def html_via_commonmark(txt):
+ return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
+
+
+class TransformHtml(HTMLParser):
+ def __init__(self, fi):
+ HTMLParser.__init__(self, convert_charrefs=True)
+
+ self.fn = fi.fn
+
+ st = self.state = argparse.Namespace(
+ list_state = [ ],
+ p_macro = ".P\n",
+ at_first_tag_in_li = False,
+ at_first_tag_in_dd = False,
+ dt_from = None,
+ in_pre = False,
+ in_code = False,
+ html_out = [ HTML_START.replace('%TITLE%', fi.title) ],
+ man_out = [ ],
+ txt = '',
+ want_manpage = fi.want_manpage,
+ created_hashtags = set(),
+ derived_hashtags = set(),
+ referenced_hashtags = set(),
+ bad_hashtags = set(),
+ latest_targets = [ ],
+ opt_prefix = 'opt',
+ a_txt_start = None,
+ target_suf = '',
+ )
+
+ if st.want_manpage:
+ st.man_out.append(MAN_START % fi.man_headings)
+
+ if '</table>' in fi.html_in:
+ st.html_out[0] = st.html_out[0].replace('</style>', TABLE_STYLE + '</style>')
+
+ self.feed(fi.html_in)
+ fi.html_in = None
+
+ if st.want_manpage:
+ st.html_out.append(MAN_HTML_END % env_subs['date'])
+ st.html_out.append(HTML_END)
+ st.man_out.append(MAN_END)
+
+ fi.html_out = ''.join(st.html_out)
+ st.html_out = None
+
+ fi.man_out = ''.join(st.man_out)
+ st.man_out = None
+
+ for tgt, txt in st.derived_hashtags:
+ derived = txt2target(txt, tgt)
+ if derived not in st.created_hashtags:
+ txt = BIN_CHARS_RE.sub('', txt.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' '))
+ warn('Unknown derived hashtag link in', self.fn, 'based on:', (tgt, txt))
+
+ for bad in st.bad_hashtags:
+ if bad in st.created_hashtags:
+ warn('Missing "#" in hashtag link in', self.fn + ':', bad)
+ else:
+ warn('Unknown non-hashtag link in', self.fn + ':', bad)
+
+ for bad in st.referenced_hashtags - st.created_hashtags:
+ warn('Unknown hashtag link in', self.fn + ':', '#' + bad)
+
+ def handle_starttag(self, tag, attrs_list):
+ st = self.state
+ if args.debug:
+ self.output_debug('START', (tag, attrs_list))
+ if st.at_first_tag_in_li:
+ if st.list_state[-1] == 'dl':
+ st.dt_from = tag
+ if tag == 'p':
+ tag = 'dt'
+ else:
+ st.html_out.append('<dt>')
+ elif tag == 'p':
+ st.at_first_tag_in_dd = True # Kluge to suppress a .P at the start of an li.
+ st.at_first_tag_in_li = False
+ if tag == 'p':
+ if not st.at_first_tag_in_dd:
+ st.man_out.append(st.p_macro)
+ elif tag == 'li':
+ st.at_first_tag_in_li = True
+ lstate = st.list_state[-1]
+ if lstate == 'dl':
+ return
+ if lstate == 'o':
+ st.man_out.append(".IP o\n")
+ else:
+ st.man_out.append(".IP " + str(lstate) + ".\n")
+ st.list_state[-1] += 1
+ elif tag == 'blockquote':
+ st.man_out.append(".RS 4\n")
+ elif tag == 'pre':
+ st.in_pre = True
+ st.man_out.append(st.p_macro + ".nf\n")
+ elif tag == 'code' and not st.in_pre:
+ st.in_code = True
+ st.txt += BOLD_FONT[0]
+ elif tag == 'strong' or tag == 'b':
+ st.txt += BOLD_FONT[0]
+ elif tag == 'em' or tag == 'i':
+ if st.want_manpage:
+ tag = 'u' # Change it into underline to be more like the manpage
+ st.txt += UNDR_FONT[0]
+ elif tag == 'ol':
+ start = 1
+ for var, val in attrs_list:
+ if var == 'start':
+ start = int(val) # We only support integers.
+ break
+ if st.list_state:
+ st.man_out.append(".RS\n")
+ if start == 0:
+ tag = 'dl'
+ attrs_list = [ ]
+ st.list_state.append('dl')
+ else:
+ st.list_state.append(start)
+ st.man_out.append(st.p_macro)
+ st.p_macro = ".IP\n"
+ elif tag == 'ul':
+ st.man_out.append(st.p_macro)
+ if st.list_state:
+ st.man_out.append(".RS\n")
+ st.p_macro = ".IP\n"
+ st.list_state.append('o')
+ elif tag == 'hr':
+ st.man_out.append(".l\n")
+ st.html_out.append("<hr />")
+ return
+ elif tag == 'a':
+ st.a_href = None
+ for var, val in attrs_list:
+ if var == 'href':
+ if val.startswith(('https://', 'http://', 'mailto:', 'ftp:')):
+ pass # nothing to check
+ elif '#' in val:
+ pg, tgt = val.split('#', 1)
+ if pg and pg not in VALID_PAGES or '#' in tgt:
+ st.bad_hashtags.add(val)
+ elif tgt in ('', 'opt', 'dopt'):
+ st.a_href = val
+ elif pg == '':
+ st.referenced_hashtags.add(tgt)
+ if tgt in st.latest_targets:
+ warn('Found link to the current section in', self.fn + ':', val)
+ elif val not in VALID_PAGES:
+ st.bad_hashtags.add(val)
+ st.a_txt_start = len(st.txt)
+ st.html_out.append('<' + tag + ''.join(' ' + var + '="' + htmlify(val) + '"' for var, val in attrs_list) + '>')
+ st.at_first_tag_in_dd = False
+
+
+ def handle_endtag(self, tag):
+ st = self.state
+ if args.debug:
+ self.output_debug('END', (tag,))
+ if tag in CONSUMES_TXT or st.dt_from == tag:
+ txt = st.txt.strip()
+ st.txt = ''
+ else:
+ txt = None
+ add_to_txt = None
+ if tag == 'h1':
+ tgt = txt
+ target_suf = ''
+ if tgt.startswith('NEWS for '):
+ m = VERSION_RE.search(tgt)
+ if m:
+ tgt = m.group(1)
+ st.target_suf = '-' + tgt
+ self.add_targets(tag, tgt)
+ elif tag == 'h2':
+ st.man_out.append(st.p_macro + '.SH "' + manify(txt) + '"\n')
+ self.add_targets(tag, txt, st.target_suf)
+ st.opt_prefix = 'dopt' if txt == 'DAEMON OPTIONS' else 'opt'
+ elif tag == 'h3':
+ st.man_out.append(st.p_macro + '.SS "' + manify(txt) + '"\n')
+ self.add_targets(tag, txt, st.target_suf)
+ elif tag == 'p':
+ if st.dt_from == 'p':
+ tag = 'dt'
+ st.man_out.append('.IP "' + manify(txt) + '"\n')
+ if txt.startswith(BOLD_FONT[0]):
+ self.add_targets(tag, txt)
+ st.dt_from = None
+ elif txt != '':
+ st.man_out.append(manify(txt) + "\n")
+ elif tag == 'li':
+ if st.list_state[-1] == 'dl':
+ if st.at_first_tag_in_li:
+ die("Invalid 0. -> td translation")
+ tag = 'dd'
+ if txt != '':
+ st.man_out.append(manify(txt) + "\n")
+ st.at_first_tag_in_li = False
+ elif tag == 'blockquote':
+ st.man_out.append(".RE\n")
+ elif tag == 'pre':
+ st.in_pre = False
+ st.man_out.append(manify(txt) + "\n.fi\n")
+ elif (tag == 'code' and not st.in_pre):
+ st.in_code = False
+ add_to_txt = NORM_FONT[0]
+ elif tag == 'strong' or tag == 'b':
+ add_to_txt = NORM_FONT[0]
+ elif tag == 'em' or tag == 'i':
+ if st.want_manpage:
+ tag = 'u' # Change it into underline to be more like the manpage
+ add_to_txt = NORM_FONT[0]
+ elif tag == 'ol' or tag == 'ul':
+ if st.list_state.pop() == 'dl':
+ tag = 'dl'
+ if st.list_state:
+ st.man_out.append(".RE\n")
+ else:
+ st.p_macro = ".P\n"
+ st.at_first_tag_in_dd = False
+ elif tag == 'hr':
+ return
+ elif tag == 'a':
+ if st.a_href:
+ atxt = st.txt[st.a_txt_start:]
+ find = 'href="' + st.a_href + '"'
+ for j in range(len(st.html_out)-1, 0, -1):
+ if find in st.html_out[j]:
+ pg, tgt = st.a_href.split('#', 1)
+ derived = txt2target(atxt, tgt)
+ if pg == '':
+ if derived in st.latest_targets:
+ warn('Found link to the current section in', self.fn + ':', st.a_href)
+ st.derived_hashtags.add((tgt, atxt))
+ st.html_out[j] = st.html_out[j].replace(find, 'href="' + pg + '#' + derived + '"')
+ break
+ else:
+ die('INTERNAL ERROR: failed to find href in html data:', find)
+ st.html_out.append('</' + tag + '>')
+ if add_to_txt:
+ if txt is None:
+ st.txt += add_to_txt
+ else:
+ txt += add_to_txt
+ if st.dt_from == tag:
+ st.man_out.append('.IP "' + manify(txt) + '"\n')
+ st.html_out.append('</dt><dd>')
+ st.at_first_tag_in_dd = True
+ st.dt_from = None
+ elif tag == 'dt':
+ st.html_out.append('<dd>')
+ st.at_first_tag_in_dd = True
+
+
+ def handle_data(self, txt):
+ st = self.state
+ if '](' in txt:
+ warn('Malformed link in', self.fn + ':', txt)
+ if args.debug:
+ self.output_debug('DATA', (txt,))
+ if st.in_pre:
+ html = htmlify(txt)
+ else:
+ txt = SPACE_DOUBLE_DASH_RE.sub(NBR_SPACE[0] + r'--\1', txt).replace('--', NBR_DASH[0]*2)
+ txt = NON_SPACE_SINGLE_DASH_RE.sub(r'\1' + NBR_DASH[0], txt)
+ html = htmlify(txt)
+ if st.in_code:
+ txt = WHITESPACE_RE.sub(NBR_SPACE[0], txt)
+ html = html.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' ') # <code> is non-breaking in CSS
+ st.html_out.append(html.replace(NBR_SPACE[0], '&nbsp;').replace(NBR_DASH[0], '-&#8288;'))
+ st.txt += txt
+
+
+ def add_targets(self, tag, txt, suf=None):
+ st = self.state
+ tag = '<' + tag + '>'
+ targets = CODE_BLOCK_RE.findall(txt)
+ if not targets:
+ targets = [ txt ]
+ tag_pos = 0
+ for txt in targets:
+ txt = txt2target(txt, st.opt_prefix)
+ if not txt:
+ continue
+ if suf:
+ txt += suf
+ if txt in st.created_hashtags:
+ for j in range(2, 1000):
+ chk = txt + '-' + str(j)
+ if chk not in st.created_hashtags:
+ print('Made link target unique:', chk)
+ txt = chk
+ break
+ if tag_pos == 0:
+ tag_pos -= 1
+ while st.html_out[tag_pos] != tag:
+ tag_pos -= 1
+ st.html_out[tag_pos] = tag[:-1] + ' id="' + txt + '">'
+ st.html_out.append('<a href="#' + txt + '" class="tgt"></a>')
+ tag_pos -= 1 # take into account the append
+ else:
+ st.html_out[tag_pos] = '<span id="' + txt + '"></span>' + st.html_out[tag_pos]
+ st.created_hashtags.add(txt)
+ st.latest_targets = targets
+
+
+ def output_debug(self, event, extra):
+ import pprint
+ st = self.state
+ if args.debug < 2:
+ st = argparse.Namespace(**vars(st))
+ if len(st.html_out) > 2:
+ st.html_out = ['...'] + st.html_out[-2:]
+ if len(st.man_out) > 2:
+ st.man_out = ['...'] + st.man_out[-2:]
+ print(event, extra)
+ pprint.PrettyPrinter(indent=2).pprint(vars(st))
+
+
+def txt2target(txt, opt_prefix):
+ txt = txt.strip().rstrip(':')
+ m = CODE_BLOCK_RE.search(txt)
+ if m:
+ txt = m.group(1)
+ txt = NBR_DASH_RE.sub('-', txt)
+ txt = BIN_CHARS_RE.sub('', txt)
+ txt = INVALID_TARGET_CHARS_RE.sub('_', txt)
+ if opt_prefix and txt.startswith('-'):
+ txt = opt_prefix + txt
+ else:
+ txt = INVALID_START_CHAR_RE.sub(r't\1', txt)
+ return txt
+
+
+def manify(txt):
+ return MANIFY_LINESTART_RE.sub(r'\&\1', txt.replace('\\', '\\\\')
+ .replace(NBR_SPACE[0], NBR_SPACE[1])
+ .replace(NBR_DASH[0], NBR_DASH[1])
+ .replace(NORM_FONT[0], NORM_FONT[1])
+ .replace(BOLD_FONT[0], BOLD_FONT[1])
+ .replace(UNDR_FONT[0], UNDR_FONT[1]))
+
+
+def htmlify(txt):
+ return txt.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
+
+
+def warn(*msg):
+ print(*msg, file=sys.stderr)
+ global warning_count
+ warning_count += 1
+
+
+def die(*msg):
+ warn(*msg)
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Convert markdown into html and (optionally) nroff. Each input filename must have a .md suffix, which is changed to .html for the output filename. If the input filename ends with .num.md (e.g. foo.1.md) then a nroff file is also output with the input filename's .md suffix removed (e.g. foo.1).", add_help=False)
+ parser.add_argument('--test', action='store_true', help="Just test the parsing without outputting any files.")
+ parser.add_argument('--dest', metavar='DIR', help="Create files in DIR instead of the current directory.")
+ parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ parser.add_argument("mdfiles", metavar='FILE.md', nargs='+', help="One or more .md files to convert.")
+ args = parser.parse_args()
+
+ try:
+ import cmarkgfm
+ md_parser = cmarkgfm.markdown_to_html
+ gfm_parser = cmarkgfm.github_flavored_markdown_to_html
+ except:
+ try:
+ import commonmark
+ md_parser = html_via_commonmark
+ except:
+ die("Failed to find cmarkgfm or commonmark for python3.")
+ gfm_parser = None
+
+ main()
+ if warning_count:
+ sys.exit(1)
diff --git a/md2man b/md2man
new file mode 120000
index 0000000..5d1a8fc
--- /dev/null
+++ b/md2man
@@ -0,0 +1 @@
+md-convert \ No newline at end of file
diff --git a/mkgitver b/mkgitver
new file mode 100755
index 0000000..0102b08
--- /dev/null
+++ b/mkgitver
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+srcdir=`dirname $0`
+
+if [ ! -f git-version.h ]; then
+ touch git-version.h
+fi
+
+if test -d "$srcdir/.git" || test -f "$srcdir/.git"; then
+ gitver=`git describe --abbrev=8 2>/dev/null`
+ # NOTE: I'm avoiding "|" in sed since I'm not sure if sed -r is portable and "\|" fails on some OSes.
+ verchk=`echo "$gitver-" | sed -n '/^v3\.[0-9][0-9]*\.[0-9][0-9]*\(pre[0-9]*\)*-/p'`
+ if [ -n "$verchk" ]; then
+ echo "#define RSYNC_GITVER \"$gitver\"" >git-version.h.new
+ if ! diff git-version.h.new git-version.h >/dev/null; then
+ echo "Updating git-version.h"
+ mv git-version.h.new git-version.h
+ else
+ rm git-version.h.new
+ fi
+ fi
+fi
diff --git a/mkproto.awk b/mkproto.awk
new file mode 100644
index 0000000..bd2e927
--- /dev/null
+++ b/mkproto.awk
@@ -0,0 +1,40 @@
+#!/usr/bin/awk -f
+
+BEGIN {
+ while ((getline i < "proto.h") > 0) old_protos = old_protos ? old_protos "\n" i : i
+ close("proto.h")
+ protos = "/* This file is automatically generated with \"make proto\". DO NOT EDIT */\n"
+}
+
+inheader {
+ protos = protos "\n" ((inheader = /\)[ \t]*$/ ? 0 : 1) ? $0 : $0 ";")
+ next
+}
+
+/^FN_(LOCAL|GLOBAL)_[^(]+\([^,()]+/ {
+ local = /^FN_LOCAL/
+ gsub(/^FN_(LOC|GLOB)AL_|,.*$/, "")
+ sub(/^BOOL\(/, "BOOL ")
+ sub(/^CHAR\(/, "char ")
+ sub(/^INTEGER\(/, "int ")
+ sub(/^STRING\(/, "char *")
+ protos = protos "\n" $0 (local ? "(int module_id);" : "(void);")
+ next
+}
+
+/^static|^extern|;/||!/^[A-Za-z][A-Za-z0-9_]* / { next }
+
+/\(.*\)[ \t]*$/ {
+ protos = protos "\n" $0 ";"
+ next
+}
+
+/\(/ {
+ inheader = 1
+ protos = protos "\n" $0
+}
+
+END {
+ if (old_protos != protos) print protos > "proto.h"
+ system("touch proto.h-tstamp")
+}
diff --git a/options.c b/options.c
new file mode 100644
index 0000000..d38bbe8
--- /dev/null
+++ b/options.c
@@ -0,0 +1,3116 @@
+/*
+ * Command-line (and received via daemon-socket) option parsing.
+ *
+ * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 2000, 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2002-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "ifuncs.h"
+#include <popt.h>
+
+extern int module_id;
+extern int local_server;
+extern int sanitize_paths;
+extern int trust_sender_args;
+extern int trust_sender_filter;
+extern unsigned int module_dirlen;
+extern filter_rule_list filter_list;
+extern filter_rule_list daemon_filter_list;
+
+int make_backups = 0;
+
+/**
+ * If 1, send the whole file as literal data rather than trying to
+ * create an incremental diff.
+ *
+ * If -1, then look at whether we're local or remote and go by that.
+ *
+ * @sa disable_deltas_p()
+ **/
+int whole_file = -1;
+
+int append_mode = 0;
+int keep_dirlinks = 0;
+int copy_dirlinks = 0;
+int copy_links = 0;
+int copy_devices = 0;
+int write_devices = 0;
+int preserve_links = 0;
+int preserve_hard_links = 0;
+int preserve_acls = 0;
+int preserve_xattrs = 0;
+int preserve_perms = 0;
+int preserve_executability = 0;
+int preserve_devices = 0;
+int preserve_specials = 0;
+int preserve_uid = 0;
+int preserve_gid = 0;
+int preserve_mtimes = 0;
+int preserve_atimes = 0;
+int preserve_crtimes = 0;
+int omit_dir_times = 0;
+int omit_link_times = 0;
+int trust_sender = 0;
+int update_only = 0;
+int open_noatime = 0;
+int cvs_exclude = 0;
+int dry_run = 0;
+int do_xfers = 1;
+int do_fsync = 0;
+int ignore_times = 0;
+int delete_mode = 0;
+int delete_during = 0;
+int delete_before = 0;
+int delete_after = 0;
+int delete_excluded = 0;
+int remove_source_files = 0;
+int one_file_system = 0;
+int protocol_version = PROTOCOL_VERSION;
+int sparse_files = 0;
+int preallocate_files = 0;
+int do_compression = 0;
+int do_compression_level = CLVL_NOT_SPECIFIED;
+int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
+int am_server = 0;
+int am_sender = 0;
+int am_starting_up = 1;
+int relative_paths = -1;
+int implied_dirs = 1;
+int missing_args = 0; /* 0 = FERROR_XFER, 1 = ignore, 2 = delete */
+int numeric_ids = 0;
+int msgs2stderr = 2; /* Default: send errors to stderr for local & remote-shell transfers */
+int saw_stderr_opt = 0;
+int allow_8bit_chars = 0;
+int force_delete = 0;
+int io_timeout = 0;
+int prune_empty_dirs = 0;
+int use_qsort = 0;
+char *files_from = NULL;
+int filesfrom_fd = -1;
+char *filesfrom_host = NULL;
+int eol_nulls = 0;
+int protect_args = -1;
+int old_style_args = -1;
+int human_readable = 1;
+int recurse = 0;
+int mkpath_dest_arg = 0;
+int allow_inc_recurse = 1;
+int xfer_dirs = -1;
+int am_daemon = 0;
+int connect_timeout = 0;
+int keep_partial = 0;
+int safe_symlinks = 0;
+int copy_unsafe_links = 0;
+int munge_symlinks = 0;
+int size_only = 0;
+int daemon_bwlimit = 0;
+int bwlimit = 0;
+int fuzzy_basis = 0;
+size_t bwlimit_writemax = 0;
+int ignore_existing = 0;
+int ignore_non_existing = 0;
+int need_messages_from_generator = 0;
+int max_delete = INT_MIN;
+OFF_T max_size = -1;
+OFF_T min_size = -1;
+int ignore_errors = 0;
+int modify_window = 0;
+int blocking_io = -1;
+int checksum_seed = 0;
+int inplace = 0;
+int delay_updates = 0;
+int32 block_size = 0;
+time_t stop_at_utime = 0;
+char *skip_compress = NULL;
+char *copy_as = NULL;
+item_list dparam_list = EMPTY_ITEM_LIST;
+
+/** Network address family. **/
+int default_af_hint
+#ifdef INET6
+ = 0; /* Any protocol */
+#else
+ = AF_INET; /* Must use IPv4 */
+# ifdef AF_INET6
+# undef AF_INET6
+# endif
+# define AF_INET6 AF_INET /* make -6 option a no-op */
+#endif
+
+/** Do not go into the background when run as --daemon. Good
+ * for debugging and required for running as a service on W32,
+ * or under Unix process-monitors. **/
+int no_detach
+#if defined _WIN32 || defined __WIN32__
+ = 1;
+#else
+ = 0;
+#endif
+
+int write_batch = 0;
+int read_batch = 0;
+int backup_dir_len = 0;
+int backup_suffix_len;
+unsigned int backup_dir_remainder;
+
+char *backup_suffix = NULL;
+char *tmpdir = NULL;
+char *partial_dir = NULL;
+char *basis_dir[MAX_BASIS_DIRS+1];
+char *config_file = NULL;
+char *shell_cmd = NULL;
+char *logfile_name = NULL;
+char *logfile_format = NULL;
+char *stdout_format = NULL;
+char *password_file = NULL;
+char *early_input_file = NULL;
+char *rsync_path = RSYNC_PATH;
+char *backup_dir = NULL;
+char backup_dir_buf[MAXPATHLEN];
+char *sockopts = NULL;
+char *usermap = NULL;
+char *groupmap = NULL;
+int rsync_port = 0;
+int alt_dest_type = 0;
+int basis_dir_cnt = 0;
+
+#define DEFAULT_MAX_ALLOC (1024L * 1024 * 1024)
+size_t max_alloc = DEFAULT_MAX_ALLOC;
+char *max_alloc_arg;
+
+static int version_opt_cnt = 0;
+static int remote_option_alloc = 0;
+int remote_option_cnt = 0;
+const char **remote_options = NULL;
+const char *checksum_choice = NULL;
+const char *compress_choice = NULL;
+
+int quiet = 0;
+int output_motd = 1;
+int log_before_transfer = 0;
+int stdout_format_has_i = 0;
+int stdout_format_has_o_or_i = 0;
+int logfile_format_has_i = 0;
+int logfile_format_has_o_or_i = 0;
+int always_checksum = 0;
+int list_only = 0;
+
+#define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
+char *batch_name = NULL;
+
+int need_unsorted_flist = 0;
+char *iconv_opt =
+#ifdef ICONV_OPTION
+ ICONV_OPTION;
+#else
+ NULL;
+#endif
+
+struct chmod_mode_struct *chmod_modes = NULL;
+
+static const char *debug_verbosity[] = {
+ /*0*/ NULL,
+ /*1*/ NULL,
+ /*2*/ "BIND,CMD,CONNECT,DEL,DELTASUM,DUP,FILTER,FLIST,ICONV",
+ /*3*/ "ACL,BACKUP,CONNECT2,DELTASUM2,DEL2,EXIT,FILTER2,FLIST2,FUZZY,GENR,OWN,RECV,SEND,TIME",
+ /*4*/ "CMD2,DELTASUM3,DEL3,EXIT2,FLIST3,ICONV2,OWN2,PROTO,TIME2",
+ /*5*/ "CHDIR,DELTASUM4,FLIST4,FUZZY2,HASH,HLINK",
+};
+
+#define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1)
+
+static const char *info_verbosity[1+MAX_VERBOSITY] = {
+ /*0*/ "NONREG",
+ /*1*/ "COPY,DEL,FLIST,MISC,NAME,STATS,SYMSAFE",
+ /*2*/ "BACKUP,MISC2,MOUNT,NAME2,REMOVE,SKIP",
+};
+
+#define MAX_OUT_LEVEL 4 /* The largest N allowed for any flagN word. */
+
+short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
+
+#define DEFAULT_PRIORITY 0 /* Default/implied/--verbose set values. */
+#define HELP_PRIORITY 1 /* The help output uses this level. */
+#define USER_PRIORITY 2 /* User-specified via --info or --debug */
+#define LIMIT_PRIORITY 3 /* Overriding priority when limiting values. */
+
+#define W_CLI (1<<0) /* client side */
+#define W_SRV (1<<1) /* server side */
+#define W_SND (1<<2) /* sending side */
+#define W_REC (1<<3) /* receiving side */
+
+struct output_struct {
+ char *name; /* The name of the info/debug flag. */
+ char *help; /* The description of the info/debug flag. */
+ uchar namelen; /* The length of the name string. */
+ uchar flag; /* The flag's value, for consistency check. */
+ uchar where; /* Bits indicating where the flag is used. */
+ uchar priority; /* See *_PRIORITY defines. */
+};
+
+#define INFO_WORD(flag, where, help) { #flag, help, sizeof #flag - 1, INFO_##flag, where, 0 }
+
+static struct output_struct info_words[COUNT_INFO+1] = {
+ INFO_WORD(BACKUP, W_REC, "Mention files backed up"),
+ INFO_WORD(COPY, W_REC, "Mention files copied locally on the receiving side"),
+ INFO_WORD(DEL, W_REC, "Mention deletions on the receiving side"),
+ INFO_WORD(FLIST, W_CLI, "Mention file-list receiving/sending (levels 1-2)"),
+ INFO_WORD(MISC, W_SND|W_REC, "Mention miscellaneous information (levels 1-2)"),
+ INFO_WORD(MOUNT, W_SND|W_REC, "Mention mounts that were found or skipped"),
+ INFO_WORD(NAME, W_SND|W_REC, "Mention 1) updated file/dir names, 2) unchanged names"),
+ INFO_WORD(NONREG, W_REC, "Mention skipped non-regular files (default 1, 0 disables)"),
+ INFO_WORD(PROGRESS, W_CLI, "Mention 1) per-file progress or 2) total transfer progress"),
+ INFO_WORD(REMOVE, W_SND, "Mention files removed on the sending side"),
+ INFO_WORD(SKIP, W_REC, "Mention files skipped due to transfer overrides (levels 1-2)"),
+ INFO_WORD(STATS, W_CLI|W_SRV, "Mention statistics at end of run (levels 1-3)"),
+ INFO_WORD(SYMSAFE, W_SND|W_REC, "Mention symlinks that are unsafe"),
+ { NULL, "--info", 0, 0, 0, 0 }
+};
+
+#define DEBUG_WORD(flag, where, help) { #flag, help, sizeof #flag - 1, DEBUG_##flag, where, 0 }
+
+static struct output_struct debug_words[COUNT_DEBUG+1] = {
+ DEBUG_WORD(ACL, W_SND|W_REC, "Debug extra ACL info"),
+ DEBUG_WORD(BACKUP, W_REC, "Debug backup actions (levels 1-2)"),
+ DEBUG_WORD(BIND, W_CLI, "Debug socket bind actions"),
+ DEBUG_WORD(CHDIR, W_CLI|W_SRV, "Debug when the current directory changes"),
+ DEBUG_WORD(CONNECT, W_CLI, "Debug connection events (levels 1-2)"),
+ DEBUG_WORD(CMD, W_CLI, "Debug commands+options that are issued (levels 1-2)"),
+ DEBUG_WORD(DEL, W_REC, "Debug delete actions (levels 1-3)"),
+ DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
+ DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"),
+ DEBUG_WORD(EXIT, W_CLI|W_SRV, "Debug exit events (levels 1-3)"),
+ DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-3)"),
+ DEBUG_WORD(FLIST, W_SND|W_REC, "Debug file-list operations (levels 1-4)"),
+ DEBUG_WORD(FUZZY, W_REC, "Debug fuzzy scoring (levels 1-2)"),
+ DEBUG_WORD(GENR, W_REC, "Debug generator functions"),
+ DEBUG_WORD(HASH, W_SND|W_REC, "Debug hashtable code"),
+ DEBUG_WORD(HLINK, W_SND|W_REC, "Debug hard-link actions (levels 1-3)"),
+ DEBUG_WORD(ICONV, W_CLI|W_SRV, "Debug iconv character conversions (levels 1-2)"),
+ DEBUG_WORD(IO, W_CLI|W_SRV, "Debug I/O routines (levels 1-4)"),
+ DEBUG_WORD(NSTR, W_CLI|W_SRV, "Debug negotiation strings"),
+ DEBUG_WORD(OWN, W_REC, "Debug ownership changes in users & groups (levels 1-2)"),
+ DEBUG_WORD(PROTO, W_CLI|W_SRV, "Debug protocol information"),
+ DEBUG_WORD(RECV, W_REC, "Debug receiver functions"),
+ DEBUG_WORD(SEND, W_SND, "Debug sender functions"),
+ DEBUG_WORD(TIME, W_REC, "Debug setting of modified times (levels 1-2)"),
+ { NULL, "--debug", 0, 0, 0, 0 }
+};
+
+static int verbose = 0;
+static int do_stats = 0;
+static int do_progress = 0;
+static int daemon_opt; /* sets am_daemon after option error-reporting */
+static int F_option_cnt = 0;
+static int modify_window_set;
+static int itemize_changes = 0;
+static int refused_delete, refused_archive_part, refused_compress;
+static int refused_partial, refused_progress, refused_delete_before;
+static int refused_delete_during;
+static int refused_inplace, refused_no_iconv;
+static BOOL usermap_via_chown, groupmap_via_chown;
+static char *outbuf_mode;
+static char *bwlimit_arg, *max_size_arg, *min_size_arg;
+static char tmp_partialdir[] = ".~tmp~";
+
+/** Local address to bind. As a character string because it's
+ * interpreted by the IPv6 layer: should be a numeric IP4 or IP6
+ * address, or a hostname. **/
+char *bind_address;
+
+static void output_item_help(struct output_struct *words);
+
+/* This constructs a string that represents all the options set for either
+ * the --info or --debug setting, skipping any implied options (by -v, etc.).
+ * This is used both when conveying the user's options to the server, and
+ * when the help output wants to tell the user what options are implied. */
+static char *make_output_option(struct output_struct *words, short *levels, uchar where)
+{
+ char *str = words == info_words ? "--info=" : "--debug=";
+ int j, counts[MAX_OUT_LEVEL+1], pos, skipped = 0, len = 0, max = 0, lev = 0;
+ int word_count = words == info_words ? COUNT_INFO : COUNT_DEBUG;
+ char *buf;
+
+ memset(counts, 0, sizeof counts);
+
+ for (j = 0; words[j].name; j++) {
+ if (words[j].flag != j) {
+ rprintf(FERROR, "rsync: internal error on %s%s: %d != %d\n",
+ words == info_words ? "INFO_" : "DEBUG_",
+ words[j].name, words[j].flag, j);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ if (!(words[j].where & where))
+ continue;
+ if (words[j].priority == DEFAULT_PRIORITY) {
+ /* Implied items don't need to be mentioned. */
+ skipped++;
+ continue;
+ }
+ len += len ? 1 : strlen(str);
+ len += strlen(words[j].name);
+ len += levels[j] == 1 ? 0 : 1;
+
+ if (words[j].priority == HELP_PRIORITY)
+ continue; /* no abbreviating for help */
+
+ assert(levels[j] <= MAX_OUT_LEVEL);
+ if (++counts[levels[j]] > max) {
+ /* Determine which level has the most items. */
+ lev = levels[j];
+ max = counts[lev];
+ }
+ }
+
+ /* Sanity check the COUNT_* define against the length of the table. */
+ if (j != word_count) {
+ rprintf(FERROR, "rsync: internal error: %s is wrong! (%d != %d)\n",
+ words == info_words ? "COUNT_INFO" : "COUNT_DEBUG",
+ j, word_count);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (!len)
+ return NULL;
+
+ len++;
+ buf = new_array(char, len);
+ pos = 0;
+
+ if (skipped || max < 5)
+ lev = -1;
+ else {
+ if (lev == 0)
+ pos += snprintf(buf, len, "%sNONE", str);
+ else if (lev == 1)
+ pos += snprintf(buf, len, "%sALL", str);
+ else
+ pos += snprintf(buf, len, "%sALL%d", str, lev);
+ }
+
+ for (j = 0; words[j].name && pos < len; j++) {
+ if (words[j].priority == DEFAULT_PRIORITY || levels[j] == lev || !(words[j].where & where))
+ continue;
+ if (pos)
+ buf[pos++] = ',';
+ else
+ pos += strlcpy(buf+pos, str, len-pos);
+ if (pos < len)
+ pos += strlcpy(buf+pos, words[j].name, len-pos);
+ /* Level 1 is implied by the name alone. */
+ if (levels[j] != 1 && pos < len)
+ buf[pos++] = '0' + levels[j];
+ }
+
+ buf[pos] = '\0';
+
+ return buf;
+}
+
+static void parse_output_words(struct output_struct *words, short *levels, const char *str, uchar priority)
+{
+ const char *s;
+ int j, len, lev;
+
+ for ( ; str; str = s) {
+ if ((s = strchr(str, ',')) != NULL)
+ len = s++ - str;
+ else
+ len = strlen(str);
+ if (!len)
+ continue;
+ if (!isDigit(str)) {
+ while (len && isDigit(str+len-1))
+ len--;
+ }
+ lev = isDigit(str+len) ? atoi(str+len) : 1;
+ if (lev > MAX_OUT_LEVEL)
+ lev = MAX_OUT_LEVEL;
+ if (len == 4 && strncasecmp(str, "help", 4) == 0) {
+ output_item_help(words);
+ exit_cleanup(0);
+ }
+ if (len == 4 && strncasecmp(str, "none", 4) == 0)
+ len = lev = 0;
+ else if (len == 3 && strncasecmp(str, "all", 3) == 0)
+ len = 0;
+ for (j = 0; words[j].name; j++) {
+ if (!len
+ || (len == words[j].namelen && strncasecmp(str, words[j].name, len) == 0)) {
+ if (priority >= words[j].priority) {
+ words[j].priority = priority;
+ levels[j] = lev;
+ }
+ if (len)
+ break;
+ }
+ }
+ if (len && !words[j].name && !am_server) {
+ rprintf(FERROR, "Unknown %s item: \"%.*s\"\n",
+ words[j].help, len, str);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+}
+
+/* Tell the user what all the info or debug flags mean. */
+static void output_item_help(struct output_struct *words)
+{
+ short *levels = words == info_words ? info_levels : debug_levels;
+ const char **verbosity = words == info_words ? info_verbosity : debug_verbosity;
+ char buf[128], *opt, *fmt = "%-10s %s\n";
+ int j;
+
+ reset_output_levels();
+
+ rprintf(FINFO, "Use OPT or OPT1 for level 1 output, OPT2 for level 2, etc.; OPT0 silences.\n");
+ rprintf(FINFO, "\n");
+ for (j = 0; words[j].name; j++)
+ rprintf(FINFO, fmt, words[j].name, words[j].help);
+ rprintf(FINFO, "\n");
+
+ snprintf(buf, sizeof buf, "Set all %s options (e.g. all%d)",
+ words[j].help, MAX_OUT_LEVEL);
+ rprintf(FINFO, fmt, "ALL", buf);
+
+ snprintf(buf, sizeof buf, "Silence all %s options (same as all0)",
+ words[j].help);
+ rprintf(FINFO, fmt, "NONE", buf);
+
+ rprintf(FINFO, fmt, "HELP", "Output this help message");
+ rprintf(FINFO, "\n");
+ rprintf(FINFO, "Options added at each level of verbosity:\n");
+
+ for (j = 0; j <= MAX_VERBOSITY; j++) {
+ parse_output_words(words, levels, verbosity[j], HELP_PRIORITY);
+ opt = make_output_option(words, levels, W_CLI|W_SRV|W_SND|W_REC);
+ if (opt) {
+ rprintf(FINFO, "%d) %s\n", j, strchr(opt, '=')+1);
+ free(opt);
+ }
+ reset_output_levels();
+ }
+}
+
+/* The --verbose option now sets info+debug flags. */
+static void set_output_verbosity(int level, uchar priority)
+{
+ int j;
+
+ if (level > MAX_VERBOSITY)
+ level = MAX_VERBOSITY;
+
+ for (j = 0; j <= level; j++) {
+ parse_output_words(info_words, info_levels, info_verbosity[j], priority);
+ parse_output_words(debug_words, debug_levels, debug_verbosity[j], priority);
+ }
+}
+
+/* Limit the info+debug flag levels given a verbose-option level limit. */
+void limit_output_verbosity(int level)
+{
+ short info_limits[COUNT_INFO], debug_limits[COUNT_DEBUG];
+ int j;
+
+ if (level > MAX_VERBOSITY)
+ return;
+
+ memset(info_limits, 0, sizeof info_limits);
+ memset(debug_limits, 0, sizeof debug_limits);
+
+ /* Compute the level limits in the above arrays. */
+ for (j = 0; j <= level; j++) {
+ parse_output_words(info_words, info_limits, info_verbosity[j], LIMIT_PRIORITY);
+ parse_output_words(debug_words, debug_limits, debug_verbosity[j], LIMIT_PRIORITY);
+ }
+
+ for (j = 0; j < COUNT_INFO; j++) {
+ if (info_levels[j] > info_limits[j])
+ info_levels[j] = info_limits[j];
+ }
+
+ for (j = 0; j < COUNT_DEBUG; j++) {
+ if (debug_levels[j] > debug_limits[j])
+ debug_levels[j] = debug_limits[j];
+ }
+}
+
+void reset_output_levels(void)
+{
+ int j;
+
+ memset(info_levels, 0, sizeof info_levels);
+ memset(debug_levels, 0, sizeof debug_levels);
+
+ for (j = 0; j < COUNT_INFO; j++)
+ info_words[j].priority = DEFAULT_PRIORITY;
+
+ for (j = 0; j < COUNT_DEBUG; j++)
+ debug_words[j].priority = DEFAULT_PRIORITY;
+}
+
+void negate_output_levels(void)
+{
+ int j;
+
+ for (j = 0; j < COUNT_INFO; j++)
+ info_levels[j] *= -1;
+
+ for (j = 0; j < COUNT_DEBUG; j++)
+ debug_levels[j] *= -1;
+}
+
+enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
+ OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP,
+ OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
+ OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
+ OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
+ OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
+ OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS,
+ OPT_STOP_AFTER, OPT_STOP_AT,
+ OPT_REFUSED_BASE = 9000};
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"help", 0, POPT_ARG_NONE, 0, OPT_HELP, 0, 0 },
+ {"version", 'V', POPT_ARG_NONE, 0, 'V', 0, 0},
+ {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 },
+ {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"info", 0, POPT_ARG_STRING, 0, OPT_INFO, 0, 0 },
+ {"debug", 0, POPT_ARG_STRING, 0, OPT_DEBUG, 0, 0 },
+ {"stderr", 0, POPT_ARG_STRING, 0, OPT_STDERR, 0, 0 },
+ {"msgs2stderr", 0, POPT_ARG_VAL, &msgs2stderr, 1, 0, 0 },
+ {"no-msgs2stderr", 0, POPT_ARG_VAL, &msgs2stderr, 0, 0, 0 },
+ {"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 },
+ {"motd", 0, POPT_ARG_VAL, &output_motd, 1, 0, 0 },
+ {"no-motd", 0, POPT_ARG_VAL, &output_motd, 0, 0, 0 },
+ {"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 },
+ {"human-readable", 'h', POPT_ARG_NONE, 0, 'h', 0, 0},
+ {"no-human-readable",0, POPT_ARG_VAL, &human_readable, 0, 0, 0},
+ {"no-h", 0, POPT_ARG_VAL, &human_readable, 0, 0, 0},
+ {"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 },
+ {"archive", 'a', POPT_ARG_NONE, 0, 'a', 0, 0 },
+ {"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 },
+ {"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
+ {"no-r", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
+ {"inc-recursive", 0, POPT_ARG_VAL, &allow_inc_recurse, 1, 0, 0 },
+ {"no-inc-recursive", 0, POPT_ARG_VAL, &allow_inc_recurse, 0, 0, 0 },
+ {"i-r", 0, POPT_ARG_VAL, &allow_inc_recurse, 1, 0, 0 },
+ {"no-i-r", 0, POPT_ARG_VAL, &allow_inc_recurse, 0, 0, 0 },
+ {"dirs", 'd', POPT_ARG_VAL, &xfer_dirs, 2, 0, 0 },
+ {"no-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 },
+ {"no-d", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 },
+ {"old-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 },
+ {"old-d", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 },
+ {"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 },
+ {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
+ {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
+ {"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 },
+ {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
+ {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
+ {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
+ {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 },
+ {"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
+ {"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
+ {"times", 't', POPT_ARG_VAL, &preserve_mtimes, 1, 0, 0 },
+ {"no-times", 0, POPT_ARG_VAL, &preserve_mtimes, 0, 0, 0 },
+ {"no-t", 0, POPT_ARG_VAL, &preserve_mtimes, 0, 0, 0 },
+ {"atimes", 'U', POPT_ARG_NONE, 0, 'U', 0, 0 },
+ {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
+ {"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
+ {"open-noatime", 0, POPT_ARG_VAL, &open_noatime, 1, 0, 0 },
+ {"no-open-noatime", 0, POPT_ARG_VAL, &open_noatime, 0, 0, 0 },
+ {"crtimes", 'N', POPT_ARG_VAL, &preserve_crtimes, 1, 0, 0 },
+ {"no-crtimes", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 },
+ {"no-N", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 },
+ {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 },
+ {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
+ {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
+ {"omit-link-times", 'J', POPT_ARG_VAL, &omit_link_times, 1, 0, 0 },
+ {"no-omit-link-times",0, POPT_ARG_VAL, &omit_link_times, 0, 0, 0 },
+ {"no-J", 0, POPT_ARG_VAL, &omit_link_times, 0, 0, 0 },
+ {"modify-window", '@', POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
+ {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
+ {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
+ {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
+ {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
+ {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
+ {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
+ {"group", 'g', POPT_ARG_VAL, &preserve_gid, 1, 0, 0 },
+ {"no-group", 0, POPT_ARG_VAL, &preserve_gid, 0, 0, 0 },
+ {"no-g", 0, POPT_ARG_VAL, &preserve_gid, 0, 0, 0 },
+ {0, 'D', POPT_ARG_NONE, 0, 'D', 0, 0 },
+ {"no-D", 0, POPT_ARG_NONE, 0, OPT_NO_D, 0, 0 },
+ {"devices", 0, POPT_ARG_VAL, &preserve_devices, 1, 0, 0 },
+ {"no-devices", 0, POPT_ARG_VAL, &preserve_devices, 0, 0, 0 },
+ {"copy-devices", 0, POPT_ARG_NONE, &copy_devices, 0, 0, 0 },
+ {"write-devices", 0, POPT_ARG_VAL, &write_devices, 1, 0, 0 },
+ {"no-write-devices", 0, POPT_ARG_VAL, &write_devices, 0, 0, 0 },
+ {"specials", 0, POPT_ARG_VAL, &preserve_specials, 1, 0, 0 },
+ {"no-specials", 0, POPT_ARG_VAL, &preserve_specials, 0, 0, 0 },
+ {"links", 'l', POPT_ARG_VAL, &preserve_links, 1, 0, 0 },
+ {"no-links", 0, POPT_ARG_VAL, &preserve_links, 0, 0, 0 },
+ {"no-l", 0, POPT_ARG_VAL, &preserve_links, 0, 0, 0 },
+ {"copy-links", 'L', POPT_ARG_NONE, &copy_links, 0, 0, 0 },
+ {"copy-unsafe-links",0, POPT_ARG_NONE, &copy_unsafe_links, 0, 0, 0 },
+ {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 },
+ {"munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 1, 0, 0 },
+ {"no-munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 0, 0, 0 },
+ {"copy-dirlinks", 'k', POPT_ARG_NONE, &copy_dirlinks, 0, 0, 0 },
+ {"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 },
+ {"hard-links", 'H', POPT_ARG_NONE, 0, 'H', 0, 0 },
+ {"no-hard-links", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 },
+ {"no-H", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 },
+ {"relative", 'R', POPT_ARG_VAL, &relative_paths, 1, 0, 0 },
+ {"no-relative", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
+ {"no-R", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
+ {"implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 1, 0, 0 },
+ {"no-implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 },
+ {"i-d", 0, POPT_ARG_VAL, &implied_dirs, 1, 0, 0 },
+ {"no-i-d", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 },
+ {"chmod", 0, POPT_ARG_STRING, 0, OPT_CHMOD, 0, 0 },
+ {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 },
+ {"size-only", 0, POPT_ARG_NONE, &size_only, 0, 0, 0 },
+ {"one-file-system", 'x', POPT_ARG_NONE, 0, 'x', 0, 0 },
+ {"no-one-file-system",0, POPT_ARG_VAL, &one_file_system, 0, 0, 0 },
+ {"no-x", 0, POPT_ARG_VAL, &one_file_system, 0, 0, 0 },
+ {"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 },
+ {"existing", 0, POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 },
+ {"ignore-non-existing",0,POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 },
+ {"ignore-existing", 0, POPT_ARG_NONE, &ignore_existing, 0, 0, 0 },
+ {"max-size", 0, POPT_ARG_STRING, &max_size_arg, OPT_MAX_SIZE, 0, 0 },
+ {"min-size", 0, POPT_ARG_STRING, &min_size_arg, OPT_MIN_SIZE, 0, 0 },
+ {"max-alloc", 0, POPT_ARG_STRING, &max_alloc_arg, 0, 0, 0 },
+ {"sparse", 'S', POPT_ARG_VAL, &sparse_files, 1, 0, 0 },
+ {"no-sparse", 0, POPT_ARG_VAL, &sparse_files, 0, 0, 0 },
+ {"no-S", 0, POPT_ARG_VAL, &sparse_files, 0, 0, 0 },
+ {"preallocate", 0, POPT_ARG_NONE, &preallocate_files, 0, 0, 0},
+ {"inplace", 0, POPT_ARG_VAL, &inplace, 1, 0, 0 },
+ {"no-inplace", 0, POPT_ARG_VAL, &inplace, 0, 0, 0 },
+ {"append", 0, POPT_ARG_NONE, 0, OPT_APPEND, 0, 0 },
+ {"append-verify", 0, POPT_ARG_VAL, &append_mode, 2, 0, 0 },
+ {"no-append", 0, POPT_ARG_VAL, &append_mode, 0, 0, 0 },
+ {"del", 0, POPT_ARG_NONE, &delete_during, 0, 0, 0 },
+ {"delete", 0, POPT_ARG_NONE, &delete_mode, 0, 0, 0 },
+ {"delete-before", 0, POPT_ARG_NONE, &delete_before, 0, 0, 0 },
+ {"delete-during", 0, POPT_ARG_VAL, &delete_during, 1, 0, 0 },
+ {"delete-delay", 0, POPT_ARG_VAL, &delete_during, 2, 0, 0 },
+ {"delete-after", 0, POPT_ARG_NONE, &delete_after, 0, 0, 0 },
+ {"delete-excluded", 0, POPT_ARG_NONE, &delete_excluded, 0, 0, 0 },
+ {"delete-missing-args",0,POPT_BIT_SET, &missing_args, 2, 0, 0 },
+ {"ignore-missing-args",0,POPT_BIT_SET, &missing_args, 1, 0, 0 },
+ {"remove-sent-files",0, POPT_ARG_VAL, &remove_source_files, 2, 0, 0 }, /* deprecated */
+ {"remove-source-files",0,POPT_ARG_VAL, &remove_source_files, 1, 0, 0 },
+ {"force", 0, POPT_ARG_VAL, &force_delete, 1, 0, 0 },
+ {"no-force", 0, POPT_ARG_VAL, &force_delete, 0, 0, 0 },
+ {"ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 1, 0, 0 },
+ {"no-ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 0, 0, 0 },
+ {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 },
+ {0, 'F', POPT_ARG_NONE, 0, 'F', 0, 0 },
+ {"filter", 'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 },
+ {"exclude", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 },
+ {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 },
+ {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 },
+ {"include-from", 0, POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 },
+ {"cvs-exclude", 'C', POPT_ARG_NONE, &cvs_exclude, 0, 0, 0 },
+ {"whole-file", 'W', POPT_ARG_VAL, &whole_file, 1, 0, 0 },
+ {"no-whole-file", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 },
+ {"no-W", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 },
+ {"checksum", 'c', POPT_ARG_VAL, &always_checksum, 1, 0, 0 },
+ {"no-checksum", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 },
+ {"no-c", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 },
+ {"checksum-choice", 0, POPT_ARG_STRING, &checksum_choice, 0, 0, 0 },
+ {"cc", 0, POPT_ARG_STRING, &checksum_choice, 0, 0, 0 },
+ {"block-size", 'B', POPT_ARG_STRING, 0, OPT_BLOCK_SIZE, 0, 0 },
+ {"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
+ {"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
+ {"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
+ {"fuzzy", 'y', POPT_ARG_NONE, 0, 'y', 0, 0 },
+ {"no-fuzzy", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
+ {"no-y", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
+ {"compress", 'z', POPT_ARG_NONE, 0, 'z', 0, 0 },
+ {"old-compress", 0, POPT_ARG_NONE, 0, OPT_OLD_COMPRESS, 0, 0 },
+ {"new-compress", 0, POPT_ARG_NONE, 0, OPT_NEW_COMPRESS, 0, 0 },
+ {"no-compress", 0, POPT_ARG_NONE, 0, OPT_NO_COMPRESS, 0, 0 },
+ {"no-z", 0, POPT_ARG_NONE, 0, OPT_NO_COMPRESS, 0, 0 },
+ {"compress-choice", 0, POPT_ARG_STRING, &compress_choice, 0, 0, 0 },
+ {"zc", 0, POPT_ARG_STRING, &compress_choice, 0, 0, 0 },
+ {"skip-compress", 0, POPT_ARG_STRING, &skip_compress, 0, 0, 0 },
+ {"compress-level", 0, POPT_ARG_INT, &do_compression_level, 0, 0, 0 },
+ {"zl", 0, POPT_ARG_INT, &do_compression_level, 0, 0, 0 },
+ {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
+ {"progress", 0, POPT_ARG_VAL, &do_progress, 1, 0, 0 },
+ {"no-progress", 0, POPT_ARG_VAL, &do_progress, 0, 0, 0 },
+ {"partial", 0, POPT_ARG_VAL, &keep_partial, 1, 0, 0 },
+ {"no-partial", 0, POPT_ARG_VAL, &keep_partial, 0, 0, 0 },
+ {"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 },
+ {"delay-updates", 0, POPT_ARG_VAL, &delay_updates, 1, 0, 0 },
+ {"no-delay-updates", 0, POPT_ARG_VAL, &delay_updates, 0, 0, 0 },
+ {"prune-empty-dirs",'m', POPT_ARG_VAL, &prune_empty_dirs, 1, 0, 0 },
+ {"no-prune-empty-dirs",0,POPT_ARG_VAL, &prune_empty_dirs, 0, 0, 0 },
+ {"no-m", 0, POPT_ARG_VAL, &prune_empty_dirs, 0, 0, 0 },
+ {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
+ {"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
+ {"out-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 },
+ {"log-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, /* DEPRECATED */
+ {"itemize-changes", 'i', POPT_ARG_NONE, 0, 'i', 0, 0 },
+ {"no-itemize-changes",0, POPT_ARG_VAL, &itemize_changes, 0, 0, 0 },
+ {"no-i", 0, POPT_ARG_VAL, &itemize_changes, 0, 0, 0 },
+ {"bwlimit", 0, POPT_ARG_STRING, &bwlimit_arg, OPT_BWLIMIT, 0, 0 },
+ {"no-bwlimit", 0, POPT_ARG_VAL, &bwlimit, 0, 0, 0 },
+ {"backup", 'b', POPT_ARG_VAL, &make_backups, 1, 0, 0 },
+ {"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
+ {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
+ {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
+ {"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 },
+ {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 },
+ {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
+ {"only-write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_ONLY_WRITE_BATCH, 0, 0 },
+ {"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 },
+ {"from0", '0', POPT_ARG_VAL, &eol_nulls, 1, 0, 0},
+ {"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 0, 0, 0},
+ {"old-args", 0, POPT_ARG_NONE, 0, OPT_OLD_ARGS, 0, 0},
+ {"no-old-args", 0, POPT_ARG_VAL, &old_style_args, 0, 0, 0},
+ {"secluded-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
+ {"no-secluded-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
+ {"protect-args", 0, POPT_ARG_VAL, &protect_args, 1, 0, 0},
+ {"no-protect-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
+ {"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
+ {"trust-sender", 0, POPT_ARG_VAL, &trust_sender, 1, 0, 0},
+ {"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 },
+ {"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 },
+ {"usermap", 0, POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 },
+ {"groupmap", 0, POPT_ARG_STRING, 0, OPT_GROUPMAP, 0, 0 },
+ {"chown", 0, POPT_ARG_STRING, 0, OPT_CHOWN, 0, 0 },
+ {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 },
+ {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 },
+ {"contimeout", 0, POPT_ARG_INT, &connect_timeout, 0, 0, 0 },
+ {"no-contimeout", 0, POPT_ARG_VAL, &connect_timeout, 0, 0, 0 },
+ {"fsync", 0, POPT_ARG_NONE, &do_fsync, 0, 0, 0 },
+ {"stop-after", 0, POPT_ARG_STRING, 0, OPT_STOP_AFTER, 0, 0 },
+ {"time-limit", 0, POPT_ARG_STRING, 0, OPT_STOP_AFTER, 0, 0 }, /* earlier stop-after name */
+ {"stop-at", 0, POPT_ARG_STRING, 0, OPT_STOP_AT, 0, 0 },
+ {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
+ {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
+ {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
+ {"iconv", 0, POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
+ {"no-iconv", 0, POPT_ARG_NONE, 0, OPT_NO_ICONV, 0, 0 },
+ {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
+ {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
+ {"8-bit-output", '8', POPT_ARG_VAL, &allow_8bit_chars, 1, 0, 0 },
+ {"no-8-bit-output", 0, POPT_ARG_VAL, &allow_8bit_chars, 0, 0, 0 },
+ {"no-8", 0, POPT_ARG_VAL, &allow_8bit_chars, 0, 0, 0 },
+ {"mkpath", 0, POPT_ARG_VAL, &mkpath_dest_arg, 1, 0, 0 },
+ {"no-mkpath", 0, POPT_ARG_VAL, &mkpath_dest_arg, 0, 0, 0 },
+ {"qsort", 0, POPT_ARG_NONE, &use_qsort, 0, 0, 0 },
+ {"copy-as", 0, POPT_ARG_STRING, &copy_as, 0, 0, 0 },
+ {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
+ {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
+ {"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
+ {"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 },
+ {"early-input", 0, POPT_ARG_STRING, &early_input_file, 0, 0, 0 },
+ {"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 },
+ {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 },
+ {"outbuf", 0, POPT_ARG_STRING, &outbuf_mode, 0, 0, 0 },
+ {"remote-option", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 },
+ {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 },
+ {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 },
+ {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 },
+ {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 },
+ /* All the following options switch us into daemon-mode option-parsing. */
+ {"config", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
+ {"daemon", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
+ {"dparam", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
+ {"detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
+ {"no-detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
+ {0,0,0,0, 0, 0, 0}
+};
+
+static struct poptOption long_daemon_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
+ {"bwlimit", 0, POPT_ARG_INT, &daemon_bwlimit, 0, 0, 0 },
+ {"config", 0, POPT_ARG_STRING, &config_file, 0, 0, 0 },
+ {"daemon", 0, POPT_ARG_NONE, &daemon_opt, 0, 0, 0 },
+ {"dparam", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 },
+ {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
+ {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
+ {"detach", 0, POPT_ARG_VAL, &no_detach, 0, 0, 0 },
+ {"no-detach", 0, POPT_ARG_VAL, &no_detach, 1, 0, 0 },
+ {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
+ {"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
+ {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
+ {"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
+ {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 },
+ {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 },
+ {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
+ {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 },
+ {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
+ {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 },
+ {0,0,0,0, 0, 0, 0}
+};
+
+
+static char err_buf[200];
+
+
+/**
+ * Store the option error message, if any, so that we can log the
+ * connection attempt (which requires parsing the options), and then
+ * show the error later on.
+ **/
+void option_error(void)
+{
+ if (!err_buf[0]) {
+ strlcpy(err_buf, "Error parsing options: option may "
+ "be supported on client but not on server?\n",
+ sizeof err_buf);
+ }
+
+ rprintf(FERROR, RSYNC_NAME ": %s", err_buf);
+ io_flush(MSG_FLUSH);
+ msleep(20);
+}
+
+
+static void parse_one_refuse_match(int negated, const char *ref, const struct poptOption *list_end)
+{
+ struct poptOption *op;
+ char shortName[2];
+ int is_wild = strpbrk(ref, "*?[") != NULL;
+ int found_match = 0;
+
+ shortName[1] = '\0';
+
+ if (strcmp("a", ref) == 0 || strcmp("archive", ref) == 0) {
+ ref = "[ardlptgoD]";
+ is_wild = 1;
+ }
+
+ for (op = long_options; op != list_end; op++) {
+ *shortName = op->shortName;
+ if ((op->longName && wildmatch(ref, op->longName))
+ || (*shortName && wildmatch(ref, shortName))) {
+ if (op->descrip[1] == '*')
+ op->descrip = negated ? "a*" : "r*";
+ else if (!is_wild)
+ op->descrip = negated ? "a=" : "r=";
+ found_match = 1;
+ if (!is_wild)
+ break;
+ }
+ }
+
+ if (!found_match)
+ rprintf(FLOG, "No match for refuse-options string \"%s\"\n", ref);
+}
+
+
+/**
+ * Tweak the option table to disable all options that the rsyncd.conf
+ * file has told us to refuse.
+ **/
+static void set_refuse_options(void)
+{
+ struct poptOption *op, *list_end = NULL;
+ char *cp, *ref = lp_refuse_options(module_id);
+ int negated;
+
+ if (!ref)
+ ref = "";
+
+ if (!am_daemon)
+ ref = "";
+
+ /* We abuse the descrip field in poptOption to make it easy to flag which options
+ * are refused (since we don't use it otherwise). Start by marking all options
+ * as "a"ccepted with a few options also marked as non-wild. */
+ for (op = long_options; ; op++) {
+ const char *longName = op->longName ? op->longName : "";
+ if (!op->longName && !op->shortName) {
+ list_end = op;
+ break;
+ }
+ if (!am_daemon
+ || op->shortName == 'e' /* Required for compatibility flags */
+ || op->shortName == '0' /* --from0 just modifies --files-from, so refuse that instead (or not) */
+ || op->shortName == 's' /* --secluded-args is always OK */
+ || op->shortName == 'n' /* --dry-run is always OK */
+ || strcmp("iconv", longName) == 0
+ || strcmp("no-iconv", longName) == 0
+ || strcmp("checksum-seed", longName) == 0
+ || strcmp("copy-devices", longName) == 0 /* disable wild-match (it gets refused below) */
+ || strcmp("write-devices", longName) == 0 /* disable wild-match (it gets refused below) */
+ || strcmp("log-format", longName) == 0 /* aka out-format (NOT log-file-format) */
+ || strcmp("sender", longName) == 0
+ || strcmp("server", longName) == 0)
+ op->descrip = "a="; /* exact-match only */
+ else
+ op->descrip = "a*"; /* wild-card-able */
+ }
+ assert(list_end != NULL);
+
+ if (am_daemon) { /* Refused by default, but can be accepted via a negated exact match. */
+ parse_one_refuse_match(0, "copy-devices", list_end);
+ parse_one_refuse_match(0, "write-devices", list_end);
+ }
+
+ while (1) {
+ while (*ref == ' ') ref++;
+ if (!*ref)
+ break;
+ if ((cp = strchr(ref, ' ')) != NULL)
+ *cp = '\0';
+ negated = *ref == '!';
+ if (negated && ref[1])
+ ref++;
+ parse_one_refuse_match(negated, ref, list_end);
+ if (!cp)
+ break;
+ *cp = ' ';
+ ref = cp + 1;
+ }
+
+ if (am_daemon) {
+#ifdef ICONV_OPTION
+ if (!*lp_charset(module_id))
+ parse_one_refuse_match(0, "iconv", list_end);
+#endif
+ parse_one_refuse_match(0, "log-file*", list_end);
+ }
+
+#ifndef SUPPORT_ATIMES
+ parse_one_refuse_match(0, "atimes", list_end);
+#endif
+#ifndef SUPPORT_HARD_LINKS
+ parse_one_refuse_match(0, "link-dest", list_end);
+#endif
+#ifndef HAVE_MKTIME
+ parse_one_refuse_match(0, "stop-at", list_end);
+#endif
+#ifndef ICONV_OPTION
+ parse_one_refuse_match(0, "iconv", list_end);
+#endif
+#ifndef HAVE_SETVBUF
+ parse_one_refuse_match(0, "outbuf", list_end);
+#endif
+#ifndef SUPPORT_CRTIMES
+ parse_one_refuse_match(0, "crtimes", list_end);
+#endif
+
+ /* Now we use the descrip values to actually mark the options for refusal. */
+ for (op = long_options; op != list_end; op++) {
+ int refused = op->descrip[0] == 'r';
+ op->descrip = NULL;
+ if (!refused)
+ continue;
+ if (op->argInfo == POPT_ARG_VAL)
+ op->argInfo = POPT_ARG_NONE;
+ op->val = (op - long_options) + OPT_REFUSED_BASE;
+ /* The following flags are set to let us easily check an implied option later in the code. */
+ switch (op->shortName) {
+ case 'r': case 'd': case 'l': case 'p':
+ case 't': case 'g': case 'o': case 'D':
+ refused_archive_part = op->val;
+ break;
+ case 'z':
+ refused_compress = op->val;
+ break;
+ case '\0':
+ if (strcmp("delete", op->longName) == 0)
+ refused_delete = op->val;
+ else if (strcmp("delete-before", op->longName) == 0)
+ refused_delete_before = op->val;
+ else if (strcmp("delete-during", op->longName) == 0)
+ refused_delete_during = op->val;
+ else if (strcmp("partial", op->longName) == 0)
+ refused_partial = op->val;
+ else if (strcmp("progress", op->longName) == 0)
+ refused_progress = op->val;
+ else if (strcmp("inplace", op->longName) == 0)
+ refused_inplace = op->val;
+ else if (strcmp("no-iconv", op->longName) == 0)
+ refused_no_iconv = op->val;
+ break;
+ }
+ }
+}
+
+
+static int count_args(const char **argv)
+{
+ int i = 0;
+
+ if (argv) {
+ while (argv[i] != NULL)
+ i++;
+ }
+
+ return i;
+}
+
+/* If the size_arg is an invalid string or the value is < min_value, an error
+ * is put into err_buf & the return is -1. Note that this parser does NOT
+ * support negative numbers, so a min_value < 0 doesn't make any sense. */
+static ssize_t parse_size_arg(const char *size_arg, char def_suf, const char *opt_name,
+ ssize_t min_value, ssize_t max_value, BOOL unlimited_0)
+{
+ int reps, mult, len;
+ const char *arg, *err = "invalid", *min_max = NULL;
+ ssize_t limit = -1, size = 1;
+
+ for (arg = size_arg; isDigit(arg); arg++) {}
+ if (*arg == '.' || *arg == get_decimal_point()) /* backward compatibility: always allow '.' */
+ for (arg++; isDigit(arg); arg++) {}
+ switch (*arg && *arg != '+' && *arg != '-' ? *arg++ : def_suf) {
+ case 'b': case 'B':
+ reps = 0;
+ break;
+ case 'k': case 'K':
+ reps = 1;
+ break;
+ case 'm': case 'M':
+ reps = 2;
+ break;
+ case 'g': case 'G':
+ reps = 3;
+ break;
+ case 't': case 'T':
+ reps = 4;
+ break;
+ case 'p': case 'P':
+ reps = 5;
+ break;
+ default:
+ goto failure;
+ }
+ if (*arg == 'b' || *arg == 'B')
+ mult = 1000, arg++;
+ else if (!*arg || *arg == '+' || *arg == '-')
+ mult = 1024;
+ else if (strncasecmp(arg, "ib", 2) == 0)
+ mult = 1024, arg += 2;
+ else
+ goto failure;
+ while (reps--)
+ size *= mult;
+ size *= atof(size_arg);
+ if ((*arg == '+' || *arg == '-') && arg[1] == '1' && arg != size_arg)
+ size += atoi(arg), arg += 2;
+ if (*arg)
+ goto failure;
+ if (size < 0 || (max_value >= 0 && size > max_value)) {
+ err = "too large";
+ min_max = "max";
+ limit = max_value;
+ goto failure;
+ }
+ if (size < min_value && (!unlimited_0 || size != 0)) {
+ err = "too small";
+ min_max = "min";
+ limit = min_value;
+ goto failure;
+ }
+ return size;
+
+failure:
+ len = snprintf(err_buf, sizeof err_buf - 1, "--%s=%s is %s", opt_name, size_arg, err);
+ if (min_max && limit >= 0 && len < (int)sizeof err_buf - 10) {
+ len += snprintf(err_buf + len, sizeof err_buf - len - 1, " (%s: %s%s)",
+ min_max, do_big_num(limit, 3, NULL),
+ unlimited_0 && min_max[1] == 'i' ? " or 0 for unlimited" : "");
+ }
+ err_buf[len] = '\n';
+ err_buf[len+1] = '\0';
+ return -1;
+}
+
+#ifdef HAVE_MKTIME
+/* Allow the user to specify a time in the format yyyy-mm-ddThh:mm while
+ * also allowing abbreviated data. For instance, if the time is omitted,
+ * it defaults to midnight. If the date is omitted, it defaults to the
+ * next possible date in the future with the specified time. Even the
+ * year or year-month can be omitted, again defaulting to the next date
+ * in the future that matches the specified information. A 2-digit year
+ * is also OK, as is using '/' instead of '-'. */
+static time_t parse_time(const char *arg)
+{
+ const char *cp;
+ time_t val, now = time(NULL);
+ struct tm t, *today = localtime(&now);
+ int in_date, old_mday, n;
+
+ memset(&t, 0, sizeof t);
+ t.tm_year = t.tm_mon = t.tm_mday = -1;
+ t.tm_hour = t.tm_min = t.tm_isdst = -1;
+ cp = arg;
+ if (*cp == 'T' || *cp == 't' || *cp == ':') {
+ in_date = *cp == ':' ? 0 : -1;
+ cp++;
+ } else
+ in_date = 1;
+ for ( ; ; cp++) {
+ if (!isDigit(cp))
+ return (time_t)-1;
+ n = 0;
+ do {
+ n = n * 10 + *cp++ - '0';
+ } while (isDigit(cp));
+ if (*cp == ':')
+ in_date = 0;
+ if (in_date > 0) {
+ if (t.tm_year != -1)
+ return (time_t)-1;
+ t.tm_year = t.tm_mon;
+ t.tm_mon = t.tm_mday;
+ t.tm_mday = n;
+ if (!*cp)
+ break;
+ if (*cp == 'T' || *cp == 't') {
+ if (!cp[1])
+ break;
+ in_date = -1;
+ } else if (*cp != '-' && *cp != '/')
+ return (time_t)-1;
+ continue;
+ }
+ if (t.tm_hour != -1)
+ return (time_t)-1;
+ t.tm_hour = t.tm_min;
+ t.tm_min = n;
+ if (!*cp) {
+ if (in_date < 0)
+ return (time_t)-1;
+ break;
+ }
+ if (*cp != ':')
+ return (time_t)-1;
+ in_date = 0;
+ }
+
+ in_date = 0;
+ if (t.tm_year < 0) {
+ t.tm_year = today->tm_year;
+ in_date = 1;
+ } else if (t.tm_year < 100) {
+ while (t.tm_year < today->tm_year)
+ t.tm_year += 100;
+ } else
+ t.tm_year -= 1900;
+ if (t.tm_mon < 0) {
+ t.tm_mon = today->tm_mon;
+ in_date = 2;
+ } else
+ t.tm_mon--;
+ if (t.tm_mday < 0) {
+ t.tm_mday = today->tm_mday;
+ in_date = 3;
+ }
+
+ n = 0;
+ if (t.tm_min < 0) {
+ t.tm_hour = t.tm_min = 0;
+ } else if (t.tm_hour < 0) {
+ if (in_date != 3)
+ return (time_t)-1;
+ in_date = 0;
+ t.tm_hour = today->tm_hour;
+ n = 60*60;
+ }
+
+ /* Note that mktime() might change a too-large tm_mday into the start of
+ * the following month which we need to undo in the following code! */
+ old_mday = t.tm_mday;
+ if (t.tm_hour > 23 || t.tm_min > 59
+ || t.tm_mon < 0 || t.tm_mon >= 12
+ || t.tm_mday < 1 || t.tm_mday > 31
+ || (val = mktime(&t)) == (time_t)-1)
+ return (time_t)-1;
+
+ while (in_date && (val <= now || t.tm_mday < old_mday)) {
+ switch (in_date) {
+ case 3:
+ old_mday = ++t.tm_mday;
+ break;
+ case 2:
+ if (t.tm_mday < old_mday)
+ t.tm_mday = old_mday; /* The month already got bumped forward */
+ else if (++t.tm_mon == 12) {
+ t.tm_mon = 0;
+ t.tm_year++;
+ }
+ break;
+ case 1:
+ if (t.tm_mday < old_mday) {
+ /* mon==1 mday==29 got bumped to mon==2 */
+ if (t.tm_mon != 2 || old_mday != 29)
+ return (time_t)-1;
+ t.tm_mon = 1;
+ t.tm_mday = 29;
+ }
+ t.tm_year++;
+ break;
+ }
+ if ((val = mktime(&t)) == (time_t)-1) {
+ /* This code shouldn't be needed, as mktime() should auto-round to the next month. */
+ if (in_date != 3 || t.tm_mday <= 28)
+ return (time_t)-1;
+ t.tm_mday = old_mday = 1;
+ in_date = 2;
+ }
+ }
+ if (n) {
+ while (val <= now)
+ val += n;
+ }
+ return val;
+}
+#endif
+
+static void create_refuse_error(int which)
+{
+ const char *msg;
+ if (am_daemon)
+ msg = "The server is configured to refuse";
+ else if (am_server)
+ msg = "The server does not support";
+ else
+ msg = "This rsync does not support";
+
+ /* The "which" value is the index + OPT_REFUSED_BASE. */
+ struct poptOption *op = &long_options[which - OPT_REFUSED_BASE];
+ int n = snprintf(err_buf, sizeof err_buf, "%s --%s\n", msg, op->longName) - 1;
+ if (op->shortName)
+ snprintf(err_buf + n, sizeof err_buf - n, " (-%c)\n", op->shortName);
+}
+
+/* This is used to make sure that --daemon & --server cannot be aliased to
+ * something else. These options have always disabled popt aliases for the
+ * parsing of a daemon or server command-line, but we have to make sure that
+ * these options cannot vanish so that the alias disabling can take effect. */
+static void popt_unalias(poptContext con, const char *opt)
+{
+ struct poptAlias unalias;
+
+ memset(&unalias, 0, sizeof unalias);
+
+ unalias.longName = opt + 2; /* point past the leading "--" */
+ unalias.argc = 1;
+ unalias.argv = new_array0(const char*, 2);
+ unalias.argv[0] = strdup(opt);
+
+ poptAddAlias(con, unalias, 0);
+}
+
+char *alt_dest_opt(int type)
+{
+ if (!type)
+ type = alt_dest_type;
+
+ switch (type) {
+ case COMPARE_DEST:
+ return "--compare-dest";
+ case COPY_DEST:
+ return "--copy-dest";
+ case LINK_DEST:
+ return "--link-dest";
+ default:
+ NOISY_DEATH("Unknown alt_dest_opt type");
+ }
+}
+
+/**
+ * Process command line arguments. Called on both local and remote.
+ *
+ * @retval 1 if all options are OK; with globals set to appropriate
+ * values
+ *
+ * @retval 0 on error, with err_buf containing an explanation
+ **/
+int parse_arguments(int *argc_p, const char ***argv_p)
+{
+ static poptContext pc;
+ const char *arg, **argv = *argv_p;
+ int argc = *argc_p;
+ int opt, want_dest_type;
+ int orig_protect_args = protect_args;
+
+ if (argc == 0) {
+ strlcpy(err_buf, "argc is zero!\n", sizeof err_buf);
+ return 0;
+ }
+
+ set_refuse_options();
+
+#ifdef ICONV_OPTION
+ if (!am_daemon && protect_args <= 0 && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
+ iconv_opt = strdup(arg);
+#endif
+
+ /* TODO: Call poptReadDefaultConfig; handle errors. */
+
+ /* The context leaks in case of an error, but if there's a
+ * problem we always exit anyhow. */
+ if (pc)
+ poptFreeContext(pc);
+ pc = poptGetContext(RSYNC_NAME, argc, argv, long_options, 0);
+ if (!am_server) {
+ poptReadDefaultConfig(pc, 0);
+ popt_unalias(pc, "--daemon");
+ popt_unalias(pc, "--server");
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ /* most options are handled automatically by popt;
+ * only special cases are returned and listed here. */
+
+ switch (opt) {
+ case 'V':
+ version_opt_cnt++;
+ break;
+
+ case OPT_SERVER:
+ if (!am_server) {
+ /* Disable popt aliases on the server side and
+ * then start parsing the options again. */
+ poptFreeContext(pc);
+ pc = poptGetContext(RSYNC_NAME, argc, argv, long_options, 0);
+ am_server = 1;
+ }
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
+ break;
+
+ case OPT_SENDER:
+ if (!am_server) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ am_sender = 1;
+ break;
+
+ case OPT_DAEMON:
+ if (am_daemon) {
+ strlcpy(err_buf,
+ "Attempt to hack rsync thwarted!\n",
+ sizeof err_buf);
+ return 0;
+ }
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
+ protect_args = 0;
+ poptFreeContext(pc);
+ pc = poptGetContext(RSYNC_NAME, argc, argv, long_daemon_options, 0);
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ char **cpp;
+ switch (opt) {
+ case 'h':
+ daemon_usage(FINFO);
+ exit_cleanup(0);
+
+ case 'M':
+ arg = poptGetOptArg(pc);
+ if (!strchr(arg, '=')) {
+ rprintf(FERROR,
+ "--dparam value is missing an '=': %s\n",
+ arg);
+ goto daemon_error;
+ }
+ cpp = EXPAND_ITEM_LIST(&dparam_list, char *, 4);
+ *cpp = strdup(arg);
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ rprintf(FERROR,
+ "rsync: %s: %s (in daemon mode)\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ goto daemon_error;
+ }
+ }
+
+ if (dparam_list.count && !set_dparams(1))
+ exit_cleanup(RERR_SYNTAX);
+
+ if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
+ snprintf(err_buf, sizeof err_buf,
+ "the --temp-dir path is WAY too long.\n");
+ return 0;
+ }
+
+ if (!daemon_opt) {
+ rprintf(FERROR, "Daemon option(s) used without --daemon.\n");
+ daemon_error:
+ rprintf(FERROR, "(Type \"rsync --daemon --help\" for assistance with daemon mode.)\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ *argv_p = argv = poptGetArgs(pc);
+ *argc_p = argc = count_args(argv);
+ am_starting_up = 0;
+ daemon_opt = 0;
+ am_daemon = 1;
+ return 1;
+
+ case OPT_MODIFY_WINDOW:
+ /* The value has already been set by popt, but
+ * we need to remember that we're using a
+ * non-default setting. */
+ modify_window_set = 1;
+ break;
+
+ case OPT_FILTER:
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(0), 0);
+ break;
+
+ case OPT_EXCLUDE:
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(0), XFLG_OLD_PREFIXES);
+ break;
+
+ case OPT_INCLUDE:
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(FILTRULE_INCLUDE), XFLG_OLD_PREFIXES);
+ break;
+
+ case OPT_EXCLUDE_FROM:
+ case OPT_INCLUDE_FROM:
+ arg = poptGetOptArg(pc);
+ if (sanitize_paths)
+ arg = sanitize_path(NULL, arg, NULL, 0, SP_DEFAULT);
+ if (daemon_filter_list.head) {
+ int rej;
+ char *cp = strdup(arg);
+ if (!*cp)
+ rej = 1;
+ else {
+ char *dir = cp + (*cp == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ rej = check_filter(&daemon_filter_list, FLOG, dir, 0) < 0;
+ }
+ free(cp);
+ if (rej)
+ goto options_rejected;
+ }
+ parse_filter_file(&filter_list, arg,
+ rule_template(opt == OPT_INCLUDE_FROM ? FILTRULE_INCLUDE : 0),
+ XFLG_FATAL_ERRORS | XFLG_OLD_PREFIXES);
+ break;
+
+ case 'a':
+ if (refused_archive_part) {
+ create_refuse_error(refused_archive_part);
+ return 0;
+ }
+ if (!recurse) /* preserve recurse == 2 */
+ recurse = 1;
+#ifdef SUPPORT_LINKS
+ preserve_links = 1;
+#endif
+ preserve_perms = 1;
+ preserve_mtimes = 1;
+ preserve_gid = 1;
+ preserve_uid = 1;
+ preserve_devices = 1;
+ preserve_specials = 1;
+ break;
+
+ case 'D':
+ preserve_devices = preserve_specials = 1;
+ break;
+
+ case OPT_NO_D:
+ preserve_devices = preserve_specials = 0;
+ break;
+
+ case 'h':
+ human_readable++;
+ break;
+
+ case 'H':
+ preserve_hard_links++;
+ break;
+
+ case 'i':
+ itemize_changes++;
+ break;
+
+ case 'U':
+ if (++preserve_atimes > 1)
+ open_noatime = 1;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'y':
+ fuzzy_basis++;
+ break;
+
+ case 'q':
+ quiet++;
+ break;
+
+ case 'x':
+ one_file_system++;
+ break;
+
+ case 'F':
+ switch (++F_option_cnt) {
+ case 1:
+ parse_filter_str(&filter_list,": /.rsync-filter",rule_template(0),0);
+ break;
+ case 2:
+ parse_filter_str(&filter_list,"- .rsync-filter",rule_template(0),0);
+ break;
+ }
+ break;
+
+ case 'P':
+ if (refused_partial || refused_progress) {
+ create_refuse_error(refused_partial ? refused_partial : refused_progress);
+ return 0;
+ }
+ do_progress = 1;
+ keep_partial = 1;
+ break;
+
+ case 'z':
+ do_compression++;
+ break;
+
+ case OPT_OLD_COMPRESS:
+ compress_choice = "zlib";
+ break;
+
+ case OPT_NEW_COMPRESS:
+ compress_choice = "zlibx";
+ break;
+
+ case OPT_NO_COMPRESS:
+ do_compression = 0;
+ compress_choice = NULL;
+ break;
+
+ case OPT_OLD_ARGS:
+ if (old_style_args <= 0)
+ old_style_args = 1;
+ else
+ old_style_args++;
+ break;
+
+ case 'M':
+ arg = poptGetOptArg(pc);
+ if (*arg != '-') {
+ snprintf(err_buf, sizeof err_buf,
+ "Remote option must start with a dash: %s\n", arg);
+ return 0;
+ }
+ if (remote_option_cnt+2 >= remote_option_alloc) {
+ remote_option_alloc += 16;
+ remote_options = realloc_array(remote_options,
+ const char *, remote_option_alloc);
+ if (!remote_option_cnt)
+ remote_options[0] = "ARG0";
+ }
+ remote_options[++remote_option_cnt] = arg;
+ remote_options[remote_option_cnt+1] = NULL;
+ break;
+
+ case OPT_WRITE_BATCH:
+ /* batch_name is already set */
+ write_batch = 1;
+ break;
+
+ case OPT_ONLY_WRITE_BATCH:
+ /* batch_name is already set */
+ write_batch = -1;
+ break;
+
+ case OPT_READ_BATCH:
+ /* batch_name is already set */
+ read_batch = 1;
+ break;
+
+ case OPT_NO_ICONV:
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
+ break;
+
+ case OPT_BLOCK_SIZE: {
+ /* We may not know the real protocol_version at this point if this is the client
+ * option parsing, but we still want to check it so that the client can specify
+ * a --protocol=29 option with a larger block size. */
+ int max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE;
+ ssize_t size;
+ arg = poptGetOptArg(pc);
+ if ((size = parse_size_arg(arg, 'b', "block-size", 0, max_blength, False)) < 0)
+ return 0;
+ block_size = (int32)size;
+ break;
+ }
+
+ case OPT_MAX_SIZE:
+ if ((max_size = parse_size_arg(max_size_arg, 'b', "max-size", 0, -1, False)) < 0)
+ return 0;
+ max_size_arg = strdup(do_big_num(max_size, 0, NULL));
+ break;
+
+ case OPT_MIN_SIZE:
+ if ((min_size = parse_size_arg(min_size_arg, 'b', "min-size", 0, -1, False)) < 0)
+ return 0;
+ min_size_arg = strdup(do_big_num(min_size, 0, NULL));
+ break;
+
+ case OPT_BWLIMIT: {
+ ssize_t size = parse_size_arg(bwlimit_arg, 'K', "bwlimit", 512, -1, True);
+ if (size < 0)
+ return 0;
+ bwlimit_arg = strdup(do_big_num(size, 0, NULL));
+ bwlimit = (size + 512) / 1024;
+ break;
+ }
+
+ case OPT_APPEND:
+ if (am_server)
+ append_mode++;
+ else
+ append_mode = 1;
+ break;
+
+ case OPT_LINK_DEST:
+ want_dest_type = LINK_DEST;
+ goto set_dest_dir;
+
+ case OPT_COPY_DEST:
+ want_dest_type = COPY_DEST;
+ goto set_dest_dir;
+
+ case OPT_COMPARE_DEST:
+ want_dest_type = COMPARE_DEST;
+
+ set_dest_dir:
+ if (alt_dest_type && alt_dest_type != want_dest_type) {
+ snprintf(err_buf, sizeof err_buf,
+ "ERROR: the %s option conflicts with the %s option\n",
+ alt_dest_opt(want_dest_type), alt_dest_opt(0));
+ return 0;
+ }
+ alt_dest_type = want_dest_type;
+
+ if (basis_dir_cnt >= MAX_BASIS_DIRS) {
+ snprintf(err_buf, sizeof err_buf,
+ "ERROR: at most %d %s args may be specified\n",
+ MAX_BASIS_DIRS, alt_dest_opt(0));
+ return 0;
+ }
+ /* We defer sanitizing this arg until we know what
+ * our destination directory is going to be. */
+ basis_dir[basis_dir_cnt++] = (char *)poptGetOptArg(pc);
+ break;
+
+ case OPT_CHMOD:
+ arg = poptGetOptArg(pc);
+ if (!parse_chmod(arg, &chmod_modes)) {
+ snprintf(err_buf, sizeof err_buf,
+ "Invalid argument passed to --chmod (%s)\n",
+ arg);
+ return 0;
+ }
+ break;
+
+ case OPT_INFO:
+ arg = poptGetOptArg(pc);
+ parse_output_words(info_words, info_levels, arg, USER_PRIORITY);
+ break;
+
+ case OPT_DEBUG:
+ arg = poptGetOptArg(pc);
+ parse_output_words(debug_words, debug_levels, arg, USER_PRIORITY);
+ break;
+
+ case OPT_USERMAP:
+ if (usermap) {
+ if (usermap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--usermap conflicts with prior --chown.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify --usermap once.\n");
+ return 0;
+ }
+ usermap = (char *)poptGetOptArg(pc);
+ usermap_via_chown = False;
+ preserve_uid = 1;
+ break;
+
+ case OPT_GROUPMAP:
+ if (groupmap) {
+ if (groupmap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--groupmap conflicts with prior --chown.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify --groupmap once.\n");
+ return 0;
+ }
+ groupmap = (char *)poptGetOptArg(pc);
+ groupmap_via_chown = False;
+ preserve_gid = 1;
+ break;
+
+ case OPT_CHOWN: {
+ const char *chown = poptGetOptArg(pc);
+ int len;
+ if ((arg = strchr(chown, ':')) != NULL)
+ len = arg++ - chown;
+ else
+ len = strlen(chown);
+ if (len) {
+ if (usermap) {
+ if (!usermap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--chown conflicts with prior --usermap.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify a user-affecting --chown once.\n");
+ return 0;
+ }
+ if (asprintf(&usermap, "*:%.*s", len, chown) < 0)
+ out_of_memory("parse_arguments");
+ usermap_via_chown = True;
+ preserve_uid = 1;
+ }
+ if (arg && *arg) {
+ if (groupmap) {
+ if (!groupmap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--chown conflicts with prior --groupmap.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify a group-affecting --chown once.\n");
+ return 0;
+ }
+ if (asprintf(&groupmap, "*:%s", arg) < 0)
+ out_of_memory("parse_arguments");
+ groupmap_via_chown = True;
+ preserve_gid = 1;
+ }
+ break;
+ }
+
+ case OPT_HELP:
+ usage(FINFO);
+ exit_cleanup(0);
+
+ case 'A':
+#ifdef SUPPORT_ACLS
+ preserve_acls = 1;
+ preserve_perms = 1;
+ break;
+#else
+ /* FIXME: this should probably be ignored with a
+ * warning and then countermeasures taken to
+ * restrict group and other access in the presence
+ * of any more restrictive ACLs, but this is safe
+ * for now */
+ snprintf(err_buf,sizeof(err_buf),
+ "ACLs are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+#endif
+
+ case 'X':
+#ifdef SUPPORT_XATTRS
+ preserve_xattrs++;
+ break;
+#else
+ snprintf(err_buf,sizeof(err_buf),
+ "extended attributes are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+#endif
+
+ case OPT_STOP_AFTER: {
+ long val;
+ arg = poptGetOptArg(pc);
+ stop_at_utime = time(NULL);
+ if ((val = atol(arg) * 60) <= 0 || LONG_MAX - val < stop_at_utime || (long)(time_t)val != val) {
+ snprintf(err_buf, sizeof err_buf, "invalid --stop-after value: %s\n", arg);
+ return 0;
+ }
+ stop_at_utime += val;
+ break;
+ }
+
+#ifdef HAVE_MKTIME
+ case OPT_STOP_AT:
+ arg = poptGetOptArg(pc);
+ if ((stop_at_utime = parse_time(arg)) == (time_t)-1) {
+ snprintf(err_buf, sizeof err_buf, "invalid --stop-at format: %s\n", arg);
+ return 0;
+ }
+ if (stop_at_utime <= time(NULL)) {
+ snprintf(err_buf, sizeof err_buf, "--stop-at time is not in the future: %s\n", arg);
+ return 0;
+ }
+ break;
+#endif
+
+ case OPT_STDERR: {
+ int len;
+ arg = poptGetOptArg(pc);
+ len = strlen(arg);
+ if (len && strncmp("errors", arg, len) == 0)
+ msgs2stderr = 2;
+ else if (len && strncmp("all", arg, len) == 0)
+ msgs2stderr = 1;
+ else if (len && strncmp("client", arg, len) == 0)
+ msgs2stderr = 0;
+ else {
+ snprintf(err_buf, sizeof err_buf,
+ "--stderr mode \"%s\" is not one of errors, all, or client\n", arg);
+ return 0;
+ }
+ saw_stderr_opt = 1;
+ break;
+ }
+
+ default:
+ /* A large opt value means that set_refuse_options()
+ * turned this option off. */
+ if (opt >= OPT_REFUSED_BASE) {
+ create_refuse_error(opt);
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf, "%s%s: %s\n",
+ am_server ? "on remote machine: " : "",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ return 0;
+ }
+ }
+
+ if (msgs2stderr != 2)
+ saw_stderr_opt = 1;
+
+ if (version_opt_cnt) {
+ print_rsync_version(version_opt_cnt > 1 && !am_server ? FNONE : FINFO);
+ exit_cleanup(0);
+ }
+
+ if (!max_alloc_arg) {
+ max_alloc_arg = getenv("RSYNC_MAX_ALLOC");
+ if (max_alloc_arg && !*max_alloc_arg)
+ max_alloc_arg = NULL;
+ }
+ if (max_alloc_arg) {
+ ssize_t size = parse_size_arg(max_alloc_arg, 'B', "max-alloc", 1024*1024, -1, True);
+ if (size < 0)
+ return 0;
+ max_alloc = size;
+ }
+
+ if (old_style_args < 0) {
+ if (!am_server && protect_args <= 0 && (arg = getenv("RSYNC_OLD_ARGS")) != NULL && *arg) {
+ protect_args = 0;
+ old_style_args = atoi(arg);
+ } else
+ old_style_args = 0;
+ } else if (old_style_args) {
+ if (protect_args > 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--secluded-args conflicts with --old-args.\n");
+ return 0;
+ }
+ protect_args = 0;
+ }
+
+ if (protect_args < 0) {
+ if (am_server)
+ protect_args = 0;
+ else if ((arg = getenv("RSYNC_PROTECT_ARGS")) != NULL && *arg)
+ protect_args = atoi(arg) ? 1 : 0;
+ else {
+#ifdef RSYNC_USE_SECLUDED_ARGS
+ protect_args = 1;
+#else
+ protect_args = 0;
+#endif
+ }
+ }
+
+ if (checksum_choice && strcasecmp(checksum_choice, "auto") != 0 && strcasecmp(checksum_choice, "auto,auto") != 0) {
+ /* Call this early to verify the args and figure out if we need to force
+ * --whole-file. Note that the parse function will get called again later,
+ * just in case an "auto" choice needs to know the protocol_version. */
+ parse_checksum_choice(0);
+ } else
+ checksum_choice = NULL;
+
+ if (human_readable > 1 && argc == 2 && !am_server) {
+ /* Allow the old meaning of 'h' (--help) on its own. */
+ usage(FINFO);
+ exit_cleanup(0);
+ }
+
+ if (!compress_choice && do_compression > 1)
+ compress_choice = "zlibx";
+ if (compress_choice && strcasecmp(compress_choice, "auto") != 0)
+ parse_compress_choice(0); /* Twiddles do_compression and can possibly NULL-out compress_choice. */
+ else
+ compress_choice = NULL;
+
+ if (do_compression || do_compression_level != CLVL_NOT_SPECIFIED) {
+ if (!do_compression)
+ do_compression = CPRES_AUTO;
+ if (do_compression && refused_compress) {
+ create_refuse_error(refused_compress);
+ return 0;
+ }
+ }
+
+#ifdef HAVE_SETVBUF
+ if (outbuf_mode && !am_server) {
+ int mode = *(uchar *)outbuf_mode;
+ if (islower(mode))
+ mode = toupper(mode);
+ fflush(stdout); /* Just in case... */
+ switch (mode) {
+ case 'N': /* None */
+ case 'U': /* Unbuffered */
+ mode = _IONBF;
+ break;
+ case 'L': /* Line */
+ mode = _IOLBF;
+ break;
+ case 'B': /* Block */
+ case 'F': /* Full */
+ mode = _IOFBF;
+ break;
+ default:
+ snprintf(err_buf, sizeof err_buf,
+ "Invalid --outbuf setting -- specify N, L, or B.\n");
+ return 0;
+ }
+ setvbuf(stdout, (char *)NULL, mode, 0);
+ }
+
+ if (msgs2stderr == 1) { /* Are all messages going to stderr? */
+ /* Make stderr line buffered for better sharing of the stream. */
+ fflush(stderr); /* Just in case... */
+ setvbuf(stderr, (char *)NULL, _IOLBF, 0);
+ }
+#endif
+
+ set_output_verbosity(verbose, DEFAULT_PRIORITY);
+
+ if (do_stats) {
+ parse_output_words(info_words, info_levels,
+ verbose > 1 ? "stats3" : "stats2", DEFAULT_PRIORITY);
+ }
+
+#ifdef ICONV_OPTION
+ if (iconv_opt && protect_args != 2) {
+ if (!am_server && strcmp(iconv_opt, "-") == 0)
+ iconv_opt = NULL;
+ else
+ need_unsorted_flist = 1;
+ }
+ if (refused_no_iconv && !iconv_opt) {
+ create_refuse_error(refused_no_iconv);
+ return 0;
+ }
+#endif
+
+ if (fuzzy_basis > 1)
+ fuzzy_basis = basis_dir_cnt + 1;
+
+ /* Don't let the client reset protect_args if it was already processed */
+ if (orig_protect_args == 2 && am_server)
+ protect_args = orig_protect_args;
+
+ if (protect_args == 1 && am_server)
+ return 1;
+
+ *argv_p = argv = poptGetArgs(pc);
+ *argc_p = argc = count_args(argv);
+
+#ifndef SUPPORT_LINKS
+ if (preserve_links && !am_sender) {
+ snprintf(err_buf, sizeof err_buf,
+ "symlinks are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+ }
+#endif
+
+#ifndef SUPPORT_HARD_LINKS
+ if (preserve_hard_links) {
+ snprintf(err_buf, sizeof err_buf,
+ "hard links are not supported on this %s\n",
+ am_server ? "server" : "client");
+ return 0;
+ }
+#endif
+
+#ifdef SUPPORT_XATTRS
+ if (am_root < 0 && preserve_xattrs > 1) {
+ snprintf(err_buf, sizeof err_buf,
+ "--fake-super conflicts with -XX\n");
+ return 0;
+ }
+#else
+ if (am_root < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--fake-super requires an rsync with extended attributes enabled\n");
+ return 0;
+ }
+#endif
+
+ if (write_batch && read_batch) {
+ snprintf(err_buf, sizeof err_buf,
+ "--write-batch and --read-batch can not be used together\n");
+ return 0;
+ }
+ if (write_batch > 0 || read_batch) {
+ if (am_server) {
+ rprintf(FINFO,
+ "ignoring --%s-batch option sent to server\n",
+ write_batch ? "write" : "read");
+ /* We don't actually exit_cleanup(), so that we can
+ * still service older version clients that still send
+ * batch args to server. */
+ read_batch = write_batch = 0;
+ batch_name = NULL;
+ } else if (dry_run)
+ write_batch = 0;
+ } else if (write_batch < 0 && dry_run)
+ write_batch = 0;
+ if (read_batch && files_from) {
+ snprintf(err_buf, sizeof err_buf,
+ "--read-batch cannot be used with --files-from\n");
+ return 0;
+ }
+ if (read_batch && remove_source_files) {
+ snprintf(err_buf, sizeof err_buf,
+ "--read-batch cannot be used with --remove-%s-files\n",
+ remove_source_files == 1 ? "source" : "sent");
+ return 0;
+ }
+ if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) {
+ snprintf(err_buf, sizeof err_buf,
+ "the batch-file name must be %d characters or less.\n",
+ MAX_BATCH_NAME_LEN);
+ return 0;
+ }
+
+ if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
+ snprintf(err_buf, sizeof err_buf,
+ "the --temp-dir path is WAY too long.\n");
+ return 0;
+ }
+
+ if (max_delete < 0 && max_delete != INT_MIN) {
+ /* Negative numbers are treated as "no deletions". */
+ max_delete = 0;
+ }
+
+ if (files_from) {
+ if (recurse == 1) /* preserve recurse == 2 */
+ recurse = 0;
+ if (xfer_dirs < 0)
+ xfer_dirs = 1;
+ }
+
+ if (argc < 2 && !read_batch && !am_server)
+ list_only |= 1;
+
+ if (xfer_dirs >= 4) {
+ parse_filter_str(&filter_list, "- /*/*", rule_template(0), 0);
+ recurse = xfer_dirs = 1;
+ } else if (recurse)
+ xfer_dirs = 1;
+ else if (xfer_dirs < 0)
+ xfer_dirs = list_only ? 1 : 0;
+
+ if (relative_paths < 0)
+ relative_paths = files_from? 1 : 0;
+ if (!relative_paths)
+ implied_dirs = 0;
+
+ if (delete_before + !!delete_during + delete_after > 1) {
+ snprintf(err_buf, sizeof err_buf,
+ "You may not combine multiple --delete-WHEN options.\n");
+ return 0;
+ }
+ if (delete_before || delete_during || delete_after)
+ delete_mode = 1;
+ else if (delete_mode || delete_excluded) {
+ /* Only choose now between before & during if one is refused. */
+ if (refused_delete_before) {
+ if (!refused_delete_during)
+ delete_during = 1;
+ else {
+ create_refuse_error(refused_delete_before);
+ return 0;
+ }
+ } else if (refused_delete_during)
+ delete_before = 1;
+ delete_mode = 1;
+ }
+ if (!xfer_dirs && delete_mode) {
+ snprintf(err_buf, sizeof err_buf,
+ "--delete does not work without --recursive (-r) or --dirs (-d).\n");
+ return 0;
+ }
+
+ if (missing_args == 3) /* simplify if both options were specified */
+ missing_args = 2;
+ if (refused_delete && (delete_mode || missing_args == 2)) {
+ create_refuse_error(refused_delete);
+ return 0;
+ }
+
+ if (remove_source_files) {
+ /* We only want to infer this refusal of --remove-source-files
+ * via the refusal of "delete", not any of the "delete-FOO"
+ * options. */
+ if (refused_delete && am_sender) {
+ create_refuse_error(refused_delete);
+ return 0;
+ }
+ need_messages_from_generator = 1;
+ }
+
+ if (munge_symlinks && !am_daemon) {
+ STRUCT_STAT st;
+ char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */
+ strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */
+ if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) {
+ rprintf(FERROR, "Symlink munging is unsafe when a %s directory exists.\n",
+ prefix);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ }
+
+ if (sanitize_paths) {
+ int i;
+ for (i = argc; i-- > 0; )
+ argv[i] = sanitize_path(NULL, argv[i], "", 0, SP_KEEP_DOT_DIRS);
+ if (tmpdir)
+ tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
+ if (backup_dir)
+ backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
+ }
+ if (daemon_filter_list.head && !am_sender) {
+ filter_rule_list *elp = &daemon_filter_list;
+ if (tmpdir) {
+ char *dir;
+ if (!*tmpdir)
+ goto options_rejected;
+ dir = tmpdir + (*tmpdir == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(elp, FLOG, dir, 1) < 0)
+ goto options_rejected;
+ }
+ if (backup_dir) {
+ char *dir;
+ if (!*backup_dir)
+ goto options_rejected;
+ dir = backup_dir + (*backup_dir == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(elp, FLOG, dir, 1) < 0)
+ goto options_rejected;
+ }
+ }
+
+ if (!backup_suffix)
+ backup_suffix = backup_dir ? "" : BACKUP_SUFFIX;
+ backup_suffix_len = strlen(backup_suffix);
+ if (strchr(backup_suffix, '/') != NULL) {
+ snprintf(err_buf, sizeof err_buf,
+ "--suffix cannot contain slashes: %s\n",
+ backup_suffix);
+ return 0;
+ }
+ if (backup_dir) {
+ size_t len;
+ make_backups = 1; /* --backup-dir implies --backup */
+ while (*backup_dir == '.' && backup_dir[1] == '/')
+ backup_dir += 2;
+ if (*backup_dir == '.' && backup_dir[1] == '\0')
+ backup_dir++;
+ len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf);
+ if (len > sizeof backup_dir_buf - 128) {
+ snprintf(err_buf, sizeof err_buf,
+ "the --backup-dir path is WAY too long.\n");
+ return 0;
+ }
+ backup_dir_len = (int)len;
+ if (!backup_dir_len) {
+ backup_dir_len = -1;
+ backup_dir = NULL;
+ } else if (backup_dir_buf[backup_dir_len - 1] != '/') {
+ backup_dir_buf[backup_dir_len++] = '/';
+ backup_dir_buf[backup_dir_len] = '\0';
+ }
+ backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len;
+ }
+ if (backup_dir) {
+ /* No need for a suffix or a protect rule. */
+ } else if (!backup_suffix_len && (!am_server || !am_sender)) {
+ snprintf(err_buf, sizeof err_buf,
+ "--suffix cannot be empty %s\n", backup_dir_len < 0
+ ? "when --backup-dir is the same as the dest dir"
+ : "without a --backup-dir");
+ return 0;
+ } else if (make_backups && delete_mode && !delete_excluded && !am_server) {
+ snprintf(backup_dir_buf, sizeof backup_dir_buf,
+ "P *%s", backup_suffix);
+ parse_filter_str(&filter_list, backup_dir_buf, rule_template(0), 0);
+ }
+
+ if (make_backups && !backup_dir)
+ omit_dir_times = -1; /* Implied, so avoid -O to sender. */
+
+ if (stdout_format) {
+ if (am_server && log_format_has(stdout_format, 'I'))
+ stdout_format_has_i = 2;
+ else if (log_format_has(stdout_format, 'i'))
+ stdout_format_has_i = itemize_changes | 1;
+ if (!log_format_has(stdout_format, 'b')
+ && !log_format_has(stdout_format, 'c')
+ && !log_format_has(stdout_format, 'C'))
+ log_before_transfer = !am_server;
+ } else if (itemize_changes) {
+ stdout_format = "%i %n%L";
+ stdout_format_has_i = itemize_changes;
+ log_before_transfer = !am_server;
+ }
+
+ if (do_progress && !am_server) {
+ if (!log_before_transfer && INFO_EQ(NAME, 0))
+ parse_output_words(info_words, info_levels, "name", DEFAULT_PRIORITY);
+ parse_output_words(info_words, info_levels, "flist2,progress", DEFAULT_PRIORITY);
+ }
+
+ if (dry_run)
+ do_xfers = 0;
+
+ set_io_timeout(io_timeout);
+
+ if (INFO_GTE(NAME, 1) && !stdout_format) {
+ stdout_format = "%n%L";
+ log_before_transfer = !am_server;
+ }
+ if (stdout_format_has_i || log_format_has(stdout_format, 'o'))
+ stdout_format_has_o_or_i = 1;
+
+ if (logfile_name && !am_daemon) {
+ if (!logfile_format) {
+ logfile_format = "%i %n%L";
+ logfile_format_has_i = logfile_format_has_o_or_i = 1;
+ } else {
+ if (log_format_has(logfile_format, 'i'))
+ logfile_format_has_i = 1;
+ if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
+ logfile_format_has_o_or_i = 1;
+ }
+ log_init(0);
+ } else if (!am_daemon)
+ logfile_format = NULL;
+
+ if (daemon_bwlimit && (!bwlimit || bwlimit > daemon_bwlimit))
+ bwlimit = daemon_bwlimit;
+ if (bwlimit) {
+ bwlimit_writemax = (size_t)bwlimit * 128;
+ if (bwlimit_writemax < 512)
+ bwlimit_writemax = 512;
+ }
+
+ if (append_mode) {
+ if (whole_file > 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--append cannot be used with --whole-file\n");
+ return 0;
+ }
+ if (refused_inplace) {
+ create_refuse_error(refused_inplace);
+ return 0;
+ }
+ inplace = 1;
+ }
+
+ if (write_devices) {
+ if (refused_inplace) {
+ create_refuse_error(refused_inplace);
+ return 0;
+ }
+ inplace = 1;
+ }
+
+ if (delay_updates && !partial_dir)
+ partial_dir = tmp_partialdir;
+
+ if (inplace) {
+#ifdef HAVE_FTRUNCATE
+ if (partial_dir) {
+ snprintf(err_buf, sizeof err_buf,
+ "--%s cannot be used with --%s\n",
+ append_mode ? "append" : "inplace",
+ delay_updates ? "delay-updates" : "partial-dir");
+ return 0;
+ }
+ /* --inplace implies --partial for refusal purposes, but we
+ * clear the keep_partial flag for internal logic purposes. */
+ if (refused_partial) {
+ create_refuse_error(refused_partial);
+ return 0;
+ }
+ keep_partial = 0;
+#else
+ snprintf(err_buf, sizeof err_buf,
+ "--%s is not supported on this %s\n",
+ append_mode ? "append" : "inplace",
+ am_server ? "server" : "client");
+ return 0;
+#endif
+ } else {
+ if (keep_partial && !partial_dir && !am_server) {
+ if ((arg = getenv("RSYNC_PARTIAL_DIR")) != NULL && *arg)
+ partial_dir = strdup(arg);
+ }
+ if (partial_dir) {
+ if (*partial_dir)
+ clean_fname(partial_dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (!*partial_dir || strcmp(partial_dir, ".") == 0)
+ partial_dir = NULL;
+ if (!partial_dir && refused_partial) {
+ create_refuse_error(refused_partial);
+ return 0;
+ }
+ keep_partial = 1;
+ }
+ }
+
+ if (files_from) {
+ char *h, *p;
+ int q;
+ if (argc > 2 || (!am_daemon && !am_server && argc == 1)) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (strcmp(files_from, "-") == 0) {
+ filesfrom_fd = 0;
+ if (am_server)
+ filesfrom_host = ""; /* reading from socket */
+ } else if ((p = check_for_hostspec(files_from, &h, &q)) != 0) {
+ if (am_server) {
+ snprintf(err_buf, sizeof err_buf,
+ "The --files-from sent to the server cannot specify a host.\n");
+ return 0;
+ }
+ files_from = p;
+ filesfrom_host = h;
+ if (strcmp(files_from, "-") == 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "Invalid --files-from remote filename\n");
+ return 0;
+ }
+ } else {
+ if (sanitize_paths)
+ files_from = sanitize_path(NULL, files_from, NULL, 0, SP_DEFAULT);
+ if (daemon_filter_list.head) {
+ char *dir;
+ if (!*files_from)
+ goto options_rejected;
+ dir = files_from + (*files_from == '/' ? module_dirlen : 0);
+ clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+ if (check_filter(&daemon_filter_list, FLOG, dir, 0) < 0)
+ goto options_rejected;
+ }
+ filesfrom_fd = open(files_from, O_RDONLY|O_BINARY);
+ if (filesfrom_fd < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "failed to open files-from file %s: %s\n",
+ files_from, strerror(errno));
+ return 0;
+ }
+ }
+ }
+
+ if (trust_sender || am_server || read_batch)
+ trust_sender_args = trust_sender_filter = 1;
+ else if (old_style_args || filesfrom_host != NULL)
+ trust_sender_args = 1;
+
+ am_starting_up = 0;
+
+ return 1;
+
+ options_rejected:
+ snprintf(err_buf, sizeof err_buf,
+ "Your options have been rejected by the server.\n");
+ return 0;
+}
+
+
+static char SPLIT_ARG_WHEN_OLD[1];
+
+/**
+ * Do backslash quoting of any weird chars in "arg", append the resulting
+ * string to the end of the "opt" (which gets a "=" appended if it is not
+ * an empty or NULL string), and return the (perhaps malloced) result.
+ * If opt is NULL, arg is considered a filename arg that allows wildcards.
+ * If it is "" or any other value, it is considered an option.
+ **/
+char *safe_arg(const char *opt, const char *arg)
+{
+#define SHELL_CHARS "!#$&;|<>(){}\"' \t\\"
+#define WILD_CHARS "*?[]" /* We don't allow remote brace expansion */
+ BOOL is_filename_arg = !opt;
+ char *escapes = is_filename_arg ? SHELL_CHARS : WILD_CHARS SHELL_CHARS;
+ BOOL escape_leading_dash = is_filename_arg && *arg == '-';
+ BOOL escape_leading_tilde = 0;
+ int len1 = opt && *opt ? strlen(opt) + 1 : 0;
+ int len2 = strlen(arg);
+ int extras = escape_leading_dash ? 2 : 0;
+ char *ret;
+ if (!protect_args && old_style_args < 2 && (!old_style_args || (!is_filename_arg && opt != SPLIT_ARG_WHEN_OLD))) {
+ const char *f;
+ if (!trust_sender_args && *arg == '~'
+ && ((relative_paths && !strstr(arg, "/./"))
+ || !strchr(arg, '/'))) {
+ extras++;
+ escape_leading_tilde = 1;
+ }
+ for (f = arg; *f; f++) {
+ if (strchr(escapes, *f))
+ extras++;
+ }
+ }
+ if (!len1 && !extras)
+ return (char*)arg;
+ ret = new_array(char, len1 + len2 + extras + 1);
+ if (len1) {
+ memcpy(ret, opt, len1-1);
+ ret[len1-1] = '=';
+ }
+ if (escape_leading_dash) {
+ ret[len1++] = '.';
+ ret[len1++] = '/';
+ extras -= 2;
+ }
+ if (!extras)
+ memcpy(ret + len1, arg, len2);
+ else {
+ const char *f = arg;
+ char *t = ret + len1;
+ if (escape_leading_tilde)
+ *t++ = '\\';
+ while (*f) {
+ if (*f == '\\') {
+ if (!is_filename_arg || !strchr(WILD_CHARS, f[1]))
+ *t++ = '\\';
+ } else if (strchr(escapes, *f))
+ *t++ = '\\';
+ *t++ = *f++;
+ }
+ }
+ ret[len1+len2+extras] = '\0';
+ return ret;
+}
+
+
+/**
+ * Construct a filtered list of options to pass through from the
+ * client to the server.
+ *
+ * This involves setting options that will tell the server how to
+ * behave, and also filtering out options that are processed only
+ * locally.
+ **/
+void server_options(char **args, int *argc_p)
+{
+ static char argstr[64];
+ int ac = *argc_p;
+ uchar where;
+ char *arg;
+ int i, x;
+
+ /* This should always remain first on the server's command-line. */
+ args[ac++] = "--server";
+
+ if (!am_sender)
+ args[ac++] = "--sender";
+
+ x = 1;
+ argstr[0] = '-';
+
+ if (protect_args)
+ argstr[x++] = 's';
+
+ for (i = 0; i < verbose; i++)
+ argstr[x++] = 'v';
+
+ if (quiet && msgs2stderr)
+ argstr[x++] = 'q';
+ if (make_backups)
+ argstr[x++] = 'b';
+ if (update_only)
+ argstr[x++] = 'u';
+ if (!do_xfers) /* Note: NOT "dry_run"! */
+ argstr[x++] = 'n';
+ if (preserve_links)
+ argstr[x++] = 'l';
+ if ((xfer_dirs >= 2 && xfer_dirs < 4)
+ || (xfer_dirs && !recurse && (list_only || (delete_mode && am_sender))))
+ argstr[x++] = 'd';
+ if (am_sender) {
+ if (keep_dirlinks)
+ argstr[x++] = 'K';
+ if (prune_empty_dirs)
+ argstr[x++] = 'm';
+ if (omit_dir_times > 0)
+ argstr[x++] = 'O';
+ if (omit_link_times)
+ argstr[x++] = 'J';
+ if (fuzzy_basis) {
+ argstr[x++] = 'y';
+ if (fuzzy_basis > 1)
+ argstr[x++] = 'y';
+ }
+ } else {
+ if (copy_links)
+ argstr[x++] = 'L';
+ if (copy_dirlinks)
+ argstr[x++] = 'k';
+ }
+
+ if (whole_file > 0)
+ argstr[x++] = 'W';
+ /* We don't need to send --no-whole-file, because it's the
+ * default for remote transfers, and in any case old versions
+ * of rsync will not understand it. */
+
+ if (preserve_hard_links) {
+ argstr[x++] = 'H';
+ if (preserve_hard_links > 1)
+ argstr[x++] = 'H';
+ }
+ if (preserve_uid)
+ argstr[x++] = 'o';
+ if (preserve_gid)
+ argstr[x++] = 'g';
+ if (preserve_devices) /* ignore preserve_specials here */
+ argstr[x++] = 'D';
+ if (preserve_mtimes)
+ argstr[x++] = 't';
+ if (preserve_atimes) {
+ argstr[x++] = 'U';
+ if (preserve_atimes > 1)
+ argstr[x++] = 'U';
+ }
+#ifdef SUPPORT_CRTIMES
+ if (preserve_crtimes)
+ argstr[x++] = 'N';
+#endif
+ if (preserve_perms)
+ argstr[x++] = 'p';
+ else if (preserve_executability && am_sender)
+ argstr[x++] = 'E';
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
+ argstr[x++] = 'A';
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ argstr[x++] = 'X';
+ if (preserve_xattrs > 1)
+ argstr[x++] = 'X';
+ }
+#endif
+ if (recurse)
+ argstr[x++] = 'r';
+ if (always_checksum)
+ argstr[x++] = 'c';
+ if (cvs_exclude)
+ argstr[x++] = 'C';
+ if (ignore_times)
+ argstr[x++] = 'I';
+ if (relative_paths)
+ argstr[x++] = 'R';
+ if (one_file_system) {
+ argstr[x++] = 'x';
+ if (one_file_system > 1)
+ argstr[x++] = 'x';
+ }
+ if (sparse_files)
+ argstr[x++] = 'S';
+ if (do_compression == CPRES_ZLIB)
+ argstr[x++] = 'z';
+
+ set_allow_inc_recurse();
+
+ /* This '\0'-terminates argstr and makes sure it didn't overflow. */
+ x += maybe_add_e_option(argstr + x, (int)sizeof argstr - x);
+
+ if (x > 1)
+ args[ac++] = argstr;
+
+#ifdef ICONV_OPTION
+ if (iconv_opt) {
+ char *set = strchr(iconv_opt, ',');
+ if (set)
+ set++;
+ else
+ set = iconv_opt;
+ args[ac++] = safe_arg("--iconv", set);
+ }
+#endif
+
+ if (protect_args && !local_server) /* unprotected args stop here */
+ args[ac++] = NULL;
+
+ if (list_only > 1)
+ args[ac++] = "--list-only";
+
+ /* This makes sure that the remote rsync can handle deleting with -d
+ * sans -r because the --no-r option was added at the same time. */
+ if (xfer_dirs && !recurse && delete_mode && am_sender)
+ args[ac++] = "--no-r";
+
+ if (do_compression && do_compression_level != CLVL_NOT_SPECIFIED) {
+ if (asprintf(&arg, "--compress-level=%d", do_compression_level) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (preserve_devices) {
+ /* Note: sending "--devices" would not be backward-compatible. */
+ if (!preserve_specials)
+ args[ac++] = "--no-specials"; /* -D is already set. */
+ } else if (preserve_specials)
+ args[ac++] = "--specials";
+
+ /* The server side doesn't use our log-format, but in certain
+ * circumstances they need to know a little about the option. */
+ if (stdout_format && am_sender) {
+ /* Use --log-format, not --out-format, for compatibility. */
+ if (stdout_format_has_i > 1)
+ args[ac++] = "--log-format=%i%I";
+ else if (stdout_format_has_i)
+ args[ac++] = "--log-format=%i";
+ else if (stdout_format_has_o_or_i)
+ args[ac++] = "--log-format=%o";
+ else if (!verbose)
+ args[ac++] = "--log-format=X";
+ }
+
+ if (msgs2stderr == 1)
+ args[ac++] = "--msgs2stderr";
+ else if (msgs2stderr == 0)
+ args[ac++] = "--no-msgs2stderr";
+
+ if (block_size) {
+ if (asprintf(&arg, "-B%u", (int)block_size) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (io_timeout) {
+ if (asprintf(&arg, "--timeout=%d", io_timeout) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (bwlimit) {
+ if (asprintf(&arg, "--bwlimit=%d", bwlimit) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (backup_dir) {
+ /* This split idiom allows for ~/path expansion via the shell. */
+ args[ac++] = "--backup-dir";
+ args[ac++] = safe_arg("", backup_dir);
+ }
+
+ /* Only send --suffix if it specifies a non-default value. */
+ if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0)
+ args[ac++] = safe_arg("--suffix", backup_suffix);
+
+ if (checksum_choice)
+ args[ac++] = safe_arg("--checksum-choice", checksum_choice);
+
+ if (do_compression == CPRES_ZLIBX)
+ args[ac++] = "--new-compress";
+ else if (compress_choice && do_compression == CPRES_ZLIB)
+ args[ac++] = "--old-compress";
+ else if (compress_choice)
+ args[ac++] = safe_arg("--compress-choice", compress_choice);
+
+ if (am_sender) {
+ if (max_delete > 0) {
+ if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)
+ goto oom;
+ args[ac++] = arg;
+ } else if (max_delete == 0)
+ args[ac++] = "--max-delete=-1";
+ if (min_size >= 0)
+ args[ac++] = safe_arg("--min-size", min_size_arg);
+ if (max_size >= 0)
+ args[ac++] = safe_arg("--max-size", max_size_arg);
+ if (delete_before)
+ args[ac++] = "--delete-before";
+ else if (delete_during == 2)
+ args[ac++] = "--delete-delay";
+ else if (delete_during)
+ args[ac++] = "--delete-during";
+ else if (delete_after)
+ args[ac++] = "--delete-after";
+ else if (delete_mode && !delete_excluded)
+ args[ac++] = "--delete";
+ if (delete_excluded)
+ args[ac++] = "--delete-excluded";
+ if (force_delete)
+ args[ac++] = "--force";
+ if (write_batch < 0)
+ args[ac++] = "--only-write-batch=X";
+ if (am_root > 1)
+ args[ac++] = "--super";
+ if (size_only)
+ args[ac++] = "--size-only";
+ if (do_stats)
+ args[ac++] = "--stats";
+ } else {
+ if (skip_compress)
+ args[ac++] = safe_arg("--skip-compress", skip_compress);
+ }
+
+ if (max_alloc_arg && max_alloc != DEFAULT_MAX_ALLOC)
+ args[ac++] = safe_arg("--max-alloc", max_alloc_arg);
+
+ /* --delete-missing-args needs the cooperation of both sides, but
+ * the sender can handle --ignore-missing-args by itself. */
+ if (missing_args == 2)
+ args[ac++] = "--delete-missing-args";
+ else if (missing_args == 1 && !am_sender)
+ args[ac++] = "--ignore-missing-args";
+
+ if (modify_window_set && am_sender) {
+ char *fmt = modify_window < 0 ? "-@%d" : "--modify-window=%d";
+ if (asprintf(&arg, fmt, modify_window) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (checksum_seed) {
+ if (asprintf(&arg, "--checksum-seed=%d", checksum_seed) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (partial_dir && am_sender) {
+ if (partial_dir != tmp_partialdir) {
+ args[ac++] = "--partial-dir";
+ args[ac++] = safe_arg("", partial_dir);
+ }
+ if (delay_updates)
+ args[ac++] = "--delay-updates";
+ } else if (keep_partial && am_sender)
+ args[ac++] = "--partial";
+
+ if (ignore_errors)
+ args[ac++] = "--ignore-errors";
+
+ if (copy_unsafe_links)
+ args[ac++] = "--copy-unsafe-links";
+
+ if (safe_symlinks)
+ args[ac++] = "--safe-links";
+
+ if (numeric_ids)
+ args[ac++] = "--numeric-ids";
+
+ if (use_qsort)
+ args[ac++] = "--use-qsort";
+
+ if (am_sender) {
+ if (usermap)
+ args[ac++] = safe_arg("--usermap", usermap);
+
+ if (groupmap)
+ args[ac++] = safe_arg("--groupmap", groupmap);
+
+ if (ignore_existing)
+ args[ac++] = "--ignore-existing";
+
+ /* Backward compatibility: send --existing, not --ignore-non-existing. */
+ if (ignore_non_existing)
+ args[ac++] = "--existing";
+
+ if (tmpdir) {
+ args[ac++] = "--temp-dir";
+ args[ac++] = safe_arg("", tmpdir);
+ }
+
+ if (do_fsync)
+ args[ac++] = "--fsync";
+
+ if (basis_dir[0]) {
+ /* the server only needs this option if it is not the sender,
+ * and it may be an older version that doesn't know this
+ * option, so don't send it if client is the sender.
+ */
+ for (i = 0; i < basis_dir_cnt; i++) {
+ args[ac++] = alt_dest_opt(0);
+ args[ac++] = safe_arg("", basis_dir[i]);
+ }
+ }
+ }
+
+ /* What flags do we need to send to the other side? */
+ where = (am_server ? W_CLI : W_SRV) | (am_sender ? W_REC : W_SND);
+ arg = make_output_option(info_words, info_levels, where);
+ if (arg)
+ args[ac++] = arg;
+
+ if (append_mode) {
+ if (append_mode > 1)
+ args[ac++] = "--append";
+ args[ac++] = "--append";
+ } else if (inplace) {
+ args[ac++] = "--inplace";
+ /* Work around a bug in older rsync versions (on the remote side) for --inplace --sparse */
+ if (sparse_files && !whole_file && am_sender)
+ args[ac++] = "--no-W";
+ }
+
+ if (files_from && (!am_sender || filesfrom_host)) {
+ if (filesfrom_host) {
+ args[ac++] = "--files-from";
+ args[ac++] = safe_arg("", files_from);
+ if (eol_nulls)
+ args[ac++] = "--from0";
+ } else {
+ args[ac++] = "--files-from=-";
+ args[ac++] = "--from0";
+ }
+ if (!relative_paths)
+ args[ac++] = "--no-relative";
+ }
+ /* It's OK that this checks the upper-bound of the protocol_version. */
+ if (relative_paths && !implied_dirs && (!am_sender || protocol_version >= 30))
+ args[ac++] = "--no-implied-dirs";
+
+ if (write_devices && am_sender)
+ args[ac++] = "--write-devices";
+
+ if (remove_source_files == 1)
+ args[ac++] = "--remove-source-files";
+ else if (remove_source_files)
+ args[ac++] = "--remove-sent-files";
+
+ if (copy_devices && !am_sender)
+ args[ac++] = "--copy-devices";
+
+ if (preallocate_files && am_sender)
+ args[ac++] = "--preallocate";
+
+ if (open_noatime && preserve_atimes <= 1)
+ args[ac++] = "--open-noatime";
+
+ if (mkpath_dest_arg && am_sender)
+ args[ac++] = "--mkpath";
+
+ if (ac > MAX_SERVER_ARGS) { /* Not possible... */
+ rprintf(FERROR, "argc overflow in server_options().\n");
+ exit_cleanup(RERR_MALLOC);
+ }
+
+ if (remote_option_cnt) {
+ int j;
+ if (ac + remote_option_cnt > MAX_SERVER_ARGS) {
+ rprintf(FERROR, "too many remote options specified.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ for (j = 1; j <= remote_option_cnt; j++)
+ args[ac++] = safe_arg(SPLIT_ARG_WHEN_OLD, remote_options[j]);
+ }
+
+ *argc_p = ac;
+ return;
+
+ oom:
+ out_of_memory("server_options");
+}
+
+int maybe_add_e_option(char *buf, int buf_len)
+{
+ int x = 0;
+
+ /* We don't really know the actual protocol_version at this point,
+ * but checking the pre-negotiated value allows the user to use a
+ * --protocol=29 override to avoid the use of this -eFLAGS opt. */
+ if (protocol_version >= 30 && buf_len > 0) {
+ /* We make use of the -e option to let the server know about
+ * any pre-release protocol version && some behavior flags. */
+ buf[x++] = 'e';
+
+#if SUBPROTOCOL_VERSION != 0
+ if (protocol_version == PROTOCOL_VERSION)
+ x += snprintf(buf + x, buf_len - x, "%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
+ else
+#endif
+ buf[x++] = '.';
+ if (allow_inc_recurse)
+ buf[x++] = 'i';
+#ifdef CAN_SET_SYMLINK_TIMES
+ buf[x++] = 'L'; /* symlink time-setting support */
+#endif
+#ifdef ICONV_OPTION
+ buf[x++] = 's'; /* symlink iconv translation support */
+#endif
+ buf[x++] = 'f'; /* flist I/O-error safety support */
+ buf[x++] = 'x'; /* xattr hardlink optimization not desired */
+ buf[x++] = 'C'; /* support checksum seed order fix */
+ buf[x++] = 'I'; /* support inplace_partial behavior */
+ buf[x++] = 'v'; /* use varint for flist & compat flags; negotiate checksum */
+ buf[x++] = 'u'; /* include name of uid 0 & gid 0 in the id map */
+
+ /* NOTE: Avoid using 'V' -- it was represented with the high bit of a write_byte() that became a write_varint(). */
+ }
+
+ if (x >= buf_len) { /* Not possible... */
+ rprintf(FERROR, "overflow in add_e_flags().\n");
+ exit_cleanup(RERR_MALLOC);
+ }
+
+ buf[x] = '\0';
+
+ return x;
+}
+
+/* If str points to a valid hostspec, return allocated memory containing the
+ * [USER@]HOST part of the string, and set the path_start_ptr to the part of
+ * the string after the host part. Otherwise, return NULL. If port_ptr is
+ * non-NULL, we must be parsing an rsync:// URL hostname, and we will set
+ * *port_ptr if a port number is found. Note that IPv6 IPs will have their
+ * (required for parsing) [ and ] chars elided from the returned string. */
+static char *parse_hostspec(char *str, char **path_start_ptr, int *port_ptr)
+{
+ char *s, *host_start = str;
+ int hostlen = 0, userlen = 0;
+ char *ret;
+
+ for (s = str; ; s++) {
+ if (!*s) {
+ /* It is only OK if we run out of string with rsync:// */
+ if (!port_ptr)
+ return NULL;
+ if (!hostlen)
+ hostlen = s - host_start;
+ break;
+ }
+ if (*s == ':' || *s == '/') {
+ if (!hostlen)
+ hostlen = s - host_start;
+ if (*s++ == '/') {
+ if (!port_ptr)
+ return NULL;
+ } else if (port_ptr) {
+ *port_ptr = atoi(s);
+ while (isDigit(s)) s++;
+ if (*s && *s++ != '/')
+ return NULL;
+ }
+ break;
+ }
+ if (*s == '@') {
+ userlen = s - str + 1;
+ host_start = s + 1;
+ } else if (*s == '[') {
+ if (s != host_start++)
+ return NULL;
+ while (*s && *s != ']' && *s != '/') s++; /*SHARED ITERATOR*/
+ hostlen = s - host_start;
+ if (*s != ']' || (s[1] && s[1] != '/' && s[1] != ':') || !hostlen)
+ return NULL;
+ }
+ }
+
+ *path_start_ptr = s;
+ ret = new_array(char, userlen + hostlen + 1);
+ if (userlen)
+ strlcpy(ret, str, userlen + 1);
+ strlcpy(ret + userlen, host_start, hostlen + 1);
+ return ret;
+}
+
+/* Look for a HOST specification of the form "HOST:PATH", "HOST::PATH", or
+ * "rsync://HOST:PORT/PATH". If found, *host_ptr will be set to some allocated
+ * memory with the HOST. If a daemon-accessing spec was specified, the value
+ * of *port_ptr will contain a non-0 port number, otherwise it will be set to
+ * 0. The return value is a pointer to the PATH. Note that the HOST spec can
+ * be an IPv6 literal address enclosed in '[' and ']' (such as "[::1]" or
+ * "[::ffff:127.0.0.1]") which is returned without the '[' and ']'. */
+char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr)
+{
+ char *path;
+
+ if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) {
+ *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr);
+ if (*host_ptr) {
+ if (!*port_ptr)
+ *port_ptr = -1; /* -1 indicates they want the default */
+ return path;
+ }
+ }
+
+ *host_ptr = parse_hostspec(s, &path, NULL);
+ if (!*host_ptr)
+ return NULL;
+
+ if (*path == ':') {
+ if (port_ptr && !*port_ptr)
+ *port_ptr = -1;
+ return path + 1;
+ }
+ if (port_ptr)
+ *port_ptr = 0;
+
+ return path;
+}
diff --git a/packaging/auto-Makefile b/packaging/auto-Makefile
new file mode 100644
index 0000000..7f2e258
--- /dev/null
+++ b/packaging/auto-Makefile
@@ -0,0 +1,12 @@
+TARGETS := all install install-ssl-daemon install-all install-strip conf gen gensend reconfigure restatus \
+ proto man clean cleantests distclean test check check29 check30 installcheck splint \
+ doxygen doxygen-upload finddead rrsync
+
+.PHONY: $(TARGETS) auto-prep
+
+$(TARGETS): auto-prep
+ make -C build $@
+
+auto-prep:
+ @if test x`packaging/prep-auto-dir` = x; then echo "auto-build-save is not setup"; exit 1; fi
+ @echo 'Build branch: '`readlink build/.branch | tr % /`
diff --git a/packaging/branch-from-patch b/packaging/branch-from-patch
new file mode 100755
index 0000000..440b583
--- /dev/null
+++ b/packaging/branch-from-patch
@@ -0,0 +1,174 @@
+#!/usr/bin/env -S python3 -B
+
+# This script turns one or more diff files in the patches dir (which is
+# expected to be a checkout of the rsync-patches git repo) into a branch
+# in the main rsync git checkout. This allows the applied patch to be
+# merged with the latest rsync changes and tested. To update the diff
+# with the resulting changes, see the patch-update script.
+
+import os, sys, re, argparse, glob
+
+sys.path = ['packaging'] + sys.path
+
+from pkglib import *
+
+def main():
+ global created, info, local_branch
+
+ cur_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
+
+ local_branch = get_patch_branches(args.base_branch)
+
+ if args.delete_local_branches:
+ for name in sorted(local_branch):
+ branch = f"patch/{args.base_branch}/{name}"
+ cmd_chk(['git', 'branch', '-D', branch])
+ local_branch = set()
+
+ if args.add_missing:
+ for fn in sorted(glob.glob(f"{args.patches_dir}/*.diff")):
+ name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
+ if name not in local_branch and fn not in args.patch_files:
+ args.patch_files.append(fn)
+
+ if not args.patch_files:
+ return
+
+ for fn in args.patch_files:
+ if not fn.endswith('.diff'):
+ die(f"Filename is not a .diff file: {fn}")
+ if not os.path.isfile(fn):
+ die(f"File not found: {fn}")
+
+ scanned = set()
+ info = { }
+
+ patch_list = [ ]
+ for fn in args.patch_files:
+ m = re.match(r'^(?P<dir>.*?)(?P<name>[^/]+)\.diff$', fn)
+ patch = argparse.Namespace(**m.groupdict())
+ if patch.name in scanned:
+ continue
+ patch.fn = fn
+
+ lines = [ ]
+ commit_hash = None
+ with open(patch.fn, 'r', encoding='utf-8') as fh:
+ for line in fh:
+ m = re.match(r'^based-on: (\S+)', line)
+ if m:
+ commit_hash = m[1]
+ break
+ if (re.match(r'^index .*\.\..* \d', line)
+ or re.match(r'^diff --git ', line)
+ or re.match(r'^--- (old|a)/', line)):
+ break
+ lines.append(re.sub(r'\s*\Z', "\n", line, 1))
+ info_txt = ''.join(lines).strip() + "\n"
+ lines = None
+
+ parent = args.base_branch
+ patches = re.findall(r'patch -p1 <%s/(\S+)\.diff' % args.patches_dir, info_txt)
+ if patches:
+ last = patches.pop()
+ if last != patch.name:
+ warn(f"No identity patch line in {patch.fn}")
+ patches.append(last)
+ if patches:
+ parent = patches.pop()
+ if parent not in scanned:
+ diff_fn = patch.dir + parent + '.diff'
+ if not os.path.isfile(diff_fn):
+ die(f"Failed to find parent of {patch.fn}: {parent}")
+ # Add parent to args.patch_files so that we will look for the
+ # parent's parent. Any duplicates will be ignored.
+ args.patch_files.append(diff_fn)
+ else:
+ warn(f"No patch lines found in {patch.fn}")
+
+ info[patch.name] = [ parent, info_txt, commit_hash ]
+
+ patch_list.append(patch)
+
+ created = set()
+ for patch in patch_list:
+ create_branch(patch)
+
+ cmd_chk(['git', 'checkout', args.base_branch])
+
+
+def create_branch(patch):
+ if patch.name in created:
+ return
+ created.add(patch.name)
+
+ parent, info_txt, commit_hash = info[patch.name]
+ parent = argparse.Namespace(dir=patch.dir, name=parent, fn=patch.dir + parent + '.diff')
+
+ if parent.name == args.base_branch:
+ parent_branch = commit_hash if commit_hash else args.base_branch
+ else:
+ create_branch(parent)
+ parent_branch = '/'.join(['patch', args.base_branch, parent.name])
+
+ branch = '/'.join(['patch', args.base_branch, patch.name])
+ print("\n" + '=' * 64)
+ print(f"Processing {branch} ({parent_branch})")
+
+ if patch.name in local_branch:
+ cmd_chk(['git', 'branch', '-D', branch])
+
+ cmd_chk(['git', 'checkout', '-b', branch, parent_branch])
+
+ info_fn = 'PATCH.' + patch.name
+ with open(info_fn, 'w', encoding='utf-8') as fh:
+ fh.write(info_txt)
+ cmd_chk(['git', 'add', info_fn])
+
+ with open(patch.fn, 'r', encoding='utf-8') as fh:
+ patch_txt = fh.read()
+
+ cmd_run('patch -p1'.split(), input=patch_txt)
+
+ for fn in glob.glob('*.orig') + glob.glob('*/*.orig'):
+ os.unlink(fn)
+
+ pos = 0
+ new_file_re = re.compile(r'\nnew file mode (?P<mode>\d+)\s+--- /dev/null\s+\+\+\+ b/(?P<fn>.+)')
+ while True:
+ m = new_file_re.search(patch_txt, pos)
+ if not m:
+ break
+ os.chmod(m['fn'], int(m['mode'], 8))
+ cmd_chk(['git', 'add', m['fn']])
+ pos = m.end()
+
+ while True:
+ cmd_chk('git status'.split())
+ ans = input('Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ')
+ if ans == '':
+ break
+ cmd_chk("git add " + ans, shell=True)
+
+ while True:
+ s = cmd_run(['git', 'commit', '-a', '-m', f"Creating branch from {patch.name}.diff."])
+ if not s.returncode:
+ break
+ s = cmd_run(['/bin/zsh'])
+ if s.returncode:
+ die('Aborting due to shell error code')
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Create a git patch branch from an rsync patch file.", add_help=False)
+ parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.")
+ parser.add_argument('--add-missing', '-a', action='store_true', help="Add a branch for every patches/*.diff that doesn't have a branch.")
+ parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
+ parser.add_argument('--delete', dest='delete_local_branches', action='store_true', help="Delete all the local patch/BASE/* branches, not just the ones that are being recreated.")
+ parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.")
+ parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.")
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et ft=python
diff --git a/packaging/cull-options b/packaging/cull-options
new file mode 100755
index 0000000..e71818c
--- /dev/null
+++ b/packaging/cull-options
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# This script outputs either perl or python code that parses all possible options
+# that the code in options.c might send to the server. The resulting code is then
+# included in the rrsync script.
+
+import re, argparse
+
+short_no_arg = { }
+short_with_num = { '@': 1 }
+long_opts = { # These include some extra long-args that BackupPC uses:
+ 'block-size': 1,
+ 'daemon': -1,
+ 'debug': 1,
+ 'fake-super': 0,
+ 'fuzzy': 0,
+ 'group': 0,
+ 'hard-links': 0,
+ 'ignore-times': 0,
+ 'info': 1,
+ 'links': 0,
+ 'log-file': 3,
+ 'munge-links': 0,
+ 'no-munge-links': -1,
+ 'one-file-system': 0,
+ 'owner': 0,
+ 'perms': 0,
+ 'recursive': 0,
+ 'stderr': 1,
+ 'times': 0,
+ 'copy-devices': -1,
+ 'write-devices': -1,
+ }
+
+def main():
+ last_long_opt = None
+
+ with open('../options.c') as fh:
+ for line in fh:
+ m = re.search(r"argstr\[x\+\+\] = '([^.ie])'", line)
+ if m:
+ short_no_arg[m.group(1)] = 1
+ last_long_opt = None
+ continue
+
+ m = re.search(r'asprintf\([^,]+, "-([a-zA-Z0-9])\%l?[ud]"', line)
+ if m:
+ short_with_num[m.group(1)] = 1
+ last_long_opt = None
+ continue
+
+ m = re.search(r'args\[ac\+\+\] = "--([^"=]+)"', line)
+ if m:
+ last_long_opt = m.group(1)
+ if last_long_opt not in long_opts:
+ long_opts[last_long_opt] = 0
+ else:
+ last_long_opt = None
+ continue
+
+ if last_long_opt:
+ m = re.search(r'args\[ac\+\+\] = safe_arg\("", ([^[("\s]+)\);', line)
+ if m:
+ long_opts[last_long_opt] = 2
+ last_long_opt = None
+ continue
+ if 'args[ac++] = ' in line:
+ last_long_opt = None
+
+ m = re.search(r'return "--([^"]+-dest)";', line)
+ if m:
+ long_opts[m.group(1)] = 2
+ last_long_opt = None
+ continue
+
+ m = re.search(r'asprintf\([^,]+, "--([^"=]+)=', line)
+ if not m:
+ m = re.search(r'args\[ac\+\+\] = "--([^"=]+)=', line)
+ if not m:
+ m = re.search(r'args\[ac\+\+\] = safe_arg\("--([^"=]+)"', line)
+ if not m:
+ m = re.search(r'fmt = .*: "--([^"=]+)=', line)
+ if m:
+ long_opts[m.group(1)] = 1
+ last_long_opt = None
+
+ long_opts['files-from'] = 3
+
+ txt = """\
+### START of options data produced by the cull-options script. ###
+
+# To disable a short-named option, add its letter to this string:
+"""
+
+ txt += str_assign('short_disabled', 's') + "\n"
+ txt += '# These are also disabled when the restricted dir is not "/":\n'
+ txt += str_assign('short_disabled_subdir', 'KLk') + "\n"
+ txt += '# These are all possible short options that we will accept (when not disabled above):\n'
+ txt += str_assign('short_no_arg', ''.join(sorted(short_no_arg)), 'DO NOT REMOVE ANY')
+ txt += str_assign('short_with_num', ''.join(sorted(short_with_num)), 'DO NOT REMOVE ANY')
+
+ txt += """
+# To disable a long-named option, change its value to a -1. The values mean:
+# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
+# check the arg when receiving; and 3 = always check the arg.
+"""
+
+ print(txt, end='')
+
+ if args.python:
+ print("long_opts = {")
+ sep = ':'
+ else:
+ print("our %long_opt = (")
+ sep = ' =>'
+
+ for opt in sorted(long_opts):
+ if opt.startswith(('min-', 'max-')):
+ val = 1
+ else:
+ val = long_opts[opt]
+ print(' ', repr(opt) + sep, str(val) + ',')
+
+ if args.python:
+ print("}")
+ else:
+ print(");")
+ print("\n### END of options data produced by the cull-options script. ###")
+
+
+def str_assign(name, val, comment=None):
+ comment = ' # ' + comment if comment else ''
+ if args.python:
+ return name + ' = ' + repr(val) + comment + "\n"
+ return 'our $' + name + ' = ' + repr(val) + ';' + comment + "\n"
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Output culled rsync options for rrsync.", add_help=False)
+ out_group = parser.add_mutually_exclusive_group()
+ out_group.add_argument('--perl', action='store_true', help="Output perl code.")
+ out_group.add_argument('--python', action='store_true', help="Output python code (the default).")
+ parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
+ args = parser.parse_args()
+ if not args.perl:
+ args.python = True
+ main()
+
+# vim: sw=4 et
diff --git a/packaging/lsb/rsync.spec b/packaging/lsb/rsync.spec
new file mode 100644
index 0000000..f2d7aa4
--- /dev/null
+++ b/packaging/lsb/rsync.spec
@@ -0,0 +1,87 @@
+Summary: A fast, versatile, remote (and local) file-copying tool
+Name: rsync
+Version: 3.2.7
+%define fullversion %{version}
+Release: 1
+%define srcdir src
+Group: Applications/Internet
+License: GPL
+Source0: https://rsync.samba.org/ftp/rsync/%{srcdir}/rsync-%{fullversion}.tar.gz
+#Source1: https://rsync.samba.org/ftp/rsync/%{srcdir}/rsync-patches-%{fullversion}.tar.gz
+URL: https://rsync.samba.org/
+
+Prefix: %{_prefix}
+BuildRoot: /var/tmp/%{name}-root
+
+%package ssl-daemon
+Summary: An stunnel config file to support ssl rsync daemon connections.
+Group: Applications/Internet
+Requires: rsync, stunnel >= 4
+
+%description
+Rsync is a fast and extraordinarily versatile file copying tool. It can
+copy locally, to/from another host over any remote shell, or to/from a
+remote rsync daemon. It offers a large number of options that control
+every aspect of its behavior and permit very flexible specification of the
+set of files to be copied. It is famous for its delta-transfer algorithm,
+which reduces the amount of data sent over the network by sending only the
+differences between the source files and the existing files in the
+destination. Rsync is widely used for backups and mirroring and as an
+improved copy command for everyday use.
+
+%description ssl-daemon
+Provides a config file for stunnel that will (if you start your stunnel
+service) cause stunnel to listen for ssl rsync-daemon connections and run
+"rsync --daemon" to handle them.
+
+%prep
+# Choose one -- setup source only, or setup source + rsync-patches:
+%setup -q -n rsync-%{fullversion}
+#%setup -q -b1 -n rsync-%{fullversion}
+
+# If you you used "%setup -q -b1 ...", choose the patches you wish to apply:
+#patch -p1 <patches/acls.diff
+#patch -p1 <patches/xattrs.diff
+#patch -p1 <patches/remote-option.diff
+#patch -p1 <patches/db.diff
+
+# Avoid extra perl dependencies for scripts going into doc dir.
+chmod -x support/*
+
+%build
+#./prepare-source
+%configure
+
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install install-ssl-daemon DESTDIR=$RPM_BUILD_ROOT
+
+mkdir -p $RPM_BUILD_ROOT/etc/xinetd.d $RPM_BUILD_ROOT/etc/rsync-ssl/certs
+install -m 644 packaging/lsb/rsync.xinetd $RPM_BUILD_ROOT/etc/xinetd.d/rsync
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%doc COPYING NEWS.md README.md support/ tech_report.tex
+%config(noreplace) /etc/xinetd.d/rsync
+%{_prefix}/bin/rsync
+%{_prefix}/bin/rsync-ssl
+%{_mandir}/man1/rsync.1*
+%{_mandir}/man1/rsync-ssl.1*
+%{_mandir}/man5/rsyncd.conf.5*
+
+%files ssl-daemon
+%config(noreplace) /etc/stunnel/rsyncd.conf
+%dir /etc/rsync-ssl/certs
+
+%changelog
+* Thu Oct 20 2022 Wayne Davison <wayne@opencoder.net>
+Released 3.2.7.
+
+* Fri Mar 21 2008 Wayne Davison <wayne@opencoder.net>
+Added installation of /etc/xinetd.d/rsync file and some commented-out
+lines that demonstrate how to use the rsync-patches tar file.
diff --git a/packaging/lsb/rsync.xinetd b/packaging/lsb/rsync.xinetd
new file mode 100644
index 0000000..63fc11c
--- /dev/null
+++ b/packaging/lsb/rsync.xinetd
@@ -0,0 +1,13 @@
+# default: off
+# description: The rsync server is a good addition to an ftp server, as it
+# allows crc checksumming etc.
+service rsync
+{
+ disable = yes
+ socket_type = stream
+ wait = no
+ user = root
+ server = /usr/bin/rsync
+ server_args = --daemon
+ log_on_failure += USERID
+}
diff --git a/packaging/openssl-rsync.cnf b/packaging/openssl-rsync.cnf
new file mode 100644
index 0000000..7432285
--- /dev/null
+++ b/packaging/openssl-rsync.cnf
@@ -0,0 +1,18 @@
+# This config file can be used with rsync to enable legacy digests
+# (such as MD4) by using the OPENSSL_CONF environment variable.
+# See rsync's configure --with-openssl-conf=/path/name option.
+
+openssl_conf = openssl_init
+
+[openssl_init]
+providers = provider_sect
+
+[provider_sect]
+default = default_sect
+legacy = legacy_sect
+
+[default_sect]
+activate = 1
+
+[legacy_sect]
+activate = 1
diff --git a/packaging/patch-update b/packaging/patch-update
new file mode 100755
index 0000000..fd56a9d
--- /dev/null
+++ b/packaging/patch-update
@@ -0,0 +1,244 @@
+#!/usr/bin/env -S python3 -B
+
+# This script is used to turn one or more of the "patch/BASE/*" branches
+# into one or more diffs in the "patches" directory. Pass the option
+# --gen if you want generated files in the diffs. Pass the name of
+# one or more diffs if you want to just update a subset of all the
+# diffs.
+
+import os, sys, re, argparse, time, shutil
+
+sys.path = ['packaging'] + sys.path
+
+from pkglib import *
+
+MAKE_GEN_CMDS = [
+ './prepare-source'.split(),
+ 'cd build && if test -f config.status ; then ./config.status ; else ../configure ; fi',
+ 'make -C build gen'.split(),
+ ]
+TMP_DIR = "patches.gen"
+
+os.environ['GIT_MERGE_AUTOEDIT'] = 'no'
+
+def main():
+ global master_commit, parent_patch, description, completed, last_touch
+
+ if not os.path.isdir(args.patches_dir):
+ die(f'No "{args.patches_dir}" directory was found.')
+ if not os.path.isdir('.git'):
+ die('No ".git" directory present in the current dir.')
+
+ starting_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
+
+ master_commit = latest_git_hash(args.base_branch)
+
+ if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
+ die('You must setup an auto-build-save dir to use this script.')
+
+ if args.gen:
+ if os.path.lexists(TMP_DIR):
+ die(f'"{TMP_DIR}" must not exist in the current directory.')
+ gen_files = get_gen_files()
+ os.mkdir(TMP_DIR, 0o700)
+ for cmd in MAKE_GEN_CMDS:
+ cmd_chk(cmd)
+ cmd_chk(['rsync', '-a', *gen_files, f'{TMP_DIR}/master/'])
+
+ last_touch = int(time.time())
+
+ # Start by finding all patches so that we can load all possible parents.
+ patches = sorted(list(get_patch_branches(args.base_branch)))
+
+ parent_patch = { }
+ description = { }
+
+ for patch in patches:
+ branch = f"patch/{args.base_branch}/{patch}"
+ desc = ''
+ proc = cmd_pipe(['git', 'diff', '-U1000', f"{args.base_branch}...{branch}", '--', f"PATCH.{patch}"])
+ in_diff = False
+ for line in proc.stdout:
+ if in_diff:
+ if not re.match(r'^[ +]', line):
+ continue
+ line = line[1:]
+ m = re.search(r'patch -p1 <patches/(\S+)\.diff', line)
+ if m and m[1] != patch:
+ parpat = parent_patch[patch] = m[1]
+ if not parpat in patches:
+ die(f"Parent of {patch} is not a local branch: {parpat}")
+ desc += line
+ elif re.match(r'^@@ ', line):
+ in_diff = True
+ description[patch] = desc
+ proc.communicate()
+
+ if args.patch_files: # Limit the list of patches to actually process
+ valid_patches = patches
+ patches = [ ]
+ for fn in args.patch_files:
+ name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
+ if name not in valid_patches:
+ die(f"Local branch not available for patch: {name}")
+ patches.append(name)
+
+ completed = set()
+
+ for patch in patches:
+ if patch in completed:
+ continue
+ if not update_patch(patch):
+ break
+
+ if args.gen:
+ shutil.rmtree(TMP_DIR)
+
+ while last_touch >= int(time.time()):
+ time.sleep(1)
+ cmd_chk(['git', 'checkout', starting_branch])
+ cmd_chk(['packaging/prep-auto-dir'], discard='output')
+
+
+def update_patch(patch):
+ global last_touch
+
+ completed.add(patch) # Mark it as completed early to short-circuit any (bogus) dependency loops.
+
+ parent = parent_patch.get(patch, None)
+ if parent:
+ if parent not in completed:
+ if not update_patch(parent):
+ return 0
+ based_on = parent = f"patch/{args.base_branch}/{parent}"
+ else:
+ parent = args.base_branch
+ based_on = master_commit
+
+ print(f"======== {patch} ========")
+
+ while args.gen and last_touch >= int(time.time()):
+ time.sleep(1)
+
+ branch = f"patch/{args.base_branch}/{patch}"
+ s = cmd_run(['git', 'checkout', branch])
+ if s.returncode != 0:
+ return 0
+
+ s = cmd_run(['git', 'merge', based_on])
+ ok = s.returncode == 0
+ skip_shell = False
+ if not ok or args.cmd or args.make or args.shell:
+ cmd_chk(['packaging/prep-auto-dir'], discard='output')
+ if not ok:
+ print(f'"git merge {based_on}" incomplete -- please fix.')
+ if not run_a_shell(parent, patch):
+ return 0
+ if not args.make and not args.cmd:
+ skip_shell = True
+ if args.make:
+ if cmd_run(['packaging/smart-make']).returncode != 0:
+ if not run_a_shell(parent, patch):
+ return 0
+ if not args.cmd:
+ skip_shell = True
+ if args.cmd:
+ if cmd_run(args.cmd).returncode != 0:
+ if not run_a_shell(parent, patch):
+ return 0
+ skip_shell = True
+ if args.shell and not skip_shell:
+ if not run_a_shell(parent, patch):
+ return 0
+
+ with open(f"{args.patches_dir}/{patch}.diff", 'w', encoding='utf-8') as fh:
+ fh.write(description[patch])
+ fh.write(f"\nbased-on: {based_on}\n")
+
+ if args.gen:
+ gen_files = get_gen_files()
+ for cmd in MAKE_GEN_CMDS:
+ cmd_chk(cmd)
+ cmd_chk(['rsync', '-a', *gen_files, f"{TMP_DIR}/{patch}/"])
+ else:
+ gen_files = [ ]
+ last_touch = int(time.time())
+
+ proc = cmd_pipe(['git', 'diff', based_on])
+ skipping = False
+ for line in proc.stdout:
+ if skipping:
+ if not re.match(r'^diff --git a/', line):
+ continue
+ skipping = False
+ elif re.match(r'^diff --git a/PATCH', line):
+ skipping = True
+ continue
+ if not re.match(r'^index ', line):
+ fh.write(line)
+ proc.communicate()
+
+ if args.gen:
+ e_tmp_dir = re.escape(TMP_DIR)
+ diff_re = re.compile(r'^(diff -Nurp) %s/[^/]+/(.*?) %s/[^/]+/(.*)' % (e_tmp_dir, e_tmp_dir))
+ minus_re = re.compile(r'^\-\-\- %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
+ plus_re = re.compile(r'^\+\+\+ %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
+
+ if parent == args.base_branch:
+ parent_dir = 'master'
+ else:
+ m = re.search(r'([^/]+)$', parent)
+ parent_dir = m[1]
+
+ proc = cmd_pipe(['diff', '-Nurp', f"{TMP_DIR}/{parent_dir}", f"{TMP_DIR}/{patch}"])
+ for line in proc.stdout:
+ line = diff_re.sub(r'\1 a/\2 b/\3', line)
+ line = minus_re.sub(r'--- a/\1', line)
+ line = plus_re.sub(r'+++ b/\1', line)
+ fh.write(line)
+ proc.communicate()
+
+ return 1
+
+
+def run_a_shell(parent, patch):
+ m = re.search(r'([^/]+)$', parent)
+ parent_dir = m[1]
+ os.environ['PS1'] = f"[{parent_dir}] {patch}: "
+
+ while True:
+ s = cmd_run([os.environ.get('SHELL', '/bin/sh')])
+ if s.returncode != 0:
+ ans = input("Abort? [n/y] ")
+ if re.match(r'^y', ans, flags=re.I):
+ return False
+ continue
+ cur_branch, is_clean, status_txt = check_git_status(0)
+ if is_clean:
+ break
+ print(status_txt, end='')
+
+ cmd_run('rm -f build/*.o build/*/*.o')
+
+ return True
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Turn a git branch back into a diff files in the patches dir.", add_help=False)
+ parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.")
+ parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
+ parser.add_argument('--make', '-m', action='store_true', help="Run the smart-make script in every patch branch.")
+ parser.add_argument('--cmd', '-c', help="Run a command in every patch branch.")
+ parser.add_argument('--shell', '-s', action='store_true', help="Launch a shell for every patch/BASE/* branch updated, not just when a conflict occurs.")
+ parser.add_argument('--gen', metavar='DIR', nargs='?', const='', help='Include generated files. Optional DIR value overrides the default of using the "patches" dir.')
+ parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.")
+ parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.")
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ args = parser.parse_args()
+ if args.gen == '':
+ args.gen = args.patches_dir
+ elif args.gen is not None:
+ args.patches_dir = args.gen
+ main()
+
+# vim: sw=4 et ft=python
diff --git a/packaging/pkglib.py b/packaging/pkglib.py
new file mode 100644
index 0000000..c4c5741
--- /dev/null
+++ b/packaging/pkglib.py
@@ -0,0 +1,266 @@
+import os, sys, re, subprocess, argparse
+
+# This python3 library provides a few helpful routines that are
+# used by the latest packaging scripts.
+
+default_encoding = 'utf-8'
+
+# Output the msg args to stderr. Accepts all the args that print() accepts.
+def warn(*msg):
+ print(*msg, file=sys.stderr)
+
+
+# Output the msg args to stderr and die with a non-zero return-code.
+# Accepts all the args that print() accepts.
+def die(*msg):
+ warn(*msg)
+ sys.exit(1)
+
+
+# Set this to an encoding name or set it to None to avoid the default encoding idiom.
+def set_default_encoding(enc):
+ default_encoding = enc
+
+
+# Set shell=True if the cmd is a string; sets a default encoding unless raw=True was specified.
+def _tweak_opts(cmd, opts, **maybe_set_args):
+ def _maybe_set(o, **msa): # Only set a value if the user didn't already set it.
+ for var, val in msa.items():
+ if var not in o:
+ o[var] = val
+
+ opts = opts.copy()
+ _maybe_set(opts, **maybe_set_args)
+
+ if isinstance(cmd, str):
+ _maybe_set(opts, shell=True)
+
+ want_raw = opts.pop('raw', False)
+ if default_encoding and not want_raw:
+ _maybe_set(opts, encoding=default_encoding)
+
+ capture = opts.pop('capture', None)
+ if capture:
+ if capture == 'stdout':
+ _maybe_set(opts, stdout=subprocess.PIPE)
+ elif capture == 'stderr':
+ _maybe_set(opts, stderr=subprocess.PIPE)
+ elif capture == 'output':
+ _maybe_set(opts, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ elif capture == 'combined':
+ _maybe_set(opts, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ discard = opts.pop('discard', None)
+ if discard:
+ # We DO want to override any already set stdout|stderr values (unlike above).
+ if discard == 'stdout' or discard == 'output':
+ opts['stdout'] = subprocess.DEVNULL
+ if discard == 'stderr' or discard == 'output':
+ opts['stderr'] = subprocess.DEVNULL
+
+ return opts
+
+
+# This does a normal subprocess.run() with some auto-args added to make life easier.
+def cmd_run(cmd, **opts):
+ return subprocess.run(cmd, **_tweak_opts(cmd, opts))
+
+
+# Like cmd_run() with a default check=True specified.
+def cmd_chk(cmd, **opts):
+ return subprocess.run(cmd, **_tweak_opts(cmd, opts, check=True))
+
+
+# Capture stdout in a string and return an object with out, err, and rc (return code).
+# It defaults to capture='stdout' (so err is empty) but can be overridden using
+# capture='combined' or capture='output' (the latter populates the err value).
+def cmd_txt(cmd, **opts):
+ input = opts.pop('input', None)
+ if input is not None:
+ opts['stdin'] = subprocess.PIPE
+ proc = subprocess.Popen(cmd, **_tweak_opts(cmd, opts, capture='stdout'))
+ out, err = proc.communicate(input=input)
+ return argparse.Namespace(out=out, err=err, rc=proc.returncode)
+
+
+# Just like calling cmd_txt() except that it raises an error if the command has a non-0 return code.
+# The raised error includes the cmd, the return code, and the captured output.
+def cmd_txt_chk(cmd, **opts):
+ ct = cmd_txt(cmd, **opts)
+ if ct.rc != 0:
+ cmd_err = f'Command "{cmd}" returned non-0 exit status "{ct.rc}" and output:\n{ct.out}{ct.err}'
+ raise Exception(cmd_err)
+ return ct
+
+
+# Starts a piped-output command of stdout (by default) and leaves it up to you to read
+# the output and call communicate() on the returned object.
+def cmd_pipe(cmd, **opts):
+ return subprocess.Popen(cmd, **_tweak_opts(cmd, opts, capture='stdout'))
+
+
+# Runs a "git status" command and dies if the checkout is not clean (the
+# arg fatal_unless_clean can be used to make that non-fatal. Returns a
+# tuple of the current branch, the is_clean flag, and the status text.
+def check_git_status(fatal_unless_clean=True, subdir='.'):
+ status_txt = cmd_txt_chk(f"cd '{subdir}' && git status").out
+ is_clean = re.search(r'\nnothing to commit.+working (directory|tree) clean', status_txt) != None
+
+ if not is_clean and fatal_unless_clean:
+ if subdir == '.':
+ subdir = ''
+ else:
+ subdir = f" *{subdir}*"
+ die(f"The{subdir} checkout is not clean:\n" + status_txt)
+
+ m = re.match(r'^(?:# )?On branch (.+)\n', status_txt)
+ cur_branch = m[1] if m else None
+
+ return (cur_branch, is_clean, status_txt)
+
+
+# Calls check_git_status() on the current git checkout and (optionally) a subdir path's
+# checkout. Use fatal_unless_clean to indicate if an unclean checkout is fatal or not.
+# The master_branch arg indicates what branch we want both checkouts to be using, and
+# if the branch is wrong the user is given the option of either switching to the right
+# branch or aborting.
+def check_git_state(master_branch, fatal_unless_clean=True, check_extra_dir=None):
+ cur_branch = check_git_status(fatal_unless_clean)[0]
+ branch = re.sub(r'^patch/([^/]+)/[^/]+$', r'\1', cur_branch) # change patch/BRANCH/PATCH_NAME into BRANCH
+ if branch != master_branch:
+ print(f"The checkout is not on the {master_branch} branch.")
+ if master_branch != 'master':
+ sys.exit(1)
+ ans = input(f"Do you want me to continue with --branch={branch}? [n] ")
+ if not ans or not re.match(r'^y', ans, flags=re.I):
+ sys.exit(1)
+ master_branch = branch
+
+ if check_extra_dir and os.path.isdir(os.path.join(check_extra_dir, '.git')):
+ branch = check_git_status(fatal_unless_clean, check_extra_dir)[0]
+ if branch != master_branch:
+ print(f"The *{check_extra_dir}* checkout is on branch {branch}, not branch {master_branch}.")
+ ans = input(f"Do you want to change it to branch {master_branch}? [n] ")
+ if not ans or not re.match(r'^y', ans, flags=re.I):
+ sys.exit(1)
+ subdir.check_call(f"cd {check_extra_dir} && git checkout '{master_branch}'", shell=True)
+
+ return (cur_branch, master_branch)
+
+
+# Return the git hash of the most recent commit.
+def latest_git_hash(branch):
+ out = cmd_txt_chk(['git', 'log', '-1', '--no-color', branch]).out
+ m = re.search(r'^commit (\S+)', out, flags=re.M)
+ if not m:
+ die(f"Unable to determine commit hash for master branch: {branch}")
+ return m[1]
+
+
+# Return a set of all branch names that have the format "patch/BASE_BRANCH/NAME"
+# for the given base_branch string. Just the NAME portion is put into the set.
+def get_patch_branches(base_branch):
+ branches = set()
+ proc = cmd_pipe('git branch -l'.split())
+ for line in proc.stdout:
+ m = re.search(r' patch/([^/]+)/(.+)', line)
+ if m and m[1] == base_branch:
+ branches.add(m[2])
+ proc.communicate()
+ return branches
+
+
+def mandate_gensend_hook():
+ hook = '.git/hooks/pre-push'
+ if not os.path.exists(hook):
+ print('Creating hook file:', hook)
+ cmd_chk(['./rsync', '-a', 'packaging/pre-push', hook])
+ else:
+ ct = cmd_txt(['grep', 'make gensend', hook], discard='output')
+ if ct.rc:
+ die('Please add a "make gensend" into your', hook, 'script.')
+
+
+# Snag the GENFILES values out of the Makefile file and return them as a list.
+def get_gen_files(want_dir_plus_list=False):
+ cont_re = re.compile(r'\\\n')
+
+ gen_files = [ ]
+
+ auto_dir = os.path.join('auto-build-save', cmd_txt('git rev-parse --abbrev-ref HEAD').out.strip().replace('/', '%'))
+
+ with open(auto_dir + '/Makefile', 'r', encoding='utf-8') as fh:
+ for line in fh:
+ if not gen_files:
+ chk = re.sub(r'^GENFILES=', '', line)
+ if line == chk:
+ continue
+ line = chk
+ m = re.search(r'\\$', line)
+ line = re.sub(r'^\s+|\s*\\\n?$|\s+$', '', line)
+ gen_files += line.split()
+ if not m:
+ break
+
+ if want_dir_plus_list:
+ return (auto_dir, gen_files)
+
+ return [ os.path.join(auto_dir, fn) for fn in gen_files ]
+
+
+def get_rsync_version():
+ with open('version.h', 'r', encoding='utf-8') as fh:
+ txt = fh.read()
+ m = re.match(r'^#define\s+RSYNC_VERSION\s+"(\d.+?)"', txt)
+ if m:
+ return m[1]
+ die("Unable to find RSYNC_VERSION define in version.h")
+
+
+def get_NEWS_version_info():
+ rel_re = re.compile(r'^\| \S{2} \w{3} \d{4}\s+\|\s+(?P<ver>\d+\.\d+\.\d+)\s+\|\s+(?P<pdate>\d{2} \w{3} \d{4})?\s+\|\s+(?P<pver>\d+)\s+\|')
+ last_version = last_protocol_version = None
+ pdate = { }
+
+ with open('NEWS.md', 'r', encoding='utf-8') as fh:
+ for line in fh:
+ if not last_version: # Find the first non-dev|pre version with a release date.
+ m = re.search(r'rsync (\d+\.\d+\.\d+) .*\d\d\d\d', line)
+ if m:
+ last_version = m[1]
+ m = rel_re.match(line)
+ if m:
+ if m['pdate']:
+ pdate[m['ver']] = m['pdate']
+ if m['ver'] == last_version:
+ last_protocol_version = m['pver']
+
+ if not last_protocol_version:
+ die(f"Unable to determine protocol_version for {last_version}.")
+
+ return last_version, last_protocol_version, pdate
+
+
+def get_protocol_versions():
+ protocol_version = subprotocol_version = None
+
+ with open('rsync.h', 'r', encoding='utf-8') as fh:
+ for line in fh:
+ m = re.match(r'^#define\s+PROTOCOL_VERSION\s+(\d+)', line)
+ if m:
+ protocol_version = m[1]
+ continue
+ m = re.match(r'^#define\s+SUBPROTOCOL_VERSION\s+(\d+)', line)
+ if m:
+ subprotocol_version = m[1]
+ break
+
+ if not protocol_version:
+ die("Unable to determine the current PROTOCOL_VERSION.")
+
+ if not subprotocol_version:
+ die("Unable to determine the current SUBPROTOCOL_VERSION.")
+
+ return protocol_version, subprotocol_version
+
+# vim: sw=4 et
diff --git a/packaging/pre-push b/packaging/pre-push
new file mode 100755
index 0000000..8a71369
--- /dev/null
+++ b/packaging/pre-push
@@ -0,0 +1,16 @@
+#!/bin/bash -e
+
+cat >/dev/null # Just discard stdin data
+
+if [[ -f /proc/$PPID/cmdline ]]; then
+ while read -d $'\0' arg ; do
+ if [[ "$arg" == '--tags' ]] ; then
+ exit 0
+ fi
+ done </proc/$PPID/cmdline
+fi
+
+branch=`git rev-parse --abbrev-ref HEAD`
+if [[ "$branch" = master && "$*" == *github* ]]; then
+ make gensend
+fi
diff --git a/packaging/prep-auto-dir b/packaging/prep-auto-dir
new file mode 100755
index 0000000..b67f390
--- /dev/null
+++ b/packaging/prep-auto-dir
@@ -0,0 +1,43 @@
+#!/bin/sh -e
+
+# This script will setup the build dir based on the current git branch and the
+# directory auto-build-save/$BRANCH. We don't use a symlink for the build dir
+# because we want to maximize the ccache reuse, so all builds must happen in
+# the same real dir. When a dir is moved out of auto-build-save/$BRANCH to the
+# build dir, it is replaced with a symlink so that it can still be found under
+# that dir. The build dir also gets a .branch -> $BRANCH symlink so that we
+# can figure out the current build dir's branch.
+
+# To get started, just clone the rsync git repo and create the auto-build-save
+# dir. If you have an existing git checkout and it is not in a pristine state,
+# run "make distclean" before creating the auto-build-save dir.
+
+auto_top='auto-build-save'
+if test -d $auto_top && test -d .git; then
+ desired_branch=`git rev-parse --abbrev-ref HEAD | tr / %`
+ if test "$desired_branch" = HEAD; then
+ echo "ERROR: switch to the right build dir manually when in detached HEAD mode." 1>&2
+ exit 1
+ fi
+ auto_dir="$auto_top/$desired_branch"
+ if test -d build; then
+ cur_branch=`readlink build/.branch`
+ else
+ cur_branch='/'
+ fi
+ if test "$desired_branch" != "$cur_branch"; then
+ if test "$cur_branch" != /; then
+ rm -f "$auto_top/$cur_branch"
+ mv build "$auto_top/$cur_branch"
+ fi
+ test -d "$auto_dir" || mkdir "$auto_dir"
+ test -h "$auto_dir/.branch" || ln -s "$desired_branch" "$auto_dir/.branch"
+ mv "$auto_dir" build
+ ln -s ../build "$auto_dir"
+ fi
+ if test ! -h Makefile; then
+ rm -f Makefile
+ ln -s packaging/auto-Makefile Makefile
+ fi
+ echo $desired_branch
+fi
diff --git a/packaging/release-rsync b/packaging/release-rsync
new file mode 100755
index 0000000..511e90f
--- /dev/null
+++ b/packaging/release-rsync
@@ -0,0 +1,399 @@
+#!/usr/bin/env -S python3 -B
+
+# This script expects the directory ~/samba-rsync-ftp to exist and to be a
+# copy of the /home/ftp/pub/rsync dir on samba.org. When the script is done,
+# the git repository in the current directory will be updated, and the local
+# ~/samba-rsync-ftp dir will be ready to be rsynced to samba.org.
+
+import os, sys, re, argparse, glob, shutil, signal
+from datetime import datetime
+from getpass import getpass
+
+sys.path = ['packaging'] + sys.path
+
+from pkglib import *
+
+os.environ['LESS'] = 'mqeiXR'; # Make sure that -F is turned off and -R is turned on.
+dest = os.environ['HOME'] + '/samba-rsync-ftp'
+ORIGINAL_PATH = os.environ['PATH']
+
+def main():
+ if not os.path.isfile('packaging/release-rsync'):
+ die('You must run this script from the top of your rsync checkout.')
+
+ now = datetime.now()
+ cl_today = now.strftime('* %a %b %d %Y')
+ year = now.strftime('%Y')
+ ztoday = now.strftime('%d %b %Y')
+ today = ztoday.lstrip('0')
+
+ mandate_gensend_hook()
+
+ curdir = os.getcwd()
+
+ signal.signal(signal.SIGINT, signal_handler)
+
+ if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
+ die('You must setup an auto-build-save dir to use this script.');
+
+ auto_dir, gen_files = get_gen_files(True)
+ gen_pathnames = [ os.path.join(auto_dir, fn) for fn in gen_files ]
+
+ dash_line = '=' * 74
+
+ print(f"""\
+{dash_line}
+== This will release a new version of rsync onto an unsuspecting world. ==
+{dash_line}
+""")
+
+ with open('build/rsync.1') as fh:
+ for line in fh:
+ if line.startswith(r'.\" prefix='):
+ doc_prefix = line.split('=')[1].strip()
+ if doc_prefix != '/usr':
+ warn(f"*** The documentation was built with prefix {doc_prefix} instead of /usr ***")
+ die("*** Read the md2man script for a way to override this. ***")
+ break
+ if line.startswith('.P'):
+ die("Failed to find the prefix comment at the start of the rsync.1 manpage.")
+
+ if not os.path.isdir(dest):
+ die(dest, "dest does not exist")
+ if not os.path.isdir('.git'):
+ die("There is no .git dir in the current directory.")
+ if os.path.lexists('a'):
+ die('"a" must not exist in the current directory.')
+ if os.path.lexists('b'):
+ die('"b" must not exist in the current directory.')
+ if os.path.lexists('patches.gen'):
+ die('"patches.gen" must not exist in the current directory.')
+
+ check_git_state(args.master_branch, True, 'patches')
+
+ curversion = get_rsync_version()
+
+ # All version values are strings!
+ lastversion, last_protocol_version, pdate = get_NEWS_version_info()
+ protocol_version, subprotocol_version = get_protocol_versions()
+
+ version = curversion
+ m = re.search(r'pre(\d+)', version)
+ if m:
+ version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version)
+ else:
+ version = version.replace('dev', 'pre1')
+
+ ans = input(f"Please enter the version number of this release: [{version}] ")
+ if ans == '.':
+ version = re.sub(r'pre\d+', '', version)
+ elif ans != '':
+ version = ans
+ if not re.match(r'^[\d.]+(pre\d+)?$', version):
+ die(f'Invalid version: "{version}"')
+
+ v_ver = 'v' + version
+ rsync_ver = 'rsync-' + version
+
+ if os.path.lexists(rsync_ver):
+ die(f'"{rsync_ver}" must not exist in the current directory.')
+
+ out = cmd_txt_chk(['git', 'tag', '-l', v_ver]).out
+ if out != '':
+ print(f"Tag {v_ver} already exists.")
+ ans = input("\nDelete tag or quit? [Q/del] ")
+ if not re.match(r'^del', ans, flags=re.I):
+ die("Aborted")
+ cmd_chk(['git', 'tag', '-d', v_ver])
+ if os.path.isdir('patches/.git'):
+ cmd_chk(f"cd patches && git tag -d '{v_ver}'")
+
+ version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
+ if 'pre' in version and not curversion.endswith('dev'):
+ lastversion = curversion
+
+ ans = input(f"Enter the previous version to produce a patch against: [{lastversion}] ")
+ if ans != '':
+ lastversion = ans
+ lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion)
+
+ rsync_lastver = 'rsync-' + lastversion
+ if os.path.lexists(rsync_lastver):
+ die(f'"{rsync_lastver}" must not exist in the current directory.')
+
+ m = re.search(r'(pre\d+)', version)
+ pre = m[1] if m else ''
+
+ release = '0.1' if pre else '1'
+ ans = input(f"Please enter the RPM release number of this release: [{release}] ")
+ if ans != '':
+ release = ans
+ if pre:
+ release += '.' + pre
+
+ finalversion = re.sub(r'pre\d+', '', version)
+ proto_changed = protocol_version != last_protocol_version
+ if proto_changed:
+ if finalversion in pdate:
+ proto_change_date = pdate[finalversion]
+ else:
+ while True:
+ ans = input("On what date did the protocol change to {protocol_version} get checked in? (dd Mmm yyyy) ")
+ if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans):
+ break
+ proto_change_date = ans
+ else:
+ proto_change_date = ' ' * 11
+
+ if 'pre' in lastversion:
+ if not pre:
+ die("You should not diff a release version against a pre-release version.")
+ srcdir = srcdiffdir = lastsrcdir = 'src-previews'
+ skipping = ' ** SKIPPING **'
+ elif pre:
+ srcdir = srcdiffdir = 'src-previews'
+ lastsrcdir = 'src'
+ skipping = ' ** SKIPPING **'
+ else:
+ srcdir = lastsrcdir = 'src'
+ srcdiffdir = 'src-diffs'
+ skipping = ''
+
+ print(f"""
+{dash_line}
+version is "{version}"
+lastversion is "{lastversion}"
+dest is "{dest}"
+curdir is "{curdir}"
+srcdir is "{srcdir}"
+srcdiffdir is "{srcdiffdir}"
+lastsrcdir is "{lastsrcdir}"
+release is "{release}"
+
+About to:
+ - tweak SUBPROTOCOL_VERSION in rsync.h, if needed
+ - tweak the version in version.h and the spec files
+ - tweak NEWS.md to ensure header values are correct
+ - generate configure.sh, config.h.in, and proto.h
+ - page through the differences
+""")
+ ans = input("<Press Enter to continue> ")
+
+ specvars = {
+ 'Version:': finalversion,
+ 'Release:': release,
+ '%define fullversion': f'%{{version}}{pre}',
+ 'Released': version + '.',
+ '%define srcdir': srcdir,
+ }
+
+ tweak_files = 'version.h rsync.h NEWS.md'.split()
+ tweak_files += glob.glob('packaging/*.spec')
+ tweak_files += glob.glob('packaging/*/*.spec')
+
+ for fn in tweak_files:
+ with open(fn, 'r', encoding='utf-8') as fh:
+ old_txt = txt = fh.read()
+ if fn == 'version.h':
+ x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M)
+ msg = f"Unable to update RSYNC_VERSION in {fn}"
+ txt = replace_or_die(x_re, r'\1 "%s"' % version, txt, msg)
+ elif '.spec' in fn:
+ for var, val in specvars.items():
+ x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
+ txt = replace_or_die(x_re, var + ' ' + val, txt, f"Unable to update {var} in {fn}")
+ x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
+ txt = replace_or_die(x_re, r'%s \1' % cl_today, txt, f"Unable to update ChangeLog header in {fn}")
+ elif fn == 'rsync.h':
+ x_re = re.compile('(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
+ repl = lambda m: m[1] + ' ' + ('0' if not pre or not proto_changed else '1' if m[2] == '0' else m[2])
+ txt = replace_or_die(x_re, repl, txt, f"Unable to find SUBPROTOCOL_VERSION define in {fn}")
+ elif fn == 'NEWS.md':
+ efv = re.escape(finalversion)
+ x_re = re.compile(r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
+ + r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?')
+ rel_day = 'UNRELEASED' if pre else today
+ repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n'
+ + '## Changes in this version:\n')
+ if proto_changed:
+ repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n'
+ good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1)
+ msg = f"The top lines of {fn} are not in the right format. It should be:\n" + good_top
+ txt = replace_or_die(x_re, repl, txt, msg)
+ x_re = re.compile(r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv, re.M)
+ repl = lambda m: m[1] + (m[2] if pre else ztoday) + m[3] + proto_change_date + m[4] + protocol_version + m[5]
+ txt = replace_or_die(x_re, repl, txt, f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}')
+ else:
+ die(f"Unrecognized file in tweak_files: {fn}")
+
+ if txt != old_txt:
+ print(f"Updating {fn}")
+ with open(fn, 'w', encoding='utf-8') as fh:
+ fh.write(txt)
+
+ cmd_chk(['packaging/year-tweak'])
+
+ print(dash_line)
+ cmd_run("git diff".split())
+
+ srctar_name = f"{rsync_ver}.tar.gz"
+ pattar_name = f"rsync-patches-{version}.tar.gz"
+ diff_name = f"{rsync_lastver}-{version}.diffs.gz"
+ srctar_file = os.path.join(dest, srcdir, srctar_name)
+ pattar_file = os.path.join(dest, srcdir, pattar_name)
+ diff_file = os.path.join(dest, srcdiffdir, diff_name)
+ lasttar_file = os.path.join(dest, lastsrcdir, rsync_lastver + '.tar.gz')
+
+ print(f"""\
+{dash_line}
+
+About to:
+ - git commit all changes
+ - run a full build, ensuring that the manpages & configure.sh are up-to-date
+ - merge the {args.master_branch} branch into the patch/{args.master_branch}/* branches
+ - update the files in the "patches" dir and OPTIONALLY (if you type 'y') to
+ run patch-update with the --make option (which opens a shell on error)
+""")
+ ans = input("<Press Enter OR 'y' to continue> ")
+
+ s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]'])
+ if s.returncode:
+ die('Aborting')
+
+ cmd_chk('touch configure.ac && packaging/smart-make && make gen')
+
+ print('Creating any missing patch branches.')
+ s = cmd_run(f'packaging/branch-from-patch --branch={args.master_branch} --add-missing')
+ if s.returncode:
+ die('Aborting')
+
+ print('Updating files in "patches" dir ...')
+ s = cmd_run(f'packaging/patch-update --branch={args.master_branch}')
+ if s.returncode:
+ die('Aborting')
+
+ if re.match(r'^y', ans, re.I):
+ print(f'\nRunning smart-make on all "patch/{args.master_branch}/*" branches ...')
+ cmd_run(f"packaging/patch-update --branch={args.master_branch} --skip-check --make")
+
+ if os.path.isdir('patches/.git'):
+ s = cmd_run(f"cd patches && git commit -a -m 'The patches for {version}.'")
+ if s.returncode:
+ die('Aborting')
+
+ print(f"""\
+{dash_line}
+
+About to:
+ - create signed tag for this release: {v_ver}
+ - create release diffs, "{diff_name}"
+ - create release tar, "{srctar_name}"
+ - generate {rsync_ver}/patches/* files
+ - create patches tar, "{pattar_name}"
+ - update top-level README.md, NEWS.md, TODO, and ChangeLog
+ - update top-level rsync*.html manpages
+ - gpg-sign the release files
+ - update hard-linked top-level release files{skipping}
+""")
+ ans = input("<Press Enter to continue> ")
+
+ # TODO: is there a better way to ensure that our passphrase is in the agent?
+ cmd_run("touch TeMp; gpg --sign TeMp; rm TeMp*")
+
+ out = cmd_txt(f"git tag -s -m 'Version {version}.' {v_ver}", capture='combined').out
+ print(out, end='')
+ if 'bad passphrase' in out or 'failed' in out:
+ die('Aborting')
+
+ if os.path.isdir('patches/.git'):
+ out = cmd_txt(f"cd patches && git tag -s -m 'Version {version}.' {v_ver}", capture='combined').out
+ print(out, end='')
+ if 'bad passphrase' in out or 'failed' in out:
+ die('Aborting')
+
+ os.environ['PATH'] = ORIGINAL_PATH
+
+ # Extract the generated files from the old tar.
+ tweaked_gen_files = [ os.path.join(rsync_lastver, fn) for fn in gen_files ]
+ cmd_run(['tar', 'xzf', lasttar_file, *tweaked_gen_files])
+ os.rename(rsync_lastver, 'a')
+
+ print(f"Creating {diff_file} ...")
+ cmd_chk(['rsync', '-a', *gen_pathnames, 'b/'])
+
+ sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # CAUTION: must not contain any single quotes!
+ cmd_chk(f"(git diff v{lastversion} {v_ver} -- ':!.github'; diff -upN a b | sed -r '{sed_script}') | gzip -9 >{diff_file}")
+ shutil.rmtree('a')
+ os.rename('b', rsync_ver)
+
+ print(f"Creating {srctar_file} ...")
+ cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | tar xf -")
+ cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver}/")
+ cmd_chk(['fakeroot', 'tar', 'czf', srctar_file, '--exclude=.github', rsync_ver])
+ shutil.rmtree(rsync_ver)
+
+ print(f'Updating files in "{rsync_ver}/patches" dir ...')
+ os.mkdir(rsync_ver, 0o755)
+ os.mkdir(f"{rsync_ver}/patches", 0o755)
+ cmd_chk(f"packaging/patch-update --skip-check --branch={args.master_branch} --gen={rsync_ver}/patches".split())
+
+ print(f"Creating {pattar_file} ...")
+ cmd_chk(['fakeroot', 'tar', 'chzf', pattar_file, rsync_ver + '/patches'])
+ shutil.rmtree(rsync_ver)
+
+ print(f"Updating the other files in {dest} ...")
+ md_files = 'README.md NEWS.md INSTALL.md'.split()
+ html_files = [ fn for fn in gen_pathnames if fn.endswith('.html') ]
+ cmd_chk(['rsync', '-a', *md_files, *html_files, dest])
+ cmd_chk(["./md-convert", "--dest", dest, *md_files])
+
+ cmd_chk(f"git log --name-status | gzip -9 >{dest}/ChangeLog.gz")
+
+ for fn in (srctar_file, pattar_file, diff_file):
+ asc_fn = fn + '.asc'
+ if os.path.lexists(asc_fn):
+ os.unlink(asc_fn)
+ res = cmd_run(['gpg', '--batch', '-ba', fn])
+ if res.returncode != 0 and res.returncode != 2:
+ die("gpg signing failed")
+
+ if not pre:
+ for find in f'{dest}/rsync-*.gz {dest}/rsync-*.asc {dest}/src-previews/rsync-*diffs.gz*'.split():
+ for fn in glob.glob(find):
+ os.unlink(fn)
+ top_link = [
+ srctar_file, f"{srctar_file}.asc",
+ pattar_file, f"{pattar_file}.asc",
+ diff_file, f"{diff_file}.asc",
+ ]
+ for fn in top_link:
+ os.link(fn, re.sub(r'/src(-\w+)?/', '/', fn))
+
+ print(f"""\
+{dash_line}
+
+Local changes are done. When you're satisfied, push the git repository
+and rsync the release files. Remember to announce the release on *BOTH*
+rsync-announce@lists.samba.org and rsync@lists.samba.org (and the web)!
+""")
+
+
+def replace_or_die(regex, repl, txt, die_msg):
+ m = regex.search(txt)
+ if not m:
+ die(die_msg)
+ return regex.sub(repl, txt, 1)
+
+
+def signal_handler(sig, frame):
+ die("\nAborting due to SIGINT.")
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Prepare a new release of rsync in the git repo & ftp dir.", add_help=False)
+ parser.add_argument('--branch', '-b', dest='master_branch', default='master', help="The branch to release. Default: master.")
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et ft=python
diff --git a/packaging/smart-make b/packaging/smart-make
new file mode 100755
index 0000000..3826432
--- /dev/null
+++ b/packaging/smart-make
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+set -e
+
+export LANG=C
+
+branch=`packaging/prep-auto-dir`
+if test x"$branch" = x; then
+ srcdir=.
+else
+ cd build
+ srcdir=..
+fi
+
+if test -f configure.sh; then
+ cp -p configure.sh configure.sh.old
+else
+ touch configure.sh.old
+fi
+
+if test -f .fetch; then
+ $srcdir/prepare-source fetch
+else
+ $srcdir/prepare-source
+fi
+
+if diff configure.sh configure.sh.old >/dev/null 2>&1; then
+ echo "configure.sh is unchanged."
+ rm configure.sh.old
+else
+ echo "configure.sh has CHANGED."
+ if test -f config.status; then
+ ./config.status --recheck
+ else
+ $srcdir/configure
+ fi
+fi
+
+./config.status
+
+make all
+
+if test x"$1" = x"check"; then
+ make check
+fi
diff --git a/packaging/solaris/build_pkg.sh b/packaging/solaris/build_pkg.sh
new file mode 100644
index 0000000..29c035a
--- /dev/null
+++ b/packaging/solaris/build_pkg.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+# Shell script for building Solaris package of rsync
+# Author: Jens Apel <jens.apel@web.de>
+# License: GPL
+#
+# BASEDIR is /usr/local and should be the same as the
+# --prefix parameter of configure
+#
+# this script should be copied under
+# packaging/solaris/5.8/build_pkg.sh
+
+# Definitions start here
+# you can edit this, if you like
+
+# The Package name under which rsync will b installed
+PKGNAME=SMBrsync
+
+# Extract common info requires for the 'info' part of the package.
+# This should be made generic and generated by the configure script
+# but for now it is hard coded
+BASEDIR=/usr/local
+VERSION="2.5.5"
+ARCH=`uname -p`
+NAME=rsync
+
+# Definitions end here
+# Please do not edit below this line or you know what you do.
+
+## Start by faking root install
+echo "Creating install directory (fake $BASEDIR)..."
+START=`pwd`
+FAKE_ROOT=$START/${PKGNAME}
+mkdir $FAKE_ROOT
+
+# copy the binary and the man page to their places
+mkdir $FAKE_ROOT/bin
+mkdir -p $FAKE_ROOT/doc/rsync
+mkdir -p $FAKE_ROOT/man/man1
+mkdir -p $FAKE_ROOT/man/man5
+
+cp ../../../rsync $FAKE_ROOT/bin/rsync
+cp ../../../rsync.1 $FAKE_ROOT/man/man1/rsync.1
+cp ../../../rsyncd.conf.5 $FAKE_ROOT/man/man5/rsyncd.conf.5
+cp ../../../README.md $FAKE_ROOT/doc/rsync/README.md
+cp ../../../COPYING $FAKE_ROOT/doc/rsync/COPYING
+cp ../../../tech_report.pdf $FAKE_ROOT/doc/rsync/tech_report.pdf
+cp ../../../COPYING $FAKE_ROOT/COPYING
+
+## Build info file
+echo "Building pkginfo file..."
+cat > $FAKE_ROOT/pkginfo << EOF_INFO
+PKG=$PKGNAME
+NAME=$NAME
+DESC="Program for efficient remote updates of files."
+VENDOR="Samba Team URL: http://samba.anu.edu.au/rsync/"
+BASEDIR=$BASEDIR
+ARCH=$ARCH
+VERSION=$VERSION
+CATEGORY=application
+CLASSES=none
+EOF_INFO
+
+## Build prototype file
+cat > $FAKE_ROOT/prototype << EOFPROTO
+i copyright=COPYING
+i pkginfo=pkginfo
+d none bin 0755 bin bin
+f none bin/rsync 0755 bin bin
+d none doc 0755 bin bin
+d none doc/$NAME 0755 bin bin
+f none doc/$NAME/README.md 0644 bin bin
+f none doc/$NAME/COPYING 0644 bin bin
+f none doc/$NAME/tech_report.pdf 0644 bin bin
+d none man 0755 bin bin
+d none man/man1 0755 bin bin
+f none man/man1/rsync.1 0644 bin bin
+d none man/man5 0755 bin bin
+f none man/man5/rsyncd.conf.5 0644 bin bin
+EOFPROTO
+
+## And now build the package.
+OUTPUTFILE=$PKGNAME-$VERSION-sol8-$ARCH-local.pkg
+echo "Building package.."
+echo FAKE_ROOT = $FAKE_ROOT
+cd $FAKE_ROOT
+pkgmk -d . -r . -f ./prototype -o
+pkgtrans -os . $OUTPUTFILE $PKGNAME
+
+mv $OUTPUTFILE ..
+cd ..
+
+# Comment this out if you want to see, which file structure has been created
+rm -rf $FAKE_ROOT
+
diff --git a/packaging/systemd/rsync.service b/packaging/systemd/rsync.service
new file mode 100644
index 0000000..8a867ca
--- /dev/null
+++ b/packaging/systemd/rsync.service
@@ -0,0 +1,32 @@
+[Unit]
+Description=fast remote file copy program daemon
+ConditionPathExists=/etc/rsyncd.conf
+After=network.target
+Documentation=man:rsync(1) man:rsyncd.conf(5)
+
+[Service]
+ExecStart=/usr/bin/rsync --daemon --no-detach
+RestartSec=1
+Restart=on-failure
+
+# Citing README.md:
+#
+# [...] Using ssh is recommended for its security features.
+#
+# Alternatively, rsync can run in `daemon' mode, listening on a socket.
+# This is generally used for public file distribution, [...]
+#
+# So let's assume some extra security is more than welcome here. We do full
+# system protection (which makes /usr, /boot, & /etc read-only) and hide
+# devices. To override these defaults, it's best to do so in the drop-in
+# directory, often done via `systemctl edit rsync.service`. The file needs
+# just the bare minimum of the right [heading] and override values.
+# See systemd.unit(5) and search for "drop-in" for full details.
+
+ProtectSystem=full
+#ProtectHome=on|off|read-only
+PrivateDevices=on
+NoNewPrivileges=on
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/systemd/rsync.socket b/packaging/systemd/rsync.socket
new file mode 100644
index 0000000..5bceefe
--- /dev/null
+++ b/packaging/systemd/rsync.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=socket for fast remote file copy program daemon
+Conflicts=rsync.service
+
+[Socket]
+ListenStream=873
+Accept=true
+
+[Install]
+WantedBy=sockets.target
diff --git a/packaging/systemd/rsync@.service b/packaging/systemd/rsync@.service
new file mode 100644
index 0000000..63ba0c7
--- /dev/null
+++ b/packaging/systemd/rsync@.service
@@ -0,0 +1,28 @@
+[Unit]
+Description=fast remote file copy program daemon
+After=network.target
+
+[Service]
+ExecStart=-/usr/bin/rsync --daemon
+StandardInput=socket
+StandardOutput=inherit
+StandardError=journal
+
+# Citing README.md:
+#
+# [...] Using ssh is recommended for its security features.
+#
+# Alternatively, rsync can run in `daemon' mode, listening on a socket.
+# This is generally used for public file distribution, [...]
+#
+# So let's assume some extra security is more than welcome here. We do full
+# system protection (which makes /usr, /boot, & /etc read-only) and hide
+# devices. To override these defaults, it's best to do so in the drop-in
+# directory, often done via `systemctl edit rsync@.service`. The file needs
+# just the bare minimum of the right [heading] and override values.
+# See systemd.unit(5) and search for "drop-in" for full details.
+
+ProtectSystem=full
+#ProtectHome=on|off|read-only
+PrivateDevices=on
+NoNewPrivileges=on
diff --git a/packaging/var-checker b/packaging/var-checker
new file mode 100755
index 0000000..f17c69a
--- /dev/null
+++ b/packaging/var-checker
@@ -0,0 +1,94 @@
+#!/usr/bin/env -S python3 -B
+
+# This script checks the *.c files for extraneous "extern" variables,
+# for vars that are defined but not used, and for inconsistent array
+# sizes. Run it from inside the main rsync directory.
+
+import os, sys, re, argparse, glob
+
+VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z]\S*\s+.*);', re.M)
+EXTERNS_RE = re.compile(r'^extern\s+(.*);', re.M)
+
+sizes = { }
+
+def main():
+ add_syscall_c = set('t_stub.c t_unsafe.c tls.c trimslash.c'.split())
+ add_util_c = set('t_stub.c t_unsafe.c'.split())
+
+ if not os.path.exists('syscall.c'):
+ if os.path.exists('var-checker'):
+ os.chdir('..')
+ else:
+ print("Couldn't find the source dir.")
+ sys.exit(1)
+
+ syscall_c = slurp_file('syscall.c', True)
+ util_c = slurp_file('util1.c', True)
+
+ for fn in sorted(glob.glob('*.c')):
+ txt = slurp_file(fn)
+
+ var_list = parse_vars(fn, VARS_RE.findall(txt))
+ extern_list = parse_vars(fn, EXTERNS_RE.findall(txt))
+ if not var_list and not extern_list:
+ continue
+
+ if fn in add_syscall_c:
+ txt += syscall_c
+ if fn in add_util_c:
+ txt += util_c
+
+ txt = re.sub(r'INFO_GTE', 'info_levels ', txt)
+ txt = re.sub(r'DEBUG_GTE', 'debug_levels ', txt)
+ txt = re.sub(r'SIGACTION\(', 'sigact (', txt)
+
+ find = '|'.join([ re.escape(x) for x in var_list + extern_list ])
+ var_re = re.compile(r'(?<!\sstruct )\b(%s)(?!\w)' % find)
+
+ found = { x: 0 for x in var_list + extern_list }
+ for var in var_re.findall(txt):
+ found[var] += 1
+
+ for var in sorted(var_list + extern_list):
+ if found[var] == 1:
+ vtype = 'var' if var in var_list else 'extern'
+ print(fn, f'has extraneous {vtype}: "{var}"')
+
+
+def slurp_file(fn, drop_externs=False):
+ with open(fn, 'r', encoding='utf-8') as fh:
+ txt = fh.read()
+ if drop_externs:
+ txt = EXTERNS_RE.sub('', txt)
+ return txt
+
+
+def parse_vars(fn, lines):
+ ret = [ ]
+ for line in lines:
+ line = re.sub(r'\s*\{.*\}', '', line)
+ line = re.sub(r'\s*\(.*\)', '', line)
+ for item in re.split(r'\s*,\s*', line):
+ item = re.sub(r'\s*=.*', '', item)
+ m = re.search(r'(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
+ if not m:
+ print(f"Bogus match? ({item})")
+ continue
+ if m['sz']:
+ if m['var'] in sizes:
+ if sizes[m['var']] != m['sz']:
+ var = m['var']
+ print(fn, f'has inconsistent size for "{var}":', m['sz'], 'vs', sizes[var])
+ else:
+ sizes[m['var']] = m['sz']
+ ret.append(m['var'])
+ return ret
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Check the *.c files for extraneous extern vars.', add_help=False)
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et ft=python
diff --git a/packaging/year-tweak b/packaging/year-tweak
new file mode 100755
index 0000000..69d2f2f
--- /dev/null
+++ b/packaging/year-tweak
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+
+# This uses the output from "support/git-set-file-times --list" to discern
+# the last-modified year of each *.c & *.h file and updates the copyright
+# year if it isn't set right.
+
+import sys, os, re, argparse, subprocess
+from datetime import datetime
+
+MAINTAINER_NAME = 'Wayne Davison'
+MAINTAINER_SUF = ' ' + MAINTAINER_NAME + "\n"
+
+def main():
+ latest_year = '2000'
+
+ proc = subprocess.Popen('support/git-set-file-times --list'.split(), stdout=subprocess.PIPE, encoding='utf-8')
+ for line in proc.stdout:
+ m = re.match(r'^\S\s+(?P<year>\d\d\d\d)\S+\s+\S+\s+(?P<fn>.+)', line)
+ if not m:
+ print("Failed to parse line from git-set-file-times:", line)
+ sys.exit(1)
+ m = argparse.Namespace(**m.groupdict())
+ if m.year > latest_year:
+ latest_year = m.year
+ if m.fn.startswith('zlib/') or m.fn.startswith('popt/'):
+ continue
+ if re.search(r'\.(c|h|sh|test)$', m.fn):
+ maybe_edit_copyright_year(m.fn, m.year)
+ proc.communicate()
+
+ fn = 'latest-year.h'
+ with open(fn, 'r', encoding='utf-8') as fh:
+ old_txt = fh.read()
+
+ txt = f'#define LATEST_YEAR "{latest_year}"\n'
+ if txt != old_txt:
+ print(f"Updating {fn} with year {latest_year}")
+ with open(fn, 'w', encoding='utf-8') as fh:
+ fh.write(txt)
+
+
+def maybe_edit_copyright_year(fn, year):
+ opening_lines = [ ]
+ copyright_line = None
+
+ with open(fn, 'r', encoding='utf-8') as fh:
+ for lineno, line in enumerate(fh):
+ opening_lines.append(line)
+ if lineno > 3 and not re.search(r'\S', line):
+ break
+ m = re.match(r'^(?P<pre>.*Copyright\s+\S+\s+)(?P<year>\d\d\d\d(?:-\d\d\d\d)?(,\s+\d\d\d\d)*)(?P<suf>.+)', line)
+ if not m:
+ continue
+ copyright_line = argparse.Namespace(**m.groupdict())
+ copyright_line.lineno = len(opening_lines)
+ copyright_line.is_maintainer_line = MAINTAINER_NAME in copyright_line.suf
+ copyright_line.txt = line
+ if copyright_line.is_maintainer_line:
+ break
+
+ if not copyright_line:
+ return
+
+ if copyright_line.is_maintainer_line:
+ cyears = copyright_line.year.split('-')
+ if year == cyears[0]:
+ cyears = [ year ]
+ else:
+ cyears = [ cyears[0], year ]
+ txt = copyright_line.pre + '-'.join(cyears) + MAINTAINER_SUF
+ if txt == copyright_line.txt:
+ return
+ opening_lines[copyright_line.lineno - 1] = txt
+ else:
+ if fn.startswith('lib/') or fn.startswith('testsuite/'):
+ return
+ txt = copyright_line.pre + year + MAINTAINER_SUF
+ opening_lines[copyright_line.lineno - 1] += txt
+
+ remaining_txt = fh.read()
+
+ print(f"Updating {fn} with year {year}")
+
+ with open(fn, 'w', encoding='utf-8') as fh:
+ fh.write(''.join(opening_lines))
+ fh.write(remaining_txt)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Grab the year of last mod for our c & h files and make sure the Copyright comment is up-to-date.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/params.c b/params.c
new file mode 100644
index 0000000..da8a8f8
--- /dev/null
+++ b/params.c
@@ -0,0 +1,645 @@
+/* This modules is based on the params.c module from Samba, written by Karl Auer
+ and much modified by Christopher Hertel. */
+
+/*
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/* -------------------------------------------------------------------------- **
+ *
+ * Module name: params
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module performs lexical analysis and initial parsing of a
+ * Windows-like parameter file. It recognizes and handles four token
+ * types: section-name, parameter-name, parameter-value, and
+ * end-of-file. Comments and line continuation are handled
+ * internally.
+ *
+ * The entry point to the module is function pm_process(). This
+ * function opens the source file, calls the Parse() function to parse
+ * the input, and then closes the file when either the EOF is reached
+ * or a fatal error is encountered.
+ *
+ * A sample parameter file might look like this:
+ *
+ * [section one]
+ * parameter one = value string
+ * parameter two = another value
+ * [section two]
+ * new parameter = some value or t'other
+ *
+ * The parameter file is divided into sections by section headers:
+ * section names enclosed in square brackets (eg. [section one]).
+ * Each section contains parameter lines, each of which consist of a
+ * parameter name and value delimited by an equal sign. Roughly, the
+ * syntax is:
+ *
+ * <file> :== { <section> } EOF
+ *
+ * <section> :== <section header> { <parameter line> }
+ *
+ * <section header> :== '[' NAME ']'
+ *
+ * <parameter line> :== NAME '=' VALUE '\n'
+ *
+ * Blank lines and comment lines are ignored. Comment lines are lines
+ * beginning with either a semicolon (';') or a pound sign ('#').
+ *
+ * All whitespace in section names and parameter names is compressed
+ * to single spaces. Leading and trailing whitespace is stripped from
+ * both names and values.
+ *
+ * Only the first equals sign in a parameter line is significant.
+ * Parameter values may contain equals signs, square brackets and
+ * semicolons. Internal whitespace is retained in parameter values,
+ * with the exception of the '\r' character, which is stripped for
+ * historic reasons. Parameter names may not start with a left square
+ * bracket, an equal sign, a pound sign, or a semicolon, because these
+ * are used to identify other tokens.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "itypes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ */
+
+#define BUFR_INC 1024
+
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ * bufr - pointer to a global buffer. This is probably a kludge,
+ * but it was the nicest kludge I could think of (for now).
+ * bSize - The size of the global buffer <bufr>.
+ */
+
+static char *bufr = NULL;
+static int bSize = 0;
+static BOOL (*the_sfunc)(char *);
+static BOOL (*the_pfunc)(char *, char *);
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static int EatWhitespace( FILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
+ * character, or newline, or EOF.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The next non-whitespace character in the input stream.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) )
+ ;
+ return( c );
+ } /* EatWhitespace */
+
+static int EatComment( FILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan to the end of a comment.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The character that marks the end of the comment. Normally,
+ * this will be a newline, but it *might* be an EOF.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) )
+ ;
+ return( c );
+ } /* EatComment */
+
+static int Continuation( char *line, int pos )
+ /* ------------------------------------------------------------------------ **
+ * Scan backwards within a string to discover if the last non-whitespace
+ * character is a line-continuation character ('\\').
+ *
+ * Input: line - A pointer to a buffer containing the string to be
+ * scanned.
+ * pos - This is taken to be the offset of the end of the
+ * string. This position is *not* scanned.
+ *
+ * Output: The offset of the '\\' character if it was found, or -1 to
+ * indicate that it was not.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ pos--;
+ while( pos >= 0 && isSpace(line + pos) )
+ pos--;
+
+ return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
+ } /* Continuation */
+
+
+static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan a section name, and pass the name to function sfunc().
+ *
+ * Input: InFile - Input source.
+ * sfunc - Pointer to the function to be called if the section
+ * name is successfully read.
+ *
+ * Output: True if the section name was read and True was returned from
+ * <sfunc>. False if <sfunc> failed or if a lexical error was
+ * encountered.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+ int i;
+ int end;
+ char *func = "params.c:Section() -";
+
+ i = 0; /* <i> is the offset of the next free byte in bufr[] and */
+ end = 0; /* <end> is the current "end of string" offset. In most */
+ /* cases these will be the same, but if the last */
+ /* character written to bufr[] is a space, then <end> */
+ /* will be one less than <i>. */
+
+ c = EatWhitespace( InFile ); /* We've already got the '['. Scan */
+ /* past initial white space. */
+
+ while( (EOF != c) && (c > 0) )
+ {
+
+ /* Check that the buffer is big enough for the next character. */
+ if( i > (bSize - 2) )
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ }
+
+ /* Handle a single character. */
+ switch( c )
+ {
+ case ']': /* Found the closing bracket. */
+ bufr[end] = '\0';
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ rprintf(FLOG, "%s Empty section name in config file.\n", func );
+ return( False );
+ }
+ if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */
+ return( False );
+ (void)EatComment( InFile ); /* Finish off the line. */
+ return( True );
+
+ case '\n': /* Got newline before closing ']'. */
+ i = Continuation( bufr, i ); /* Check for line continuation. */
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ rprintf(FLOG, "%s Badly formed line in config file: %s\n",
+ func, bufr );
+ return( False );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = getc( InFile ); /* Continue with next line. */
+ break;
+
+ default: /* All else are a valid name chars. */
+ if( isspace( c ) ) /* One space per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others copy verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = getc( InFile );
+ }
+ }
+ }
+
+ /* We arrive here if we've met the EOF before the closing bracket. */
+ rprintf(FLOG, "%s Unexpected EOF in the config file: %s\n", func, bufr );
+ return( False );
+ } /* Section */
+
+static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c )
+ /* ------------------------------------------------------------------------ **
+ * Scan a parameter name and value, and pass these two fields to pfunc().
+ *
+ * Input: InFile - The input source.
+ * pfunc - A pointer to the function that will be called to
+ * process the parameter, once it has been scanned.
+ * c - The first character of the parameter name, which
+ * would have been read by Parse(). Unlike a comment
+ * line or a section header, there is no lead-in
+ * character that can be discarded.
+ *
+ * Output: True if the parameter name and value were scanned and processed
+ * successfully, else False.
+ *
+ * Notes: This function is in two parts. The first loop scans the
+ * parameter name. Internal whitespace is compressed, and an
+ * equal sign (=) terminates the token. Leading and trailing
+ * whitespace is discarded. The second loop scans the parameter
+ * value. When both have been successfully identified, they are
+ * passed to pfunc() for processing.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i = 0; /* Position within bufr. */
+ int end = 0; /* bufr[end] is current end-of-string. */
+ int vstart = 0; /* Starting position of the parameter value. */
+ char *func = "params.c:Parameter() -";
+
+ /* Read the parameter name. */
+ while( 0 == vstart ) /* Loop until we've found the start of the value. */
+ {
+
+ if( i > (bSize - 2) ) /* Ensure there's space for next char. */
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ }
+
+ switch( c )
+ {
+ case '=': /* Equal sign marks end of param name. */
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ rprintf(FLOG, "%s Invalid parameter name in config file.\n", func );
+ return( False );
+ }
+ bufr[end++] = '\0'; /* Mark end of string & advance. */
+ i = vstart = end; /* New string starts here. */
+ c = EatWhitespace(InFile);
+ break;
+
+ case '\n': /* Find continuation char, else error. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ rprintf(FLOG, "%s Ignoring badly formed line in config file: %s\n",
+ func, bufr );
+ return( True );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = getc( InFile ); /* Read past eoln. */
+ break;
+
+ case '\0': /* Shouldn't have EOF within param name. */
+ case EOF:
+ bufr[i] = '\0';
+ rprintf(FLOG, "%s Unexpected end-of-file at: %s\n", func, bufr );
+ return( True );
+
+ case ' ':
+ case '\t':
+ /* A directive divides at the first space or tab. */
+ if (*bufr == '&') {
+ bufr[end++] = '\0';
+ i = vstart = end;
+ c = EatWhitespace(InFile);
+ if (c == '=')
+ c = EatWhitespace(InFile);
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ if( isspace( c ) ) /* One ' ' per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = getc( InFile );
+ }
+ }
+ }
+
+ /* Now parse the value. */
+ while( (EOF !=c) && (c > 0) )
+ {
+
+ if( i > (bSize - 2) ) /* Make sure there's enough room. */
+ {
+ bSize += BUFR_INC;
+ bufr = realloc_array( bufr, char, bSize );
+ }
+
+ switch( c )
+ {
+ case '\r': /* Explicitly remove '\r' because the older */
+ c = getc( InFile ); /* version called fgets_slash() which also */
+ break; /* removes them. */
+
+ case '\n': /* Marks end of value unless there's a '\'. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ c = 0;
+ else
+ {
+ for( end = i; end >= 0 && isSpace(bufr + end); end-- )
+ ;
+ c = getc( InFile );
+ }
+ break;
+
+ default: /* All others verbatim. Note that spaces do */
+ bufr[i++] = c; /* not advance <end>. This allows trimming */
+ if( !isspace( c ) ) /* of whitespace at the end of the line. */
+ end = i;
+ c = getc( InFile );
+ break;
+ }
+ }
+ bufr[end] = '\0'; /* End of value. */
+
+ return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
+ } /* Parameter */
+
+static int name_cmp(const void *n1, const void *n2)
+{
+ return strcmp(*(char * const *)n1, *(char * const *)n2);
+}
+
+static int include_config(char *include, int manage_globals)
+{
+ STRUCT_STAT sb;
+ char *match = manage_globals ? "*.conf" : "*.inc";
+ int ret;
+
+ if (do_stat(include, &sb) < 0) {
+ rsyserr(FLOG, errno, "unable to stat config file \"%s\"", include);
+ return 0;
+ }
+
+ if (S_ISREG(sb.st_mode)) {
+ if (manage_globals && the_sfunc)
+ the_sfunc("]push");
+ ret = pm_process(include, the_sfunc, the_pfunc);
+ if (manage_globals && the_sfunc)
+ the_sfunc("]pop");
+ } else if (S_ISDIR(sb.st_mode)) {
+ char buf[MAXPATHLEN], **bpp;
+ item_list conf_list;
+ struct dirent *di;
+ size_t j;
+ DIR *d;
+
+ if (!(d = opendir(include))) {
+ rsyserr(FLOG, errno, "unable to open config dir \"%s\"", include);
+ return 0;
+ }
+
+ memset(&conf_list, 0, sizeof conf_list);
+
+ while ((di = readdir(d)) != NULL) {
+ char *dname = d_name(di);
+ if (!wildmatch(match, dname))
+ continue;
+ bpp = EXPAND_ITEM_LIST(&conf_list, char *, 32);
+ pathjoin(buf, sizeof buf, include, dname);
+ *bpp = strdup(buf);
+ }
+ closedir(d);
+
+ if (!(bpp = conf_list.items))
+ return 1;
+
+ if (conf_list.count > 1)
+ qsort(bpp, conf_list.count, sizeof (char *), name_cmp);
+
+ for (j = 0, ret = 1; j < conf_list.count; j++) {
+ if (manage_globals && the_sfunc)
+ the_sfunc(j == 0 ? "]push" : "]reset");
+ if ((ret = pm_process(bpp[j], the_sfunc, the_pfunc)) != 1)
+ break;
+ }
+
+ if (manage_globals && the_sfunc)
+ the_sfunc("]pop");
+
+ for (j = 0; j < conf_list.count; j++)
+ free(bpp[j]);
+ free(bpp);
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int parse_directives(char *name, char *val)
+{
+ if (strcasecmp(name, "&include") == 0)
+ return include_config(val, 1);
+ if (strcasecmp(name, "&merge") == 0)
+ return include_config(val, 0);
+ rprintf(FLOG, "Unknown directive: %s.\n", name);
+ return 0;
+}
+
+static int Parse( FILE *InFile,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan & parse the input.
+ *
+ * Input: InFile - Input source.
+ * sfunc - Function to be called when a section name is scanned.
+ * See Section().
+ * pfunc - Function to be called when a parameter is scanned.
+ * See Parameter().
+ *
+ * Output: 1 if the file was successfully scanned, 2 if the file was
+ * scanned until a section header with no section function, else 0.
+ *
+ * Notes: The input can be viewed in terms of 'lines'. There are four
+ * types of lines:
+ * Blank - May contain whitespace, otherwise empty.
+ * Comment - First non-whitespace character is a ';' or '#'.
+ * The remainder of the line is ignored.
+ * Section - First non-whitespace character is a '['.
+ * Parameter - The default case.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ c = EatWhitespace( InFile );
+ while( (EOF != c) && (c > 0) )
+ {
+ switch( c )
+ {
+ case '\n': /* Blank line. */
+ c = EatWhitespace( InFile );
+ break;
+
+ case ';': /* Comment line. */
+ case '#':
+ c = EatComment( InFile );
+ break;
+
+ case '[': /* Section Header. */
+ if (!sfunc)
+ return 2;
+ if( !Section( InFile, sfunc ) )
+ return 0;
+ c = EatWhitespace( InFile );
+ break;
+
+ case '\\': /* Bogus backslash. */
+ c = EatWhitespace( InFile );
+ break;
+
+ case '&': /* Handle directives */
+ the_sfunc = sfunc;
+ the_pfunc = pfunc;
+ c = Parameter( InFile, parse_directives, c );
+ if (c != 1)
+ return c;
+ c = EatWhitespace( InFile );
+ break;
+
+ default: /* Parameter line. */
+ if( !Parameter( InFile, pfunc, c ) )
+ return 0;
+ c = EatWhitespace( InFile );
+ break;
+ }
+ }
+ return 1;
+ } /* Parse */
+
+static FILE *OpenConfFile( char *FileName )
+ /* ------------------------------------------------------------------------ **
+ * Open a config file.
+ *
+ * Input: FileName - The pathname of the config file to be opened.
+ *
+ * Output: A pointer of type (FILE *) to the opened file, or NULL if the
+ * file could not be opened.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ FILE *OpenedFile;
+ char *func = "params.c:OpenConfFile() -";
+
+ if( NULL == FileName || 0 == *FileName )
+ {
+ rprintf(FLOG, "%s No config filename specified.\n", func);
+ return( NULL );
+ }
+
+ OpenedFile = fopen( FileName, "r" );
+ if( NULL == OpenedFile )
+ {
+ rsyserr(FLOG, errno, "unable to open config file \"%s\"",
+ FileName);
+ }
+
+ return( OpenedFile );
+ } /* OpenConfFile */
+
+int pm_process( char *FileName,
+ BOOL (*sfunc)(char *),
+ BOOL (*pfunc)(char *, char *) )
+ /* ------------------------------------------------------------------------ **
+ * Process the named parameter file.
+ *
+ * Input: FileName - The pathname of the parameter file to be opened.
+ * sfunc - A pointer to a function that will be called when
+ * a section name is discovered.
+ * pfunc - A pointer to a function that will be called when
+ * a parameter name and value are discovered.
+ *
+ * Output: 1 if the file was successfully parsed, 2 if parsing ended at a
+ * section header w/o a section function, else 0.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int result;
+ FILE *InFile;
+ char *func = "params.c:pm_process() -";
+
+ InFile = OpenConfFile( FileName ); /* Open the config file. */
+ if( NULL == InFile )
+ return( False );
+
+ if( NULL != bufr ) /* If we already have a buffer */
+ result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
+ /* use it. */
+
+ else /* If we don't have a buffer */
+ { /* allocate one, then parse, */
+ bSize = BUFR_INC; /* then free. */
+ bufr = new_array( char, bSize );
+ result = Parse( InFile, sfunc, pfunc );
+ free( bufr );
+ bufr = NULL;
+ bSize = 0;
+ }
+
+ fclose(InFile);
+
+ if( !result ) /* Generic failure. */
+ {
+ rprintf(FLOG, "%s Failed. Error returned from params.c:parse().\n", func);
+ return 0;
+ }
+
+ return result;
+ } /* pm_process */
+
+/* -------------------------------------------------------------------------- */
+
diff --git a/pipe.c b/pipe.c
new file mode 100644
index 0000000..3e5f038
--- /dev/null
+++ b/pipe.c
@@ -0,0 +1,178 @@
+/*
+ * Routines used to setup various kinds of inter-process pipes.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2004-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+extern int am_sender;
+extern int am_server;
+extern int blocking_io;
+extern int filesfrom_fd;
+extern int munge_symlinks;
+extern char *logfile_name;
+extern int remote_option_cnt;
+extern const char **remote_options;
+extern struct chmod_mode_struct *chmod_modes;
+
+/**
+ * Create a child connected to us via its stdin/stdout.
+ *
+ * This is derived from CVS code
+ *
+ * Note that in the child STDIN is set to blocking and STDOUT
+ * is set to non-blocking. This is necessary as rsh relies on stdin being blocking
+ * and ssh relies on stdout being non-blocking
+ *
+ * If blocking_io is set then use blocking io on both fds. That can be
+ * used to cope with badly broken rsh implementations like the one on
+ * Solaris.
+ **/
+pid_t piped_child(char **command, int *f_in, int *f_out)
+{
+ pid_t pid;
+ int to_child_pipe[2];
+ int from_child_pipe[2];
+
+ if (DEBUG_GTE(CMD, 1))
+ print_child_argv("opening connection using:", command);
+
+ if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe");
+ exit_cleanup(RERR_IPC);
+ }
+
+ pid = do_fork();
+ if (pid == -1) {
+ rsyserr(FERROR, errno, "fork");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (pid == 0) {
+ if (dup2(to_child_pipe[0], STDIN_FILENO) < 0
+ || close(to_child_pipe[1]) < 0
+ || close(from_child_pipe[0]) < 0
+ || dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
+ rsyserr(FERROR, errno, "Failed to dup/close");
+ exit_cleanup(RERR_IPC);
+ }
+ if (to_child_pipe[0] != STDIN_FILENO)
+ close(to_child_pipe[0]);
+ if (from_child_pipe[1] != STDOUT_FILENO)
+ close(from_child_pipe[1]);
+ set_blocking(STDIN_FILENO);
+ if (blocking_io > 0)
+ set_blocking(STDOUT_FILENO);
+ execvp(command[0], command);
+ rsyserr(FERROR, errno, "Failed to exec %s", command[0]);
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) {
+ rsyserr(FERROR, errno, "Failed to close");
+ exit_cleanup(RERR_IPC);
+ }
+
+ *f_in = from_child_pipe[0];
+ *f_out = to_child_pipe[1];
+
+ return pid;
+}
+
+/* This function forks a child which calls child_main(). First,
+ * however, it has to establish communication paths to and from the
+ * newborn child. It creates two socket pairs -- one for writing to
+ * the child (from the parent) and one for reading from the child
+ * (writing to the parent). Since that's four socket ends, each
+ * process has to close the two ends it doesn't need. The remaining
+ * two socket ends are retained for reading and writing. In the
+ * child, the STDIN and STDOUT file descriptors refer to these
+ * sockets. In the parent, the function arguments f_in and f_out are
+ * set to refer to these sockets. */
+pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
+ int (*child_main)(int, char*[]))
+{
+ pid_t pid;
+ int to_child_pipe[2];
+ int from_child_pipe[2];
+
+ /* The parent process is always the sender for a local rsync. */
+ assert(am_sender);
+
+ if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe");
+ exit_cleanup(RERR_IPC);
+ }
+
+ pid = do_fork();
+ if (pid == -1) {
+ rsyserr(FERROR, errno, "fork");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (pid == 0) {
+ am_sender = 0;
+ am_server = 1;
+ filesfrom_fd = -1;
+ munge_symlinks = 0; /* Each side needs its own option. */
+ chmod_modes = NULL; /* Let the sending side handle this. */
+
+ /* Let the client side handle this. */
+ if (logfile_name) {
+ logfile_name = NULL;
+ logfile_close();
+ }
+
+ if (remote_option_cnt) {
+ int rc = remote_option_cnt + 1;
+ const char **rv = remote_options;
+ if (!parse_arguments(&rc, &rv)) {
+ option_error();
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+
+ if (dup2(to_child_pipe[0], STDIN_FILENO) < 0
+ || close(to_child_pipe[1]) < 0
+ || close(from_child_pipe[0]) < 0
+ || dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
+ rsyserr(FERROR, errno, "Failed to dup/close");
+ exit_cleanup(RERR_IPC);
+ }
+ if (to_child_pipe[0] != STDIN_FILENO)
+ close(to_child_pipe[0]);
+ if (from_child_pipe[1] != STDOUT_FILENO)
+ close(from_child_pipe[1]);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ child_main(argc, argv);
+ }
+
+ if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) {
+ rsyserr(FERROR, errno, "Failed to close");
+ exit_cleanup(RERR_IPC);
+ }
+
+ *f_in = from_child_pipe[0];
+ *f_out = to_child_pipe[1];
+
+ return pid;
+}
diff --git a/popt/CHANGES b/popt/CHANGES
new file mode 100644
index 0000000..db16a5f
--- /dev/null
+++ b/popt/CHANGES
@@ -0,0 +1,46 @@
+1.5 -> 1.6
+ - add ability to perform callbacks for every, not just first, match.
+
+1.3 -> 1.5
+ - heavy dose of const's
+ - poptParseArgvString() now NULL terminates the list
+
+1.2.3 -> 1.3
+ - added support for single -
+ - misc bug fixes
+ - portability improvements
+
+1.2.2 -> 1.2.3
+ - fixed memset() in help message generation (Dale Hawkins)
+ - added extern "C" stuff to popt.h for C++ compilers (Dale Hawkins)
+ - const'ified poptParseArgvString (Jeff Garzik)
+
+1.2.1 -> 1.2.2
+ - fixed bug in chaind alias happens which seems to have only
+ affected --triggers in rpm
+ - added POPT_ARG_VAL
+ - popt.3 installed by default
+
+1.2 -> 1.2.1
+ - added POPT_ARG_INTL_DOMAIN (Elliot Lee)
+ - updated Makefile's to be more GNUish (Elliot Lee)
+
+1.1 -> 1.2
+ - added popt.3 man page (Robert Lynch)
+ - don't use mmap anymore (its lack of portability isn't worth the
+ trouble)
+ - added test script
+ - added support for exec
+ - removed support for *_POPT_ALIASES env variable -- it was a bad
+ idea
+ - reorganized into multiple source files
+ - added automatic help generation, POPT_AUTOHELP
+ - added table callbacks
+ - added table inclusion
+ - updated man page for new features
+ - added test scripts
+
+1.0 -> 1.1
+ - moved to autoconf (Fred Fish)
+ - added STRERROR replacement (Norbert Warmuth)
+ - added const keywords (Bruce Perens)
diff --git a/popt/COPYING b/popt/COPYING
new file mode 100644
index 0000000..b4c7ca8
--- /dev/null
+++ b/popt/COPYING
@@ -0,0 +1,22 @@
+Copyright (c) 1998 Red Hat Software
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/popt/README b/popt/README
new file mode 100644
index 0000000..0b5205b
--- /dev/null
+++ b/popt/README
@@ -0,0 +1,18 @@
+This is the popt command line option parsing library. While it is similiar
+to getopt(3), it contains a number of enhancements, including:
+
+ 1) popt is fully reentrant
+ 2) popt can parse arbitrary argv[] style arrays while
+ getopt(2) makes this quite difficult
+ 3) popt allows users to alias command line arguments
+ 4) popt provides convience functions for parsing strings
+ into argv[] style arrays
+
+popt is used by rpm, the Red Hat install program, and many other Red Hat
+utilities, all of which provide excellent examples of how to use popt.
+Complete documentation on popt is available in popt.ps (included in this
+tarball), which is excerpted with permission from the book "Linux
+Application Development" by Michael K. Johnson and Erik Troan (availble
+from Addison Wesley in May, 1998).
+
+Comments on popt should be addressed to ewt@redhat.com.
diff --git a/popt/README.rsync b/popt/README.rsync
new file mode 100644
index 0000000..1cf54d5
--- /dev/null
+++ b/popt/README.rsync
@@ -0,0 +1,4 @@
+This is a perfectly ordinary copy of libpopt. It is only used on platforms
+that do not have a sufficiently up-to-date copy of their own. If you build
+rsync on a platform which has popt, this directory should not be used. (You
+can control that using the --with-included-popt configure flag.)
diff --git a/popt/dummy.in b/popt/dummy.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/popt/dummy.in
diff --git a/popt/findme.c b/popt/findme.c
new file mode 100644
index 0000000..ac4cbae
--- /dev/null
+++ b/popt/findme.c
@@ -0,0 +1,55 @@
+/** \ingroup popt
+ * \file popt/findme.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+#include "findme.h"
+
+const char * findProgramPath(const char * argv0)
+{
+ char * path = getenv("PATH");
+ char * pathbuf;
+ char * start, * chptr;
+ char * buf;
+ size_t bufsize;
+
+ if (argv0 == NULL) return NULL; /* XXX can't happen */
+ /* If there is a / in the argv[0], it has to be an absolute path */
+ if (strchr(argv0, '/'))
+ return xstrdup(argv0);
+
+ if (path == NULL) return NULL;
+
+ bufsize = strlen(path) + 1;
+ start = pathbuf = alloca(bufsize);
+ if (pathbuf == NULL) return NULL; /* XXX can't happen */
+ strlcpy(pathbuf, path, bufsize);
+ bufsize += sizeof "/" - 1 + strlen(argv0);
+ buf = malloc(bufsize);
+ if (buf == NULL) return NULL; /* XXX can't happen */
+
+ chptr = NULL;
+ /*@-branchstate@*/
+ do {
+ if ((chptr = strchr(start, ':')))
+ *chptr = '\0';
+ snprintf(buf, bufsize, "%s/%s", start, argv0);
+
+ if (!access(buf, X_OK))
+ return buf;
+
+ if (chptr)
+ start = chptr + 1;
+ else
+ start = NULL;
+ } while (start && *start);
+ /*@=branchstate@*/
+
+ free(buf);
+
+ return NULL;
+}
diff --git a/popt/findme.h b/popt/findme.h
new file mode 100644
index 0000000..a016b86
--- /dev/null
+++ b/popt/findme.h
@@ -0,0 +1,20 @@
+/** \ingroup popt
+ * \file popt/findme.h
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+/**
+ * Return absolute path to executable by searching PATH.
+ * @param argv0 name of executable
+ * @return (malloc'd) absolute path to executable (or NULL)
+ */
+/*@null@*/ const char * findProgramPath(/*@null@*/ const char * argv0)
+ /*@*/;
+
+#endif
diff --git a/popt/popt.c b/popt/popt.c
new file mode 100644
index 0000000..5d927f7
--- /dev/null
+++ b/popt/popt.c
@@ -0,0 +1,1283 @@
+/** \ingroup popt
+ * \file popt/popt.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist */
+
+#undef MYDEBUG
+
+#include "system.h"
+
+#if HAVE_FLOAT_H
+#include <float.h>
+#endif
+#include <math.h>
+
+#include "findme.h"
+#include "poptint.h"
+
+#ifndef DBL_EPSILON
+#define DBL_EPSILON 2.2204460492503131e-16
+#endif
+
+#ifdef MYDEBUG
+/*@unchecked@*/
+int _popt_debug = 0;
+#endif
+
+#if !defined(HAVE_STRERROR) && !defined(__LCLINT__)
+static char * strerror(int errno)
+{
+ extern int sys_nerr;
+ extern char * sys_errlist[];
+
+ if ((0 <= errno) && (errno < sys_nerr))
+ return sys_errlist[errno];
+ else
+ return POPT_("unknown errno");
+}
+#endif
+
+#ifdef MYDEBUG
+/*@unused@*/
+static void prtcon(const char *msg, poptContext con)
+{
+ if (msg) fprintf(stderr, "%s", msg);
+ fprintf(stderr, "\tcon %p os %p nextCharArg \"%s\" nextArg \"%s\" argv[%d] \"%s\"\n",
+ con, con->os,
+ (con->os->nextCharArg ? con->os->nextCharArg : ""),
+ (con->os->nextArg ? con->os->nextArg : ""),
+ con->os->next,
+ (con->os->argv && con->os->argv[con->os->next]
+ ? con->os->argv[con->os->next] : ""));
+}
+#endif
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
+{
+ con->execPath = _free(con->execPath);
+ con->execPath = xstrdup(path);
+ con->execAbsolute = allowAbsolute;
+ /*@-nullstate@*/ /* LCL: con->execPath not NULL */
+ return;
+ /*@=nullstate@*/
+}
+
+static void invokeCallbacksPRE(poptContext con, const struct poptOption * opt)
+ /*@globals internalState@*/
+ /*@modifies internalState@*/
+{
+ if (opt != NULL)
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+ if (opt->arg == NULL) continue; /* XXX program error. */
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ invokeCallbacksPRE(con, arg);
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+ (opt->argInfo & POPT_CBFLAG_PRE))
+ { /*@-castfcnptr@*/
+ poptCallbackType cb = (poptCallbackType)opt->arg;
+ /*@=castfcnptr@*/
+ /* Perform callback. */
+ /*@-noeffectuncon @*/
+ cb(con, POPT_CALLBACK_REASON_PRE, NULL, NULL, opt->descrip);
+ /*@=noeffectuncon @*/
+ }
+ }
+}
+
+static void invokeCallbacksPOST(poptContext con, const struct poptOption * opt)
+ /*@globals internalState@*/
+ /*@modifies internalState@*/
+{
+ if (opt != NULL)
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+ if (opt->arg == NULL) continue; /* XXX program error. */
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ invokeCallbacksPOST(con, arg);
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+ (opt->argInfo & POPT_CBFLAG_POST))
+ { /*@-castfcnptr@*/
+ poptCallbackType cb = (poptCallbackType)opt->arg;
+ /*@=castfcnptr@*/
+ /* Perform callback. */
+ /*@-noeffectuncon @*/
+ cb(con, POPT_CALLBACK_REASON_POST, NULL, NULL, opt->descrip);
+ /*@=noeffectuncon @*/
+ }
+ }
+}
+
+static void invokeCallbacksOPTION(poptContext con,
+ const struct poptOption * opt,
+ const struct poptOption * myOpt,
+ /*@null@*/ const void * myData, int shorty)
+ /*@globals internalState@*/
+ /*@modifies internalState@*/
+{
+ const struct poptOption * cbopt = NULL;
+
+ if (opt != NULL)
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ void * arg = opt->arg;
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ if (opt->arg != NULL) /* XXX program error */
+ invokeCallbacksOPTION(con, opt->arg, myOpt, myData, shorty);
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK &&
+ !(opt->argInfo & POPT_CBFLAG_SKIPOPTION)) {
+ /* Save callback info. */
+ cbopt = opt;
+ } else if (cbopt != NULL &&
+ ((myOpt->shortName && opt->shortName && shorty &&
+ myOpt->shortName == opt->shortName) ||
+ (myOpt->longName && opt->longName &&
+ /*@-nullpass@*/ /* LCL: opt->longName != NULL */
+ !strcmp(myOpt->longName, opt->longName)))
+ /*@=nullpass@*/
+ )
+ { /*@-castfcnptr@*/
+ poptCallbackType cb = (poptCallbackType)cbopt->arg;
+ /*@=castfcnptr@*/
+ const void * cbData = (cbopt->descrip ? cbopt->descrip : myData);
+ /* Perform callback. */
+ if (cb != NULL) { /* XXX program error */
+ /*@-noeffectuncon @*/
+ cb(con, POPT_CALLBACK_REASON_OPTION, myOpt,
+ con->os->nextArg, cbData);
+ /*@=noeffectuncon @*/
+ }
+ /* Terminate (unless explcitly continuing). */
+ if (!(cbopt->argInfo & POPT_CBFLAG_CONTINUE))
+ return;
+ }
+ }
+}
+
+poptContext poptGetContext(const char * name, int argc, const char ** argv,
+ const struct poptOption * options, int flags)
+{
+ poptContext con = malloc(sizeof(*con));
+
+ if (con == NULL) return NULL; /* XXX can't happen */
+ memset(con, 0, sizeof(*con));
+
+ con->os = con->optionStack;
+ con->os->argc = argc;
+ /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
+ con->os->argv = argv;
+ /*@=dependenttrans =assignexpose@*/
+ con->os->argb = NULL;
+
+ if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+ con->os->next = 1; /* skip argv[0] */
+
+ con->leftovers = calloc( (argc + 1), sizeof(*con->leftovers) );
+ /*@-dependenttrans -assignexpose@*/ /* FIX: W2DO? */
+ con->options = options;
+ /*@=dependenttrans =assignexpose@*/
+ con->aliases = NULL;
+ con->numAliases = 0;
+ con->flags = flags;
+ con->execs = NULL;
+ con->numExecs = 0;
+ con->finalArgvAlloced = argc * 2;
+ con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+ con->execAbsolute = 1;
+ con->arg_strip = NULL;
+
+ if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+ con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+ if (name) {
+ size_t bufsize = strlen(name) + 1;
+ char * t = malloc(bufsize);
+ if (t) {
+ strlcpy(t, name, bufsize);
+ con->appName = t;
+ }
+ }
+
+ /*@-internalglobs@*/
+ invokeCallbacksPRE(con, con->options);
+ /*@=internalglobs@*/
+
+ return con;
+}
+
+static void cleanOSE(/*@special@*/ struct optionStackEntry *os)
+ /*@uses os @*/
+ /*@releases os->nextArg, os->argv, os->argb @*/
+ /*@modifies os @*/
+{
+ os->nextArg = _free(os->nextArg);
+ os->argv = _free(os->argv);
+ os->argb = PBM_FREE(os->argb);
+}
+
+/*@-boundswrite@*/
+void poptResetContext(poptContext con)
+{
+ int i;
+
+ if (con == NULL) return;
+ while (con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ con->os->argb = PBM_FREE(con->os->argb);
+ con->os->currAlias = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->nextArg = NULL;
+ con->os->next = 1; /* skip argv[0] */
+
+ con->numLeftovers = 0;
+ con->nextLeftover = 0;
+ con->restLeftover = 0;
+ con->doExec = NULL;
+
+ if (con->finalArgv != NULL)
+ for (i = 0; i < con->finalArgvCount; i++) {
+ /*@-unqualifiedtrans@*/ /* FIX: typedef double indirection. */
+ con->finalArgv[i] = _free(con->finalArgv[i]);
+ /*@=unqualifiedtrans@*/
+ }
+
+ con->finalArgvCount = 0;
+ con->arg_strip = PBM_FREE(con->arg_strip);
+ /*@-nullstate@*/ /* FIX: con->finalArgv != NULL */
+ return;
+ /*@=nullstate@*/
+}
+/*@=boundswrite@*/
+
+/* Only one of longName, shortName should be set, not both. */
+/*@-boundswrite@*/
+static int handleExec(/*@special@*/ poptContext con,
+ /*@null@*/ const char * longName, char shortName)
+ /*@uses con->execs, con->numExecs, con->flags, con->doExec,
+ con->finalArgv, con->finalArgvAlloced, con->finalArgvCount @*/
+ /*@modifies con @*/
+{
+ poptItem item;
+ int i;
+
+ if (con->execs == NULL || con->numExecs <= 0) /* XXX can't happen */
+ return 0;
+
+ for (i = con->numExecs - 1; i >= 0; i--) {
+ item = con->execs + i;
+ if (longName && !(item->option.longName &&
+ !strcmp(longName, item->option.longName)))
+ continue;
+ else if (shortName != item->option.shortName)
+ continue;
+ break;
+ }
+ if (i < 0) return 0;
+
+
+ if (con->flags & POPT_CONTEXT_NO_EXEC)
+ return 1;
+
+ if (con->doExec == NULL) {
+ con->doExec = con->execs + i;
+ return 1;
+ }
+
+ /* We already have an exec to do; remember this option for next
+ time 'round */
+ if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ i = con->finalArgvCount++;
+ if (con->finalArgv != NULL) /* XXX can't happen */
+ { size_t bufsize = (longName ? strlen(longName) : 0) + 3;
+ char *s = malloc(bufsize);
+ if (s != NULL) { /* XXX can't happen */
+ if (longName)
+ snprintf(s, bufsize, "--%s", longName);
+ else
+ snprintf(s, bufsize, "-%c", shortName);
+ con->finalArgv[i] = s;
+ } else
+ con->finalArgv[i] = NULL;
+ }
+
+ /*@-nullstate@*/ /* FIX: con->finalArgv[] == NULL */
+ return 1;
+ /*@=nullstate@*/
+}
+/*@=boundswrite@*/
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(/*@special@*/ poptContext con,
+ /*@null@*/ const char * longName, char shortName,
+ /*@exposed@*/ /*@null@*/ const char * nextCharArg)
+ /*@uses con->aliases, con->numAliases, con->optionStack, con->os,
+ con->os->currAlias, con->os->currAlias->option.longName @*/
+ /*@modifies con @*/
+{
+ poptItem item = con->os->currAlias;
+ int rc;
+ int i;
+
+ if (item) {
+ if (longName && (item->option.longName &&
+ !strcmp(longName, item->option.longName)))
+ return 0;
+ if (shortName && shortName == item->option.shortName)
+ return 0;
+ }
+
+ if (con->aliases == NULL || con->numAliases <= 0) /* XXX can't happen */
+ return 0;
+
+ for (i = con->numAliases - 1; i >= 0; i--) {
+ item = con->aliases + i;
+ if (longName && !(item->option.longName &&
+ !strcmp(longName, item->option.longName)))
+ continue;
+ else if (shortName != item->option.shortName)
+ continue;
+ break;
+ }
+ if (i < 0) return 0;
+
+ if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+/*@-boundsread@*/
+ if (nextCharArg && *nextCharArg)
+ con->os->nextCharArg = nextCharArg;
+/*@=boundsread@*/
+
+ con->os++;
+ con->os->next = 0;
+ con->os->stuffed = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = con->aliases + i;
+ rc = poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
+ &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+
+ return (rc ? rc : 1);
+}
+
+/*@-bounds -boundswrite @*/
+static int execCommand(poptContext con)
+ /*@globals internalState @*/
+ /*@modifies internalState @*/
+{
+ poptItem item = con->doExec;
+ const char ** argv;
+ int argc = 0;
+
+ if (item == NULL) /*XXX can't happen*/
+ return POPT_ERROR_NOARG;
+
+ if (item->argv == NULL || item->argc < 1 ||
+ (!con->execAbsolute && strchr(item->argv[0], '/')))
+ return POPT_ERROR_NOARG;
+
+ argv = malloc(sizeof(*argv) *
+ (6 + item->argc + con->numLeftovers + con->finalArgvCount));
+ if (argv == NULL) return POPT_ERROR_MALLOC;
+
+ if (!strchr(item->argv[0], '/') && con->execPath != NULL) {
+ size_t bufsize = strlen(con->execPath) + strlen(item->argv[0]) + sizeof "/";
+ char *s = alloca(bufsize);
+ snprintf(s, bufsize, "%s/%s", con->execPath, item->argv[0]);
+ argv[argc] = s;
+ } else
+ argv[argc] = findProgramPath(item->argv[0]);
+ if (argv[argc++] == NULL) return POPT_ERROR_NOARG;
+
+ if (item->argc > 1) {
+ memcpy(argv + argc, item->argv + 1, sizeof(*argv) * (item->argc - 1));
+ argc += (item->argc - 1);
+ }
+
+ if (con->finalArgv != NULL && con->finalArgvCount > 0) {
+ memcpy(argv + argc, con->finalArgv,
+ sizeof(*argv) * con->finalArgvCount);
+ argc += con->finalArgvCount;
+ }
+
+ if (con->leftovers != NULL && con->numLeftovers > 0) {
+ memcpy(argv + argc, con->leftovers, sizeof(*argv) * con->numLeftovers);
+ argc += con->numLeftovers;
+ }
+
+ argv[argc] = NULL;
+
+ {
+#ifdef __hpux
+ int rc = setresgid(getgid(), getgid(),-1);
+ if (rc) return POPT_ERROR_ERRNO;
+ rc = setresuid(getuid(), getuid(),-1);
+ if (rc) return POPT_ERROR_ERRNO;
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX sez' Timur Bakeyev <mc@bat.ru>
+ * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
+ */
+#if defined(HAVE_SETUID)
+ int rc = setgid(getgid());
+ if (rc) return POPT_ERROR_ERRNO;
+ rc = setuid(getuid());
+ if (rc) return POPT_ERROR_ERRNO;
+#elif defined (HAVE_SETREUID)
+ int rc = setregid(getgid(), getgid());
+ if (rc) return POPT_ERROR_ERRNO;
+ rc = setreuid(getuid(), getuid());
+ if (rc) return POPT_ERROR_ERRNO;
+#else
+ ; /* Can't drop privileges */
+#endif
+#endif
+ }
+
+ if (argv[0] == NULL)
+ return POPT_ERROR_NOARG;
+
+#ifdef MYDEBUG
+if (_popt_debug)
+ { const char ** avp;
+ fprintf(stderr, "==> execvp(%s) argv[%d]:", argv[0], argc);
+ for (avp = argv; *avp; avp++)
+ fprintf(stderr, " '%s'", *avp);
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ execvp(argv[0], (char *const *)argv);
+
+ return POPT_ERROR_ERRNO;
+}
+/*@=bounds =boundswrite @*/
+
+/*@-boundswrite@*/
+/*@observer@*/ /*@null@*/ static const struct poptOption *
+findOption(const struct poptOption * opt, /*@null@*/ const char * longName,
+ char shortName,
+ /*@null@*/ /*@out@*/ poptCallbackType * callback,
+ /*@null@*/ /*@out@*/ const void ** callbackData,
+ int singleDash)
+ /*@modifies *callback, *callbackData */
+{
+ const struct poptOption * cb = NULL;
+
+ /* This happens when a single - is given */
+ if (singleDash && !shortName && (longName && *longName == '\0'))
+ shortName = '-';
+
+ for (; opt->longName || opt->shortName || opt->arg; opt++) {
+
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ const struct poptOption * opt2;
+ void * arg = opt->arg;
+
+/*@-branchstate@*/
+ /* XXX sick hack to preserve pretense of ABI. */
+ if (arg == poptHelpOptions) arg = poptHelpOptionsI18N;
+/*@=branchstate@*/
+ /* Recurse on included sub-tables. */
+ if (arg == NULL) continue; /* XXX program error */
+ opt2 = findOption(arg, longName, shortName, callback,
+ callbackData, singleDash);
+ if (opt2 == NULL) continue;
+ /* Sub-table data will be inheirited if no data yet. */
+ if (!(callback && *callback)) return opt2;
+ if (!(callbackData && *callbackData == NULL)) return opt2;
+ /*@-observertrans -dependenttrans @*/
+ *callbackData = opt->descrip;
+ /*@=observertrans =dependenttrans @*/
+ return opt2;
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+ cb = opt;
+ } else if (longName && opt->longName &&
+ (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+ /*@-nullpass@*/ /* LCL: opt->longName != NULL */
+ !strcmp(longName, opt->longName))
+ /*@=nullpass@*/
+ {
+ break;
+ } else if (shortName && shortName == opt->shortName) {
+ break;
+ }
+ }
+
+ if (!opt->longName && !opt->shortName)
+ return NULL;
+ /*@-modobserver -mods @*/
+ if (callback) *callback = NULL;
+ if (callbackData) *callbackData = NULL;
+ if (cb) {
+ if (callback)
+ /*@-castfcnptr@*/
+ *callback = (poptCallbackType)cb->arg;
+ /*@=castfcnptr@*/
+ if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) {
+ if (callbackData)
+ /*@-observertrans@*/ /* FIX: typedef double indirection. */
+ *callbackData = cb->descrip;
+ /*@=observertrans@*/
+ }
+ }
+ /*@=modobserver =mods @*/
+
+ return opt;
+}
+/*@=boundswrite@*/
+
+static const char * findNextArg(/*@special@*/ poptContext con,
+ unsigned argx, int delete_arg)
+ /*@uses con->optionStack, con->os,
+ con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
+ /*@modifies con @*/
+{
+ struct optionStackEntry * os = con->os;
+ const char * arg;
+
+ do {
+ int i;
+ arg = NULL;
+ while (os->next == os->argc && os > con->optionStack) os--;
+ if (os->next == os->argc && os == con->optionStack) break;
+ if (os->argv != NULL)
+ for (i = os->next; i < os->argc; i++) {
+ /*@-sizeoftype@*/
+ if (os->argb && PBM_ISSET(i, os->argb))
+ /*@innercontinue@*/ continue;
+ if (*os->argv[i] == '-')
+ /*@innercontinue@*/ continue;
+ if (--argx > 0)
+ /*@innercontinue@*/ continue;
+ arg = os->argv[i];
+ if (delete_arg) {
+ if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
+ if (os->argb != NULL) /* XXX can't happen */
+ PBM_SET(i, os->argb);
+ }
+ /*@innerbreak@*/ break;
+ /*@=sizeoftype@*/
+ }
+ if (os > con->optionStack) os--;
+ } while (arg == NULL);
+ return arg;
+}
+
+/*@-boundswrite@*/
+static /*@only@*/ /*@null@*/ const char *
+expandNextArg(/*@special@*/ poptContext con, const char * s)
+ /*@uses con->optionStack, con->os,
+ con->os->next, con->os->argb, con->os->argc, con->os->argv @*/
+ /*@modifies con @*/
+{
+ const char * a = NULL;
+ size_t alen, pos;
+ char *t, *te;
+ size_t tn = strlen(s) + 1;
+ char c;
+
+ te = t = malloc(tn);;
+ if (t == NULL) return NULL; /* XXX can't happen */
+ while ((c = *s++) != '\0') {
+ switch (c) {
+#if 0 /* XXX can't do this */
+ case '\\': /* escape */
+ c = *s++;
+ /*@switchbreak@*/ break;
+#endif
+ case '!':
+ if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
+ /*@switchbreak@*/ break;
+ /* XXX Make sure that findNextArg deletes only next arg. */
+ if (a == NULL) {
+ if ((a = findNextArg(con, 1, 1)) == NULL)
+ /*@switchbreak@*/ break;
+ }
+ s += 3;
+
+ alen = strlen(a);
+ tn += alen;
+ pos = te - t;
+ t = realloc(t, tn);
+ te = t + pos;
+ memcpy(te, a, alen+1); te += alen;
+ continue;
+ /*@notreached@*/ /*@switchbreak@*/ break;
+ default:
+ /*@switchbreak@*/ break;
+ }
+ *te++ = c;
+ }
+ *te = '\0';
+ t = realloc(t, strlen(t) + 1); /* XXX memory leak, hard to plug */
+ return t;
+}
+/*@=boundswrite@*/
+
+static void poptStripArg(/*@special@*/ poptContext con, int which)
+ /*@uses con->arg_strip, con->optionStack @*/
+ /*@defines con->arg_strip @*/
+ /*@modifies con @*/
+{
+ /*@-sizeoftype@*/
+ if (con->arg_strip == NULL)
+ con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
+ if (con->arg_strip != NULL) /* XXX can't happen */
+ PBM_SET(which, con->arg_strip);
+ /*@=sizeoftype@*/
+ /*@-compdef@*/ /* LCL: con->arg_strip udefined? */
+ return;
+ /*@=compdef@*/
+}
+
+int poptSaveLong(long * arg, int argInfo, long aLong)
+{
+ /* XXX Check alignment, may fail on funky platforms. */
+ if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+ return POPT_ERROR_NULLARG;
+
+ if (argInfo & POPT_ARGFLAG_NOT)
+ aLong = ~aLong;
+ switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
+ case 0:
+ *arg = aLong;
+ break;
+ case POPT_ARGFLAG_OR:
+ *arg |= aLong;
+ break;
+ case POPT_ARGFLAG_AND:
+ *arg &= aLong;
+ break;
+ case POPT_ARGFLAG_XOR:
+ *arg ^= aLong;
+ break;
+ default:
+ return POPT_ERROR_BADOPERATION;
+ /*@notreached@*/ break;
+ }
+ return 0;
+}
+
+int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+{
+ /* XXX Check alignment, may fail on funky platforms. */
+ if (arg == NULL || (((unsigned long)arg) & (sizeof(*arg)-1)))
+ return POPT_ERROR_NULLARG;
+
+ if (argInfo & POPT_ARGFLAG_NOT)
+ aLong = ~aLong;
+ switch (argInfo & POPT_ARGFLAG_LOGICALOPS) {
+ case 0:
+ *arg = aLong;
+ break;
+ case POPT_ARGFLAG_OR:
+ *arg |= aLong;
+ break;
+ case POPT_ARGFLAG_AND:
+ *arg &= aLong;
+ break;
+ case POPT_ARGFLAG_XOR:
+ *arg ^= aLong;
+ break;
+ default:
+ return POPT_ERROR_BADOPERATION;
+ /*@notreached@*/ break;
+ }
+ return 0;
+}
+
+/*@-boundswrite@*/
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con)
+{
+ const struct poptOption * opt = NULL;
+ int done = 0;
+
+ if (con == NULL)
+ return -1;
+ while (!done) {
+ const char * origOptString = NULL;
+ poptCallbackType cb = NULL;
+ const void * cbData = NULL;
+ const char * longArg = NULL;
+ int canstrip = 0;
+ int shorty = 0;
+
+ while (!con->os->nextCharArg && con->os->next == con->os->argc
+ && con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+ /*@-internalglobs@*/
+ invokeCallbacksPOST(con, con->options);
+ /*@=internalglobs@*/
+ if (con->doExec) return execCommand(con);
+ return -1;
+ }
+
+ /* Process next long option */
+ if (!con->os->nextCharArg) {
+ char * localOptString, * optString;
+ int thisopt;
+
+ /*@-sizeoftype@*/
+ if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
+ con->os->next++;
+ continue;
+ }
+ /*@=sizeoftype@*/
+ thisopt = con->os->next;
+ if (con->os->argv != NULL) /* XXX can't happen */
+ origOptString = con->os->argv[con->os->next++];
+
+ if (origOptString == NULL) /* XXX can't happen */
+ return POPT_ERROR_BADOPT;
+
+ if (con->restLeftover || *origOptString != '-') {
+ if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+ con->restLeftover = 1;
+ if (con->flags & POPT_CONTEXT_ARG_OPTS) {
+ con->os->nextArg = xstrdup(origOptString);
+ return 0;
+ }
+ if (con->leftovers != NULL) /* XXX can't happen */
+ con->leftovers[con->numLeftovers++] = origOptString;
+ continue;
+ }
+
+ /* Make a copy we can hack at */
+ { size_t bufsize = strlen(origOptString) + 1;
+ localOptString = optString = alloca(bufsize);
+ if (optString == NULL) /* XXX can't happen */
+ return POPT_ERROR_BADOPT;
+ strlcpy(optString, origOptString, bufsize);
+ }
+
+ if (optString[0] == '\0')
+ return POPT_ERROR_BADOPT;
+
+ if (optString[1] == '-' && !optString[2]) {
+ con->restLeftover = 1;
+ continue;
+ } else {
+ char *oe;
+ int singleDash;
+
+ optString++;
+ if (*optString == '-')
+ singleDash = 0, optString++;
+ else
+ singleDash = 1;
+
+ /* XXX aliases with arg substitution need "--alias=arg" */
+ if (handleAlias(con, optString, '\0', NULL))
+ continue;
+
+ if (handleExec(con, optString, '\0'))
+ continue;
+
+ /* Check for "--long=arg" option. */
+ for (oe = optString; *oe && *oe != '='; oe++)
+ {};
+ if (*oe == '=') {
+ *oe++ = '\0';
+ /* XXX longArg is mapped back to persistent storage. */
+ longArg = origOptString + (oe - localOptString);
+ } else
+ oe = NULL;
+
+ opt = findOption(con->options, optString, '\0', &cb, &cbData,
+ singleDash);
+ if (!opt && !singleDash)
+ return POPT_ERROR_BADOPT;
+ if (!opt && oe)
+ oe[-1] = '='; /* restore overwritten '=' */
+ }
+
+ if (!opt) {
+ con->os->nextCharArg = origOptString + 1;
+ longArg = NULL;
+ } else {
+ if (con->os == con->optionStack &&
+ opt->argInfo & POPT_ARGFLAG_STRIP)
+ {
+ canstrip = 1;
+ poptStripArg(con, thisopt);
+ }
+ shorty = 0;
+ }
+ }
+
+ /* Process next short option */
+ /*@-branchstate@*/ /* FIX: W2DO? */
+ if (con->os->nextCharArg) {
+ origOptString = con->os->nextCharArg;
+
+ con->os->nextCharArg = NULL;
+
+ if (handleAlias(con, NULL, *origOptString, origOptString + 1))
+ continue;
+
+ if (handleExec(con, NULL, *origOptString)) {
+ /* Restore rest of short options for further processing */
+ origOptString++;
+ if (*origOptString != '\0')
+ con->os->nextCharArg = origOptString;
+ continue;
+ }
+
+ opt = findOption(con->options, NULL, *origOptString, &cb,
+ &cbData, 0);
+ if (!opt)
+ return POPT_ERROR_BADOPT;
+ shorty = 1;
+
+ origOptString++;
+ if (*origOptString != '\0')
+ con->os->nextCharArg = origOptString;
+ }
+ /*@=branchstate@*/
+
+ if (opt == NULL) return POPT_ERROR_BADOPT; /* XXX can't happen */
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE
+ || (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+ if (longArg || (con->os->nextCharArg && con->os->nextCharArg[0] == '='))
+ return POPT_ERROR_UNWANTEDARG;
+ if (opt->arg) {
+ long val = (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL ? opt->val : 1;
+ if (poptSaveInt((int *)opt->arg, opt->argInfo, val))
+ return POPT_ERROR_BADOPERATION;
+ }
+ } else {
+ con->os->nextArg = _free(con->os->nextArg);
+ /*@-usedef@*/ /* FIX: W2DO? */
+ if (longArg) {
+ /*@=usedef@*/
+ longArg = expandNextArg(con, longArg);
+ con->os->nextArg = longArg;
+ } else if (con->os->nextCharArg) {
+ longArg = expandNextArg(con, con->os->nextCharArg + (con->os->nextCharArg[0] == '='));
+ con->os->nextArg = longArg;
+ con->os->nextCharArg = NULL;
+ } else {
+ while (con->os->next == con->os->argc &&
+ con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (con->os->next == con->os->argc) {
+ if (!(opt->argInfo & POPT_ARGFLAG_OPTIONAL))
+ /*@-compdef@*/ /* FIX: con->os->argv not defined */
+ return POPT_ERROR_NOARG;
+ /*@=compdef@*/
+ con->os->nextArg = NULL;
+ } else {
+
+ /*
+ * Make sure this isn't part of a short arg or the
+ * result of an alias expansion.
+ */
+ if (con->os == con->optionStack &&
+ (opt->argInfo & POPT_ARGFLAG_STRIP) &&
+ canstrip) {
+ poptStripArg(con, con->os->next);
+ }
+
+ if (con->os->argv != NULL) { /* XXX can't happen */
+ /* XXX watchout: subtle side-effects live here. */
+ longArg = con->os->argv[con->os->next++];
+ longArg = expandNextArg(con, longArg);
+ con->os->nextArg = longArg;
+ }
+ }
+ }
+ longArg = NULL;
+
+ if (opt->arg) {
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_STRING:
+ /* XXX memory leak, hard to plug */
+ *((const char **) opt->arg) = (con->os->nextArg)
+ ? xstrdup(con->os->nextArg) : NULL;
+ /*@switchbreak@*/ break;
+
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ { long aLong = 0;
+ char *end;
+
+ if (con->os->nextArg) {
+ aLong = strtol(con->os->nextArg, &end, 0);
+ if (!(end && *end == '\0'))
+ return POPT_ERROR_BADNUMBER;
+ }
+
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+ if (aLong == LONG_MIN || aLong == LONG_MAX)
+ return POPT_ERROR_OVERFLOW;
+ if (poptSaveLong((long *)opt->arg, opt->argInfo, aLong))
+ return POPT_ERROR_BADOPERATION;
+ } else {
+ if (aLong > INT_MAX || aLong < INT_MIN)
+ return POPT_ERROR_OVERFLOW;
+ if (poptSaveInt((int *)opt->arg, opt->argInfo, aLong))
+ return POPT_ERROR_BADOPERATION;
+ }
+ } /*@switchbreak@*/ break;
+
+ case POPT_ARG_FLOAT:
+ case POPT_ARG_DOUBLE:
+ { double aDouble = 0.0;
+ char *end;
+
+ if (con->os->nextArg) {
+ /*@-mods@*/
+ int saveerrno = errno;
+ errno = 0;
+ aDouble = strtod(con->os->nextArg, &end);
+ if (errno == ERANGE)
+ return POPT_ERROR_OVERFLOW;
+ errno = saveerrno;
+ /*@=mods@*/
+ if (*end != '\0')
+ return POPT_ERROR_BADNUMBER;
+ }
+
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_DOUBLE) {
+ *((double *) opt->arg) = aDouble;
+ } else {
+#define MY_ABS(a) ((((a) - 0.0) < DBL_EPSILON) ? -(a) : (a))
+ if ((MY_ABS(aDouble) - FLT_MAX) > DBL_EPSILON)
+ return POPT_ERROR_OVERFLOW;
+ if ((FLT_MIN - MY_ABS(aDouble)) > DBL_EPSILON)
+ return POPT_ERROR_OVERFLOW;
+ *((float *) opt->arg) = aDouble;
+ }
+ } /*@switchbreak@*/ break;
+ default:
+ fprintf(stdout,
+ POPT_("option type (%d) not implemented in popt\n"),
+ (opt->argInfo & POPT_ARG_MASK));
+ exit(EXIT_FAILURE);
+ /*@notreached@*/ /*@switchbreak@*/ break;
+ }
+ }
+ }
+
+ if (cb) {
+ /*@-internalglobs@*/
+ invokeCallbacksOPTION(con, con->options, opt, cbData, shorty);
+ /*@=internalglobs@*/
+ } else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+ done = 1;
+
+ if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ if (con->finalArgv != NULL)
+ { ssize_t bufsize = (opt->longName ? strlen(opt->longName) : 0) + 3;
+ char *s = malloc(bufsize);
+ if (s != NULL) { /* XXX can't happen */
+ if (opt->longName)
+ snprintf(s, bufsize, "%s%s",
+ ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+ opt->longName);
+ else
+ snprintf(s, bufsize, "-%c", opt->shortName);
+ con->finalArgv[con->finalArgvCount++] = s;
+ } else
+ con->finalArgv[con->finalArgvCount++] = NULL;
+ }
+
+ if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE)
+ /*@-ifempty@*/ ; /*@=ifempty@*/
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL)
+ /*@-ifempty@*/ ; /*@=ifempty@*/
+ else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+ if (con->finalArgv != NULL && con->os->nextArg)
+ con->finalArgv[con->finalArgvCount++] =
+ /*@-nullpass@*/ /* LCL: con->os->nextArg != NULL */
+ xstrdup(con->os->nextArg);
+ /*@=nullpass@*/
+ }
+ }
+
+ return (opt ? opt->val : -1); /* XXX can't happen */
+}
+/*@=boundswrite@*/
+
+const char * poptGetOptArg(poptContext con)
+{
+ const char * ret = NULL;
+ /*@-branchstate@*/
+ if (con) {
+ ret = con->os->nextArg;
+ con->os->nextArg = NULL;
+ }
+ /*@=branchstate@*/
+ return ret;
+}
+
+const char * poptGetArg(poptContext con)
+{
+ const char * ret = NULL;
+ if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
+ ret = con->leftovers[con->nextLeftover++];
+ return ret;
+}
+
+const char * poptPeekArg(poptContext con)
+{
+ const char * ret = NULL;
+ if (con && con->leftovers != NULL && con->nextLeftover < con->numLeftovers)
+ ret = con->leftovers[con->nextLeftover];
+ return ret;
+}
+
+/*@-boundswrite@*/
+const char ** poptGetArgs(poptContext con)
+{
+ if (con == NULL ||
+ con->leftovers == NULL || con->numLeftovers == con->nextLeftover)
+ return NULL;
+
+ /* some apps like [like RPM ;-) ] need this NULL terminated */
+ con->leftovers[con->numLeftovers] = NULL;
+
+ /*@-nullret -nullstate @*/ /* FIX: typedef double indirection. */
+ return (con->leftovers + con->nextLeftover);
+ /*@=nullret =nullstate @*/
+}
+/*@=boundswrite@*/
+
+poptContext poptFreeContext(poptContext con)
+{
+ poptItem item;
+ int i;
+
+ if (con == NULL) return con;
+ poptResetContext(con);
+ con->os->argb = _free(con->os->argb);
+
+ if (con->aliases != NULL)
+ for (i = 0; i < con->numAliases; i++) {
+ item = con->aliases + i;
+ /*@-modobserver -observertrans -dependenttrans@*/
+ item->option.longName = _free(item->option.longName);
+ item->option.descrip = _free(item->option.descrip);
+ item->option.argDescrip = _free(item->option.argDescrip);
+ /*@=modobserver =observertrans =dependenttrans@*/
+ item->argv = _free(item->argv);
+ }
+ con->aliases = _free(con->aliases);
+
+ if (con->execs != NULL)
+ for (i = 0; i < con->numExecs; i++) {
+ item = con->execs + i;
+ /*@-modobserver -observertrans -dependenttrans@*/
+ item->option.longName = _free(item->option.longName);
+ item->option.descrip = _free(item->option.descrip);
+ item->option.argDescrip = _free(item->option.argDescrip);
+ /*@=modobserver =observertrans =dependenttrans@*/
+ item->argv = _free(item->argv);
+ }
+ con->execs = _free(con->execs);
+
+ con->leftovers = _free(con->leftovers);
+ con->finalArgv = _free(con->finalArgv);
+ con->appName = _free(con->appName);
+ con->otherHelp = _free(con->otherHelp);
+ con->execPath = _free(con->execPath);
+ con->arg_strip = PBM_FREE(con->arg_strip);
+
+ con = _free(con);
+ return con;
+}
+
+int poptAddAlias(poptContext con, struct poptAlias alias,
+ /*@unused@*/ UNUSED(int flags))
+{
+ poptItem item = (poptItem) alloca(sizeof(*item));
+ memset(item, 0, sizeof(*item));
+ item->option.longName = alias.longName;
+ item->option.shortName = alias.shortName;
+ item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
+ item->option.arg = 0;
+ item->option.val = 0;
+ item->option.descrip = NULL;
+ item->option.argDescrip = NULL;
+ item->argc = alias.argc;
+ item->argv = alias.argv;
+ return poptAddItem(con, item, 0);
+}
+
+/*@-boundswrite@*/
+/*@-mustmod@*/ /* LCL: con not modified? */
+int poptAddItem(poptContext con, poptItem newItem, int flags)
+{
+ poptItem * items, item;
+ int * nitems;
+
+ switch (flags) {
+ case 1:
+ items = &con->execs;
+ nitems = &con->numExecs;
+ break;
+ case 0:
+ items = &con->aliases;
+ nitems = &con->numAliases;
+ break;
+ default:
+ return 1;
+ /*@notreached@*/ break;
+ }
+
+ *items = realloc((*items), ((*nitems) + 1) * sizeof(**items));
+ if ((*items) == NULL)
+ return 1;
+
+ item = (*items) + (*nitems);
+
+ item->option.longName =
+ (newItem->option.longName ? xstrdup(newItem->option.longName) : NULL);
+ item->option.shortName = newItem->option.shortName;
+ item->option.argInfo = newItem->option.argInfo;
+ item->option.arg = newItem->option.arg;
+ item->option.val = newItem->option.val;
+ item->option.descrip =
+ (newItem->option.descrip ? xstrdup(newItem->option.descrip) : NULL);
+ item->option.argDescrip =
+ (newItem->option.argDescrip ? xstrdup(newItem->option.argDescrip) : NULL);
+ item->argc = newItem->argc;
+ item->argv = newItem->argv;
+
+ (*nitems)++;
+
+ return 0;
+}
+/*@=mustmod@*/
+/*@=boundswrite@*/
+
+const char * poptBadOption(poptContext con, int flags)
+{
+ struct optionStackEntry * os = NULL;
+
+ if (con != NULL)
+ os = (flags & POPT_BADOPTION_NOALIAS) ? con->optionStack : con->os;
+
+ /*@-nullderef@*/ /* LCL: os->argv != NULL */
+ return (os && os->argv ? os->argv[os->next - 1] : NULL);
+ /*@=nullderef@*/
+}
+
+const char * poptStrerror(const int error)
+{
+ switch (error) {
+ case POPT_ERROR_NOARG:
+ return POPT_("missing argument");
+ case POPT_ERROR_UNWANTEDARG:
+ return POPT_("option does not take an argument");
+ case POPT_ERROR_BADOPT:
+ return POPT_("unknown option");
+ case POPT_ERROR_BADOPERATION:
+ return POPT_("mutually exclusive logical operations requested");
+ case POPT_ERROR_NULLARG:
+ return POPT_("opt->arg should not be NULL");
+ case POPT_ERROR_OPTSTOODEEP:
+ return POPT_("aliases nested too deeply");
+ case POPT_ERROR_BADQUOTE:
+ return POPT_("error in parameter quoting");
+ case POPT_ERROR_BADNUMBER:
+ return POPT_("invalid numeric value");
+ case POPT_ERROR_OVERFLOW:
+ return POPT_("number too large or too small");
+ case POPT_ERROR_MALLOC:
+ return POPT_("memory allocation failed");
+ case POPT_ERROR_ERRNO:
+ return strerror(errno);
+ default:
+ return POPT_("unknown error");
+ }
+}
+
+int poptStuffArgs(poptContext con, const char ** argv)
+{
+ int argc;
+ int rc;
+
+ if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ for (argc = 0; argv[argc]; argc++)
+ {};
+
+ con->os++;
+ con->os->next = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = NULL;
+ rc = poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+ con->os->stuffed = 1;
+
+ return rc;
+}
+
+const char * poptGetInvocationName(poptContext con)
+{
+ return (con->os->argv ? con->os->argv[0] : "");
+}
+
+/*@-boundswrite@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv)
+{
+ int numargs = argc;
+ int j = 1;
+ int i;
+
+ /*@-sizeoftype@*/
+ if (con->arg_strip)
+ for (i = 1; i < argc; i++) {
+ if (PBM_ISSET(i, con->arg_strip))
+ numargs--;
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (con->arg_strip && PBM_ISSET(i, con->arg_strip))
+ continue;
+ argv[j] = (j < numargs) ? argv[i] : NULL;
+ j++;
+ }
+ /*@=sizeoftype@*/
+
+ return numargs;
+}
+/*@=boundswrite@*/
diff --git a/popt/popt.h b/popt/popt.h
new file mode 100644
index 0000000..8d85f73
--- /dev/null
+++ b/popt/popt.h
@@ -0,0 +1,565 @@
+/** \file popt/popt.h
+ * \ingroup popt
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_POPT
+#define H_POPT
+
+#include <stdio.h> /* for FILE * */
+
+#define POPT_OPTION_DEPTH 10
+
+/** \ingroup popt
+ * \name Arg type identifiers
+ */
+/*@{*/
+#define POPT_ARG_NONE 0 /*!< no arg */
+#define POPT_ARG_STRING 1 /*!< arg will be saved as string */
+#define POPT_ARG_INT 2 /*!< arg will be converted to int */
+#define POPT_ARG_LONG 3 /*!< arg will be converted to long */
+#define POPT_ARG_INCLUDE_TABLE 4 /*!< arg points to table */
+#define POPT_ARG_CALLBACK 5 /*!< table-wide callback... must be
+ set first in table; arg points
+ to callback, descrip points to
+ callback data to pass */
+#define POPT_ARG_INTL_DOMAIN 6 /*!< set the translation domain
+ for this table and any
+ included tables; arg points
+ to the domain string */
+#define POPT_ARG_VAL 7 /*!< arg should take value val */
+#define POPT_ARG_FLOAT 8 /*!< arg will be converted to float */
+#define POPT_ARG_DOUBLE 9 /*!< arg will be converted to double */
+
+#define POPT_ARG_MASK 0x0000FFFF
+/*@}*/
+
+/** \ingroup popt
+ * \name Arg modifiers
+ */
+/*@{*/
+#define POPT_ARGFLAG_ONEDASH 0x80000000 /*!< allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /*!< don't show in help/usage */
+#define POPT_ARGFLAG_STRIP 0x20000000 /*!< strip this arg from argv(only applies to long args) */
+#define POPT_ARGFLAG_OPTIONAL 0x10000000 /*!< arg may be missing */
+
+#define POPT_ARGFLAG_OR 0x08000000 /*!< arg will be or'ed */
+#define POPT_ARGFLAG_NOR 0x09000000 /*!< arg will be nor'ed */
+#define POPT_ARGFLAG_AND 0x04000000 /*!< arg will be and'ed */
+#define POPT_ARGFLAG_NAND 0x05000000 /*!< arg will be nand'ed */
+#define POPT_ARGFLAG_XOR 0x02000000 /*!< arg will be xor'ed */
+#define POPT_ARGFLAG_NOT 0x01000000 /*!< arg will be negated */
+#define POPT_ARGFLAG_LOGICALOPS \
+ (POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
+
+#define POPT_BIT_SET (POPT_ARG_VAL|POPT_ARGFLAG_OR)
+ /*!< set arg bit(s) */
+#define POPT_BIT_CLR (POPT_ARG_VAL|POPT_ARGFLAG_NAND)
+ /*!< clear arg bit(s) */
+
+#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
+
+/*@}*/
+
+/** \ingroup popt
+ * \name Callback modifiers
+ */
+/*@{*/
+#define POPT_CBFLAG_PRE 0x80000000 /*!< call the callback before parse */
+#define POPT_CBFLAG_POST 0x40000000 /*!< call the callback after parse */
+#define POPT_CBFLAG_INC_DATA 0x20000000 /*!< use data from the include line,
+ not the subtable */
+#define POPT_CBFLAG_SKIPOPTION 0x10000000 /*!< don't callback with option */
+#define POPT_CBFLAG_CONTINUE 0x08000000 /*!< continue callbacks with option */
+/*@}*/
+
+/** \ingroup popt
+ * \name Error return values
+ */
+/*@{*/
+#define POPT_ERROR_NOARG -10 /*!< missing argument */
+#define POPT_ERROR_BADOPT -11 /*!< unknown option */
+#define POPT_ERROR_UNWANTEDARG -12 /*!< option does not take an argument */
+#define POPT_ERROR_OPTSTOODEEP -13 /*!< aliases nested too deeply */
+#define POPT_ERROR_BADQUOTE -15 /*!< error in paramter quoting */
+#define POPT_ERROR_ERRNO -16 /*!< errno set, use strerror(errno) */
+#define POPT_ERROR_BADNUMBER -17 /*!< invalid numeric value */
+#define POPT_ERROR_OVERFLOW -18 /*!< number too large or too small */
+#define POPT_ERROR_BADOPERATION -19 /*!< mutually exclusive logical operations requested */
+#define POPT_ERROR_NULLARG -20 /*!< opt->arg should not be NULL */
+#define POPT_ERROR_MALLOC -21 /*!< memory allocation failed */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptBadOption() flags
+ */
+/*@{*/
+#define POPT_BADOPTION_NOALIAS (1 << 0) /*!< don't go into an alias */
+/*@}*/
+
+/** \ingroup popt
+ * \name poptGetContext() flags
+ */
+/*@{*/
+#define POPT_CONTEXT_NO_EXEC (1 << 0) /*!< ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /*!< pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
+#define POPT_CONTEXT_ARG_OPTS (1 << 4) /*!< return args as options with value 0 */
+/*@}*/
+
+/** \ingroup popt
+ */
+struct poptOption {
+/*@observer@*/ /*@null@*/
+ const char * longName; /*!< may be NULL */
+ char shortName; /*!< may be NUL */
+ int argInfo;
+/*@shared@*/ /*@null@*/
+ void * arg; /*!< depends on argInfo */
+ int val; /*!< 0 means don't return, just update flag */
+/*@observer@*/ /*@null@*/
+ const char * descrip; /*!< description for autohelp -- may be NULL */
+/*@observer@*/ /*@null@*/
+ const char * argDescrip; /*!< argument description for autohelp */
+};
+
+/** \ingroup popt
+ * A popt alias argument for poptAddAlias().
+ */
+struct poptAlias {
+/*@owned@*/ /*@null@*/
+ const char * longName; /*!< may be NULL */
+ char shortName; /*!< may be NUL */
+ int argc;
+/*@owned@*/
+ const char ** argv; /*!< must be free()able */
+};
+
+/** \ingroup popt
+ * A popt alias or exec argument for poptAddItem().
+ */
+/*@-exporttype@*/
+typedef struct poptItem_s {
+ struct poptOption option; /*!< alias/exec name(s) and description. */
+ int argc; /*!< (alias) no. of args. */
+/*@owned@*/
+ const char ** argv; /*!< (alias) args, must be free()able. */
+} * poptItem;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ * \name Auto-generated help/usage
+ */
+/*@{*/
+
+/**
+ * Empty table marker to enable displaying popt alias/exec options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptAliasOptions[];
+/*@=exportvar@*/
+#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
+ 0, "Options implemented via popt alias/exec:", NULL },
+
+/**
+ * Auto help table options.
+ */
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption poptHelpOptions[];
+/*@=exportvar@*/
+
+/*@-exportvar@*/
+/*@unchecked@*/ /*@observer@*/
+extern struct poptOption * poptHelpOptionsI18N;
+/*@=exportvar@*/
+
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+ 0, "Help options:", NULL },
+
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+/*@}*/
+
+/** \ingroup popt
+ */
+/*@-exporttype@*/
+typedef /*@abstract@*/ struct poptContext_s * poptContext;
+/*@=exporttype@*/
+
+/** \ingroup popt
+ */
+#ifndef __cplusplus
+/*@-exporttype -typeuse@*/
+typedef struct poptOption * poptOption;
+/*@=exporttype =typeuse@*/
+#endif
+
+/*@-exportconst@*/
+enum poptCallbackReason {
+ POPT_CALLBACK_REASON_PRE = 0,
+ POPT_CALLBACK_REASON_POST = 1,
+ POPT_CALLBACK_REASON_OPTION = 2
+};
+/*@=exportconst@*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*@-type@*/
+
+/** \ingroup popt
+ * Table callback prototype.
+ * @param con context
+ * @param reason reason for callback
+ * @param opt option that triggered callback
+ * @param arg @todo Document.
+ * @param data @todo Document.
+ */
+typedef void (*poptCallbackType) (poptContext con,
+ enum poptCallbackReason reason,
+ /*@null@*/ const struct poptOption * opt,
+ /*@null@*/ const char * arg,
+ /*@null@*/ const void * data)
+ /*@globals internalState @*/
+ /*@modifies internalState @*/;
+
+/** \ingroup popt
+ * Initialize popt context.
+ * @param name context name (usually argv[0] program name)
+ * @param argc no. of arguments
+ * @param argv argument array
+ * @param options address of popt option table
+ * @param flags or'd POPT_CONTEXT_* bits
+ * @return initialized popt context
+ */
+/*@only@*/ /*@null@*/
+poptContext poptGetContext(
+ /*@dependent@*/ /*@keep@*/ const char * name,
+ int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
+ /*@dependent@*/ /*@keep@*/ const struct poptOption * options,
+ int flags)
+ /*@*/;
+
+/** \ingroup popt
+ * Reinitialize popt context.
+ * @param con context
+ */
+/*@unused@*/
+void poptResetContext(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Return value of next option found.
+ * @param con context
+ * @return next option val, -1 on last item, POPT_ERROR_* on error
+ */
+int poptGetNextOpt(/*@null@*/poptContext con)
+ /*@globals fileSystem, internalState @*/
+ /*@modifies con, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Return next option argument (if any).
+ * @param con context
+ * @return option argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetOptArg(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Return next argument.
+ * @param con context
+ * @return next argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptGetArg(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Peek at current argument.
+ * @param con context
+ * @return current argument, NULL if no argument is available
+ */
+/*@observer@*/ /*@null@*/ /*@unused@*/
+const char * poptPeekArg(/*@null@*/poptContext con)
+ /*@*/;
+
+/** \ingroup popt
+ * Return remaining arguments.
+ * @param con context
+ * @return argument array, NULL terminated
+ */
+/*@observer@*/ /*@null@*/
+const char ** poptGetArgs(/*@null@*/poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Return the option which caused the most recent error.
+ * @param con context
+ * @param flags
+ * @return offending option
+ */
+/*@observer@*/
+const char * poptBadOption(/*@null@*/poptContext con, int flags)
+ /*@*/;
+
+/** \ingroup popt
+ * Destroy context.
+ * @param con context
+ * @return NULL always
+ */
+/*@null@*/
+poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Add arguments to context.
+ * @param con context
+ * @param argv argument array, NULL terminated
+ * @return 0 on success, POPT_ERROR_OPTSTOODEEP on failure
+ */
+/*@unused@*/
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias to context.
+ * @todo Pass alias by reference, not value.
+ * @deprecated Use poptAddItem instead.
+ * @param con context
+ * @param alias alias to add
+ * @param flags (unused)
+ * @return 0 on success
+ */
+/*@unused@*/
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Add alias/exec item to context.
+ * @param con context
+ * @param newItem alias/exec item to add
+ * @param flags 0 for alias, 1 for exec
+ * @return 0 on success
+ */
+int poptAddItem(poptContext con, poptItem newItem, int flags)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Read configuration file.
+ * @param con context
+ * @param fn file name to read
+ * @return 0 on success, POPT_ERROR_ERRNO on failure
+ */
+int poptReadConfigFile(poptContext con, const char * fn)
+ /*@globals errno, fileSystem, internalState @*/
+ /*@modifies con->execs, con->numExecs,
+ errno, fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Read default configuration from /etc/popt and $HOME/.popt.
+ * @param con context
+ * @param useEnv (unused)
+ * @return 0 on success, POPT_ERROR_ERRNO on failure
+ */
+/*@unused@*/
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
+ /*@globals fileSystem, internalState @*/
+ /*@modifies con->execs, con->numExecs,
+ fileSystem, internalState @*/;
+
+/** \ingroup popt
+ * Duplicate an argument array.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param argc no. of arguments
+ * @param argv argument array
+ * @retval argcPtr address of returned no. of arguments
+ * @retval argvPtr address of returned argument array
+ * @return 0 on success, POPT_ERROR_NOARG on failure
+ */
+int poptDupArgv(int argc, /*@null@*/ const char **argv,
+ /*@null@*/ /*@out@*/ int * argcPtr,
+ /*@null@*/ /*@out@*/ const char *** argvPtr)
+ /*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parse a string into an argument array.
+ * The parse allows ', ", and \ quoting, but ' is treated the same as " and
+ * both may include \ quotes.
+ * @note: The argument array is malloc'd as a single area, so only argv must
+ * be free'd.
+ *
+ * @param s string to parse
+ * @retval argcPtr address of returned no. of arguments
+ * @retval argvPtr address of returned argument array
+ */
+int poptParseArgvString(const char * s,
+ /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
+ /*@modifies *argcPtr, *argvPtr @*/;
+
+/** \ingroup popt
+ * Parses an input configuration file and returns an string that is a
+ * command line. For use with popt. You must free the return value when done.
+ *
+ * Given the file:
+\verbatim
+# this line is ignored
+ # this one too
+aaa
+ bbb
+ ccc
+bla=bla
+
+this_is = fdsafdas
+ bad_line=
+ reall bad line
+ reall bad line = again
+5555= 55555
+ test = with lots of spaces
+\endverbatim
+*
+* The result is:
+\verbatim
+--aaa --bbb --ccc --bla="bla" --this_is="fdsafdas" --5555="55555" --test="with lots of spaces"
+\endverbatim
+*
+* Passing this to poptParseArgvString() yields an argv of:
+\verbatim
+'--aaa'
+'--bbb'
+'--ccc'
+'--bla=bla'
+'--this_is=fdsafdas'
+'--5555=55555'
+'--test=with lots of spaces'
+\endverbatim
+ *
+ * @bug NULL is returned if file line is too long.
+ * @bug Silently ignores invalid lines.
+ *
+ * @param fp file handle to read
+ * @param *argstrp return string of options (malloc'd)
+ * @param flags unused
+ * @return 0 on success
+ * @see poptParseArgvString
+ */
+/*@-fcnuse@*/
+int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, *argstrp, fileSystem @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return formatted error string for popt failure.
+ * @param error popt error
+ * @return error string
+ */
+/*@observer@*/
+const char * poptStrerror(const int error)
+ /*@*/;
+
+/** \ingroup popt
+ * Limit search for executables.
+ * @param con context
+ * @param path single path to search for executables
+ * @param allowAbsolute absolute paths only?
+ */
+/*@unused@*/
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
+ /*@modifies con @*/;
+
+/** \ingroup popt
+ * Print detailed description of options.
+ * @param con context
+ * @param fp ouput file handle
+ * @param flags (unused)
+ */
+void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Print terse description of options.
+ * @param con context
+ * @param fp ouput file handle
+ * @param flags (unused)
+ */
+void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/;
+
+/** \ingroup popt
+ * Provide text to replace default "[OPTION...]" in help/usage output.
+ * @param con context
+ * @param text replacement text
+ */
+/*@-fcnuse@*/
+void poptSetOtherOptionHelp(poptContext con, const char * text)
+ /*@modifies con @*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Return argv[0] from context.
+ * @param con context
+ * @return argv[0]
+ */
+/*@-fcnuse@*/
+/*@observer@*/
+const char * poptGetInvocationName(poptContext con)
+ /*@*/;
+/*@=fcnuse@*/
+
+/** \ingroup popt
+ * Shuffle argv pointers to remove stripped args, returns new argc.
+ * @param con context
+ * @param argc no. of args
+ * @param argv arg vector
+ * @return new argc
+ */
+/*@-fcnuse@*/
+int poptStrippedArgv(poptContext con, int argc, char ** argv)
+ /*@modifies *argv @*/;
+/*@=fcnuse@*/
+
+/**
+ * Save a long, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg integer pointer, aligned on int boundary.
+ * @param argInfo logical operation (see POPT_ARGFLAG_*)
+ * @param aLong value to use
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
+ /*@modifies *arg @*/
+ /*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/**
+ * Save an integer, performing logical operation with value.
+ * @warning Alignment check may be too strict on certain platorms.
+ * @param arg integer pointer, aligned on int boundary.
+ * @param argInfo logical operation (see POPT_ARGFLAG_*)
+ * @param aLong value to use
+ * @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
+ */
+/*@-incondefs@*/
+/*@unused@*/
+int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
+ /*@modifies *arg @*/
+ /*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
+/*@=incondefs@*/
+
+/*@=type@*/
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/popt/poptconfig.c b/popt/poptconfig.c
new file mode 100644
index 0000000..9733d15
--- /dev/null
+++ b/popt/poptconfig.c
@@ -0,0 +1,183 @@
+/** \ingroup popt
+ * \file popt/poptconfig.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+#include "poptint.h"
+/*@access poptContext @*/
+
+/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
+static void configLine(poptContext con, char * line)
+ /*@modifies con @*/
+{
+ size_t nameLength;
+ const char * entryType;
+ const char * opt;
+ poptItem item = (poptItem) alloca(sizeof(*item));
+ int i, j;
+
+ if (con->appName == NULL)
+ return;
+ nameLength = strlen(con->appName);
+
+/*@-boundswrite@*/
+ memset(item, 0, sizeof(*item));
+
+ if (strncmp(line, con->appName, nameLength)) return;
+
+ line += nameLength;
+ if (*line == '\0' || !isSpace(line)) return;
+
+ while (*line != '\0' && isSpace(line)) line++;
+ entryType = line;
+ while (*line == '\0' || !isSpace(line)) line++;
+ *line++ = '\0';
+
+ while (*line != '\0' && isSpace(line)) line++;
+ if (*line == '\0') return;
+ opt = line;
+ while (*line == '\0' || !isSpace(line)) line++;
+ *line++ = '\0';
+
+ while (*line != '\0' && isSpace(line)) line++;
+ if (*line == '\0') return;
+
+ /*@-temptrans@*/ /* FIX: line alias is saved */
+ if (opt[0] == '-' && opt[1] == '-')
+ item->option.longName = opt + 2;
+ else if (opt[0] == '-' && opt[2] == '\0')
+ item->option.shortName = opt[1];
+ /*@=temptrans@*/
+
+ if (poptParseArgvString(line, &item->argc, &item->argv)) return;
+
+ /*@-modobserver@*/
+ item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
+ for (i = 0, j = 0; i < item->argc; i++, j++) {
+ const char * f;
+ if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
+ f = item->argv[i] + sizeof("--POPTdesc=");
+ if (f[0] == '$' && f[1] == '"') f++;
+ item->option.descrip = f;
+ item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
+ j--;
+ } else
+ if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
+ f = item->argv[i] + sizeof("--POPTargs=");
+ if (f[0] == '$' && f[1] == '"') f++;
+ item->option.argDescrip = f;
+ item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
+ item->option.argInfo |= POPT_ARG_STRING;
+ j--;
+ } else
+ if (j != i)
+ item->argv[j] = item->argv[i];
+ }
+ if (j != i) {
+ item->argv[j] = NULL;
+ item->argc = j;
+ }
+ /*@=modobserver@*/
+/*@=boundswrite@*/
+
+ /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
+ if (!strcmp(entryType, "alias"))
+ (void) poptAddItem(con, item, 0);
+ else if (!strcmp(entryType, "exec"))
+ (void) poptAddItem(con, item, 1);
+ /*@=nullstate@*/
+}
+/*@=compmempass@*/
+
+int poptReadConfigFile(poptContext con, const char * fn)
+{
+ const char * file, * chptr, * end;
+ char * buf;
+/*@dependent@*/ char * dst;
+ int fd, rc;
+ off_t fileLength;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0)
+ return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);
+
+ fileLength = lseek(fd, 0, SEEK_END);
+ if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
+ rc = errno;
+ (void) close(fd);
+ errno = rc;
+ return POPT_ERROR_ERRNO;
+ }
+
+ file = alloca(fileLength + 1);
+ if (read(fd, (char *)file, fileLength) != fileLength) {
+ rc = errno;
+ (void) close(fd);
+ errno = rc;
+ return POPT_ERROR_ERRNO;
+ }
+ if (close(fd) == -1)
+ return POPT_ERROR_ERRNO;
+
+/*@-boundswrite@*/
+ dst = buf = alloca(fileLength + 1);
+
+ chptr = file;
+ end = (file + fileLength);
+ /*@-infloops@*/ /* LCL: can't detect chptr++ */
+ while (chptr < end) {
+ switch (*chptr) {
+ case '\n':
+ *dst = '\0';
+ dst = buf;
+ while (*dst && isSpace(dst)) dst++;
+ if (*dst && *dst != '#')
+ configLine(con, dst);
+ chptr++;
+ /*@switchbreak@*/ break;
+ case '\\':
+ *dst++ = *chptr++;
+ if (chptr < end) {
+ if (*chptr == '\n')
+ dst--, chptr++;
+ /* \ at the end of a line does not insert a \n */
+ else
+ *dst++ = *chptr++;
+ }
+ /*@switchbreak@*/ break;
+ default:
+ *dst++ = *chptr++;
+ /*@switchbreak@*/ break;
+ }
+ }
+ /*@=infloops@*/
+/*@=boundswrite@*/
+
+ return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
+{
+ char * fn, * home;
+ int rc;
+
+ if (con->appName == NULL) return 0;
+
+ rc = poptReadConfigFile(con, "/etc/popt");
+ if (rc) return rc;
+
+ if ((home = getenv("HOME"))) {
+ size_t bufsize = strlen(home) + 20;
+ fn = alloca(bufsize);
+ if (fn == NULL) return 0;
+ snprintf(fn, bufsize, "%s/.popt", home);
+ rc = poptReadConfigFile(con, fn);
+ if (rc) return rc;
+ }
+
+ return 0;
+}
diff --git a/popt/popthelp.c b/popt/popthelp.c
new file mode 100644
index 0000000..6a00976
--- /dev/null
+++ b/popt/popthelp.c
@@ -0,0 +1,826 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/** \ingroup popt
+ * \file popt/popthelp.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+
+/*#define POPT_WCHAR_HACK*/
+#ifdef POPT_WCHAR_HACK
+#include <wchar.h> /* for mbsrtowcs */
+/*@access mbstate_t @*/
+#endif
+#include "poptint.h"
+
+/*@access poptContext@*/
+
+/**
+ * Display arguments.
+ * @param con context
+ * @param foo (unused)
+ * @param key option(s)
+ * @param arg (unused)
+ * @param data (unused)
+ */
+static void displayArgs(poptContext con,
+ /*@unused@*/ UNUSED(enum poptCallbackReason foo),
+ struct poptOption * key,
+ /*@unused@*/ UNUSED(const char * arg), /*@unused@*/ UNUSED(void * data))
+ /*@globals fileSystem@*/
+ /*@modifies fileSystem@*/
+{
+ if (key->shortName == '?')
+ poptPrintHelp(con, stdout, 0);
+ else
+ poptPrintUsage(con, stdout, 0);
+ exit(0);
+}
+
+#ifdef NOTYET
+/*@unchecked@*/
+static int show_option_defaults = 0;
+#endif
+
+/**
+ * Empty table marker to enable displaying popt alias/exec options.
+ */
+/*@observer@*/ /*@unchecked@*/
+struct poptOption poptAliasOptions[] = {
+ POPT_TABLEEND
+};
+
+/**
+ * Auto help table options.
+ */
+/*@-castfcnptr@*/
+/*@observer@*/ /*@unchecked@*/
+struct poptOption poptHelpOptions[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+ POPT_TABLEEND
+} ;
+
+/*@observer@*/ /*@unchecked@*/
+static struct poptOption poptHelpOptions2[] = {
+/*@-readonlytrans@*/
+ { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL},
+/*@=readonlytrans@*/
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+#ifdef NOTYET
+ { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
+ N_("Display option defaults in message"), NULL },
+#endif
+ POPT_TABLEEND
+} ;
+
+/*@observer@*/ /*@unchecked@*/
+struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
+/*@=castfcnptr@*/
+
+/**
+ * @param table option(s)
+ */
+/*@observer@*/ /*@null@*/ static const char *
+getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
+ /*@*/
+{
+ const struct poptOption *opt;
+
+ if (table != NULL)
+ for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
+ if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
+ return opt->arg;
+ }
+ return NULL;
+}
+
+/**
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ */
+/*@observer@*/ /*@null@*/ static const char *
+getArgDescrip(const struct poptOption * opt,
+ /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@=paramuse@*/
+ /*@*/
+{
+ if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+ if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+ if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+ if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ /*case POPT_ARG_NONE: return POPT_("NONE");*/ /* impossible */
+#ifdef DYING
+ case POPT_ARG_VAL: return POPT_("VAL");
+#else
+ case POPT_ARG_VAL: return NULL;
+#endif
+ case POPT_ARG_INT: return POPT_("INT");
+ case POPT_ARG_LONG: return POPT_("LONG");
+ case POPT_ARG_STRING: return POPT_("STRING");
+ case POPT_ARG_FLOAT: return POPT_("FLOAT");
+ case POPT_ARG_DOUBLE: return POPT_("DOUBLE");
+ default: return POPT_("ARG");
+ }
+}
+
+/**
+ * Display default value for an option.
+ * @param lineLength display positions remaining
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ * @return
+ */
+static /*@only@*/ /*@null@*/ char *
+singleOptionDefaultValue(size_t lineLength,
+ const struct poptOption * opt,
+ /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@=paramuse@*/
+ /*@*/
+{
+ const char * defstr = D_(translation_domain, "default");
+ size_t limit, bufsize = 4*lineLength + 1;
+ char * le = malloc(bufsize);
+ char * l = le;
+
+ if (le == NULL) return NULL; /* XXX can't happen */
+/*@-boundswrite@*/
+ *le++ = '(';
+ le += strlcpy(le, defstr, bufsize - 3);
+ *le++ = ':';
+ *le++ = ' ';
+ limit = bufsize - (le - l) - 1; /* -1 for closing paren */
+ if (opt->arg) /* XXX programmer error */
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_VAL:
+ case POPT_ARG_INT:
+ { long aLong = *((int *)opt->arg);
+ le += snprintf(le, limit, "%ld", aLong);
+ } break;
+ case POPT_ARG_LONG:
+ { long aLong = *((long *)opt->arg);
+ le += snprintf(le, limit, "%ld", aLong);
+ } break;
+ case POPT_ARG_FLOAT:
+ { double aDouble = *((float *)opt->arg);
+ le += snprintf(le, limit, "%g", aDouble);
+ } break;
+ case POPT_ARG_DOUBLE:
+ { double aDouble = *((double *)opt->arg);
+ le += snprintf(le, limit, "%g", aDouble);
+ } break;
+ case POPT_ARG_STRING:
+ { const char * s = *(const char **)opt->arg;
+ if (s == NULL) {
+ le += strlcpy(le, "null", limit);
+ } else {
+ size_t len;
+ limit -= 2; /* make room for quotes */
+ *le++ = '"';
+ len = strlcpy(le, s, limit);
+ if (len >= limit) {
+ le += limit - 3 - 1;
+ *le++ = '.'; *le++ = '.'; *le++ = '.';
+ } else
+ le += len;
+ *le++ = '"';
+ }
+ } break;
+ case POPT_ARG_NONE:
+ default:
+ l = _free(l);
+ return NULL;
+ /*@notreached@*/ break;
+ }
+ *le++ = ')';
+ *le = '\0';
+/*@=boundswrite@*/
+
+ return l;
+}
+
+/**
+ * Display help text for an option.
+ * @param fp output file handle
+ * @param maxLeftCol largest argument display width
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ */
+static void singleOptionHelp(FILE * fp, size_t maxLeftCol,
+ const struct poptOption * opt,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ size_t indentLength = maxLeftCol + 5;
+ size_t lineLength = 79 - indentLength;
+ const char * help = D_(translation_domain, opt->descrip);
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+ size_t helpLength;
+ char * defs = NULL;
+ char * left;
+ size_t lelen, limit;
+ size_t nb = maxLeftCol + 1;
+ int displaypad = 0;
+
+ /* Make sure there's more than enough room in target buffer. */
+ if (opt->longName) nb += strlen(opt->longName);
+ if (argDescrip) nb += strlen(argDescrip);
+
+/*@-boundswrite@*/
+ left = malloc(nb);
+ if (left == NULL) return; /* XXX can't happen */
+ left[0] = '\0';
+ left[maxLeftCol] = '\0';
+
+ if (opt->longName && opt->shortName)
+ snprintf(left, nb, "-%c, %s%s", opt->shortName,
+ ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+ opt->longName);
+ else if (opt->shortName != '\0')
+ snprintf(left, nb, "-%c", opt->shortName);
+ else if (opt->longName)
+ snprintf(left, nb, "%s%s",
+ ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
+ opt->longName);
+ if (!*left) goto out;
+
+ if (argDescrip) {
+ char * le = left + strlen(left);
+
+ if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+ *le++ = '[';
+
+ /* Choose type of output */
+ /*@-branchstate@*/
+ if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
+ defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
+ if (defs) {
+ size_t bufsize = (help ? strlen(help) : 0) + sizeof " " + strlen(defs);
+ char * t = malloc(bufsize);
+ if (t) {
+ snprintf(t, bufsize, "%s %s", help ? help : "", defs);
+ defs = _free(defs);
+ }
+ defs = t;
+ }
+ }
+ /*@=branchstate@*/
+
+ if (opt->argDescrip == NULL) {
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_NONE:
+ break;
+ case POPT_ARG_VAL:
+#ifdef NOTNOW /* XXX pug ugly nerdy output */
+ { long aLong = opt->val;
+ int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS);
+ int negate = (opt->argInfo & POPT_ARGFLAG_NOT);
+
+ /* Don't bother displaying typical values */
+ if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
+ break;
+ *le++ = '[';
+ switch (ops) {
+ case POPT_ARGFLAG_OR:
+ *le++ = '|';
+ /*@innerbreak@*/ break;
+ case POPT_ARGFLAG_AND:
+ *le++ = '&';
+ /*@innerbreak@*/ break;
+ case POPT_ARGFLAG_XOR:
+ *le++ = '^';
+ /*@innerbreak@*/ break;
+ default:
+ /*@innerbreak@*/ break;
+ }
+ *le++ = (opt->longName != NULL ? '=' : ' ');
+ if (negate) *le++ = '~';
+ /*@-formatconst@*/
+ limit = nb - (le - left);
+ lelen = snprintf(le, limit, (ops ? "0x%lx" : "%ld"), aLong);
+ le += lelen >= limit ? limit - 1 : lelen;
+ /*@=formatconst@*/
+ *le++ = ']';
+ }
+#endif
+ break;
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ case POPT_ARG_FLOAT:
+ case POPT_ARG_DOUBLE:
+ case POPT_ARG_STRING:
+ *le++ = (opt->longName != NULL ? '=' : ' ');
+ limit = nb - (le - left);
+ lelen = strlcpy(le, argDescrip, limit);
+ le += lelen >= limit ? limit - 1 : lelen;
+ break;
+ default:
+ break;
+ }
+ } else {
+
+ *le++ = '=';
+ limit = nb - (le - left);
+ lelen = strlcpy(le, argDescrip, limit);
+ if (lelen >= limit)
+ lelen = limit - 1;
+ le += lelen;
+
+#ifdef POPT_WCHAR_HACK
+ { const char * scopy = argDescrip;
+ mbstate_t t;
+ size_t n;
+
+ memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
+ /* Determine number of characters. */
+ n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+
+ displaypad = (int) (lelen-n);
+ }
+#endif
+ }
+ if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
+ *le++ = ']';
+ *le = '\0';
+ }
+/*@=boundswrite@*/
+
+ if (help)
+ fprintf(fp," %-*s ", (int)maxLeftCol+displaypad, left);
+ else {
+ fprintf(fp," %s\n", left);
+ goto out;
+ }
+
+ left = _free(left);
+/*@-branchstate@*/
+ if (defs) {
+ help = defs;
+ defs = NULL;
+ }
+/*@=branchstate@*/
+
+ helpLength = strlen(help);
+/*@-boundsread@*/
+ while (helpLength > lineLength) {
+ const char * ch;
+ char format[16];
+
+ ch = help + lineLength - 1;
+ while (ch > help && !isSpace(ch)) ch--;
+ if (ch == help) break; /* give up */
+ while (ch > (help + 1) && isSpace(ch)) ch--;
+ ch++;
+
+ snprintf(format, sizeof format, "%%.%ds\n%%%ds", (int) (ch - help), (int) indentLength);
+ /*@-formatconst@*/
+ fprintf(fp, format, help, " ");
+ /*@=formatconst@*/
+ help = ch;
+ while (isSpace(help) && *help) help++;
+ helpLength = strlen(help);
+ }
+/*@=boundsread@*/
+
+ if (helpLength) fprintf(fp, "%s\n", help);
+
+out:
+ /*@-dependenttrans@*/
+ defs = _free(defs);
+ /*@=dependenttrans@*/
+ left = _free(left);
+}
+
+/**
+ * Find display width for longest argument string.
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ * @return display width
+ */
+static size_t maxArgWidth(const struct poptOption * opt,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@*/
+{
+ size_t max = 0;
+ size_t len = 0;
+ const char * s;
+
+ if (opt != NULL)
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ if (opt->arg) /* XXX program error */
+ len = maxArgWidth(opt->arg, translation_domain);
+ if (len > max) max = len;
+ } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ len = sizeof(" ")-1;
+ if (opt->shortName != '\0') len += sizeof("-X")-1;
+ if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1;
+ if (opt->longName) {
+ len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
+ ? sizeof("-")-1 : sizeof("--")-1);
+ len += strlen(opt->longName);
+ }
+
+ s = getArgDescrip(opt, translation_domain);
+
+#ifdef POPT_WCHAR_HACK
+ /* XXX Calculate no. of display characters. */
+ if (s) {
+ const char * scopy = s;
+ mbstate_t t;
+ size_t n;
+
+/*@-boundswrite@*/
+ memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
+/*@=boundswrite@*/
+ /* Determine number of characters. */
+ n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+ len += sizeof("=")-1 + n;
+ }
+#else
+ if (s)
+ len += sizeof("=")-1 + strlen(s);
+#endif
+
+ if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1;
+ if (len > max) max = len;
+ }
+
+ opt++;
+ }
+
+ return max;
+}
+
+/**
+ * Display popt alias and exec help.
+ * @param fp output file handle
+ * @param items alias/exec array
+ * @param nitems no. of alias/exec entries
+ * @param left largest argument display width
+ * @param translation_domain translation domain
+ */
+static void itemHelp(FILE * fp,
+ /*@null@*/ poptItem items, int nitems, size_t left,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ poptItem item;
+ int i;
+
+ if (items != NULL)
+ for (i = 0, item = items; i < nitems; i++, item++) {
+ const struct poptOption * opt;
+ opt = &item->option;
+ if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ singleOptionHelp(fp, left, opt, translation_domain);
+ }
+}
+
+/**
+ * Display help text for a table of options.
+ * @param con context
+ * @param fp output file handle
+ * @param table option(s)
+ * @param left largest argument display width
+ * @param translation_domain translation domain
+ */
+static void singleTableHelp(poptContext con, FILE * fp,
+ /*@null@*/ const struct poptOption * table, size_t left,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ const struct poptOption * opt;
+ const char *sub_transdom;
+
+ if (table == poptAliasOptions) {
+ itemHelp(fp, con->aliases, con->numAliases, left, NULL);
+ itemHelp(fp, con->execs, con->numExecs, left, NULL);
+ return;
+ }
+
+ if (table != NULL)
+ for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
+ if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ singleOptionHelp(fp, left, opt, translation_domain);
+ }
+
+ if (table != NULL)
+ for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
+ if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
+ continue;
+ sub_transdom = getTableTranslationDomain(opt->arg);
+ if (sub_transdom == NULL)
+ sub_transdom = translation_domain;
+
+ if (opt->descrip)
+ fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+ singleTableHelp(con, fp, opt->arg, left, sub_transdom);
+ }
+}
+
+/**
+ * @param con context
+ * @param fp output file handle
+ */
+static int showHelpIntro(poptContext con, FILE * fp)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ int len = 6;
+ const char * fn;
+
+ fprintf(fp, POPT_("Usage:"));
+ if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+/*@-boundsread@*/
+ /*@-nullderef -type@*/ /* LCL: wazzup? */
+ fn = con->optionStack->argv[0];
+ /*@=nullderef =type@*/
+/*@=boundsread@*/
+ if (fn == NULL) return len;
+ if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
+ fprintf(fp, " %s", fn);
+ len += strlen(fn) + 1;
+ }
+
+ return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
+{
+ size_t leftColWidth;
+
+ (void) showHelpIntro(con, fp);
+ if (con->otherHelp)
+ fprintf(fp, " %s\n", con->otherHelp);
+ else
+ fprintf(fp, " %s\n", POPT_("[OPTION...]"));
+
+ leftColWidth = maxArgWidth(con->options, NULL);
+ singleTableHelp(con, fp, con->options, leftColWidth, NULL);
+}
+
+/**
+ * Display usage text for an option.
+ * @param fp output file handle
+ * @param cursor current display position
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ */
+static size_t singleOptionUsage(FILE * fp, size_t cursor,
+ const struct poptOption * opt,
+ /*@null@*/ const char *translation_domain)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ size_t len = 4;
+ char shortStr[2] = { '\0', '\0' };
+ const char * item = shortStr;
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ if (opt->shortName != '\0' && opt->longName != NULL) {
+ len += 2;
+ if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
+ len += strlen(opt->longName);
+ } else if (opt->shortName != '\0') {
+ len++;
+ shortStr[0] = opt->shortName;
+ shortStr[1] = '\0';
+ } else if (opt->longName) {
+ len += strlen(opt->longName);
+ if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
+ item = opt->longName;
+ }
+
+ if (len == 4) return cursor;
+
+#ifdef POPT_WCHAR_HACK
+ /* XXX Calculate no. of display characters. */
+ if (argDescrip) {
+ const char * scopy = argDescrip;
+ mbstate_t t;
+ size_t n;
+
+/*@-boundswrite@*/
+ memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */
+/*@=boundswrite@*/
+ /* Determine number of characters. */
+ n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
+ len += sizeof("=")-1 + n;
+ }
+#else
+ if (argDescrip)
+ len += sizeof("=")-1 + strlen(argDescrip);
+#endif
+
+ if ((cursor + len) > 79) {
+ fprintf(fp, "\n ");
+ cursor = 7;
+ }
+
+ if (opt->longName && opt->shortName) {
+ fprintf(fp, " [-%c|-%s%s%s%s]",
+ opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
+ opt->longName,
+ (argDescrip ? " " : ""),
+ (argDescrip ? argDescrip : ""));
+ } else {
+ fprintf(fp, " [-%s%s%s%s]",
+ ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
+ item,
+ (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
+ (argDescrip ? argDescrip : ""));
+ }
+
+ return cursor + len + 1;
+}
+
+/**
+ * Display popt alias and exec usage.
+ * @param fp output file handle
+ * @param cursor current display position
+ * @param item alias/exec array
+ * @param nitems no. of ara/exec entries
+ * @param translation_domain translation domain
+ */
+static size_t itemUsage(FILE * fp, size_t cursor,
+ /*@null@*/ poptItem item, int nitems,
+ /*@null@*/ UNUSED(const char * translation_domain))
+ /*@globals fileSystem @*/
+ /*@modifies *fp, fileSystem @*/
+{
+ int i;
+
+ /*@-branchstate@*/ /* FIX: W2DO? */
+ if (item != NULL)
+ for (i = 0; i < nitems; i++, item++) {
+ const struct poptOption * opt;
+ opt = &item->option;
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+ translation_domain = (const char *)opt->arg;
+ } else if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+ }
+ }
+ /*@=branchstate@*/
+
+ return cursor;
+}
+
+/**
+ * Keep track of option tables already processed.
+ */
+typedef struct poptDone_s {
+ int nopts;
+ int maxopts;
+ const void ** opts;
+} * poptDone;
+
+/**
+ * Display usage text for a table of options.
+ * @param con context
+ * @param fp output file handle
+ * @param cursor current display position
+ * @param opt option(s)
+ * @param translation_domain translation domain
+ * @param done tables already processed
+ * @return
+ */
+static size_t singleTableUsage(poptContext con, FILE * fp, size_t cursor,
+ /*@null@*/ const struct poptOption * opt,
+ /*@null@*/ UNUSED(const char * translation_domain),
+ /*@null@*/ poptDone done)
+ /*@globals fileSystem @*/
+ /*@modifies *fp, done, fileSystem @*/
+{
+ /*@-branchstate@*/ /* FIX: W2DO? */
+ if (opt != NULL)
+ for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
+ translation_domain = (const char *)opt->arg;
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ if (done) {
+ int i = 0;
+ for (i = 0; i < done->nopts; i++) {
+/*@-boundsread@*/
+ const void * that = done->opts[i];
+/*@=boundsread@*/
+ if (that == NULL || that != opt->arg)
+ /*@innercontinue@*/ continue;
+ /*@innerbreak@*/ break;
+ }
+ /* Skip if this table has already been processed. */
+ if (opt->arg == NULL || i < done->nopts)
+ continue;
+/*@-boundswrite@*/
+ if (done->nopts < done->maxopts)
+ done->opts[done->nopts++] = (const void *) opt->arg;
+/*@=boundswrite@*/
+ }
+ cursor = singleTableUsage(con, fp, cursor, opt->arg,
+ translation_domain, done);
+ } else if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
+ }
+ }
+ /*@=branchstate@*/
+
+ return cursor;
+}
+
+/**
+ * Return concatenated short options for display.
+ * @todo Sub-tables should be recursed.
+ * @param opt option(s)
+ * @param fp output file handle
+ * @retval str concatenation of short options
+ * @return length of display string
+ */
+static int showShortOptions(const struct poptOption * opt, FILE * fp,
+ /*@null@*/ char * str)
+ /*@globals fileSystem @*/
+ /*@modifies *str, *fp, fileSystem @*/
+ /*@requires maxRead(str) >= 0 @*/
+{
+ /* bufsize larger then the ascii set, lazy alloca on top level call. */
+ char * s = (str != NULL ? str : memset(alloca(300), 0, 300));
+ int len = 0;
+
+ if (s == NULL)
+ return 0;
+
+/*@-boundswrite@*/
+ if (opt != NULL)
+ for (; (opt->longName || opt->shortName || opt->arg); opt++) {
+ if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+ s[strlen(s)] = opt->shortName;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ if (opt->arg) /* XXX program error */
+ len = showShortOptions(opt->arg, fp, s);
+ }
+/*@=boundswrite@*/
+
+ /* On return to top level, print the short options, return print length. */
+ if (s == str && *s != '\0') {
+ fprintf(fp, " [-%s]", s);
+ len = strlen(s) + sizeof(" [-]")-1;
+ }
+ return len;
+}
+
+void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
+{
+ poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done));
+ size_t cursor;
+
+ done->nopts = 0;
+ done->maxopts = 64;
+ cursor = done->maxopts * sizeof(*done->opts);
+/*@-boundswrite@*/
+ done->opts = memset(alloca(cursor), 0, cursor);
+ /*@-keeptrans@*/
+ done->opts[done->nopts++] = (const void *) con->options;
+ /*@=keeptrans@*/
+/*@=boundswrite@*/
+
+ cursor = showHelpIntro(con, fp);
+ cursor += showShortOptions(con->options, fp, NULL);
+ cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
+ cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
+ cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
+
+ if (con->otherHelp) {
+ cursor += strlen(con->otherHelp) + 1;
+ if (cursor > 79) fprintf(fp, "\n ");
+ fprintf(fp, " %s", con->otherHelp);
+ }
+
+ fprintf(fp, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text)
+{
+ con->otherHelp = _free(con->otherHelp);
+ con->otherHelp = xstrdup(text);
+}
diff --git a/popt/poptint.h b/popt/poptint.h
new file mode 100644
index 0000000..bec7c97
--- /dev/null
+++ b/popt/poptint.h
@@ -0,0 +1,122 @@
+/** \ingroup popt
+ * \file popt/poptint.h
+ */
+
+/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+/**
+ * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
+ * @param p memory to free
+ * @retval NULL always
+ */
+/*@unused@*/ static inline /*@null@*/ void *
+_free(/*@only@*/ /*@null@*/ const void * p)
+ /*@modifies p @*/
+{
+ if (p != NULL) free((void *)p);
+ return NULL;
+}
+
+static inline int
+isSpace(const char *ptr)
+{
+ return isspace(*(unsigned char *)ptr);
+}
+
+/* Bit mask macros. */
+/*@-exporttype -redef @*/
+typedef unsigned int __pbm_bits;
+/*@=exporttype =redef @*/
+#define __PBM_NBITS (8 * sizeof (__pbm_bits))
+#define __PBM_IX(d) ((d) / __PBM_NBITS)
+#define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
+/*@-exporttype -redef @*/
+typedef struct {
+ __pbm_bits bits[1];
+} pbm_set;
+/*@=exporttype =redef @*/
+#define __PBM_BITS(set) ((set)->bits)
+
+#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define PBM_FREE(s) _free(s);
+#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
+#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
+#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+
+struct optionStackEntry {
+ int argc;
+/*@only@*/ /*@null@*/
+ const char ** argv;
+/*@only@*/ /*@null@*/
+ pbm_set * argb;
+ int next;
+/*@only@*/ /*@null@*/
+ const char * nextArg;
+/*@observer@*/ /*@null@*/
+ const char * nextCharArg;
+/*@dependent@*/ /*@null@*/
+ poptItem currAlias;
+ int stuffed;
+};
+
+struct poptContext_s {
+ struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
+/*@dependent@*/
+ struct optionStackEntry * os;
+/*@owned@*/ /*@null@*/
+ const char ** leftovers;
+ int numLeftovers;
+ int nextLeftover;
+/*@keep@*/
+ const struct poptOption * options;
+ int restLeftover;
+/*@only@*/ /*@null@*/
+ const char * appName;
+/*@only@*/ /*@null@*/
+ poptItem aliases;
+ int numAliases;
+ int flags;
+/*@owned@*/ /*@null@*/
+ poptItem execs;
+ int numExecs;
+/*@only@*/ /*@null@*/
+ const char ** finalArgv;
+ int finalArgvCount;
+ int finalArgvAlloced;
+/*@dependent@*/ /*@null@*/
+ poptItem doExec;
+/*@only@*/
+ const char * execPath;
+ int execAbsolute;
+/*@only@*/ /*@relnull@*/
+ const char * otherHelp;
+/*@null@*/
+ pbm_set * arg_strip;
+};
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
+#define _(foo) gettext(foo)
+#else
+#define _(foo) foo
+#endif
+
+#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
+#define D_(dom, str) dgettext(dom, str)
+#define POPT_(foo) D_("popt", foo)
+#else
+#define D_(dom, str) str
+#define POPT_(foo) foo
+#endif
+
+#define N_(foo) foo
+
+#endif
diff --git a/popt/poptparse.c b/popt/poptparse.c
new file mode 100644
index 0000000..e003a04
--- /dev/null
+++ b/popt/poptparse.c
@@ -0,0 +1,231 @@
+/** \ingroup popt
+ * \file popt/poptparse.c
+ */
+
+/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.rpm.org/pub/rpm/dist. */
+
+#include "system.h"
+
+#include "poptint.h"
+
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+/*@-boundswrite@*/
+int poptDupArgv(int argc, const char **argv,
+ int * argcPtr, const char *** argvPtr)
+{
+ size_t nb = (argc + 1) * sizeof(*argv);
+ const char ** argv2;
+ char * dst;
+ int i;
+
+ if (argc <= 0 || argv == NULL) /* XXX can't happen */
+ return POPT_ERROR_NOARG;
+ for (i = 0; i < argc; i++) {
+ if (argv[i] == NULL)
+ return POPT_ERROR_NOARG;
+ nb += strlen(argv[i]) + 1;
+ }
+
+ dst = malloc(nb);
+ if (dst == NULL) /* XXX can't happen */
+ return POPT_ERROR_MALLOC;
+ argv2 = (void *) dst;
+ dst += (argc + 1) * sizeof(*argv);
+
+ /*@-branchstate@*/
+ for (i = 0; i < argc; i++) {
+ argv2[i] = dst;
+ dst += strlcpy(dst, argv[i], nb) + 1;
+ }
+ /*@=branchstate@*/
+ argv2[argc] = NULL;
+
+ if (argvPtr) {
+ *argvPtr = argv2;
+ } else {
+ free(argv2);
+ argv2 = NULL;
+ }
+ if (argcPtr)
+ *argcPtr = argc;
+ return 0;
+}
+/*@=boundswrite@*/
+
+/*@-bounds@*/
+int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
+{
+ const char * src;
+ char quote = '\0';
+ int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+ const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+ int argc = 0;
+ int buflen = strlen(s) + 1;
+ char * buf = memset(alloca(buflen), 0, buflen);
+ int rc = POPT_ERROR_MALLOC;
+
+ if (argv == NULL) return rc;
+ argv[argc] = buf;
+
+ for (src = s; *src != '\0'; src++) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote != '\0') {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ rc = POPT_ERROR_BADQUOTE;
+ goto exit;
+ }
+ if (*src != quote) *buf++ = '\\';
+ }
+ *buf++ = *src;
+ } else if (isSpace(src)) {
+ if (*argv[argc] != '\0') {
+ buf++, argc++;
+ if (argc == argvAlloced) {
+ argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+ argv = realloc(argv, sizeof(*argv) * argvAlloced);
+ if (argv == NULL) goto exit;
+ }
+ argv[argc] = buf;
+ }
+ } else switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ /*@switchbreak@*/ break;
+ case '\\':
+ src++;
+ if (!*src) {
+ rc = POPT_ERROR_BADQUOTE;
+ goto exit;
+ }
+ /*@fallthrough@*/
+ default:
+ *buf++ = *src;
+ /*@switchbreak@*/ break;
+ }
+ }
+
+ if (strlen(argv[argc])) {
+ argc++, buf++;
+ }
+
+ rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
+
+exit:
+ if (argv) free(argv);
+ return rc;
+}
+/*@=bounds@*/
+
+/* still in the dev stage.
+ * return values, perhaps 1== file erro
+ * 2== line to long
+ * 3== umm.... more?
+ */
+int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int flags))
+{
+ char line[999];
+ char * argstr;
+ char * p;
+ char * q;
+ char * x;
+ int t;
+ int argvlen = 0;
+ size_t maxlinelen = sizeof(line);
+ size_t linelen;
+ int maxargvlen = 480;
+ int linenum = 0;
+
+ *argstrp = NULL;
+
+ /* | this_is = our_line
+ * p q x
+ */
+
+ if (fp == NULL)
+ return POPT_ERROR_NULLARG;
+
+ argstr = calloc(maxargvlen, sizeof(*argstr));
+ if (argstr == NULL) return POPT_ERROR_MALLOC;
+
+ while (fgets(line, (int)maxlinelen, fp) != NULL) {
+ linenum++;
+ p = line;
+
+ /* loop until first non-space char or EOL */
+ while( *p != '\0' && isSpace(p) )
+ p++;
+
+ linelen = strlen(p);
+ if (linelen >= maxlinelen-1) {
+ free(argstr);
+ return POPT_ERROR_OVERFLOW; /* XXX line too long */
+ }
+
+ if (*p == '\0' || *p == '\n') continue; /* line is empty */
+ if (*p == '#') continue; /* comment line */
+
+ q = p;
+
+ while (*q != '\0' && (!isSpace(q)) && *q != '=')
+ q++;
+
+ if (isSpace(q)) {
+ /* a space after the name, find next non space */
+ *q++='\0';
+ while( *q != '\0' && isSpace(q) ) q++;
+ }
+ if (*q == '\0') {
+ /* single command line option (ie, no name=val, just name) */
+ q[-1] = '\0'; /* kill off newline from fgets() call */
+ argvlen += (t = q - p) + (sizeof(" --")-1);
+ if (argvlen >= maxargvlen) {
+ maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
+ argstr = realloc(argstr, maxargvlen);
+ if (argstr == NULL) return POPT_ERROR_MALLOC;
+ }
+ strlcat(argstr, " --", maxargvlen);
+ strlcat(argstr, p, maxargvlen);
+ continue;
+ }
+ if (*q != '=')
+ continue; /* XXX for now, silently ignore bogus line */
+
+ /* *q is an equal sign. */
+ *q++ = '\0';
+
+ /* find next non-space letter of value */
+ while (*q != '\0' && isSpace(q))
+ q++;
+ if (*q == '\0')
+ continue; /* XXX silently ignore missing value */
+
+ /* now, loop and strip all ending whitespace */
+ x = p + linelen;
+ while (isSpace(--x))
+ *x = 0; /* null out last char if space (including fgets() NL) */
+
+ /* rest of line accept */
+ t = x - p;
+ argvlen += t + (sizeof("' --='")-1);
+ if (argvlen >= maxargvlen) {
+ maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
+ argstr = realloc(argstr, maxargvlen);
+ if (argstr == NULL) return POPT_ERROR_MALLOC;
+ }
+ strlcat(argstr, " --", maxargvlen);
+ strlcat(argstr, p, maxargvlen);
+ strlcat(argstr, "=\"", maxargvlen);
+ strlcat(argstr, q, maxargvlen);
+ strlcat(argstr, "\"", maxargvlen);
+ }
+
+ *argstrp = argstr;
+ return 0;
+}
diff --git a/popt/system.h b/popt/system.h
new file mode 100644
index 0000000..25c22da
--- /dev/null
+++ b/popt/system.h
@@ -0,0 +1,134 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined (__GLIBC__) && defined(__LCLINT__)
+/*@-declundef@*/
+/*@unchecked@*/
+extern __const __int32_t *__ctype_tolower;
+/*@unchecked@*/
+extern __const __int32_t *__ctype_toupper;
+/*@=declundef@*/
+#endif
+
+#ifdef __TANDEM
+# include <floss.h(floss_execvp,floss_read)>
+#endif
+
+#include <ctype.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#if HAVE_MCHECK_H
+#include <mcheck.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include <libc.h>
+#endif
+
+#if defined(__LCLINT__)
+/*@-declundef -incondefs @*/ /* LCL: missing annotation */
+/*@only@*/ /*@out@*/
+void * alloca (size_t __size)
+ /*@ensures MaxSet(result) == (__size - 1) @*/
+ /*@*/;
+/*@=declundef =incondefs @*/
+#endif
+
+/* AIX requires this to be the first thing in the file. */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+#pragma alloca
+# else
+# ifdef HAVE_ALLOCA
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca(size_t size);
+# endif
+# else
+# ifdef alloca
+# undef alloca
+# endif
+# define alloca(sz) malloc(sz) /* Kludge this for now */
+# endif
+# endif
+# endif
+#elif !defined(alloca)
+#define alloca __builtin_alloca
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *d, const char *s, size_t bufsize);
+#endif
+
+#if HAVE_MCHECK_H && defined(__GNUC__)
+static inline char *
+xstrdup(const char *s)
+{
+ size_t memsize = strlen(s) + 1;
+ char *ptr = malloc(memsize);
+ if (!ptr) {
+ fprintf(stderr, "virtual memory exhausted.\n");
+ exit(EXIT_FAILURE);
+ }
+ strlcpy(ptr, s, memsize);
+ return ptr;
+}
+#else
+#define xstrdup(_str) strdup(_str)
+#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
+
+#if HAVE___SECURE_GETENV && !defined(__LCLINT__)
+#define getenv(_s) __secure_getenv(_s)
+#endif
+
+#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
+#define snprintf rsync_snprintf
+int snprintf(char *str,size_t count,const char *fmt,...);
+#endif
+
+#define UNUSED(x) x __attribute__((__unused__))
+
+#define PACKAGE "rsync"
+
+#include "popt.h"
diff --git a/prepare-source b/prepare-source
new file mode 100755
index 0000000..a4e78e6
--- /dev/null
+++ b/prepare-source
@@ -0,0 +1,72 @@
+#!/bin/sh
+# Either use autoconf and autoheader to create configure.sh and config.h.in
+# or (optionally) fetch the latest development versions of generated files.
+#
+# Specify one action or more than one to provide a fall-back:
+#
+# build build the config files [the default w/no arg]
+# fetch fetch the latest dev autoconfig files
+# fetchgen fetch all the latest dev generated files (including manpages)
+# fetchSRC fetch the latest dev source files [NON-GENERATED FILES]
+#
+# The script stops after the first successful action.
+
+dir=`dirname $0`
+if test x"$dir" = x; then
+ dir=.
+fi
+
+if test "$dir" = '.'; then
+ branch=`packaging/prep-auto-dir` || exit 1
+ if test x"$branch" != x; then
+ cd build || exit 1
+ dir=..
+ fi
+fi
+
+if test "$dir" != '.'; then
+ for lnk in configure.ac m4; do
+ if test ! -h $lnk; then
+ rm -f $lnk # Just in case
+ ln -s "$dir/$lnk" $lnk
+ fi
+ done
+ for fn in configure.sh config.h.in aclocal.m4; do
+ test ! -f $fn && test -f "$dir/$fn" && cp -p "$dir/$fn" $fn
+ done
+fi
+
+if test $# = 0; then
+ set -- build
+fi
+
+for action in "${@}"; do
+ case "$action" in
+ build|make)
+ make -f "$dir/prepare-source.mak"
+ ;;
+ fetch|fetchgen)
+ if test "$action" = fetchgen; then
+ match='*'
+ else
+ match='[ca]*'
+ fi
+ $dir/rsync-ssl -iipc --no-motd "rsync://download.samba.org/rsyncftp/generated-files/$match" ./
+ test $? != 0 && continue
+ sleep 1 # The following files need to be newer than aclocal.m4
+ touch configure.sh config.h.in
+ ;;
+ fetchSRC)
+ ./rsync-ssl -iipr --no-motd --exclude=/.git/ rsync://download.samba.org/ftp/pub/unpacked/rsync/ .
+ ;;
+ *)
+ echo "Unknown action: $action"
+ exit 1
+ ;;
+ esac
+ if test $? = 0; then
+ exit
+ fi
+done
+
+exit 1
diff --git a/prepare-source.mak b/prepare-source.mak
new file mode 100644
index 0000000..12027cb
--- /dev/null
+++ b/prepare-source.mak
@@ -0,0 +1,13 @@
+SHELL=/bin/sh
+
+conf: configure.sh config.h.in
+.PHONY: conf
+
+aclocal.m4: m4/*.m4
+ aclocal -I m4
+
+configure.sh: configure.ac aclocal.m4
+ autoconf -o configure.sh
+
+config.h.in: configure.ac aclocal.m4
+ autoheader && touch config.h.in
diff --git a/progress.c b/progress.c
new file mode 100644
index 0000000..87207fb
--- /dev/null
+++ b/progress.c
@@ -0,0 +1,241 @@
+/*
+ * Routines to output progress information during a file transfer.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+extern int am_server;
+extern int flist_eof;
+extern int quiet;
+extern int need_unsorted_flist;
+extern int output_needs_newline;
+extern int stdout_format_has_i;
+extern struct stats stats;
+extern struct file_list *cur_flist;
+
+BOOL want_progress_now = False;
+
+#define PROGRESS_HISTORY_SECS 5
+
+#ifdef GETPGRP_VOID
+#define GETPGRP_ARG
+#else
+#define GETPGRP_ARG 0
+#endif
+
+struct progress_history {
+ struct timeval time;
+ OFF_T ofs;
+};
+
+static struct progress_history ph_start;
+static struct progress_history ph_list[PROGRESS_HISTORY_SECS];
+static int newest_hpos, oldest_hpos;
+static int current_file_index;
+
+static unsigned long msdiff(struct timeval *t1, struct timeval *t2)
+{
+ return (t2->tv_sec - t1->tv_sec) * 1000L
+ + (t2->tv_usec - t1->tv_usec) / 1000;
+}
+
+
+/**
+ * @param ofs Current position in file
+ * @param size Total size of file
+ * @param is_last True if this is the last time progress will be
+ * printed for this file, so we should output a newline. (Not
+ * necessarily the same as all bytes being received.)
+ **/
+static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, int is_last)
+{
+ char rembuf[64], eol[128];
+ const char *units;
+ unsigned long diff;
+ double rate, remain;
+ int pct;
+
+ if (is_last) {
+ int len = snprintf(eol, sizeof eol,
+ " (xfr#%d, %s-chk=%d/%d)\n",
+ stats.xferred_files, flist_eof ? "to" : "ir",
+ stats.num_files - current_file_index - 1,
+ stats.num_files);
+ if (INFO_GTE(PROGRESS, 2)) {
+ static int last_len = 0;
+ /* Drop \n and pad with spaces if line got shorter. */
+ if (last_len < --len)
+ last_len = len;
+ eol[last_len] = '\0';
+ while (last_len > len)
+ eol[--last_len] = ' ';
+ is_last = 0;
+ }
+ /* Compute stats based on the starting info. */
+ if (!ph_start.time.tv_sec || !(diff = msdiff(&ph_start.time, now)))
+ diff = 1;
+ rate = (double) (ofs - ph_start.ofs) * 1000.0 / diff / 1024.0;
+ /* Switch to total time taken for our last update. */
+ remain = (double) diff / 1000.0;
+ } else {
+ strlcpy(eol, " ", sizeof eol);
+ /* Compute stats based on recent progress. */
+ if (!(diff = msdiff(&ph_list[oldest_hpos].time, now)))
+ diff = 1;
+ rate = (double) (ofs - ph_list[oldest_hpos].ofs) * 1000.0 / diff / 1024.0;
+ remain = rate ? (double) (size - ofs) / rate / 1000.0 : 0.0;
+ }
+
+ if (rate > 1024*1024) {
+ rate /= 1024.0 * 1024.0;
+ units = "GB/s";
+ } else if (rate > 1024) {
+ rate /= 1024.0;
+ units = "MB/s";
+ } else {
+ units = "kB/s";
+ }
+
+ if (remain < 0 || remain > 9999.0 * 3600.0)
+ strlcpy(rembuf, " ??:??:??", sizeof rembuf);
+ else {
+ snprintf(rembuf, sizeof rembuf, "%4u:%02u:%02u",
+ (unsigned int) (remain / 3600.0),
+ (unsigned int) (remain / 60.0) % 60,
+ (unsigned int) remain % 60);
+ }
+
+ output_needs_newline = 0;
+ pct = ofs == size ? 100 : (int) (100.0 * ofs / size);
+ rprintf(FCLIENT, "\r%15s %3d%% %7.2f%s %s%s",
+ human_num(ofs), pct, rate, units, rembuf, eol);
+ if (!is_last && !quiet) {
+ output_needs_newline = 1;
+ rflush(FCLIENT);
+ }
+}
+
+void progress_init(void)
+{
+ if (!am_server && !INFO_GTE(PROGRESS, 1)) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ ph_start.time.tv_sec = now.tv_sec;
+ ph_start.time.tv_usec = now.tv_usec;
+ }
+}
+
+void set_current_file_index(struct file_struct *file, int ndx)
+{
+ if (!file)
+ current_file_index = cur_flist->used + cur_flist->ndx_start - 1;
+ else if (need_unsorted_flist)
+ current_file_index = flist_find(cur_flist, file) + cur_flist->ndx_start;
+ else
+ current_file_index = ndx;
+ current_file_index -= cur_flist->flist_num;
+}
+
+void instant_progress(const char *fname)
+{
+ /* We only get here if want_progress_now is True */
+ if (!stdout_format_has_i && !INFO_GTE(NAME, 1))
+ rprintf(FINFO, "%s\n", fname);
+ end_progress(0);
+ want_progress_now = False;
+}
+
+void end_progress(OFF_T size)
+{
+ if (!am_server) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ if (INFO_GTE(PROGRESS, 2) || want_progress_now) {
+ rprint_progress(stats.total_transferred_size,
+ stats.total_size, &now, True);
+ } else {
+ rprint_progress(size, size, &now, True);
+ memset(&ph_start, 0, sizeof ph_start);
+ }
+ }
+}
+
+void show_progress(OFF_T ofs, OFF_T size)
+{
+ struct timeval now;
+#if defined HAVE_GETPGRP && defined HAVE_TCGETPGRP
+ static pid_t pgrp = -1;
+ pid_t tc_pgrp;
+#endif
+
+ if (am_server)
+ return;
+
+#if defined HAVE_GETPGRP && defined HAVE_TCGETPGRP
+ if (pgrp == -1)
+ pgrp = getpgrp(GETPGRP_ARG);
+#endif
+
+ gettimeofday(&now, NULL);
+
+ if (INFO_GTE(PROGRESS, 2)) {
+ ofs = stats.total_transferred_size - size + ofs;
+ size = stats.total_size;
+ }
+
+ if (!ph_start.time.tv_sec) {
+ int i;
+
+ /* Try to guess the real starting time when the sender started
+ * to send us data by using the time we last received some data
+ * in the last file (if it was recent enough). */
+ if (msdiff(&ph_list[newest_hpos].time, &now) <= 1500) {
+ ph_start.time = ph_list[newest_hpos].time;
+ ph_start.ofs = 0;
+ } else {
+ ph_start.time.tv_sec = now.tv_sec;
+ ph_start.time.tv_usec = now.tv_usec;
+ ph_start.ofs = ofs;
+ }
+
+ for (i = 0; i < PROGRESS_HISTORY_SECS; i++)
+ ph_list[i] = ph_start;
+ }
+ else {
+ if (msdiff(&ph_list[newest_hpos].time, &now) < 1000)
+ return;
+
+ newest_hpos = oldest_hpos;
+ oldest_hpos = (oldest_hpos + 1) % PROGRESS_HISTORY_SECS;
+ ph_list[newest_hpos].time.tv_sec = now.tv_sec;
+ ph_list[newest_hpos].time.tv_usec = now.tv_usec;
+ ph_list[newest_hpos].ofs = ofs;
+ }
+
+#if defined HAVE_GETPGRP && defined HAVE_TCGETPGRP
+ tc_pgrp = tcgetpgrp(STDOUT_FILENO);
+ if (tc_pgrp != pgrp && tc_pgrp != -1)
+ return;
+#endif
+
+ rprint_progress(ofs, size, &now, False);
+}
diff --git a/receiver.c b/receiver.c
new file mode 100644
index 0000000..c9d7e01
--- /dev/null
+++ b/receiver.c
@@ -0,0 +1,980 @@
+/*
+ * Routines only used by the receiving process.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+extern int dry_run;
+extern int do_xfers;
+extern int am_root;
+extern int am_server;
+extern int inc_recurse;
+extern int log_before_transfer;
+extern int stdout_format_has_i;
+extern int logfile_format_has_i;
+extern int want_xattr_optim;
+extern int csum_length;
+extern int read_batch;
+extern int write_batch;
+extern int batch_gen_fd;
+extern int protocol_version;
+extern int relative_paths;
+extern int preserve_hard_links;
+extern int preserve_perms;
+extern int write_devices;
+extern int preserve_xattrs;
+extern int do_fsync;
+extern int basis_dir_cnt;
+extern int make_backups;
+extern int cleanup_got_literal;
+extern int remove_source_files;
+extern int append_mode;
+extern int sparse_files;
+extern int preallocate_files;
+extern int keep_partial;
+extern int checksum_seed;
+extern int whole_file;
+extern int inplace;
+extern int inplace_partial;
+extern int allowed_lull;
+extern int delay_updates;
+extern BOOL want_progress_now;
+extern mode_t orig_umask;
+extern struct stats stats;
+extern char *tmpdir;
+extern char *partial_dir;
+extern char *basis_dir[MAX_BASIS_DIRS+1];
+extern char sender_file_sum[MAX_DIGEST_LEN];
+extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern filter_rule_list daemon_filter_list;
+extern OFF_T preallocated_len;
+
+extern struct name_num_item *xfer_sum_nni;
+extern int xfer_sum_len;
+
+static struct bitbag *delayed_bits = NULL;
+static int phase = 0, redoing = 0;
+static flist_ndx_list batch_redo_list;
+/* This is non-0 when we are updating the basis file or an identical copy: */
+static int updating_basis_or_equiv;
+
+#define TMPNAME_SUFFIX ".XXXXXX"
+#define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1)
+#define MAX_UNIQUE_NUMBER 999999
+#define MAX_UNIQUE_LOOP 100
+
+/* get_tmpname() - create a tmp filename for a given filename
+ *
+ * If a tmpdir is defined, use that as the directory to put it in. Otherwise,
+ * the tmp filename is in the same directory as the given name. Note that
+ * there may be no directory at all in the given name!
+ *
+ * The tmp filename is basically the given filename with a dot prepended, and
+ * .XXXXXX appended (for mkstemp() to put its unique gunk in). We take care
+ * to not exceed either the MAXPATHLEN or NAME_MAX, especially the last, as
+ * the basename basically becomes 8 characters longer. In such a case, the
+ * original name is shortened sufficiently to make it all fit.
+ *
+ * If the make_unique arg is True, the XXXXXX string is replaced with a unique
+ * string that doesn't exist at the time of the check. This is intended to be
+ * used for creating hard links, symlinks, devices, and special files, since
+ * normal files should be handled by mkstemp() for safety.
+ *
+ * Of course, the only reason the file is based on the original name is to
+ * make it easier to figure out what purpose a temp file is serving when a
+ * transfer is in progress. */
+int get_tmpname(char *fnametmp, const char *fname, BOOL make_unique)
+{
+ int maxname, length = 0;
+ const char *f;
+ char *suf;
+
+ if (tmpdir) {
+ /* Note: this can't overflow, so the return value is safe */
+ length = strlcpy(fnametmp, tmpdir, MAXPATHLEN - 2);
+ fnametmp[length++] = '/';
+ }
+
+ if ((f = strrchr(fname, '/')) != NULL) {
+ ++f;
+ if (!tmpdir) {
+ length = f - fname;
+ /* copy up to and including the slash */
+ strlcpy(fnametmp, fname, length + 1);
+ }
+ } else
+ f = fname;
+
+ if (!tmpdir) { /* using a tmpdir avoids the leading dot on our temp names */
+ if (*f == '.') /* avoid an extra leading dot for OS X's sake */
+ f++;
+ fnametmp[length++] = '.';
+ }
+
+ /* The maxname value is bufsize, and includes space for the '\0'.
+ * NAME_MAX needs an extra -1 for the name's leading dot. */
+ maxname = MIN(MAXPATHLEN - length - TMPNAME_SUFFIX_LEN,
+ NAME_MAX - 1 - TMPNAME_SUFFIX_LEN);
+
+ if (maxname < 0) {
+ rprintf(FERROR_XFER, "temporary filename too long: %s\n", fname);
+ fnametmp[0] = '\0';
+ return 0;
+ }
+
+ if (maxname) {
+ int added = strlcpy(fnametmp + length, f, maxname);
+ if (added >= maxname)
+ added = maxname - 1;
+ suf = fnametmp + length + added;
+
+ /* Trim any dangling high-bit chars if the first-trimmed char (if any) is
+ * also a high-bit char, just in case we cut into a multi-byte sequence.
+ * We are guaranteed to stop because of the leading '.' we added. */
+ if ((int)f[added] & 0x80) {
+ while ((int)suf[-1] & 0x80)
+ suf--;
+ }
+ /* trim one trailing dot before our suffix's dot */
+ if (suf[-1] == '.')
+ suf--;
+ } else
+ suf = fnametmp + length - 1; /* overwrite the leading dot with suffix's dot */
+
+ if (make_unique) {
+ static unsigned counter_limit;
+ unsigned counter;
+
+ if (!counter_limit) {
+ counter_limit = (unsigned)getpid() + MAX_UNIQUE_LOOP;
+ if (counter_limit > MAX_UNIQUE_NUMBER || counter_limit < MAX_UNIQUE_LOOP)
+ counter_limit = MAX_UNIQUE_LOOP;
+ }
+ counter = counter_limit - MAX_UNIQUE_LOOP;
+
+ /* This doesn't have to be very good because we don't need
+ * to worry about someone trying to guess the values: all
+ * a conflict will do is cause a device, special file, hard
+ * link, or symlink to fail to be created. Also: avoid
+ * using mktemp() due to gcc's annoying warning. */
+ while (1) {
+ snprintf(suf, TMPNAME_SUFFIX_LEN+1, ".%d", counter);
+ if (access(fnametmp, 0) < 0)
+ break;
+ if (++counter >= counter_limit)
+ return 0;
+ }
+ } else
+ memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1);
+
+ return 1;
+}
+
+/* Opens a temporary file for writing.
+ * Success: Writes name into fnametmp, returns fd.
+ * Failure: Clobbers fnametmp, returns -1.
+ * Calling cleanup_set() is the caller's job. */
+int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
+{
+ int fd;
+ mode_t added_perms;
+
+ if (!get_tmpname(fnametmp, fname, False))
+ return -1;
+
+ if (am_root < 0) {
+ /* For --fake-super, the file must be useable by the copying
+ * user, just like it would be for root. */
+ added_perms = S_IRUSR|S_IWUSR;
+ } else {
+ /* For a normal copy, we need to be able to tweak things like xattrs. */
+ added_perms = S_IWUSR;
+ }
+
+ /* We initially set the perms without the setuid/setgid bits or group
+ * access to ensure that there is no race condition. They will be
+ * correctly updated after the right owner and group info is set.
+ * (Thanks to snabb@epipe.fi for pointing this out.) */
+ fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
+
+#if 0
+ /* In most cases parent directories will already exist because their
+ * information should have been previously transferred, but that may
+ * not be the case with -R */
+ if (fd == -1 && relative_paths && errno == ENOENT
+ && make_path(fnametmp, MKP_SKIP_SLASH | MKP_DROP_NAME) == 0) {
+ /* Get back to name with XXXXXX in it. */
+ get_tmpname(fnametmp, fname, False);
+ fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
+ }
+#endif
+
+ if (fd == -1) {
+ rsyserr(FERROR_XFER, errno, "mkstemp %s failed",
+ full_fname(fnametmp));
+ return -1;
+ }
+
+ return fd;
+}
+
+static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
+ const char *fname, int fd, struct file_struct *file, int inplace_sizing)
+{
+ static char file_sum1[MAX_DIGEST_LEN];
+ struct map_struct *mapbuf;
+ struct sum_struct sum;
+ int32 len;
+ OFF_T total_size = F_LENGTH(file);
+ OFF_T offset = 0;
+ OFF_T offset2;
+ char *data;
+ int32 i;
+ char *map = NULL;
+
+#ifdef SUPPORT_PREALLOCATION
+ if (preallocate_files && fd != -1 && total_size > 0 && (!inplace_sizing || total_size > size_r)) {
+ /* Try to preallocate enough space for file's eventual length. Can
+ * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */
+ if ((preallocated_len = do_fallocate(fd, 0, total_size)) < 0)
+ rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(fname));
+ } else
+#endif
+ if (inplace_sizing) {
+#ifdef HAVE_FTRUNCATE
+ /* The most compatible way to create a sparse file is to start with no length. */
+ if (sparse_files > 0 && whole_file && fd >= 0 && do_ftruncate(fd, 0) == 0)
+ preallocated_len = 0;
+ else
+#endif
+ preallocated_len = size_r;
+ } else
+ preallocated_len = 0;
+
+ read_sum_head(f_in, &sum);
+
+ if (fd_r >= 0 && size_r > 0) {
+ int32 read_size = MAX(sum.blength * 2, 16*1024);
+ mapbuf = map_file(fd_r, size_r, read_size, sum.blength);
+ if (DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO, "recv mapped %s of size %s\n",
+ fname_r, big_num(size_r));
+ }
+ } else
+ mapbuf = NULL;
+
+ sum_init(xfer_sum_nni, checksum_seed);
+
+ if (append_mode > 0) {
+ OFF_T j;
+ sum.flength = (OFF_T)sum.count * sum.blength;
+ if (sum.remainder)
+ sum.flength -= sum.blength - sum.remainder;
+ if (append_mode == 2 && mapbuf) {
+ for (j = CHUNK_SIZE; j < sum.flength; j += CHUNK_SIZE) {
+ if (INFO_GTE(PROGRESS, 1))
+ show_progress(offset, total_size);
+ sum_update(map_ptr(mapbuf, offset, CHUNK_SIZE),
+ CHUNK_SIZE);
+ offset = j;
+ }
+ if (offset < sum.flength) {
+ int32 len = (int32)(sum.flength - offset);
+ if (INFO_GTE(PROGRESS, 1))
+ show_progress(offset, total_size);
+ sum_update(map_ptr(mapbuf, offset, len), len);
+ }
+ }
+ offset = sum.flength;
+ if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) {
+ rsyserr(FERROR_XFER, errno, "lseek of %s returned %s, not %s",
+ full_fname(fname), big_num(j), big_num(offset));
+ exit_cleanup(RERR_FILEIO);
+ }
+ }
+
+ while ((i = recv_token(f_in, &data)) != 0) {
+ if (INFO_GTE(PROGRESS, 1))
+ show_progress(offset, total_size);
+
+ if (allowed_lull)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
+
+ if (i > 0) {
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,"data recv %d at %s\n",
+ i, big_num(offset));
+ }
+
+ stats.literal_data += i;
+ cleanup_got_literal = 1;
+
+ sum_update(data, i);
+
+ if (fd != -1 && write_file(fd, 0, offset, data, i) != i)
+ goto report_write_error;
+ offset += i;
+ continue;
+ }
+
+ i = -(i+1);
+ offset2 = i * (OFF_T)sum.blength;
+ len = sum.blength;
+ if (i == (int)sum.count-1 && sum.remainder != 0)
+ len = sum.remainder;
+
+ stats.matched_data += len;
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,
+ "chunk[%d] of size %ld at %s offset=%s%s\n",
+ i, (long)len, big_num(offset2), big_num(offset),
+ updating_basis_or_equiv && offset == offset2 ? " (seek)" : "");
+ }
+
+ if (mapbuf) {
+ map = map_ptr(mapbuf,offset2,len);
+
+ see_token(map, len);
+ sum_update(map, len);
+ }
+
+ if (updating_basis_or_equiv) {
+ if (offset == offset2 && fd != -1) {
+ if (skip_matched(fd, offset, map, len) < 0)
+ goto report_write_error;
+ offset += len;
+ continue;
+ }
+ }
+ if (fd != -1 && map && write_file(fd, 0, offset, map, len) != (int)len)
+ goto report_write_error;
+ offset += len;
+ }
+
+ if (fd != -1 && offset > 0) {
+ if (sparse_files > 0) {
+ if (sparse_end(fd, offset) != 0)
+ goto report_write_error;
+ } else if (flush_write_file(fd) < 0) {
+ report_write_error:
+ rsyserr(FERROR_XFER, errno, "write failed on %s", full_fname(fname));
+ exit_cleanup(RERR_FILEIO);
+ }
+ }
+
+#ifdef HAVE_FTRUNCATE
+ /* inplace: New data could be shorter than old data.
+ * preallocate_files: total_size could have been an overestimate.
+ * Cut off any extra preallocated zeros from dest file. */
+ if ((inplace_sizing || preallocated_len > offset) && fd != -1 && !IS_DEVICE(file->mode)) {
+ if (do_ftruncate(fd, offset) < 0)
+ rsyserr(FERROR_XFER, errno, "ftruncate failed on %s", full_fname(fname));
+ }
+#endif
+
+ if (INFO_GTE(PROGRESS, 1))
+ end_progress(total_size);
+
+ sum_end(file_sum1);
+
+ if (do_fsync && fd != -1 && fsync(fd) != 0) {
+ rsyserr(FERROR, errno, "fsync failed on %s", full_fname(fname));
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (mapbuf)
+ unmap_file(mapbuf);
+
+ read_buf(f_in, sender_file_sum, xfer_sum_len);
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO,"got file_sum\n");
+ if (fd != -1 && memcmp(file_sum1, sender_file_sum, xfer_sum_len) != 0)
+ return 0;
+ return 1;
+}
+
+
+static void discard_receive_data(int f_in, struct file_struct *file)
+{
+ receive_data(f_in, NULL, -1, 0, NULL, -1, file, 0);
+}
+
+static void handle_delayed_updates(char *local_name)
+{
+ char *fname, *partialptr;
+ int ndx;
+
+ for (ndx = -1; (ndx = bitbag_next_bit(delayed_bits, ndx)) >= 0; ) {
+ struct file_struct *file = cur_flist->files[ndx];
+ fname = local_name ? local_name : f_name(file, NULL);
+ if ((partialptr = partial_dir_fname(fname)) != NULL) {
+ if (make_backups > 0 && !make_backup(fname, False))
+ continue;
+ if (DEBUG_GTE(RECV, 1)) {
+ rprintf(FINFO, "renaming %s to %s\n",
+ partialptr, fname);
+ }
+ /* We don't use robust_rename() here because the
+ * partial-dir must be on the same drive. */
+ if (do_rename(partialptr, fname) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "rename failed for %s (from %s)",
+ full_fname(fname), partialptr);
+ } else {
+ if (remove_source_files || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_success(fname, ndx);
+ handle_partial_dir(partialptr, PDIR_DELETE);
+ }
+ }
+ }
+}
+
+static void no_batched_update(int ndx, BOOL is_redo)
+{
+ struct file_list *flist = flist_for_ndx(ndx, "no_batched_update");
+ struct file_struct *file = flist->files[ndx - flist->ndx_start];
+
+ rprintf(FERROR_XFER, "(No batched update for%s \"%s\")\n",
+ is_redo ? " resend of" : "", f_name(file, NULL));
+
+ if (inc_recurse && !dry_run)
+ send_msg_int(MSG_NO_SEND, ndx);
+}
+
+static int we_want_redo(int desired_ndx)
+{
+ static int redo_ndx = -1;
+
+ while (redo_ndx < desired_ndx) {
+ if (redo_ndx >= 0)
+ no_batched_update(redo_ndx, True);
+ if ((redo_ndx = flist_ndx_pop(&batch_redo_list)) < 0)
+ return 0;
+ }
+
+ if (redo_ndx == desired_ndx) {
+ redo_ndx = -1;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int gen_wants_ndx(int desired_ndx, int flist_num)
+{
+ static int next_ndx = -1;
+ static int done_cnt = 0;
+ static BOOL got_eof = False;
+
+ if (got_eof)
+ return 0;
+
+ /* TODO: integrate gen-reading I/O into perform_io() so this is not needed? */
+ io_flush(FULL_FLUSH);
+
+ while (next_ndx < desired_ndx) {
+ if (inc_recurse && flist_num <= done_cnt)
+ return 0;
+ if (next_ndx >= 0)
+ no_batched_update(next_ndx, False);
+ if ((next_ndx = read_int(batch_gen_fd)) < 0) {
+ if (inc_recurse) {
+ done_cnt++;
+ continue;
+ }
+ got_eof = True;
+ return 0;
+ }
+ }
+
+ if (next_ndx == desired_ndx) {
+ next_ndx = -1;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * main routine for receiver process.
+ *
+ * Receiver process runs on the same host as the generator process. */
+int recv_files(int f_in, int f_out, char *local_name)
+{
+ int fd1,fd2;
+ STRUCT_STAT st;
+ int iflags, xlen;
+ char *fname, fbuf[MAXPATHLEN];
+ char xname[MAXPATHLEN];
+ char *fnametmp, fnametmpbuf[MAXPATHLEN];
+ char *fnamecmp, *partialptr;
+ char fnamecmpbuf[MAXPATHLEN];
+ uchar fnamecmp_type;
+ struct file_struct *file;
+ int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
+ enum logcode log_code = log_before_transfer ? FLOG : FINFO;
+ int max_phase = protocol_version >= 29 ? 2 : 1;
+ int dflt_perms = (ACCESSPERMS & ~orig_umask);
+#ifdef SUPPORT_ACLS
+ const char *parent_dirname = "";
+#endif
+ int ndx, recv_ok, one_inplace;
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used);
+
+ if (delay_updates)
+ delayed_bits = bitbag_create(cur_flist->used + 1);
+
+ if (whole_file < 0)
+ whole_file = 0;
+
+ progress_init();
+
+ while (1) {
+ cleanup_disable();
+
+ /* This call also sets cur_flist. */
+ ndx = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type,
+ xname, &xlen);
+ if (ndx == NDX_DONE) {
+ if (!am_server && cur_flist) {
+ set_current_file_index(NULL, 0);
+ if (INFO_GTE(PROGRESS, 2))
+ end_progress(0);
+ }
+ if (inc_recurse && first_flist) {
+ if (read_batch) {
+ ndx = first_flist->used + first_flist->ndx_start;
+ gen_wants_ndx(ndx, first_flist->flist_num);
+ }
+ flist_free(first_flist);
+ if (first_flist)
+ continue;
+ } else if (read_batch && first_flist) {
+ ndx = first_flist->used;
+ gen_wants_ndx(ndx, first_flist->flist_num);
+ }
+ if (++phase > max_phase)
+ break;
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "recv_files phase=%d\n", phase);
+ if (phase == 2 && delay_updates)
+ handle_delayed_updates(local_name);
+ write_int(f_out, NDX_DONE);
+ continue;
+ }
+
+ if (ndx - cur_flist->ndx_start >= 0)
+ file = cur_flist->files[ndx - cur_flist->ndx_start];
+ else
+ file = dir_flist->files[cur_flist->parent_ndx];
+ fname = local_name ? local_name : f_name(file, fbuf);
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "recv_files(%s)\n", fname);
+
+ if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')) {
+ int filt_flags = S_ISDIR(file->mode) ? NAME_IS_DIR : NAME_IS_FILE;
+ if (check_filter(&daemon_filter_list, FLOG, fname, filt_flags) < 0) {
+ rprintf(FERROR, "ERROR: rejecting file transfer request for daemon excluded file: %s\n",
+ fname);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers
+ && !(want_xattr_optim && BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE)))
+ recv_xattr_request(file, f_in);
+#endif
+
+ if (!(iflags & ITEM_TRANSFER)) {
+ maybe_log_item(file, iflags, itemizing, xname);
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers
+ && !BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE))
+ set_file_attrs(fname, file, NULL, fname, 0);
+#endif
+ if (iflags & ITEM_IS_NEW) {
+ stats.created_files++;
+ if (S_ISREG(file->mode)) {
+ /* Nothing further to count. */
+ } else if (S_ISDIR(file->mode))
+ stats.created_dirs++;
+#ifdef SUPPORT_LINKS
+ else if (S_ISLNK(file->mode))
+ stats.created_symlinks++;
+#endif
+ else if (IS_DEVICE(file->mode))
+ stats.created_devices++;
+ else
+ stats.created_specials++;
+ }
+ continue;
+ }
+ if (phase == 2) {
+ rprintf(FERROR,
+ "got transfer request in phase 2 [%s]\n",
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (file->flags & FLAG_FILE_SENT) {
+ if (csum_length == SHORT_SUM_LENGTH) {
+ if (keep_partial && !partial_dir)
+ make_backups = -make_backups; /* prevents double backup */
+ if (append_mode)
+ sparse_files = -sparse_files;
+ append_mode = -append_mode;
+ csum_length = SUM_LENGTH;
+ redoing = 1;
+ }
+ } else {
+ if (csum_length != SHORT_SUM_LENGTH) {
+ if (keep_partial && !partial_dir)
+ make_backups = -make_backups;
+ if (append_mode)
+ sparse_files = -sparse_files;
+ append_mode = -append_mode;
+ csum_length = SHORT_SUM_LENGTH;
+ redoing = 0;
+ }
+ if (iflags & ITEM_IS_NEW)
+ stats.created_files++;
+ }
+
+ if (!am_server)
+ set_current_file_index(file, ndx);
+ stats.xferred_files++;
+ stats.total_transferred_size += F_LENGTH(file);
+
+ cleanup_got_literal = 0;
+
+ if (read_batch) {
+ int wanted = redoing
+ ? we_want_redo(ndx)
+ : gen_wants_ndx(ndx, cur_flist->flist_num);
+ if (!wanted) {
+ rprintf(FINFO,
+ "(Skipping batched update for%s \"%s\")\n",
+ redoing ? " resend of" : "",
+ fname);
+ discard_receive_data(f_in, file);
+ file->flags |= FLAG_FILE_SENT;
+ continue;
+ }
+ }
+
+ remember_initial_stats();
+
+ if (!do_xfers) { /* log the transfer */
+ log_item(FCLIENT, file, iflags, NULL);
+ if (read_batch)
+ discard_receive_data(f_in, file);
+ continue;
+ }
+ if (write_batch < 0) {
+ log_item(FCLIENT, file, iflags, NULL);
+ if (!am_server)
+ discard_receive_data(f_in, file);
+ if (inc_recurse)
+ send_msg_success(fname, ndx);
+ continue;
+ }
+
+ partialptr = partial_dir ? partial_dir_fname(fname) : fname;
+
+ if (protocol_version >= 29) {
+ switch (fnamecmp_type) {
+ case FNAMECMP_FNAME:
+ fnamecmp = fname;
+ break;
+ case FNAMECMP_PARTIAL_DIR:
+ fnamecmp = partialptr;
+ break;
+ case FNAMECMP_BACKUP:
+ fnamecmp = get_backup_name(fname);
+ break;
+ case FNAMECMP_FUZZY:
+ if (file->dirname) {
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
+ fnamecmp = fnamecmpbuf;
+ } else
+ fnamecmp = xname;
+ break;
+ default:
+ if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
+ fnamecmp_type -= FNAMECMP_FUZZY + 1;
+ if (file->dirname) {
+ stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
+ basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
+ } else
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
+ } else if (fnamecmp_type >= basis_dir_cnt) {
+ rprintf(FERROR,
+ "invalid basis_dir index: %d.\n",
+ fnamecmp_type);
+ exit_cleanup(RERR_PROTOCOL);
+ } else
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
+ fnamecmp = fnamecmpbuf;
+ break;
+ }
+ if (!fnamecmp || (daemon_filter_list.head
+ && check_filter(&daemon_filter_list, FLOG, fnamecmp, 0) < 0)) {
+ fnamecmp = fname;
+ fnamecmp_type = FNAMECMP_FNAME;
+ }
+ } else {
+ /* Reminder: --inplace && --partial-dir are never
+ * enabled at the same time. */
+ if (inplace && make_backups > 0) {
+ if (!(fnamecmp = get_backup_name(fname)))
+ fnamecmp = fname;
+ else
+ fnamecmp_type = FNAMECMP_BACKUP;
+ } else if (partial_dir && partialptr)
+ fnamecmp = partialptr;
+ else
+ fnamecmp = fname;
+ }
+
+ /* open the file */
+ fd1 = do_open(fnamecmp, O_RDONLY, 0);
+
+ if (fd1 == -1 && protocol_version < 29) {
+ if (fnamecmp != fname) {
+ fnamecmp = fname;
+ fnamecmp_type = FNAMECMP_FNAME;
+ fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ }
+
+ if (fd1 == -1 && basis_dir[0]) {
+ /* pre-29 allowed only one alternate basis */
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
+ basis_dir[0], fname);
+ fnamecmp = fnamecmpbuf;
+ fnamecmp_type = FNAMECMP_BASIS_DIR_LOW;
+ fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ }
+ }
+
+ one_inplace = inplace_partial && fnamecmp_type == FNAMECMP_PARTIAL_DIR;
+ updating_basis_or_equiv = one_inplace
+ || (inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP));
+
+ if (fd1 == -1) {
+ st.st_mode = 0;
+ st.st_size = 0;
+ } else if (do_fstat(fd1,&st) != 0) {
+ rsyserr(FERROR_XFER, errno, "fstat %s failed",
+ full_fname(fnamecmp));
+ discard_receive_data(f_in, file);
+ close(fd1);
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) {
+ /* this special handling for directories
+ * wouldn't be necessary if robust_rename()
+ * and the underlying robust_unlink could cope
+ * with directories
+ */
+ rprintf(FERROR_XFER, "recv_files: %s is a directory\n",
+ full_fname(fnamecmp));
+ discard_receive_data(f_in, file);
+ close(fd1);
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ if (write_devices && IS_DEVICE(st.st_mode)) {
+ if (fd1 != -1 && st.st_size == 0)
+ st.st_size = get_device_size(fd1, fname);
+ /* Mark the file entry as a device so that we don't try to truncate it later on. */
+ file->mode = S_IFBLK | (file->mode & ACCESSPERMS);
+ } else if (fd1 != -1 && !(S_ISREG(st.st_mode))) {
+ close(fd1);
+ fd1 = -1;
+ }
+
+ /* If we're not preserving permissions, change the file-list's
+ * mode based on the local permissions and some heuristics. */
+ if (!preserve_perms) {
+ int exists = fd1 != -1;
+#ifdef SUPPORT_ACLS
+ const char *dn = file->dirname ? file->dirname : ".";
+ if (parent_dirname != dn
+ && strcmp(parent_dirname, dn) != 0) {
+ dflt_perms = default_perms_for_dir(dn);
+ parent_dirname = dn;
+ }
+#endif
+ file->mode = dest_mode(file->mode, st.st_mode, dflt_perms, exists);
+ }
+
+ /* We now check to see if we are writing the file "inplace" */
+ if (inplace || one_inplace) {
+ fnametmp = one_inplace ? partialptr : fname;
+ fd2 = do_open(fnametmp, O_WRONLY|O_CREAT, 0600);
+#ifdef linux
+ if (fd2 == -1 && errno == EACCES) {
+ /* Maybe the error was due to protected_regular setting? */
+ fd2 = do_open(fname, O_WRONLY, 0600);
+ }
+#endif
+ if (fd2 == -1) {
+ rsyserr(FERROR_XFER, errno, "open %s failed",
+ full_fname(fnametmp));
+ } else if (updating_basis_or_equiv)
+ cleanup_set(NULL, NULL, file, fd1, fd2);
+ } else {
+ fnametmp = fnametmpbuf;
+ fd2 = open_tmpfile(fnametmp, fname, file);
+ if (fd2 != -1)
+ cleanup_set(fnametmp, partialptr, file, fd1, fd2);
+ }
+
+ if (fd2 == -1) {
+ discard_receive_data(f_in, file);
+ if (fd1 != -1)
+ close(fd1);
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ /* log the transfer */
+ if (log_before_transfer)
+ log_item(FCLIENT, file, iflags, NULL);
+ else if (!am_server && INFO_GTE(NAME, 1) && INFO_EQ(PROGRESS, 1))
+ rprintf(FINFO, "%s\n", fname);
+
+ /* recv file data */
+ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, file, inplace || one_inplace);
+
+ log_item(log_code, file, iflags, NULL);
+ if (want_progress_now)
+ instant_progress(fname);
+
+ if (fd1 != -1)
+ close(fd1);
+ if (close(fd2) < 0) {
+ rsyserr(FERROR, errno, "close failed on %s",
+ full_fname(fnametmp));
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if ((recv_ok && (!delay_updates || !partialptr)) || inplace) {
+ if (partialptr == fname)
+ partialptr = NULL;
+ if (!finish_transfer(fname, fnametmp, fnamecmp, partialptr, file, recv_ok, 1))
+ recv_ok = -1;
+ else if (fnamecmp == partialptr) {
+ if (!one_inplace)
+ do_unlink(partialptr);
+ handle_partial_dir(partialptr, PDIR_DELETE);
+ }
+ } else if (keep_partial && partialptr && (!one_inplace || delay_updates)) {
+ if (!handle_partial_dir(partialptr, PDIR_CREATE)) {
+ rprintf(FERROR,
+ "Unable to create partial-dir for %s -- discarding %s.\n",
+ local_name ? local_name : f_name(file, NULL),
+ recv_ok ? "completed file" : "partial file");
+ do_unlink(fnametmp);
+ recv_ok = -1;
+ } else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
+ file, recv_ok, !partial_dir))
+ recv_ok = -1;
+ else if (delay_updates && recv_ok) {
+ bitbag_set_bit(delayed_bits, ndx);
+ recv_ok = 2;
+ } else
+ partialptr = NULL;
+ } else if (!one_inplace)
+ do_unlink(fnametmp);
+
+ cleanup_disable();
+
+ if (read_batch)
+ file->flags |= FLAG_FILE_SENT;
+
+ switch (recv_ok) {
+ case 2:
+ break;
+ case 1:
+ if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_success(fname, ndx);
+ break;
+ case 0: {
+ enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;
+ if (msgtype == FERROR_XFER || INFO_GTE(NAME, 1) || stdout_format_has_i) {
+ char *errstr, *redostr, *keptstr;
+ if (!(keep_partial && partialptr) && !inplace)
+ keptstr = "discarded";
+ else if (partial_dir)
+ keptstr = "put into partial-dir";
+ else
+ keptstr = "retained";
+ if (msgtype == FERROR_XFER) {
+ errstr = "ERROR";
+ redostr = "";
+ } else {
+ errstr = "WARNING";
+ redostr = read_batch ? " (may try again)"
+ : " (will try again)";
+ }
+ rprintf(msgtype,
+ "%s: %s failed verification -- update %s%s.\n",
+ errstr, local_name ? f_name(file, NULL) : fname,
+ keptstr, redostr);
+ }
+ if (!redoing) {
+ if (read_batch)
+ flist_ndx_push(&batch_redo_list, ndx);
+ send_msg_int(MSG_REDO, ndx);
+ file->flags |= FLAG_FILE_SENT;
+ } else if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ break;
+ }
+ case -1:
+ if (inc_recurse)
+ send_msg_int(MSG_NO_SEND, ndx);
+ break;
+ }
+ }
+ if (make_backups < 0)
+ make_backups = -make_backups;
+
+ if (phase == 2 && delay_updates) /* for protocol_version < 29 */
+ handle_delayed_updates(local_name);
+
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO,"recv_files finished\n");
+
+ return 0;
+}
diff --git a/rounding.c b/rounding.c
new file mode 100644
index 0000000..1bc353a
--- /dev/null
+++ b/rounding.c
@@ -0,0 +1,38 @@
+/*
+ * A pre-compilation helper program to aid in the creation of rounding.h.
+ *
+ * Copyright (C) 2007-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+#define ARRAY_LEN (EXTRA_ROUNDING+1)
+#define SIZEOF(x) ((long int)sizeof (x))
+
+struct test {
+ union file_extras extras[ARRAY_LEN];
+ int64 test;
+};
+
+#define ACTUAL_SIZE SIZEOF(struct test)
+#define EXPECTED_SIZE (SIZEOF(union file_extras) * ARRAY_LEN + SIZEOF(int64))
+
+ int main(UNUSED(int argc), UNUSED(char *argv[]))
+{
+ static int test_array[1 - 2 * (ACTUAL_SIZE != EXPECTED_SIZE)];
+ test_array[0] = 0;
+ return 0;
+}
diff --git a/rrsync.1 b/rrsync.1
new file mode 100644
index 0000000..74f9d34
--- /dev/null
+++ b/rrsync.1
@@ -0,0 +1,166 @@
+.TH "rrsync" "1" "20 Oct 2022" "rrsync from rsync 3.2.7" "User Commands"
+.\" prefix=/usr
+.P
+.SH "NAME"
+.P
+rrsync \- a script to setup restricted rsync users via ssh logins
+.P
+.SH "SYNOPSIS"
+.P
+.nf
+rrsync [-ro|-rw] [-munge] [-no-del] [-no-lock] DIR
+.fi
+.P
+The single non-option argument specifies the restricted \fIDIR\fP to use. It can be
+relative to the user's home directory or an absolute path.
+.P
+The online version of this manpage (that includes cross-linking of topics)
+is available at https://download.samba.org/pub/rsync/rrsync.1.
+.P
+.SH "DESCRIPTION"
+.P
+A user's ssh login can be restricted to only allow the running of an rsync
+transfer in one of two easy ways:
+.P
+.IP o
+forcing the running of the rrsync script
+.IP o
+forcing the running of an rsync daemon-over-ssh command.
+.P
+Both of these setups use a feature of ssh that allows a command to be forced to
+run instead of an interactive shell. However, if the user's home shell is bash,
+please see BASH SECURITY ISSUE for a potential issue.
+.P
+To use the rrsync script, edit the user's \fB~/.ssh/authorized_keys\fP file and add
+a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:
+.RS 4
+.P
+.nf
+command="rrsync DIR"
+command="rrsync -ro DIR"
+command="rrsync -munge -no-del DIR"
+.fi
+.RE
+.P
+Then, ensure that the rrsync script has your desired option restrictions. You
+may want to copy the script to a local bin dir with a unique name if you want
+to have multiple configurations. One or more rrsync options can be specified
+prior to the \fIDIR\fP if you want to further restrict the transfer.
+.P
+To use an rsync daemon setup, edit the user's \fB~/.ssh/authorized_keys\fP file and
+add a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:
+.RS 4
+.P
+.nf
+command="rsync --server --daemon ."
+command="rsync --server --daemon --config=/PATH/TO/rsyncd.conf ."
+.fi
+.RE
+.P
+Then, ensure that the rsyncd.conf file is created with one or more module names
+with the appropriate path and option restrictions. If rsync's
+\fB\-\-config\fP option is omitted, it defaults to \fB~/rsyncd.conf\fP.
+See the \fBrsyncd.conf\fP(5) manpage for details of how to
+configure an rsync daemon.
+.P
+When using rrsync, there can be just one restricted dir per authorized key. A
+daemon setup, on the other hand, allows multiple module names inside the config
+file, each one with its own path setting.
+.P
+The remainder of this manpage is dedicated to using the rrsync script.
+.P
+.SH "OPTIONS"
+.P
+.IP "\fB\-ro\fP"
+Allow only reading from the DIR. Implies \fB\-no-del\fP and
+\fB\-no-lock\fP.
+.IP "\fB\-wo\fP"
+Allow only writing to the DIR.
+.IP "\fB\-munge\fP"
+Enable rsync's \fB\-\-munge-links\fP on the server side.
+.IP "\fB\-no-del\fP"
+Disable rsync's \fB\-\-delete*\fP and \fB\-\-remove*\fP options.
+.IP "\fB\-no-lock\fP"
+Avoid the single-run (per-user) lock check. Useful with \fB\-munge\fP.
+.IP "\fB\-help\fP, \fB\-h\fP"
+Output this help message and exit.
+.P
+.SH "SECURITY RESTRICTIONS"
+.P
+The rrsync script validates the path arguments it is sent to try to restrict
+them to staying within the specified DIR.
+.P
+The rrsync script rejects rsync's \fB\-\-copy-links\fP option (by
+default) so that a copy cannot dereference a symlink within the DIR to get to a
+file outside the DIR.
+.P
+The rrsync script rejects rsync's \fB\-\-protect-args\fP (\fB\-s\fP) option
+because it would allow options to be sent to the server-side that the script
+cannot check. If you want to support \fB\-\-protect-args\fP, use a daemon-over-ssh
+setup.
+.P
+The rrsync script accepts just a subset of rsync's options that the real rsync
+uses when running the server command. A few extra convenience options are also
+included to help it to interact with BackupPC and accept some convenient user
+overrides.
+.P
+The script (or a copy of it) can be manually edited if you want it to customize
+the option handling.
+.P
+.SH "BASH SECURITY ISSUE"
+.P
+If your users have bash set as their home shell, bash may try to be overly
+helpful and ensure that the user's login bashrc files are run prior to
+executing the forced command. This can be a problem if the user can somehow
+update their home bashrc files, perhaps via the restricted copy, a shared home
+directory, or something similar.
+.P
+One simple way to avoid the issue is to switch the user to a simpler shell,
+such as dash. When choosing the new home shell, make sure that you're not
+choosing bash in disguise, as it is unclear if it avoids the security issue.
+.P
+Another potential fix is to ensure that the user's home directory is not a
+shared mount and that they have no means of copying files outside of their
+restricted directories. This may require you to force the enabling of symlink
+munging on the server side.
+.P
+A future version of openssh may have a change to the handling of forced
+commands that allows it to avoid using the user's home shell.
+.P
+.SH "EXAMPLES"
+.P
+The \fB~/.ssh/authorized_keys\fP file might have lines in it like this:
+.RS 4
+.P
+.nf
+command="rrsync client/logs" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzG...
+command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmk...
+.fi
+.RE
+.P
+.SH "FILES"
+.P
+~/.ssh/authorized_keys
+.P
+.SH "SEE ALSO"
+.P
+\fBrsync\fP(1), \fBrsyncd.conf\fP(5)
+.P
+.SH "VERSION"
+.P
+This manpage is current for version 3.2.7 of rsync.
+.P
+.SH "CREDITS"
+.P
+rsync is distributed under the GNU General Public License. See the file
+COPYING for details.
+.P
+An rsync web site is available at https://rsync.samba.org/ and its github
+project is https://github.com/WayneD/rsync.
+.P
+.SH "AUTHOR"
+.P
+The original rrsync perl script was written by Joe Smith. Many people have
+later contributed to it. The python version was created by Wayne Davison.
diff --git a/rrsync.1.html b/rrsync.1.html
new file mode 100644
index 0000000..d0f88ee
--- /dev/null
+++ b/rrsync.1.html
@@ -0,0 +1,164 @@
+<html><head>
+<title>rrsync(1) manpage</title>
+<meta charset="UTF-8"/>
+<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
+<style>
+body {
+ max-width: 50em;
+ margin: auto;
+}
+body, b, strong, u {
+ font-family: 'Roboto', sans-serif;
+}
+a.tgt { font-face: symbol; font-weight: 400; font-size: 70%; visibility: hidden; text-decoration: none; color: #ddd; padding: 0 4px; border: 0; }
+a.tgt:after { content: '🔗'; }
+a.tgt:hover { color: #444; background-color: #eaeaea; }
+h1:hover > a.tgt, h2:hover > a.tgt, h3:hover > a.tgt, dt:hover > a.tgt { visibility: visible; }
+code {
+ font-family: 'Roboto Mono', monospace;
+ font-weight: bold;
+ white-space: pre;
+}
+pre code {
+ display: block;
+ font-weight: normal;
+}
+blockquote pre code {
+ background: #f1f1f1;
+}
+dd p:first-of-type {
+ margin-block-start: 0em;
+}
+</style>
+</head><body>
+<h2 id="NAME">NAME<a href="#NAME" class="tgt"></a></h2>
+<p>rrsync -&#8288; a script to setup restricted rsync users via ssh logins</p>
+<h2 id="SYNOPSIS">SYNOPSIS<a href="#SYNOPSIS" class="tgt"></a></h2>
+<pre><code>rrsync [-ro|-rw] [-munge] [-no-del] [-no-lock] DIR
+</code></pre>
+<p>The single non-option argument specifies the restricted <u>DIR</u> to use. It can be
+relative to the user's home directory or an absolute path.</p>
+<p>The online version of this manpage (that includes cross-linking of topics)
+is available at <a href="https://download.samba.org/pub/rsync/rrsync.1">https://download.samba.org/pub/rsync/rrsync.1</a>.</p>
+<h2 id="DESCRIPTION">DESCRIPTION<a href="#DESCRIPTION" class="tgt"></a></h2>
+<p>A user's ssh login can be restricted to only allow the running of an rsync
+transfer in one of two easy ways:</p>
+<ul>
+<li>forcing the running of the rrsync script</li>
+<li>forcing the running of an rsync daemon-over-ssh command.</li>
+</ul>
+<p>Both of these setups use a feature of ssh that allows a command to be forced to
+run instead of an interactive shell. However, if the user's home shell is bash,
+please see <a href="#BASH_SECURITY_ISSUE">BASH SECURITY ISSUE</a> for a potential issue.</p>
+<p>To use the rrsync script, edit the user's <code>~/.ssh/authorized_keys</code> file and add
+a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:</p>
+<blockquote>
+<pre><code>command=&quot;rrsync DIR&quot;
+command=&quot;rrsync -ro DIR&quot;
+command=&quot;rrsync -munge -no-del DIR&quot;
+</code></pre>
+</blockquote>
+<p>Then, ensure that the rrsync script has your desired option restrictions. You
+may want to copy the script to a local bin dir with a unique name if you want
+to have multiple configurations. One or more rrsync options can be specified
+prior to the <u>DIR</u> if you want to further restrict the transfer.</p>
+<p>To use an rsync daemon setup, edit the user's <code>~/.ssh/authorized_keys</code> file and
+add a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:</p>
+<blockquote>
+<pre><code>command=&quot;rsync --server --daemon .&quot;
+command=&quot;rsync --server --daemon --config=/PATH/TO/rsyncd.conf .&quot;
+</code></pre>
+</blockquote>
+<p>Then, ensure that the rsyncd.conf file is created with one or more module names
+with the appropriate path and option restrictions. If rsync's
+<a href="rsync.1#dopt--config"><code>--config</code></a> option is omitted, it defaults to <code>~/rsyncd.conf</code>.
+See the <a href="rsyncd.conf.5"><strong>rsyncd.conf</strong>(5)</a> manpage for details of how to
+configure an rsync daemon.</p>
+<p>When using rrsync, there can be just one restricted dir per authorized key. A
+daemon setup, on the other hand, allows multiple module names inside the config
+file, each one with its own path setting.</p>
+<p>The remainder of this manpage is dedicated to using the rrsync script.</p>
+<h2 id="OPTIONS">OPTIONS<a href="#OPTIONS" class="tgt"></a></h2>
+<dl>
+
+<dt id="opt-ro"><code>-ro</code><a href="#opt-ro" class="tgt"></a></dt><dd>
+<p>Allow only reading from the DIR. Implies <a href="#opt-no-del"><code>-no-del</code></a> and
+<a href="#opt-no-lock"><code>-no-lock</code></a>.</p>
+</dd>
+
+<dt id="opt-wo"><code>-wo</code><a href="#opt-wo" class="tgt"></a></dt><dd>
+<p>Allow only writing to the DIR.</p>
+</dd>
+
+<dt id="opt-munge"><code>-munge</code><a href="#opt-munge" class="tgt"></a></dt><dd>
+<p>Enable rsync's <a href="rsync.1#opt--munge-links"><code>--munge-links</code></a> on the server side.</p>
+</dd>
+
+<dt id="opt-no-del"><code>-no-del</code><a href="#opt-no-del" class="tgt"></a></dt><dd>
+<p>Disable rsync's <code>--delete*</code> and <code>--remove*</code> options.</p>
+</dd>
+
+<dt id="opt-no-lock"><code>-no-lock</code><a href="#opt-no-lock" class="tgt"></a></dt><dd>
+<p>Avoid the single-run (per-user) lock check. Useful with <a href="#opt-munge"><code>-munge</code></a>.</p>
+</dd>
+
+<span id="opt-h"></span><dt id="opt-help"><code>-help</code>, <code>-h</code><a href="#opt-help" class="tgt"></a></dt><dd>
+<p>Output this help message and exit.</p>
+</dd>
+</dl>
+<h2 id="SECURITY_RESTRICTIONS">SECURITY RESTRICTIONS<a href="#SECURITY_RESTRICTIONS" class="tgt"></a></h2>
+<p>The rrsync script validates the path arguments it is sent to try to restrict
+them to staying within the specified DIR.</p>
+<p>The rrsync script rejects rsync's <a href="rsync.1#opt--copy-links"><code>--copy-links</code></a> option (by
+default) so that a copy cannot dereference a symlink within the DIR to get to a
+file outside the DIR.</p>
+<p>The rrsync script rejects rsync's <a href="rsync.1#opt--protect-args"><code>--protect-args</code></a> (<code>-s</code>) option
+because it would allow options to be sent to the server-side that the script
+cannot check. If you want to support <code>--protect-args</code>, use a daemon-over-ssh
+setup.</p>
+<p>The rrsync script accepts just a subset of rsync's options that the real rsync
+uses when running the server command. A few extra convenience options are also
+included to help it to interact with BackupPC and accept some convenient user
+overrides.</p>
+<p>The script (or a copy of it) can be manually edited if you want it to customize
+the option handling.</p>
+<h2 id="BASH_SECURITY_ISSUE">BASH SECURITY ISSUE<a href="#BASH_SECURITY_ISSUE" class="tgt"></a></h2>
+<p>If your users have bash set as their home shell, bash may try to be overly
+helpful and ensure that the user's login bashrc files are run prior to
+executing the forced command. This can be a problem if the user can somehow
+update their home bashrc files, perhaps via the restricted copy, a shared home
+directory, or something similar.</p>
+<p>One simple way to avoid the issue is to switch the user to a simpler shell,
+such as dash. When choosing the new home shell, make sure that you're not
+choosing bash in disguise, as it is unclear if it avoids the security issue.</p>
+<p>Another potential fix is to ensure that the user's home directory is not a
+shared mount and that they have no means of copying files outside of their
+restricted directories. This may require you to force the enabling of symlink
+munging on the server side.</p>
+<p>A future version of openssh may have a change to the handling of forced
+commands that allows it to avoid using the user's home shell.</p>
+<h2 id="EXAMPLES">EXAMPLES<a href="#EXAMPLES" class="tgt"></a></h2>
+<p>The <code>~/.ssh/authorized_keys</code> file might have lines in it like this:</p>
+<blockquote>
+<pre><code>command=&quot;rrsync client/logs&quot; ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzG...
+command=&quot;rrsync -ro results&quot; ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmk...
+</code></pre>
+</blockquote>
+<h2 id="FILES">FILES<a href="#FILES" class="tgt"></a></h2>
+<p>~/.ssh/authorized_keys</p>
+<h2 id="SEE_ALSO">SEE ALSO<a href="#SEE_ALSO" class="tgt"></a></h2>
+<p><a href="rsync.1"><strong>rsync</strong>(1)</a>, <a href="rsyncd.conf.5"><strong>rsyncd.conf</strong>(5)</a></p>
+<h2 id="VERSION">VERSION<a href="#VERSION" class="tgt"></a></h2>
+<p>This manpage is current for version 3.2.7 of rsync.</p>
+<h2 id="CREDITS">CREDITS<a href="#CREDITS" class="tgt"></a></h2>
+<p>rsync is distributed under the GNU General Public License. See the file
+<a href="COPYING">COPYING</a> for details.</p>
+<p>An rsync web site is available at <a href="https://rsync.samba.org/">https://rsync.samba.org/</a> and its github
+project is <a href="https://github.com/WayneD/rsync">https://github.com/WayneD/rsync</a>.</p>
+<h2 id="AUTHOR">AUTHOR<a href="#AUTHOR" class="tgt"></a></h2>
+<p>The original rrsync perl script was written by Joe Smith. Many people have
+later contributed to it. The python version was created by Wayne Davison.</p>
+<div style="float: right"><p><i>20 Oct 2022</i></p></div>
+</body></html>
diff --git a/rsync-ssl b/rsync-ssl
new file mode 100755
index 0000000..56ee7df
--- /dev/null
+++ b/rsync-ssl
@@ -0,0 +1,198 @@
+#!/usr/bin/env bash
+
+# This script uses openssl, gnutls, or stunnel to secure an rsync daemon connection.
+
+# By default this script takes rsync args and hands them off to the actual
+# rsync command with an --rsh option that makes it open an SSL connection to an
+# rsync daemon. See the rsync-ssl manpage for usage details and env variables.
+
+# When the first arg is --HELPER, we are being used by rsync as an --rsh helper
+# script, and the args are (note the trailing dot):
+#
+# rsync-ssl --HELPER HOSTNAME rsync --server --daemon .
+#
+# --HELPER is not a user-facing option, so it is not documented in the manpage.
+
+# The first SSL setup was based on: http://dozzie.jarowit.net/trac/wiki/RsyncSSL
+# Note that an stunnel connection requires at least version 4.x of stunnel.
+
+function rsync_ssl_run {
+ case "$*" in
+ *rsync://*) ;;
+ *::*) ;;
+ *)
+ echo "You must use rsync-ssl with a daemon-style hostname." 1>&2
+ exit 1
+ ;;
+ esac
+
+ exec rsync --rsh="$0 --HELPER" "${@}"
+}
+
+function rsync_ssl_helper {
+ if [[ -z "$RSYNC_SSL_TYPE" ]]; then
+ found=`path_search openssl stunnel4 stunnel` || exit 1
+ if [[ "$found" == */openssl ]]; then
+ RSYNC_SSL_TYPE=openssl
+ RSYNC_SSL_OPENSSL="$found"
+ elif [[ "$found" == */gnutls-cli ]]; then
+ RSYNC_SSL_TYPE=gnutls
+ RSYNC_SSL_GNUTLS="$found"
+ else
+ RSYNC_SSL_TYPE=stunnel
+ RSYNC_SSL_STUNNEL="$found"
+ fi
+ fi
+
+ case "$RSYNC_SSL_TYPE" in
+ openssl)
+ if [[ -z "$RSYNC_SSL_OPENSSL" ]]; then
+ RSYNC_SSL_OPENSSL=`path_search openssl` || exit 1
+ fi
+ optsep=' '
+ ;;
+ gnutls)
+ if [[ -z "$RSYNC_SSL_GNUTLS" ]]; then
+ RSYNC_SSL_GNUTLS=`path_search gnutls-cli` || exit 1
+ fi
+ optsep=' '
+ ;;
+ stunnel)
+ if [[ -z "$RSYNC_SSL_STUNNEL" ]]; then
+ RSYNC_SSL_STUNNEL=`path_search stunnel4 stunnel` || exit 1
+ fi
+ optsep=' = '
+ ;;
+ *)
+ echo "The RSYNC_SSL_TYPE specifies an unknown type: $RSYNC_SSL_TYPE" 1>&2
+ exit 1
+ ;;
+ esac
+
+ if [[ -z "$RSYNC_SSL_CERT" ]]; then
+ certopt=""
+ gnutls_cert_opt=""
+ else
+ certopt="-cert$optsep$RSYNC_SSL_CERT"
+ gnutls_cert_opt="--x509certfile=$RSYNC_SSL_CERT"
+ fi
+
+ if [[ -z "$RSYNC_SSL_KEY" ]]; then
+ keyopt=""
+ gnutls_key_opt=""
+ else
+ keyopt="-key$optsep$RSYNC_SSL_KEY"
+ gnutls_key_opt="--x509keyfile=$RSYNC_SSL_KEY"
+ fi
+
+ if [[ -z ${RSYNC_SSL_CA_CERT+x} ]]; then
+ # RSYNC_SSL_CA_CERT unset - default CA set AND verify:
+ # openssl:
+ caopt="-verify_return_error -verify 4"
+ # gnutls:
+ gnutls_opts=""
+ # stunnel:
+ # Since there is no way of using the default CA certificate collection,
+ # we cannot do any verification. Thus, stunnel should really only be
+ # used if nothing else is available.
+ cafile=""
+ verify=""
+ elif [[ "$RSYNC_SSL_CA_CERT" == "" ]]; then
+ # RSYNC_SSL_CA_CERT set but empty -do NO verifications:
+ # openssl:
+ caopt="-verify 1"
+ # gnutls:
+ gnutls_opts="--insecure"
+ # stunnel:
+ cafile=""
+ verify="verifyChain = no"
+ else
+ # RSYNC_SSL_CA_CERT set - use CA AND verify:
+ # openssl:
+ caopt="-CAfile $RSYNC_SSL_CA_CERT -verify_return_error -verify 4"
+ # gnutls:
+ gnutls_opts="--x509cafile=$RSYNC_SSL_CA_CERT"
+ # stunnel:
+ cafile="CAfile = $RSYNC_SSL_CA_CERT"
+ verify="verifyChain = yes"
+ fi
+
+ port="${RSYNC_PORT:-0}"
+ if [[ "$port" == 0 ]]; then
+ port="${RSYNC_SSL_PORT:-874}"
+ fi
+
+ # If the user specified USER@HOSTNAME::module, then rsync passes us
+ # the -l USER option too, so we must be prepared to ignore it.
+ if [[ "$1" == "-l" ]]; then
+ shift 2
+ fi
+
+ hostname="$1"
+ shift
+
+ if [[ -z "$hostname" || "$1" != rsync || "$2" != --server || "$3" != --daemon ]]; then
+ echo "Usage: rsync-ssl --HELPER HOSTNAME rsync --server --daemon ." 1>&2
+ exit 1
+ fi
+
+ if [[ $RSYNC_SSL_TYPE == openssl ]]; then
+ exec $RSYNC_SSL_OPENSSL s_client $caopt $certopt $keyopt -quiet -verify_quiet -servername $hostname -verify_hostname $hostname -connect $hostname:$port
+ elif [[ $RSYNC_SSL_TYPE == gnutls ]]; then
+ exec $RSYNC_SSL_GNUTLS --logfile=/dev/null $gnutls_cert_opt $gnutls_key_opt $gnutls_opts $hostname:$port
+ else
+ # devzero@web.de came up with this no-tmpfile calling syntax:
+ exec $RSYNC_SSL_STUNNEL -fd 10 11<&0 <<EOF 10<&0 0<&11 11<&-
+foreground = yes
+debug = crit
+connect = $hostname:$port
+client = yes
+TIMEOUTclose = 0
+$verify
+$certopt
+$cafile
+EOF
+ fi
+}
+
+function path_search {
+ IFS_SAVE="$IFS"
+ IFS=:
+ for prog in "${@}"; do
+ for dir in $PATH; do
+ [[ -z "$dir" ]] && dir=.
+ if [[ -f "$dir/$prog" && -x "$dir/$prog" ]]; then
+ echo "$dir/$prog"
+ IFS="$IFS_SAVE"
+ return 0
+ fi
+ done
+ done
+
+ IFS="$IFS_SAVE"
+ echo "Failed to find on your path: $*" 1>&2
+ echo "See the rsync-ssl manpage for configuration assistance." 1>&2
+ return 1
+}
+
+if [[ "$#" == 0 ]]; then
+ echo "Usage: rsync-ssl [--type=SSL_TYPE] RSYNC_ARG [...]" 1>&2
+ echo "The SSL_TYPE can be openssl or stunnel"
+ exit 1
+fi
+
+if [[ "$1" = --help || "$1" = -h ]]; then
+ exec rsync --help
+fi
+
+if [[ "$1" == --HELPER ]]; then
+ shift
+ rsync_ssl_helper "${@}"
+fi
+
+if [[ "$1" == --type=* ]]; then
+ export RSYNC_SSL_TYPE="${1/--type=/}"
+ shift
+fi
+
+rsync_ssl_run "${@}"
diff --git a/rsync-ssl.1 b/rsync-ssl.1
new file mode 100644
index 0000000..c7f5ad1
--- /dev/null
+++ b/rsync-ssl.1
@@ -0,0 +1,144 @@
+.TH "rsync-ssl" "1" "20 Oct 2022" "rsync-ssl from rsync 3.2.7" "User Commands"
+.\" prefix=/usr
+.P
+.SH "NAME"
+.P
+rsync-ssl \- a helper script for connecting to an ssl rsync daemon
+.P
+.SH "SYNOPSIS"
+.P
+.nf
+rsync-ssl [--type=SSL_TYPE] RSYNC_ARGS
+.fi
+.P
+The online version of this manpage (that includes cross-linking of topics)
+is available at https://download.samba.org/pub/rsync/rsync-ssl.1.
+.P
+.SH "DESCRIPTION"
+.P
+The rsync-ssl script helps you to run an rsync copy to/from an rsync daemon
+that requires ssl connections.
+.P
+The script requires that you specify an rsync-daemon arg in the style of either
+\fBhostname::\fP (with 2 colons) or \fBrsync://hostname/\fP. The default port used for
+connecting is 874 (one higher than the normal 873) unless overridden in the
+environment. You can specify an overriding port via \fB\-\-port\fP or by including
+it in the normal spot in the URL format, though both of those require your
+rsync version to be at least 3.2.0.
+.P
+.SH "OPTIONS"
+.P
+If the \fBfirst\fP arg is a \fB\-\-type=SSL_TYPE\fP option, the script will only use
+that particular program to open an ssl connection instead of trying to find an
+openssl or stunnel executable via a simple heuristic (assuming that the
+\fBRSYNC_SSL_TYPE\fP environment variable is not set as well\ \-\- see below). This
+option must specify one of \fBopenssl\fP or \fBstunnel\fP. The equal sign is
+required for this particular option.
+.P
+All the other options are passed through to the rsync command, so consult the
+\fBrsync\fP(1) manpage for more information on how it works.
+.P
+.SH "ENVIRONMENT VARIABLES"
+.P
+The ssl helper scripts are affected by the following environment variables:
+.P
+.IP "\fBRSYNC_SSL_TYPE\fP"
+Specifies the program type that should be used to open the ssl connection.
+It must be one of \fBopenssl\fP or \fBstunnel\fP. The \fB\-\-type=SSL_TYPE\fP option
+overrides this, when specified.
+.IP "\fBRSYNC_SSL_PORT\fP"
+If specified, the value is the port number that is used as the default when
+the user does not specify a port in their rsync command. When not
+specified, the default port number is 874. (Note that older rsync versions
+(prior to 3.2.0) did not communicate an overriding port number value to the
+helper script.)
+.IP "\fBRSYNC_SSL_CERT\fP"
+If specified, the value is a filename that contains a certificate to use
+for the connection.
+.IP "\fBRSYNC_SSL_KEY\fP"
+If specified, the value is a filename that contains a key for the provided
+certificate to use for the connection.
+.IP "\fBRSYNC_SSL_CA_CERT\fP"
+If specified, the value is a filename that contains a certificate authority
+certificate that is used to validate the connection.
+.IP "\fBRSYNC_SSL_OPENSSL\fP"
+Specifies the openssl executable to run when the connection type is set to
+openssl. If unspecified, the $PATH is searched for "openssl".
+.IP "\fBRSYNC_SSL_GNUTLS\fP"
+Specifies the gnutls-cli executable to run when the connection type is set
+to gnutls. If unspecified, the $PATH is searched for "gnutls-cli".
+.IP "\fBRSYNC_SSL_STUNNEL\fP"
+Specifies the stunnel executable to run when the connection type is set to
+stunnel. If unspecified, the $PATH is searched first for "stunnel4" and
+then for "stunnel".
+.P
+.SH "EXAMPLES"
+.RS 4
+.P
+.nf
+rsync-ssl -aiv example.com::mod/ dest
+.fi
+.RE
+.RS 4
+.P
+.nf
+rsync-ssl --type=openssl -aiv example.com::mod/ dest
+.fi
+.RE
+.RS 4
+.P
+.nf
+rsync-ssl -aiv --port 9874 example.com::mod/ dest
+.fi
+.RE
+.RS 4
+.P
+.nf
+rsync-ssl -aiv rsync://example.com:9874/mod/ dest
+.fi
+.RE
+.P
+.SH "THE SERVER SIDE"
+.P
+For help setting up an SSL/TLS supporting rsync, see the instructions in
+rsyncd.conf.
+.P
+.SH "SEE ALSO"
+.P
+\fBrsync\fP(1), \fBrsyncd.conf\fP(5)
+.P
+.SH "CAVEATS"
+.P
+Note that using an stunnel connection requires at least version 4 of stunnel,
+which should be the case on modern systems. Also, it does not verify a
+connection against the CA certificate collection, so it only encrypts the
+connection without any cert validation unless you have specified the
+certificate environment options.
+.P
+This script also supports a \fB\-\-type=gnutls\fP option, but at the time of this
+release the gnutls-cli command was dropping output, making it unusable. If
+that bug has been fixed in your version, feel free to put gnutls into an
+exported RSYNC_SSL_TYPE environment variable to make its use the default.
+.P
+.SH "BUGS"
+.P
+Please report bugs! See the web site at https://rsync.samba.org/.
+.P
+.SH "VERSION"
+.P
+This manpage is current for version 3.2.7 of rsync.
+.P
+.SH "CREDITS"
+.P
+Rsync is distributed under the GNU General Public License. See the file
+COPYING for details.
+.P
+A web site is available at https://rsync.samba.org/. The site includes an
+FAQ-O-Matic which may cover questions unanswered by this manual page.
+.P
+.SH "AUTHOR"
+.P
+This manpage was written by Wayne Davison.
+.P
+Mailing lists for support and development are available at
+https://lists.samba.org/.
diff --git a/rsync-ssl.1.html b/rsync-ssl.1.html
new file mode 100644
index 0000000..682a7e9
--- /dev/null
+++ b/rsync-ssl.1.html
@@ -0,0 +1,154 @@
+<html><head>
+<title>rsync-ssl(1) manpage</title>
+<meta charset="UTF-8"/>
+<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
+<style>
+body {
+ max-width: 50em;
+ margin: auto;
+}
+body, b, strong, u {
+ font-family: 'Roboto', sans-serif;
+}
+a.tgt { font-face: symbol; font-weight: 400; font-size: 70%; visibility: hidden; text-decoration: none; color: #ddd; padding: 0 4px; border: 0; }
+a.tgt:after { content: '🔗'; }
+a.tgt:hover { color: #444; background-color: #eaeaea; }
+h1:hover > a.tgt, h2:hover > a.tgt, h3:hover > a.tgt, dt:hover > a.tgt { visibility: visible; }
+code {
+ font-family: 'Roboto Mono', monospace;
+ font-weight: bold;
+ white-space: pre;
+}
+pre code {
+ display: block;
+ font-weight: normal;
+}
+blockquote pre code {
+ background: #f1f1f1;
+}
+dd p:first-of-type {
+ margin-block-start: 0em;
+}
+</style>
+</head><body>
+<h2 id="NAME">NAME<a href="#NAME" class="tgt"></a></h2>
+<p>rsync-ssl -&#8288; a helper script for connecting to an ssl rsync daemon</p>
+<h2 id="SYNOPSIS">SYNOPSIS<a href="#SYNOPSIS" class="tgt"></a></h2>
+<pre><code>rsync-ssl [--type=SSL_TYPE] RSYNC_ARGS
+</code></pre>
+<p>The online version of this manpage (that includes cross-linking of topics)
+is available at <a href="https://download.samba.org/pub/rsync/rsync-ssl.1">https://download.samba.org/pub/rsync/rsync-ssl.1</a>.</p>
+<h2 id="DESCRIPTION">DESCRIPTION<a href="#DESCRIPTION" class="tgt"></a></h2>
+<p>The rsync-ssl script helps you to run an rsync copy to/from an rsync daemon
+that requires ssl connections.</p>
+<p>The script requires that you specify an rsync-daemon arg in the style of either
+<code>hostname::</code> (with 2 colons) or <code>rsync://hostname/</code>. The default port used for
+connecting is 874 (one higher than the normal 873) unless overridden in the
+environment. You can specify an overriding port via <code>--port</code> or by including
+it in the normal spot in the URL format, though both of those require your
+rsync version to be at least 3.2.0.</p>
+<h2 id="OPTIONS">OPTIONS<a href="#OPTIONS" class="tgt"></a></h2>
+<p>If the <strong>first</strong> arg is a <code>--type=SSL_TYPE</code> option, the script will only use
+that particular program to open an ssl connection instead of trying to find an
+openssl or stunnel executable via a simple heuristic (assuming that the
+<code>RSYNC_SSL_TYPE</code> environment variable is not set as well&nbsp;-&#8288;-&#8288; see below). This
+option must specify one of <code>openssl</code> or <code>stunnel</code>. The equal sign is
+required for this particular option.</p>
+<p>All the other options are passed through to the rsync command, so consult the
+<strong>rsync</strong>(1) manpage for more information on how it works.</p>
+<h2 id="ENVIRONMENT_VARIABLES">ENVIRONMENT VARIABLES<a href="#ENVIRONMENT_VARIABLES" class="tgt"></a></h2>
+<p>The ssl helper scripts are affected by the following environment variables:</p>
+<dl>
+
+<dt id="RSYNC_SSL_TYPE"><code>RSYNC_SSL_TYPE</code><a href="#RSYNC_SSL_TYPE" class="tgt"></a></dt><dd>
+<p>Specifies the program type that should be used to open the ssl connection.
+It must be one of <code>openssl</code> or <code>stunnel</code>. The <code>--type=SSL_TYPE</code> option
+overrides this, when specified.</p>
+</dd>
+
+<dt id="RSYNC_SSL_PORT"><code>RSYNC_SSL_PORT</code><a href="#RSYNC_SSL_PORT" class="tgt"></a></dt><dd>
+<p>If specified, the value is the port number that is used as the default when
+the user does not specify a port in their rsync command. When not
+specified, the default port number is 874. (Note that older rsync versions
+(prior to 3.2.0) did not communicate an overriding port number value to the
+helper script.)</p>
+</dd>
+
+<dt id="RSYNC_SSL_CERT"><code>RSYNC_SSL_CERT</code><a href="#RSYNC_SSL_CERT" class="tgt"></a></dt><dd>
+<p>If specified, the value is a filename that contains a certificate to use
+for the connection.</p>
+</dd>
+
+<dt id="RSYNC_SSL_KEY"><code>RSYNC_SSL_KEY</code><a href="#RSYNC_SSL_KEY" class="tgt"></a></dt><dd>
+<p>If specified, the value is a filename that contains a key for the provided
+certificate to use for the connection.</p>
+</dd>
+
+<dt id="RSYNC_SSL_CA_CERT"><code>RSYNC_SSL_CA_CERT</code><a href="#RSYNC_SSL_CA_CERT" class="tgt"></a></dt><dd>
+<p>If specified, the value is a filename that contains a certificate authority
+certificate that is used to validate the connection.</p>
+</dd>
+
+<dt id="RSYNC_SSL_OPENSSL"><code>RSYNC_SSL_OPENSSL</code><a href="#RSYNC_SSL_OPENSSL" class="tgt"></a></dt><dd>
+<p>Specifies the openssl executable to run when the connection type is set to
+openssl. If unspecified, the $PATH is searched for &quot;openssl&quot;.</p>
+</dd>
+
+<dt id="RSYNC_SSL_GNUTLS"><code>RSYNC_SSL_GNUTLS</code><a href="#RSYNC_SSL_GNUTLS" class="tgt"></a></dt><dd>
+<p>Specifies the gnutls-cli executable to run when the connection type is set
+to gnutls. If unspecified, the $PATH is searched for &quot;gnutls-cli&quot;.</p>
+</dd>
+
+<dt id="RSYNC_SSL_STUNNEL"><code>RSYNC_SSL_STUNNEL</code><a href="#RSYNC_SSL_STUNNEL" class="tgt"></a></dt><dd>
+<p>Specifies the stunnel executable to run when the connection type is set to
+stunnel. If unspecified, the $PATH is searched first for &quot;stunnel4&quot; and
+then for &quot;stunnel&quot;.</p>
+</dd>
+</dl>
+<h2 id="EXAMPLES">EXAMPLES<a href="#EXAMPLES" class="tgt"></a></h2>
+<blockquote>
+<pre><code>rsync-ssl -aiv example.com::mod/ dest
+</code></pre>
+</blockquote>
+<blockquote>
+<pre><code>rsync-ssl --type=openssl -aiv example.com::mod/ dest
+</code></pre>
+</blockquote>
+<blockquote>
+<pre><code>rsync-ssl -aiv --port 9874 example.com::mod/ dest
+</code></pre>
+</blockquote>
+<blockquote>
+<pre><code>rsync-ssl -aiv rsync://example.com:9874/mod/ dest
+</code></pre>
+</blockquote>
+<h2 id="THE_SERVER_SIDE">THE SERVER SIDE<a href="#THE_SERVER_SIDE" class="tgt"></a></h2>
+<p>For help setting up an SSL/TLS supporting rsync, see the <a href="rsyncd.conf.5#SSL_TLS_Daemon_Setup">instructions in
+rsyncd.conf</a>.</p>
+<h2 id="SEE_ALSO">SEE ALSO<a href="#SEE_ALSO" class="tgt"></a></h2>
+<p><a href="rsync.1"><strong>rsync</strong>(1)</a>, <a href="rsyncd.conf.5"><strong>rsyncd.conf</strong>(5)</a></p>
+<h2 id="CAVEATS">CAVEATS<a href="#CAVEATS" class="tgt"></a></h2>
+<p>Note that using an stunnel connection requires at least version 4 of stunnel,
+which should be the case on modern systems. Also, it does not verify a
+connection against the CA certificate collection, so it only encrypts the
+connection without any cert validation unless you have specified the
+certificate environment options.</p>
+<p>This script also supports a <code>--type=gnutls</code> option, but at the time of this
+release the gnutls-cli command was dropping output, making it unusable. If
+that bug has been fixed in your version, feel free to put gnutls into an
+exported RSYNC_SSL_TYPE environment variable to make its use the default.</p>
+<h2 id="BUGS">BUGS<a href="#BUGS" class="tgt"></a></h2>
+<p>Please report bugs! See the web site at <a href="https://rsync.samba.org/">https://rsync.samba.org/</a>.</p>
+<h2 id="VERSION">VERSION<a href="#VERSION" class="tgt"></a></h2>
+<p>This manpage is current for version 3.2.7 of rsync.</p>
+<h2 id="CREDITS">CREDITS<a href="#CREDITS" class="tgt"></a></h2>
+<p>Rsync is distributed under the GNU General Public License. See the file
+<a href="COPYING">COPYING</a> for details.</p>
+<p>A web site is available at <a href="https://rsync.samba.org/">https://rsync.samba.org/</a>. The site includes an
+FAQ-O-Matic which may cover questions unanswered by this manual page.</p>
+<h2 id="AUTHOR">AUTHOR<a href="#AUTHOR" class="tgt"></a></h2>
+<p>This manpage was written by Wayne Davison.</p>
+<p>Mailing lists for support and development are available at
+<a href="https://lists.samba.org/">https://lists.samba.org/</a>.</p>
+<div style="float: right"><p><i>20 Oct 2022</i></p></div>
+</body></html>
diff --git a/rsync-ssl.1.md b/rsync-ssl.1.md
new file mode 100644
index 0000000..a6f1e3d
--- /dev/null
+++ b/rsync-ssl.1.md
@@ -0,0 +1,140 @@
+## NAME
+
+rsync-ssl - a helper script for connecting to an ssl rsync daemon
+
+## SYNOPSIS
+
+```
+rsync-ssl [--type=SSL_TYPE] RSYNC_ARGS
+```
+
+The online version of this manpage (that includes cross-linking of topics)
+is available at <https://download.samba.org/pub/rsync/rsync-ssl.1>.
+
+## DESCRIPTION
+
+The rsync-ssl script helps you to run an rsync copy to/from an rsync daemon
+that requires ssl connections.
+
+The script requires that you specify an rsync-daemon arg in the style of either
+`hostname::` (with 2 colons) or `rsync://hostname/`. The default port used for
+connecting is 874 (one higher than the normal 873) unless overridden in the
+environment. You can specify an overriding port via `--port` or by including
+it in the normal spot in the URL format, though both of those require your
+rsync version to be at least 3.2.0.
+
+## OPTIONS
+
+If the **first** arg is a `--type=SSL_TYPE` option, the script will only use
+that particular program to open an ssl connection instead of trying to find an
+openssl or stunnel executable via a simple heuristic (assuming that the
+`RSYNC_SSL_TYPE` environment variable is not set as well -- see below). This
+option must specify one of `openssl` or `stunnel`. The equal sign is
+required for this particular option.
+
+All the other options are passed through to the rsync command, so consult the
+**rsync**(1) manpage for more information on how it works.
+
+## ENVIRONMENT VARIABLES
+
+The ssl helper scripts are affected by the following environment variables:
+
+0. `RSYNC_SSL_TYPE`
+
+ Specifies the program type that should be used to open the ssl connection.
+ It must be one of `openssl` or `stunnel`. The `--type=SSL_TYPE` option
+ overrides this, when specified.
+
+0. `RSYNC_SSL_PORT`
+
+ If specified, the value is the port number that is used as the default when
+ the user does not specify a port in their rsync command. When not
+ specified, the default port number is 874. (Note that older rsync versions
+ (prior to 3.2.0) did not communicate an overriding port number value to the
+ helper script.)
+
+0. `RSYNC_SSL_CERT`
+
+ If specified, the value is a filename that contains a certificate to use
+ for the connection.
+
+0. `RSYNC_SSL_KEY`
+
+ If specified, the value is a filename that contains a key for the provided
+ certificate to use for the connection.
+
+0. `RSYNC_SSL_CA_CERT`
+
+ If specified, the value is a filename that contains a certificate authority
+ certificate that is used to validate the connection.
+
+0. `RSYNC_SSL_OPENSSL`
+
+ Specifies the openssl executable to run when the connection type is set to
+ openssl. If unspecified, the $PATH is searched for "openssl".
+
+0. `RSYNC_SSL_GNUTLS`
+
+ Specifies the gnutls-cli executable to run when the connection type is set
+ to gnutls. If unspecified, the $PATH is searched for "gnutls-cli".
+
+0. `RSYNC_SSL_STUNNEL`
+
+ Specifies the stunnel executable to run when the connection type is set to
+ stunnel. If unspecified, the $PATH is searched first for "stunnel4" and
+ then for "stunnel".
+
+## EXAMPLES
+
+> rsync-ssl -aiv example.com::mod/ dest
+
+> rsync-ssl --type=openssl -aiv example.com::mod/ dest
+
+> rsync-ssl -aiv --port 9874 example.com::mod/ dest
+
+> rsync-ssl -aiv rsync://example.com:9874/mod/ dest
+
+## THE SERVER SIDE
+
+For help setting up an SSL/TLS supporting rsync, see the [instructions in
+rsyncd.conf](rsyncd.conf.5#SSL_TLS_Daemon_Setup).
+
+## SEE ALSO
+
+[**rsync**(1)](rsync.1), [**rsyncd.conf**(5)](rsyncd.conf.5)
+
+## CAVEATS
+
+Note that using an stunnel connection requires at least version 4 of stunnel,
+which should be the case on modern systems. Also, it does not verify a
+connection against the CA certificate collection, so it only encrypts the
+connection without any cert validation unless you have specified the
+certificate environment options.
+
+This script also supports a `--type=gnutls` option, but at the time of this
+release the gnutls-cli command was dropping output, making it unusable. If
+that bug has been fixed in your version, feel free to put gnutls into an
+exported RSYNC_SSL_TYPE environment variable to make its use the default.
+
+## BUGS
+
+Please report bugs! See the web site at <https://rsync.samba.org/>.
+
+## VERSION
+
+This manpage is current for version @VERSION@ of rsync.
+
+## CREDITS
+
+Rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
+
+A web site is available at <https://rsync.samba.org/>. The site includes an
+FAQ-O-Matic which may cover questions unanswered by this manual page.
+
+## AUTHOR
+
+This manpage was written by Wayne Davison.
+
+Mailing lists for support and development are available at
+<https://lists.samba.org/>.
diff --git a/rsync.1 b/rsync.1
new file mode 100644
index 0000000..66a2da3
--- /dev/null
+++ b/rsync.1
@@ -0,0 +1,5051 @@
+.TH "rsync" "1" "20 Oct 2022" "rsync 3.2.7" "User Commands"
+.\" prefix=/usr
+.P
+.SH "NAME"
+.P
+rsync \- a fast, versatile, remote (and local) file-copying tool
+.P
+.SH "SYNOPSIS"
+.P
+.nf
+Local:
+ rsync [OPTION...] SRC... [DEST]
+
+Access via remote shell:
+ Pull:
+ rsync [OPTION...] [USER@]HOST:SRC... [DEST]
+ Push:
+ rsync [OPTION...] SRC... [USER@]HOST:DEST
+
+Access via rsync daemon:
+ Pull:
+ rsync [OPTION...] [USER@]HOST::SRC... [DEST]
+ rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
+ Push:
+ rsync [OPTION...] SRC... [USER@]HOST::DEST
+ rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST)
+.fi
+.P
+Usages with just one SRC arg and no DEST arg will list the source files instead
+of copying.
+.P
+The online version of this manpage (that includes cross-linking of topics)
+is available at https://download.samba.org/pub/rsync/rsync.1.
+.P
+.SH "DESCRIPTION"
+.P
+Rsync is a fast and extraordinarily versatile file copying tool. It can copy
+locally, to/from another host over any remote shell, or to/from a remote rsync
+daemon. It offers a large number of options that control every aspect of its
+behavior and permit very flexible specification of the set of files to be
+copied. It is famous for its delta-transfer algorithm, which reduces the
+amount of data sent over the network by sending only the differences between
+the source files and the existing files in the destination. Rsync is widely
+used for backups and mirroring and as an improved copy command for everyday
+use.
+.P
+Rsync finds files that need to be transferred using a "quick check" algorithm
+(by default) that looks for files that have changed in size or in last-modified
+time. Any changes in the other preserved attributes (as requested by options)
+are made on the destination file directly when the quick check indicates that
+the file's data does not need to be updated.
+.P
+Some of the additional features of rsync are:
+.P
+.IP o
+support for copying links, devices, owners, groups, and permissions
+.IP o
+exclude and exclude-from options similar to GNU tar
+.IP o
+a CVS exclude mode for ignoring the same files that CVS would ignore
+.IP o
+can use any transparent remote shell, including ssh or rsh
+.IP o
+does not require super-user privileges
+.IP o
+pipelining of file transfers to minimize latency costs
+.IP o
+support for anonymous or authenticated rsync daemons (ideal for mirroring)
+.P
+.SH "GENERAL"
+.P
+Rsync copies files either to or from a remote host, or locally on the current
+host (it does not support copying files between two remote hosts).
+.P
+There are two different ways for rsync to contact a remote system: using a
+remote-shell program as the transport (such as ssh or rsh) or contacting an
+rsync daemon directly via TCP. The remote-shell transport is used whenever the
+source or destination path contains a single colon (:) separator after a host
+specification. Contacting an rsync daemon directly happens when the source or
+destination path contains a double colon (::) separator after a host
+specification, OR when an rsync:// URL is specified (see also the USING
+RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION section for an
+exception to this latter rule).
+.P
+As a special case, if a single source arg is specified without a destination,
+the files are listed in an output format similar to "\fBls\ \-l\fP".
+.P
+As expected, if neither the source or destination path specify a remote host,
+the copy occurs locally (see also the \fB\-\-list-only\fP option).
+.P
+Rsync refers to the local side as the client and the remote side as the server.
+Don't confuse server with an rsync daemon. A daemon is always a server, but a
+server can be either a daemon or a remote-shell spawned process.
+.P
+.SH "SETUP"
+.P
+See the file README.md for installation instructions.
+.P
+Once installed, you can use rsync to any machine that you can access via a
+remote shell (as well as some that you can access using the rsync daemon-mode
+protocol). For remote transfers, a modern rsync uses ssh for its
+communications, but it may have been configured to use a different remote shell
+by default, such as rsh or remsh.
+.P
+You can also specify any remote shell you like, either by using the \fB\-e\fP
+command line option, or by setting the \fBRSYNC_RSH\fP environment variable.
+.P
+Note that rsync must be installed on both the source and destination machines.
+.P
+.SH "USAGE"
+.P
+You use rsync in the same way you use rcp. You must specify a source and a
+destination, one of which may be remote.
+.P
+Perhaps the best way to explain the syntax is with some examples:
+.RS 4
+.P
+.nf
+rsync -t *.c foo:src/
+.fi
+.RE
+.P
+This would transfer all files matching the pattern \fB*.c\fP from the current
+directory to the directory src on the machine foo. If any of the files already
+exist on the remote system then the rsync remote-update protocol is used to
+update the file by sending only the differences in the data. Note that the
+expansion of wildcards on the command-line (\fB*.c\fP) into a list of files is
+handled by the shell before it runs rsync and not by rsync itself (exactly the
+same as all other Posix-style programs).
+.RS 4
+.P
+.nf
+rsync -avz foo:src/bar /data/tmp
+.fi
+.RE
+.P
+This would recursively transfer all files from the directory src/bar on the
+machine foo into the /data/tmp/bar directory on the local machine. The files
+are transferred in archive mode, which ensures that symbolic links, devices,
+attributes, permissions, ownerships, etc. are preserved in the transfer.
+Additionally, compression will be used to reduce the size of data portions of
+the transfer.
+.RS 4
+.P
+.nf
+rsync -avz foo:src/bar/ /data/tmp
+.fi
+.RE
+.P
+A trailing slash on the source changes this behavior to avoid creating an
+additional directory level at the destination. You can think of a trailing /
+on a source as meaning "copy the contents of this directory" as opposed to
+"copy the directory by name", but in both cases the attributes of the
+containing directory are transferred to the containing directory on the
+destination. In other words, each of the following commands copies the files
+in the same way, including their setting of the attributes of /dest/foo:
+.RS 4
+.P
+.nf
+rsync -av /src/foo /dest
+rsync -av /src/foo/ /dest/foo
+.fi
+.RE
+.P
+Note also that host and module references don't require a trailing slash to
+copy the contents of the default directory. For example, both of these copy
+the remote directory's contents into "/dest":
+.RS 4
+.P
+.nf
+rsync -av host: /dest
+rsync -av host::module /dest
+.fi
+.RE
+.P
+You can also use rsync in local-only mode, where both the source and
+destination don't have a ':' in the name. In this case it behaves like an
+improved copy command.
+.P
+Finally, you can list all the (listable) modules available from a particular
+rsync daemon by leaving off the module name:
+.RS 4
+.P
+.nf
+rsync somehost.mydomain.com::
+.fi
+.RE
+.P
+.SH "COPYING TO A DIFFERENT NAME"
+.P
+When you want to copy a directory to a different name, use a trailing slash on
+the source directory to put the contents of the directory into any destination
+directory you like:
+.RS 4
+.P
+.nf
+rsync -ai foo/ bar/
+.fi
+.RE
+.P
+Rsync also has the ability to customize a destination file's name when copying
+a single item. The rules for this are:
+.P
+.IP o
+The transfer list must consist of a single item (either a file or an empty
+directory)
+.IP o
+The final element of the destination path must not exist as a directory
+.IP o
+The destination path must not have been specified with a trailing slash
+.P
+Under those circumstances, rsync will set the name of the destination's single
+item to the last element of the destination path. Keep in mind that it is best
+to only use this idiom when copying a file and use the above trailing-slash
+idiom when copying a directory.
+.P
+The following example copies the \fBfoo.c\fP file as \fBbar.c\fP in the \fBsave\fP dir
+(assuming that \fBbar.c\fP isn't a directory):
+.RS 4
+.P
+.nf
+rsync -ai src/foo.c save/bar.c
+.fi
+.RE
+.P
+The single-item copy rule might accidentally bite you if you unknowingly copy a
+single item and specify a destination dir that doesn't exist (without using a
+trailing slash). For example, if \fBsrc/*.c\fP matches one file and \fBsave/dir\fP
+doesn't exist, this will confuse you by naming the destination file \fBsave/dir\fP:
+.RS 4
+.P
+.nf
+rsync -ai src/*.c save/dir
+.fi
+.RE
+.P
+To prevent such an accident, either make sure the destination dir exists or
+specify the destination path with a trailing slash:
+.RS 4
+.P
+.nf
+rsync -ai src/*.c save/dir/
+.fi
+.RE
+.P
+.SH "SORTED TRANSFER ORDER"
+.P
+Rsync always sorts the specified filenames into its internal transfer list.
+This handles the merging together of the contents of identically named
+directories, makes it easy to remove duplicate filenames. It can, however,
+confuse someone when the files are transferred in a different order than what
+was given on the command-line.
+.P
+If you need a particular file to be transferred prior to another, either
+separate the files into different rsync calls, or consider using
+\fB\-\-delay-updates\fP (which doesn't affect the sorted transfer order, but
+does make the final file-updating phase happen much more rapidly).
+.P
+.SH "MULTI-HOST SECURITY"
+.P
+Rsync takes steps to ensure that the file requests that are shared in a
+transfer are protected against various security issues. Most of the potential
+problems arise on the receiving side where rsync takes steps to ensure that the
+list of files being transferred remains within the bounds of what was
+requested.
+.P
+Toward this end, rsync 3.1.2 and later have aborted when a file list contains
+an absolute or relative path that tries to escape out of the top of the
+transfer. Also, beginning with version 3.2.5, rsync does two more safety
+checks of the file list to (1) ensure that no extra source arguments were added
+into the transfer other than those that the client requested and (2) ensure
+that the file list obeys the exclude rules that were sent to the sender.
+.P
+For those that don't yet have a 3.2.5 client rsync (or those that want to be
+extra careful), it is safest to do a copy into a dedicated destination
+directory for the remote files when you don't trust the remote host. For
+example, instead of doing an rsync copy into your home directory:
+.RS 4
+.P
+.nf
+rsync -aiv host1:dir1 ~
+.fi
+.RE
+.P
+Dedicate a "host1-files" dir to the remote content:
+.RS 4
+.P
+.nf
+rsync -aiv host1:dir1 ~/host1-files
+.fi
+.RE
+.P
+See the \fB\-\-trust-sender\fP option for additional details.
+.P
+CAUTION: it is not particularly safe to use rsync to copy files from a
+case-preserving filesystem to a case-ignoring filesystem. If you must perform
+such a copy, you should either disable symlinks via \fB\-\-no-links\fP or enable the
+munging of symlinks via \fB\-\-munge-links\fP (and make sure you use the
+right local or remote option). This will prevent rsync from doing potentially
+dangerous things if a symlink name overlaps with a file or directory. It does
+not, however, ensure that you get a full copy of all the files (since that may
+not be possible when the names overlap). A potentially better solution is to
+list all the source files and create a safe list of filenames that you pass to
+the \fB\-\-files-from\fP option. Any files that conflict in name would need
+to be copied to different destination directories using more than one copy.
+.P
+While a copy of a case-ignoring filesystem to a case-ignoring filesystem can
+work out fairly well, if no \fB\-\-delete-during\fP or \fB\-\-delete-before\fP option is
+active, rsync can potentially update an existing file on the receiveing side
+without noticing that the upper-/lower-case of the filename should be changed
+to match the sender.
+.P
+.SH "ADVANCED USAGE"
+.P
+The syntax for requesting multiple files from a remote host is done by
+specifying additional remote-host args in the same style as the first, or with
+the hostname omitted. For instance, all these work:
+.RS 4
+.P
+.nf
+rsync -aiv host:file1 :file2 host:file{3,4} /dest/
+rsync -aiv host::modname/file{1,2} host::modname/extra /dest/
+rsync -aiv host::modname/first ::extra-file{1,2} /dest/
+.fi
+.RE
+.P
+Note that a daemon connection only supports accessing one module per copy
+command, so if the start of a follow-up path doesn't begin with the
+modname of the first path, it is assumed to be a path in the module (such as
+the extra-file1 & extra-file2 that are grabbed above).
+.P
+Really old versions of rsync (2.6.9 and before) only allowed specifying one
+remote-source arg, so some people have instead relied on the remote-shell
+performing space splitting to break up an arg into multiple paths. Such
+unintuitive behavior is no longer supported by default (though you can request
+it, as described below).
+.P
+Starting in 3.2.4, filenames are passed to a remote shell in such a way as to
+preserve the characters you give it. Thus, if you ask for a file with spaces
+in the name, that's what the remote rsync looks for:
+.RS 4
+.P
+.nf
+rsync -aiv host:'a simple file.pdf' /dest/
+.fi
+.RE
+.P
+If you use scripts that have been written to manually apply extra quoting to
+the remote rsync args (or to require remote arg splitting), you can ask rsync
+to let your script handle the extra escaping. This is done by either adding
+the \fB\-\-old-args\fP option to the rsync runs in the script (which requires
+a new rsync) or exporting RSYNC_OLD_ARGS=1 and RSYNC_PROTECT_ARGS=0
+(which works with old or new rsync versions).
+.P
+.SH "CONNECTING TO AN RSYNC DAEMON"
+.P
+It is also possible to use rsync without a remote shell as the transport. In
+this case you will directly connect to a remote rsync daemon, typically using
+TCP port 873. (This obviously requires the daemon to be running on the remote
+system, so refer to the STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS
+section below for information on that.)
+.P
+Using rsync in this way is the same as using it with a remote shell except
+that:
+.P
+.IP o
+Use either double-colon syntax or rsync:// URL syntax instead of the
+single-colon (remote shell) syntax.
+.IP o
+The first element of the "path" is actually a module name.
+.IP o
+Additional remote source args can use an abbreviated syntax that omits the
+hostname and/or the module name, as discussed in ADVANCED USAGE.
+.IP o
+The remote daemon may print a "message of the day" when you connect.
+.IP o
+If you specify only the host (with no module or path) then a list of
+accessible modules on the daemon is output.
+.IP o
+If you specify a remote source path but no destination, a listing of the
+matching files on the remote daemon is output.
+.IP o
+The \fB\-\-rsh\fP (\fB\-e\fP) option must be omitted to avoid changing the
+connection style from using a socket connection to USING RSYNC-DAEMON
+FEATURES VIA A REMOTE-SHELL CONNECTION.
+.P
+An example that copies all the files in a remote module named "src":
+.RS 4
+.P
+.nf
+rsync -av host::src /dest
+.fi
+.RE
+.P
+Some modules on the remote daemon may require authentication. If so, you will
+receive a password prompt when you connect. You can avoid the password prompt
+by setting the environment variable \fBRSYNC_PASSWORD\fP to the password you
+want to use or using the \fB\-\-password-file\fP option. This may be useful
+when scripting rsync.
+.P
+WARNING: On some systems environment variables are visible to all users. On
+those systems using \fB\-\-password-file\fP is recommended.
+.P
+You may establish the connection via a web proxy by setting the environment
+variable \fBRSYNC_PROXY\fP to a hostname:port pair pointing to your web proxy.
+Note that your web proxy's configuration must support proxy connections to port
+873.
+.P
+You may also establish a daemon connection using a program as a proxy by
+setting the environment variable \fBRSYNC_CONNECT_PROG\fP to the commands you
+wish to run in place of making a direct socket connection. The string may
+contain the escape "%H" to represent the hostname specified in the rsync
+command (so use "%%" if you need a single "%" in your string). For example:
+.RS 4
+.P
+.nf
+export RSYNC_CONNECT_PROG='ssh proxyhost nc %H 873'
+rsync -av targethost1::module/src/ /dest/
+rsync -av rsync://targethost2/module/src/ /dest/
+.fi
+.RE
+.P
+The command specified above uses ssh to run nc (netcat) on a proxyhost, which
+forwards all data to port 873 (the rsync daemon) on the targethost (%H).
+.P
+Note also that if the \fBRSYNC_SHELL\fP environment variable is set, that
+program will be used to run the \fBRSYNC_CONNECT_PROG\fP command instead of using
+the default shell of the \fBsystem()\fP call.
+.P
+.SH "USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION"
+.P
+It is sometimes useful to use various features of an rsync daemon (such as
+named modules) without actually allowing any new socket connections into a
+system (other than what is already required to allow remote-shell access).
+Rsync supports connecting to a host using a remote shell and then spawning a
+single-use "daemon" server that expects to read its config file in the home dir
+of the remote user. This can be useful if you want to encrypt a daemon-style
+transfer's data, but since the daemon is started up fresh by the remote user,
+you may not be able to use features such as chroot or change the uid used by
+the daemon. (For another way to encrypt a daemon transfer, consider using ssh
+to tunnel a local port to a remote machine and configure a normal rsync daemon
+on that remote host to only allow connections from "localhost".)
+.P
+From the user's perspective, a daemon transfer via a remote-shell connection
+uses nearly the same command-line syntax as a normal rsync-daemon transfer,
+with the only exception being that you must explicitly set the remote shell
+program on the command-line with the \fB\-\-rsh=COMMAND\fP option. (Setting the
+RSYNC_RSH in the environment will not turn on this functionality.) For example:
+.RS 4
+.P
+.nf
+rsync -av --rsh=ssh host::module /dest
+.fi
+.RE
+.P
+If you need to specify a different remote-shell user, keep in mind that the
+user@ prefix in front of the host is specifying the rsync-user value (for a
+module that requires user-based authentication). This means that you must give
+the '\-l user' option to ssh when specifying the remote-shell, as in this
+example that uses the short version of the \fB\-\-rsh\fP option:
+.RS 4
+.P
+.nf
+rsync -av -e "ssh -l ssh-user" rsync-user@host::module /dest
+.fi
+.RE
+.P
+The "ssh-user" will be used at the ssh level; the "rsync-user" will be used to
+log-in to the "module".
+.P
+In this setup, the daemon is started by the ssh command that is accessing the
+system (which can be forced via the \fB~/.ssh/authorized_keys\fP file, if desired).
+However, when accessing a daemon directly, it needs to be started beforehand.
+.P
+.SH "STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS"
+.P
+In order to connect to an rsync daemon, the remote system needs to have a
+daemon already running (or it needs to have configured something like inetd to
+spawn an rsync daemon for incoming connections on a particular port). For full
+information on how to start a daemon that will handling incoming socket
+connections, see the \fBrsyncd.conf\fP(5) manpage\ \-\- that is
+the config file for the daemon, and it contains the full details for how to run
+the daemon (including stand-alone and inetd configurations).
+.P
+If you're using one of the remote-shell transports for the transfer, there is
+no need to manually start an rsync daemon.
+.P
+.SH "EXAMPLES"
+.P
+Here are some examples of how rsync can be used.
+.P
+To backup a home directory, which consists of large MS Word files and mail
+folders, a per-user cron job can be used that runs this each day:
+.RS 4
+.P
+.nf
+rsync -aiz . bkhost:backup/joe/
+.fi
+.RE
+.P
+To move some files from a remote host to the local host, you could run:
+.RS 4
+.P
+.nf
+rsync -aiv --remove-source-files rhost:/tmp/{file1,file2}.c ~/src/
+.fi
+.RE
+.P
+.SH "OPTION SUMMARY"
+.P
+Here is a short summary of the options available in rsync. Each option also
+has its own detailed description later in this manpage.
+.P
+.nf
+--verbose, -v increase verbosity
+--info=FLAGS fine-grained informational verbosity
+--debug=FLAGS fine-grained debug verbosity
+--stderr=e|a|c change stderr output mode (default: errors)
+--quiet, -q suppress non-error messages
+--no-motd suppress daemon-mode MOTD
+--checksum, -c skip based on checksum, not mod-time & size
+--archive, -a archive mode is -rlptgoD (no -A,-X,-U,-N,-H)
+--no-OPTION turn off an implied OPTION (e.g. --no-D)
+--recursive, -r recurse into directories
+--relative, -R use relative path names
+--no-implied-dirs don't send implied dirs with --relative
+--backup, -b make backups (see --suffix & --backup-dir)
+--backup-dir=DIR make backups into hierarchy based in DIR
+--suffix=SUFFIX backup suffix (default ~ w/o --backup-dir)
+--update, -u skip files that are newer on the receiver
+--inplace update destination files in-place
+--append append data onto shorter files
+--append-verify --append w/old data in file checksum
+--dirs, -d transfer directories without recursing
+--old-dirs, --old-d works like --dirs when talking to old rsync
+--mkpath create destination's missing path components
+--links, -l copy symlinks as symlinks
+--copy-links, -L transform symlink into referent file/dir
+--copy-unsafe-links only "unsafe" symlinks are transformed
+--safe-links ignore symlinks that point outside the tree
+--munge-links munge symlinks to make them safe & unusable
+--copy-dirlinks, -k transform symlink to dir into referent dir
+--keep-dirlinks, -K treat symlinked dir on receiver as dir
+--hard-links, -H preserve hard links
+--perms, -p preserve permissions
+--executability, -E preserve executability
+--chmod=CHMOD affect file and/or directory permissions
+--acls, -A preserve ACLs (implies --perms)
+--xattrs, -X preserve extended attributes
+--owner, -o preserve owner (super-user only)
+--group, -g preserve group
+--devices preserve device files (super-user only)
+--copy-devices copy device contents as a regular file
+--write-devices write to devices as files (implies --inplace)
+--specials preserve special files
+-D same as --devices --specials
+--times, -t preserve modification times
+--atimes, -U preserve access (use) times
+--open-noatime avoid changing the atime on opened files
+--crtimes, -N preserve create times (newness)
+--omit-dir-times, -O omit directories from --times
+--omit-link-times, -J omit symlinks from --times
+--super receiver attempts super-user activities
+--fake-super store/recover privileged attrs using xattrs
+--sparse, -S turn sequences of nulls into sparse blocks
+--preallocate allocate dest files before writing them
+--dry-run, -n perform a trial run with no changes made
+--whole-file, -W copy files whole (w/o delta-xfer algorithm)
+--checksum-choice=STR choose the checksum algorithm (aka --cc)
+--one-file-system, -x don't cross filesystem boundaries
+--block-size=SIZE, -B force a fixed checksum block-size
+--rsh=COMMAND, -e specify the remote shell to use
+--rsync-path=PROGRAM specify the rsync to run on remote machine
+--existing skip creating new files on receiver
+--ignore-existing skip updating files that exist on receiver
+--remove-source-files sender removes synchronized files (non-dir)
+--del an alias for --delete-during
+--delete delete extraneous files from dest dirs
+--delete-before receiver deletes before xfer, not during
+--delete-during receiver deletes during the transfer
+--delete-delay find deletions during, delete after
+--delete-after receiver deletes after transfer, not during
+--delete-excluded also delete excluded files from dest dirs
+--ignore-missing-args ignore missing source args without error
+--delete-missing-args delete missing source args from destination
+--ignore-errors delete even if there are I/O errors
+--force force deletion of dirs even if not empty
+--max-delete=NUM don't delete more than NUM files
+--max-size=SIZE don't transfer any file larger than SIZE
+--min-size=SIZE don't transfer any file smaller than SIZE
+--max-alloc=SIZE change a limit relating to memory alloc
+--partial keep partially transferred files
+--partial-dir=DIR put a partially transferred file into DIR
+--delay-updates put all updated files into place at end
+--prune-empty-dirs, -m prune empty directory chains from file-list
+--numeric-ids don't map uid/gid values by user/group name
+--usermap=STRING custom username mapping
+--groupmap=STRING custom groupname mapping
+--chown=USER:GROUP simple username/groupname mapping
+--timeout=SECONDS set I/O timeout in seconds
+--contimeout=SECONDS set daemon connection timeout in seconds
+--ignore-times, -I don't skip files that match size and time
+--size-only skip files that match in size
+--modify-window=NUM, -@ set the accuracy for mod-time comparisons
+--temp-dir=DIR, -T create temporary files in directory DIR
+--fuzzy, -y find similar file for basis if no dest file
+--compare-dest=DIR also compare destination files relative to DIR
+--copy-dest=DIR ... and include copies of unchanged files
+--link-dest=DIR hardlink to files in DIR when unchanged
+--compress, -z compress file data during the transfer
+--compress-choice=STR choose the compression algorithm (aka --zc)
+--compress-level=NUM explicitly set compression level (aka --zl)
+--skip-compress=LIST skip compressing files with suffix in LIST
+--cvs-exclude, -C auto-ignore files in the same way CVS does
+--filter=RULE, -f add a file-filtering RULE
+-F same as --filter='dir-merge /.rsync-filter'
+ repeated: --filter='- .rsync-filter'
+--exclude=PATTERN exclude files matching PATTERN
+--exclude-from=FILE read exclude patterns from FILE
+--include=PATTERN don't exclude files matching PATTERN
+--include-from=FILE read include patterns from FILE
+--files-from=FILE read list of source-file names from FILE
+--from0, -0 all *-from/filter files are delimited by 0s
+--old-args disable the modern arg-protection idiom
+--secluded-args, -s use the protocol to safely send the args
+--trust-sender trust the remote sender's file list
+--copy-as=USER[:GROUP] specify user & optional group for the copy
+--address=ADDRESS bind address for outgoing socket to daemon
+--port=PORT specify double-colon alternate port number
+--sockopts=OPTIONS specify custom TCP options
+--blocking-io use blocking I/O for the remote shell
+--outbuf=N|L|B set out buffering to None, Line, or Block
+--stats give some file-transfer stats
+--8-bit-output, -8 leave high-bit chars unescaped in output
+--human-readable, -h output numbers in a human-readable format
+--progress show progress during transfer
+-P same as --partial --progress
+--itemize-changes, -i output a change-summary for all updates
+--remote-option=OPT, -M send OPTION to the remote side only
+--out-format=FORMAT output updates using the specified FORMAT
+--log-file=FILE log what we're doing to the specified FILE
+--log-file-format=FMT log updates using the specified FMT
+--password-file=FILE read daemon-access password from FILE
+--early-input=FILE use FILE for daemon's early exec input
+--list-only list the files instead of copying them
+--bwlimit=RATE limit socket I/O bandwidth
+--stop-after=MINS Stop rsync after MINS minutes have elapsed
+--stop-at=y-m-dTh:m Stop rsync at the specified point in time
+--fsync fsync every written file
+--write-batch=FILE write a batched update to FILE
+--only-write-batch=FILE like --write-batch but w/o updating dest
+--read-batch=FILE read a batched update from FILE
+--protocol=NUM force an older protocol version to be used
+--iconv=CONVERT_SPEC request charset conversion of filenames
+--checksum-seed=NUM set block/file checksum seed (advanced)
+--ipv4, -4 prefer IPv4
+--ipv6, -6 prefer IPv6
+--version, -V print the version + other info and exit
+--help, -h (*) show this help (* -h is help only on its own)
+.fi
+.P
+Rsync can also be run as a daemon, in which case the following options are
+accepted:
+.P
+.nf
+--daemon run as an rsync daemon
+--address=ADDRESS bind to the specified address
+--bwlimit=RATE limit socket I/O bandwidth
+--config=FILE specify alternate rsyncd.conf file
+--dparam=OVERRIDE, -M override global daemon config parameter
+--no-detach do not detach from the parent
+--port=PORT listen on alternate port number
+--log-file=FILE override the "log file" setting
+--log-file-format=FMT override the "log format" setting
+--sockopts=OPTIONS specify custom TCP options
+--verbose, -v increase verbosity
+--ipv4, -4 prefer IPv4
+--ipv6, -6 prefer IPv6
+--help, -h show this help (when used with --daemon)
+.fi
+.P
+.SH "OPTIONS"
+.P
+Rsync accepts both long (double-dash + word) and short (single-dash + letter)
+options. The full list of the available options are described below. If an
+option can be specified in more than one way, the choices are comma-separated.
+Some options only have a long variant, not a short.
+.P
+If the option takes a parameter, the parameter is only listed after the long
+variant, even though it must also be specified for the short. When specifying
+a parameter, you can either use the form \fB\-\-option=param\fP, \fB\-\-option\ param\fP,
+\fB\-o=param\fP, \fB\-o\ param\fP, or \fB\-oparam\fP (the latter choices assume that your
+option has a short variant).
+.P
+The parameter may need to be quoted in some manner for it to survive the
+shell's command-line parsing. Also keep in mind that a leading tilde (\fB~\fP) in
+a pathname is substituted by your shell, so make sure that you separate the
+option name from the pathname using a space if you want the local shell to
+expand it.
+.P
+.IP "\fB\-\-help\fP"
+Print a short help page describing the options available in rsync and exit.
+You can also use \fB\-h\fP for \fB\-\-help\fP when it is used without any other
+options (since it normally means \fB\-\-human-readable\fP).
+.IP "\fB\-\-version\fP, \fB\-V\fP"
+Print the rsync version plus other info and exit. When repeated, the
+information is output is a JSON format that is still fairly readable
+(client side only).
+.IP
+The output includes a list of compiled-in capabilities, a list of
+optimizations, the default list of checksum algorithms, the default list of
+compression algorithms, the default list of daemon auth digests, a link to
+the rsync web site, and a few other items.
+.IP "\fB\-\-verbose\fP, \fB\-v\fP"
+This option increases the amount of information you are given during the
+transfer. By default, rsync works silently. A single \fB\-v\fP will give you
+information about what files are being transferred and a brief summary at
+the end. Two \fB\-v\fP options will give you information on what files are
+being skipped and slightly more information at the end. More than two \fB\-v\fP
+options should only be used if you are debugging rsync.
+.IP
+The end-of-run summary tells you the number of bytes sent to the remote
+rsync (which is the receiving side on a local copy), the number of bytes
+received from the remote host, and the average bytes per second of the
+transferred data computed over the entire length of the rsync run. The
+second line shows the total size (in bytes), which is the sum of all the
+file sizes that rsync considered transferring. It also shows a "speedup"
+value, which is a ratio of the total file size divided by the sum of the
+sent and received bytes (which is really just a feel-good bigger-is-better
+number). Note that these byte values can be made more (or less)
+human-readable by using the \fB\-\-human-readable\fP (or
+\fB\-\-no-human-readable\fP) options.
+.IP
+In a modern rsync, the \fB\-v\fP option is equivalent to the setting of groups
+of \fB\-\-info\fP and \fB\-\-debug\fP options. You can choose to use
+these newer options in addition to, or in place of using \fB\-\-verbose\fP, as
+any fine-grained settings override the implied settings of \fB\-v\fP. Both
+\fB\-\-info\fP and \fB\-\-debug\fP have a way to ask for help that
+tells you exactly what flags are set for each increase in verbosity.
+.IP
+However, do keep in mind that a daemon's "\fBmax\ verbosity\fP" setting will limit
+how high of a level the various individual flags can be set on the daemon
+side. For instance, if the max is 2, then any info and/or debug flag that
+is set to a higher value than what would be set by \fB\-vv\fP will be downgraded
+to the \fB\-vv\fP level in the daemon's logging.
+.IP "\fB\-\-info=FLAGS\fP"
+This option lets you have fine-grained control over the information output
+you want to see. An individual flag name may be followed by a level
+number, with 0 meaning to silence that output, 1 being the default output
+level, and higher numbers increasing the output of that flag (for those
+that support higher levels). Use \fB\-\-info=help\fP to see all the available
+flag names, what they output, and what flag names are added for each
+increase in the verbose level. Some examples:
+.RS 4
+.IP
+.nf
+rsync -a --info=progress2 src/ dest/
+rsync -avv --info=stats2,misc1,flist0 src/ dest/
+.fi
+.RE
+.IP
+Note that \fB\-\-info=name\fP's output is affected by the \fB\-\-out-format\fP
+and \fB\-\-itemize-changes\fP (\fB\-i\fP) options. See those options for more
+information on what is output and when.
+.IP
+This option was added to 3.1.0, so an older rsync on the server side might
+reject your attempts at fine-grained control (if one or more flags needed
+to be send to the server and the server was too old to understand them).
+See also the "\fBmax\ verbosity\fP" caveat above when dealing with a daemon.
+.IP "\fB\-\-debug=FLAGS\fP"
+This option lets you have fine-grained control over the debug output you
+want to see. An individual flag name may be followed by a level number,
+with 0 meaning to silence that output, 1 being the default output level,
+and higher numbers increasing the output of that flag (for those that
+support higher levels). Use \fB\-\-debug=help\fP to see all the available flag
+names, what they output, and what flag names are added for each increase in
+the verbose level. Some examples:
+.RS 4
+.IP
+.nf
+rsync -avvv --debug=none src/ dest/
+rsync -avA --del --debug=del2,acl src/ dest/
+.fi
+.RE
+.IP
+Note that some debug messages will only be output when the \fB\-\-stderr=all\fP
+option is specified, especially those pertaining to I/O and buffer debugging.
+.IP
+Beginning in 3.2.0, this option is no longer auto-forwarded to the server
+side in order to allow you to specify different debug values for each side
+of the transfer, as well as to specify a new debug option that is only
+present in one of the rsync versions. If you want to duplicate the same
+option on both sides, using brace expansion is an easy way to save you some
+typing. This works in zsh and bash:
+.RS 4
+.IP
+.nf
+rsync -aiv {-M,}--debug=del2 src/ dest/
+.fi
+.RE
+.IP "\fB\-\-stderr=errors|all|client\fP"
+This option controls which processes output to stderr and if info messages
+are also changed to stderr. The mode strings can be abbreviated, so feel
+free to use a single letter value. The 3 possible choices are:
+.IP
+.RS
+.IP o
+\fBerrors\fP \- (the default) causes all the rsync processes to send an
+error directly to stderr, even if the process is on the remote side of
+the transfer. Info messages are sent to the client side via the protocol
+stream. If stderr is not available (i.e. when directly connecting with a
+daemon via a socket) errors fall back to being sent via the protocol
+stream.
+.IP o
+\fBall\fP \- causes all rsync messages (info and error) to get written
+directly to stderr from all (possible) processes. This causes stderr to
+become line-buffered (instead of raw) and eliminates the ability to
+divide up the info and error messages by file handle. For those doing
+debugging or using several levels of verbosity, this option can help to
+avoid clogging up the transfer stream (which should prevent any chance of
+a deadlock bug hanging things up). It also allows \fB\-\-debug\fP to
+enable some extra I/O related messages.
+.IP o
+\fBclient\fP \- causes all rsync messages to be sent to the client side
+via the protocol stream. One client process outputs all messages, with
+errors on stderr and info messages on stdout. This \fBwas\fP the default
+in older rsync versions, but can cause error delays when a lot of
+transfer data is ahead of the messages. If you're pushing files to an
+older rsync, you may want to use \fB\-\-stderr=all\fP since that idiom has
+been around for several releases.
+.RE
+.IP
+This option was added in rsync 3.2.3. This version also began the
+forwarding of a non-default setting to the remote side, though rsync uses
+the backward-compatible options \fB\-\-msgs2stderr\fP and \fB\-\-no-msgs2stderr\fP to
+represent the \fBall\fP and \fBclient\fP settings, respectively. A newer rsync
+will continue to accept these older option names to maintain compatibility.
+.IP "\fB\-\-quiet\fP, \fB\-q\fP"
+This option decreases the amount of information you are given during the
+transfer, notably suppressing information messages from the remote server.
+This option is useful when invoking rsync from cron.
+.IP "\fB\-\-no-motd\fP"
+This option affects the information that is output by the client at the
+start of a daemon transfer. This suppresses the message-of-the-day (MOTD)
+text, but it also affects the list of modules that the daemon sends in
+response to the "rsync host::" request (due to a limitation in the rsync
+protocol), so omit this option if you want to request the list of modules
+from the daemon.
+.IP "\fB\-\-ignore-times\fP, \fB\-I\fP"
+Normally rsync will skip any files that are already the same size and have
+the same modification timestamp. This option turns off this "quick check"
+behavior, causing all files to be updated.
+.IP
+This option can be confusing compared to \fB\-\-ignore-existing\fP and
+\fB\-\-ignore-non-existing\fP in that that they cause rsync to transfer
+fewer files, while this option causes rsync to transfer more files.
+.IP "\fB\-\-size-only\fP"
+This modifies rsync's "quick check" algorithm for finding files that need
+to be transferred, changing it from the default of transferring files with
+either a changed size or a changed last-modified time to just looking for
+files that have changed in size. This is useful when starting to use rsync
+after using another mirroring system which may not preserve timestamps
+exactly.
+.IP "\fB\-\-modify-window=NUM\fP, \fB\-@\fP"
+When comparing two timestamps, rsync treats the timestamps as being equal
+if they differ by no more than the modify-window value. The default is 0,
+which matches just integer seconds. If you specify a negative value (and
+the receiver is at least version 3.1.3) then nanoseconds will also be taken
+into account. Specifying 1 is useful for copies to/from MS Windows FAT
+filesystems, because FAT represents times with a 2-second resolution
+(allowing times to differ from the original by up to 1 second).
+.IP
+If you want all your transfers to default to comparing nanoseconds, you can
+create a \fB~/.popt\fP file and put these lines in it:
+.RS 4
+.IP
+.nf
+rsync alias -a -a@-1
+rsync alias -t -t@-1
+.fi
+.RE
+.IP
+With that as the default, you'd need to specify \fB\-\-modify-window=0\fP (aka
+\fB\-@0\fP) to override it and ignore nanoseconds, e.g. if you're copying
+between ext3 and ext4, or if the receiving rsync is older than 3.1.3.
+.IP "\fB\-\-checksum\fP, \fB\-c\fP"
+This changes the way rsync checks if the files have been changed and are in
+need of a transfer. Without this option, rsync uses a "quick check" that
+(by default) checks if each file's size and time of last modification match
+between the sender and receiver. This option changes this to compare a
+128-bit checksum for each file that has a matching size. Generating the
+checksums means that both sides will expend a lot of disk I/O reading all
+the data in the files in the transfer, so this can slow things down
+significantly (and this is prior to any reading that will be done to
+transfer changed files)
+.IP
+The sending side generates its checksums while it is doing the file-system
+scan that builds the list of the available files. The receiver generates
+its checksums when it is scanning for changed files, and will checksum any
+file that has the same size as the corresponding sender's file: files with
+either a changed size or a changed checksum are selected for transfer.
+.IP
+Note that rsync always verifies that each \fItransferred\fP file was correctly
+reconstructed on the receiving side by checking a whole-file checksum that
+is generated as the file is transferred, but that automatic
+after-the-transfer verification has nothing to do with this option's
+before-the-transfer "Does this file need to be updated?" check.
+.IP
+The checksum used is auto-negotiated between the client and the server, but
+can be overridden using either the \fB\-\-checksum-choice\fP (\fB\-\-cc\fP)
+option or an environment variable that is discussed in that option's
+section.
+.IP "\fB\-\-archive\fP, \fB\-a\fP"
+This is equivalent to \fB\-rlptgoD\fP. It is a quick way of saying you want
+recursion and want to preserve almost everything. Be aware that it does
+\fBnot\fP include preserving ACLs (\fB\-A\fP), xattrs (\fB\-X\fP), atimes (\fB\-U\fP),
+crtimes (\fB\-N\fP), nor the finding and preserving of hardlinks (\fB\-H\fP).
+.IP
+The only exception to the above equivalence is when \fB\-\-files-from\fP
+is specified, in which case \fB\-r\fP is not implied.
+.IP "\fB\-\-no-OPTION\fP"
+You may turn off one or more implied options by prefixing the option name
+with "no-". Not all positive options have a negated opposite, but a lot
+do, including those that can be used to disable an implied option (e.g.
+\fB\-\-no-D\fP, \fB\-\-no-perms\fP) or have different defaults in various circumstances
+(e.g. \fB\-\-no-whole-file\fP, \fB\-\-no-blocking-io\fP, \fB\-\-no-dirs\fP). Every
+valid negated option accepts both the short and the long option name after
+the "no-" prefix (e.g. \fB\-\-no-R\fP is the same as \fB\-\-no-relative\fP).
+.IP
+As an example, if you want to use \fB\-\-archive\fP (\fB\-a\fP) but don't want
+\fB\-\-owner\fP (\fB\-o\fP), instead of converting \fB\-a\fP into \fB\-rlptgD\fP, you
+can specify \fB\-a\ \-\-no-o\fP (aka \fB\-\-archive\ \-\-no-owner\fP).
+.IP
+The order of the options is important: if you specify \fB\-\-no-r\ \-a\fP, the \fB\-r\fP
+option would end up being turned on, the opposite of \fB\-a\ \-\-no-r\fP. Note
+also that the side-effects of the \fB\-\-files-from\fP option are NOT
+positional, as it affects the default state of several options and slightly
+changes the meaning of \fB\-a\fP (see the \fB\-\-files-from\fP option
+for more details).
+.IP "\fB\-\-recursive\fP, \fB\-r\fP"
+This tells rsync to copy directories recursively. See also
+\fB\-\-dirs\fP (\fB\-d\fP) for an option that allows the scanning of a single
+directory.
+.IP
+See the \fB\-\-inc-recursive\fP option for a discussion of the
+incremental recursion for creating the list of files to transfer.
+.IP "\fB\-\-inc-recursive\fP, \fB\-\-i-r\fP"
+This option explicitly enables on incremental recursion when scanning for
+files, which is enabled by default when using the \fB\-\-recursive\fP
+option and both sides of the transfer are running rsync 3.0.0 or newer.
+.IP
+Incremental recursion uses much less memory than non-incremental, while
+also beginning the transfer more quickly (since it doesn't need to scan the
+entire transfer hierarchy before it starts transferring files). If no
+recursion is enabled in the source files, this option has no effect.
+.IP
+Some options require rsync to know the full file list, so these options
+disable the incremental recursion mode. These include:
+.IP
+.RS
+.IP o
+\fB\-\-delete-before\fP (the old default of \fB\-\-delete\fP)
+.IP o
+\fB\-\-delete-after\fP
+.IP o
+\fB\-\-prune-empty-dirs\fP
+.IP o
+\fB\-\-delay-updates\fP
+.RE
+.IP
+In order to make \fB\-\-delete\fP compatible with incremental recursion,
+rsync 3.0.0 made \fB\-\-delete-during\fP the default delete mode (which
+was first added in 2.6.4).
+.IP
+One side-effect of incremental recursion is that any missing
+sub-directories inside a recursively-scanned directory are (by default)
+created prior to recursing into the sub-dirs. This earlier creation point
+(compared to a non-incremental recursion) allows rsync to then set the
+modify time of the finished directory right away (without having to delay
+that until a bunch of recursive copying has finished). However, these
+early directories don't yet have their completed mode, mtime, or ownership
+set\ \-\- they have more restrictive rights until the subdirectory's copying
+actually begins. This early-creation idiom can be avoided by using the
+\fB\-\-omit-dir-times\fP option.
+.IP
+Incremental recursion can be disabled using the
+\fB\-\-no-inc-recursive\fP (\fB\-\-no-i-r\fP) option.
+.IP "\fB\-\-no-inc-recursive\fP, \fB\-\-no-i-r\fP"
+Disables the new incremental recursion algorithm of the
+\fB\-\-recursive\fP option. This makes rsync scan the full file list
+before it begins to transfer files. See \fB\-\-inc-recursive\fP for more
+info.
+.IP "\fB\-\-relative\fP, \fB\-R\fP"
+Use relative paths. This means that the full path names specified on the
+command line are sent to the server rather than just the last parts of the
+filenames. This is particularly useful when you want to send several
+different directories at the same time. For example, if you used this
+command:
+.RS 4
+.IP
+.nf
+rsync -av /foo/bar/baz.c remote:/tmp/
+.fi
+.RE
+.IP
+would create a file named baz.c in /tmp/ on the remote machine. If instead
+you used
+.RS 4
+.IP
+.nf
+rsync -avR /foo/bar/baz.c remote:/tmp/
+.fi
+.RE
+.IP
+then a file named /tmp/foo/bar/baz.c would be created on the remote
+machine, preserving its full path. These extra path elements are called
+"implied directories" (i.e. the "foo" and the "foo/bar" directories in the
+above example).
+.IP
+Beginning with rsync 3.0.0, rsync always sends these implied directories as
+real directories in the file list, even if a path element is really a
+symlink on the sending side. This prevents some really unexpected behaviors
+when copying the full path of a file that you didn't realize had a symlink
+in its path. If you want to duplicate a server-side symlink, include both
+the symlink via its path, and referent directory via its real path. If
+you're dealing with an older rsync on the sending side, you may need to use
+the \fB\-\-no-implied-dirs\fP option.
+.IP
+It is also possible to limit the amount of path information that is sent as
+implied directories for each path you specify. With a modern rsync on the
+sending side (beginning with 2.6.7), you can insert a dot and a slash into
+the source path, like this:
+.RS 4
+.IP
+.nf
+rsync -avR /foo/./bar/baz.c remote:/tmp/
+.fi
+.RE
+.IP
+That would create /tmp/bar/baz.c on the remote machine. (Note that the dot
+must be followed by a slash, so "/foo/." would not be abbreviated.) For
+older rsync versions, you would need to use a chdir to limit the source
+path. For example, when pushing files:
+.RS 4
+.IP
+.nf
+(cd /foo; rsync -avR bar/baz.c remote:/tmp/)
+.fi
+.RE
+.IP
+(Note that the parens put the two commands into a sub-shell, so that the
+"cd" command doesn't remain in effect for future commands.) If you're
+pulling files from an older rsync, use this idiom (but only for a
+non-daemon transfer):
+.RS 4
+.IP
+.nf
+rsync -avR --rsync-path="cd /foo; rsync" \\
+ remote:bar/baz.c /tmp/
+.fi
+.RE
+.IP "\fB\-\-no-implied-dirs\fP"
+This option affects the default behavior of the \fB\-\-relative\fP option. When
+it is specified, the attributes of the implied directories from the source
+names are not included in the transfer. This means that the corresponding
+path elements on the destination system are left unchanged if they exist,
+and any missing implied directories are created with default attributes.
+This even allows these implied path elements to have big differences, such
+as being a symlink to a directory on the receiving side.
+.IP
+For instance, if a command-line arg or a files-from entry told rsync to
+transfer the file "path/foo/file", the directories "path" and "path/foo"
+are implied when \fB\-\-relative\fP is used. If "path/foo" is a symlink to "bar"
+on the destination system, the receiving rsync would ordinarily delete
+"path/foo", recreate it as a directory, and receive the file into the new
+directory. With \fB\-\-no-implied-dirs\fP, the receiving rsync updates
+"path/foo/file" using the existing path elements, which means that the file
+ends up being created in "path/bar". Another way to accomplish this link
+preservation is to use the \fB\-\-keep-dirlinks\fP option (which will also affect
+symlinks to directories in the rest of the transfer).
+.IP
+When pulling files from an rsync older than 3.0.0, you may need to use this
+option if the sending side has a symlink in the path you request and you
+wish the implied directories to be transferred as normal directories.
+.IP "\fB\-\-backup\fP, \fB\-b\fP"
+With this option, preexisting destination files are renamed as each file is
+transferred or deleted. You can control where the backup file goes and
+what (if any) suffix gets appended using the \fB\-\-backup-dir\fP and
+\fB\-\-suffix\fP options.
+.IP
+If you don't specify \fB\-\-backup-dir\fP:
+.RS
+.IP
+.IP 1.
+the \fB\-\-omit-dir-times\fP option will be forced on
+.IP 2.
+the use of \fB\-\-delete\fP (without \fB\-\-delete-excluded\fP),
+causes rsync to add a "protect" filter-rule for the
+backup suffix to the end of all your existing filters that looks like
+this: \fB\-f\ "P\ *~"\fP. This rule prevents previously backed-up files from
+being deleted.
+.RE
+.IP
+Note that if you are supplying your own filter rules, you may need to
+manually insert your own exclude/protect rule somewhere higher up in the
+list so that it has a high enough priority to be effective (e.g. if your
+rules specify a trailing inclusion/exclusion of \fB*\fP, the auto-added rule
+would never be reached).
+.IP "\fB\-\-backup-dir=DIR\fP"
+This implies the \fB\-\-backup\fP option, and tells rsync to store all
+backups in the specified directory on the receiving side. This can be used
+for incremental backups. You can additionally specify a backup suffix
+using the \fB\-\-suffix\fP option (otherwise the files backed up in the
+specified directory will keep their original filenames).
+.IP
+Note that if you specify a relative path, the backup directory will be
+relative to the destination directory, so you probably want to specify
+either an absolute path or a path that starts with "../". If an rsync
+daemon is the receiver, the backup dir cannot go outside the module's path
+hierarchy, so take extra care not to delete it or copy into it.
+.IP "\fB\-\-suffix=SUFFIX\fP"
+This option allows you to override the default backup suffix used with the
+\fB\-\-backup\fP (\fB\-b\fP) option. The default suffix is a \fB~\fP if no
+\fB\-\-backup-dir\fP was specified, otherwise it is an empty string.
+.IP "\fB\-\-update\fP, \fB\-u\fP"
+This forces rsync to skip any files which exist on the destination and have
+a modified time that is newer than the source file. (If an existing
+destination file has a modification time equal to the source file's, it
+will be updated if the sizes are different.)
+.IP
+Note that this does not affect the copying of dirs, symlinks, or other
+special files. Also, a difference of file format between the sender and
+receiver is always considered to be important enough for an update, no
+matter what date is on the objects. In other words, if the source has a
+directory where the destination has a file, the transfer would occur
+regardless of the timestamps.
+.IP
+This option is a TRANSFER RULE, so don't expect any
+exclude side effects.
+.IP
+A caution for those that choose to combine \fB\-\-inplace\fP with
+\fB\-\-update\fP: an interrupted transfer will leave behind a partial file on the
+receiving side that has a very recent modified time, so re-running the
+transfer will probably \fBnot\fP continue the interrupted file. As such, it
+is usually best to avoid combining this with \fB\-\-inplace\fP unless you
+have implemented manual steps to handle any interrupted in-progress files.
+.IP "\fB\-\-inplace\fP"
+This option changes how rsync transfers a file when its data needs to be
+updated: instead of the default method of creating a new copy of the file
+and moving it into place when it is complete, rsync instead writes the
+updated data directly to the destination file.
+.IP
+This has several effects:
+.IP
+.RS
+.IP o
+Hard links are not broken. This means the new data will be visible
+through other hard links to the destination file. Moreover, attempts to
+copy differing source files onto a multiply-linked destination file will
+result in a "tug of war" with the destination data changing back and
+forth.
+.IP o
+In-use binaries cannot be updated (either the OS will prevent this from
+happening, or binaries that attempt to swap-in their data will misbehave
+or crash).
+.IP o
+The file's data will be in an inconsistent state during the transfer and
+will be left that way if the transfer is interrupted or if an update
+fails.
+.IP o
+A file that rsync cannot write to cannot be updated. While a super user
+can update any file, a normal user needs to be granted write permission
+for the open of the file for writing to be successful.
+.IP o
+The efficiency of rsync's delta-transfer algorithm may be reduced if some
+data in the destination file is overwritten before it can be copied to a
+position later in the file. This does not apply if you use \fB\-\-backup\fP,
+since rsync is smart enough to use the backup file as the basis file for
+the transfer.
+.RE
+.IP
+WARNING: you should not use this option to update files that are being
+accessed by others, so be careful when choosing to use this for a copy.
+.IP
+This option is useful for transferring large files with block-based changes
+or appended data, and also on systems that are disk bound, not network
+bound. It can also help keep a copy-on-write filesystem snapshot from
+diverging the entire contents of a file that only has minor changes.
+.IP
+The option implies \fB\-\-partial\fP (since an interrupted transfer does
+not delete the file), but conflicts with \fB\-\-partial-dir\fP and
+\fB\-\-delay-updates\fP. Prior to rsync 2.6.4 \fB\-\-inplace\fP was also
+incompatible with \fB\-\-compare-dest\fP and \fB\-\-link-dest\fP.
+.IP "\fB\-\-append\fP"
+This special copy mode only works to efficiently update files that are
+known to be growing larger where any existing content on the receiving side
+is also known to be the same as the content on the sender. The use of
+\fB\-\-append\fP \fBcan be dangerous\fP if you aren't 100% sure that all the files
+in the transfer are shared, growing files. You should thus use filter
+rules to ensure that you weed out any files that do not fit this criteria.
+.IP
+Rsync updates these growing file in-place without verifying any of the
+existing content in the file (it only verifies the content that it is
+appending). Rsync skips any files that exist on the receiving side that
+are not shorter than the associated file on the sending side (which means
+that new files are transferred). It also skips any files whose size on the
+sending side gets shorter during the send negotiations (rsync warns about a
+"diminished" file when this happens).
+.IP
+This does not interfere with the updating of a file's non-content
+attributes (e.g. permissions, ownership, etc.) when the file does not need
+to be transferred, nor does it affect the updating of any directories or
+non-regular files.
+.IP "\fB\-\-append-verify\fP"
+This special copy mode works like \fB\-\-append\fP except that all the
+data in the file is included in the checksum verification (making it less
+efficient but also potentially safer). This option \fBcan be dangerous\fP if
+you aren't 100% sure that all the files in the transfer are shared, growing
+files. See the \fB\-\-append\fP option for more details.
+.IP
+Note: prior to rsync 3.0.0, the \fB\-\-append\fP option worked like
+\fB\-\-append-verify\fP, so if you are interacting with an older rsync (or the
+transfer is using a protocol prior to 30), specifying either append option
+will initiate an \fB\-\-append-verify\fP transfer.
+.IP "\fB\-\-dirs\fP, \fB\-d\fP"
+Tell the sending side to include any directories that are encountered.
+Unlike \fB\-\-recursive\fP, a directory's contents are not copied unless
+the directory name specified is "." or ends with a trailing slash (e.g.
+".", "dir/.", "dir/", etc.). Without this option or the
+\fB\-\-recursive\fP option, rsync will skip all directories it encounters
+(and output a message to that effect for each one). If you specify both
+\fB\-\-dirs\fP and \fB\-\-recursive\fP, \fB\-\-recursive\fP takes precedence.
+.IP
+The \fB\-\-dirs\fP option is implied by the \fB\-\-files-from\fP option or the
+\fB\-\-list-only\fP option (including an implied \fB\-\-list-only\fP
+usage) if \fB\-\-recursive\fP wasn't specified (so that directories are
+seen in the listing). Specify \fB\-\-no-dirs\fP (or \fB\-\-no-d\fP) if you want to
+turn this off.
+.IP
+There is also a backward-compatibility helper option, \fB\-\-old-dirs\fP
+(\fB\-\-old-d\fP) that tells rsync to use a hack of \fB\-r\ \-\-exclude='/*/*'\fP to get
+an older rsync to list a single directory without recursing.
+.IP "\fB\-\-mkpath\fP"
+Create all missing path components of the destination path.
+.IP
+By default, rsync allows only the final component of the destination path
+to not exist, which is an attempt to help you to validate your destination
+path. With this option, rsync creates all the missing destination-path
+components, just as if \fBmkdir\ \-p\ $DEST_PATH\fP had been run on the receiving
+side.
+.IP
+When specifying a destination path, including a trailing slash ensures that
+the whole path is treated as directory names to be created, even when the
+file list has a single item. See the COPYING TO A DIFFERENT NAME
+section for full details on how rsync decides if a final destination-path
+component should be created as a directory or not.
+.IP
+If you would like the newly-created destination dirs to match the dirs on
+the sending side, you should be using \fB\-\-relative\fP (\fB\-R\fP) instead
+of \fB\-\-mkpath\fP. For instance, the following two commands result in the same
+destination tree, but only the second command ensures that the
+"some/extra/path" components match the dirs on the sending side:
+.RS 4
+.IP
+.nf
+rsync -ai --mkpath host:some/extra/path/*.c some/extra/path/
+rsync -aiR host:some/extra/path/*.c ./
+.fi
+.RE
+.IP "\fB\-\-links\fP, \fB\-l\fP"
+Add symlinks to the transferred files instead of noisily ignoring them with
+a "non-regular file" warning for each symlink encountered. You can
+alternately silence the warning by specifying \fB\-\-info=nonreg0\fP.
+.IP
+The default handling of symlinks is to recreate each symlink's unchanged
+value on the receiving side.
+.IP
+See the SYMBOLIC LINKS section for multi-option info.
+.IP "\fB\-\-copy-links\fP, \fB\-L\fP"
+The sender transforms each symlink encountered in the transfer into the
+referent item, following the symlink chain to the file or directory that it
+references. If a symlink chain is broken, an error is output and the file
+is dropped from the transfer.
+.IP
+This option supersedes any other options that affect symlinks in the
+transfer, since there are no symlinks left in the transfer.
+.IP
+This option does not change the handling of existing symlinks on the
+receiving side, unlike versions of rsync prior to 2.6.3 which had the
+side-effect of telling the receiving side to also follow symlinks. A
+modern rsync won't forward this option to a remote receiver (since only the
+sender needs to know about it), so this caveat should only affect someone
+using an rsync client older than 2.6.7 (which is when \fB\-L\fP stopped being
+forwarded to the receiver).
+.IP
+See the \fB\-\-keep-dirlinks\fP (\fB\-K\fP) if you need a symlink to a
+directory to be treated as a real directory on the receiving side.
+.IP
+See the SYMBOLIC LINKS section for multi-option info.
+.IP "\fB\-\-copy-unsafe-links\fP"
+This tells rsync to copy the referent of symbolic links that point outside
+the copied tree. Absolute symlinks are also treated like ordinary files,
+and so are any symlinks in the source path itself when \fB\-\-relative\fP
+is used.
+.IP
+Note that the cut-off point is the top of the transfer, which is the part
+of the path that rsync isn't mentioning in the verbose output. If you copy
+"/src/subdir" to "/dest/" then the "subdir" directory is a name inside the
+transfer tree, not the top of the transfer (which is /src) so it is legal
+for created relative symlinks to refer to other names inside the /src and
+/dest directories. If you instead copy "/src/subdir/" (with a trailing
+slash) to "/dest/subdir" that would not allow symlinks to any files outside
+of "subdir".
+.IP
+Note that safe symlinks are only copied if \fB\-\-links\fP was also
+specified or implied. The \fB\-\-copy-unsafe-links\fP option has no extra effect
+when combined with \fB\-\-copy-links\fP.
+.IP
+See the SYMBOLIC LINKS section for multi-option info.
+.IP "\fB\-\-safe-links\fP"
+This tells the receiving rsync to ignore any symbolic links in the transfer
+which point outside the copied tree. All absolute symlinks are also
+ignored.
+.IP
+Since this ignoring is happening on the receiving side, it will still be
+effective even when the sending side has munged symlinks (when it is using
+\fB\-\-munge-links\fP). It also affects deletions, since the file being
+present in the transfer prevents any matching file on the receiver from
+being deleted when the symlink is deemed to be unsafe and is skipped.
+.IP
+This option must be combined with \fB\-\-links\fP (or
+\fB\-\-archive\fP) to have any symlinks in the transfer to conditionally
+ignore. Its effect is superseded by \fB\-\-copy-unsafe-links\fP.
+.IP
+Using this option in conjunction with \fB\-\-relative\fP may give
+unexpected results.
+.IP
+See the SYMBOLIC LINKS section for multi-option info.
+.IP "\fB\-\-munge-links\fP"
+This option affects just one side of the transfer and tells rsync to munge
+symlink values when it is receiving files or unmunge symlink values when it
+is sending files. The munged values make the symlinks unusable on disk but
+allows the original contents of the symlinks to be recovered.
+.IP
+The server-side rsync often enables this option without the client's
+knowledge, such as in an rsync daemon's configuration file or by an option
+given to the rrsync (restricted rsync) script. When specified on the
+client side, specify the option normally if it is the client side that
+has/needs the munged symlinks, or use \fB\-M\-\-munge-links\fP to give the option
+to the server when it has/needs the munged symlinks. Note that on a local
+transfer, the client is the sender, so specifying the option directly
+unmunges symlinks while specifying it as a remote option munges symlinks.
+.IP
+This option has no effect when sent to a daemon via \fB\-\-remote-option\fP
+because the daemon configures whether it wants munged symlinks via its
+"\fBmunge\ symlinks\fP" parameter.
+.IP
+The symlink value is munged/unmunged once it is in the transfer, so any
+option that transforms symlinks into non-symlinks occurs prior to the
+munging/unmunging \fBexcept\fP for \fB\-\-safe-links\fP, which is a choice
+that the receiver makes, so it bases its decision on the munged/unmunged
+value. This does mean that if a receiver has munging enabled, that using
+\fB\-\-safe-links\fP will cause all symlinks to be ignored (since they
+are all absolute).
+.IP
+The method that rsync uses to munge the symlinks is to prefix each one's
+value with the string "/rsyncd-munged/". This prevents the links from
+being used as long as the directory does not exist. When this option is
+enabled, rsync will refuse to run if that path is a directory or a symlink
+to a directory (though it only checks at startup). See also the
+"munge-symlinks" python script in the support directory of the source code
+for a way to munge/unmunge one or more symlinks in-place.
+.IP "\fB\-\-copy-dirlinks\fP, \fB\-k\fP"
+This option causes the sending side to treat a symlink to a directory as
+though it were a real directory. This is useful if you don't want symlinks
+to non-directories to be affected, as they would be using
+\fB\-\-copy-links\fP.
+.IP
+Without this option, if the sending side has replaced a directory with a
+symlink to a directory, the receiving side will delete anything that is in
+the way of the new symlink, including a directory hierarchy (as long as
+\fB\-\-force\fP or \fB\-\-delete\fP is in effect).
+.IP
+See also \fB\-\-keep-dirlinks\fP for an analogous option for the
+receiving side.
+.IP
+\fB\-\-copy-dirlinks\fP applies to all symlinks to directories in the source. If
+you want to follow only a few specified symlinks, a trick you can use is to
+pass them as additional source args with a trailing slash, using
+\fB\-\-relative\fP to make the paths match up right. For example:
+.RS 4
+.IP
+.nf
+rsync -r --relative src/./ src/./follow-me/ dest/
+.fi
+.RE
+.IP
+This works because rsync calls \fBlstat\fP(2) on the source arg as given, and
+the trailing slash makes \fBlstat\fP(2) follow the symlink, giving rise to a
+directory in the file-list which overrides the symlink found during the
+scan of "src/./".
+.IP
+See the SYMBOLIC LINKS section for multi-option info.
+.IP "\fB\-\-keep-dirlinks\fP, \fB\-K\fP"
+This option causes the receiving side to treat a symlink to a directory as
+though it were a real directory, but only if it matches a real directory
+from the sender. Without this option, the receiver's symlink would be
+deleted and replaced with a real directory.
+.IP
+For example, suppose you transfer a directory "foo" that contains a file
+"file", but "foo" is a symlink to directory "bar" on the receiver. Without
+\fB\-\-keep-dirlinks\fP, the receiver deletes symlink "foo", recreates it as a
+directory, and receives the file into the new directory. With
+\fB\-\-keep-dirlinks\fP, the receiver keeps the symlink and "file" ends up in
+"bar".
+.IP
+One note of caution: if you use \fB\-\-keep-dirlinks\fP, you must trust all the
+symlinks in the copy or enable the \fB\-\-munge-links\fP option on the
+receiving side! If it is possible for an untrusted user to create their
+own symlink to any real directory, the user could then (on a subsequent
+copy) replace the symlink with a real directory and affect the content of
+whatever directory the symlink references. For backup copies, you are
+better off using something like a bind mount instead of a symlink to modify
+your receiving hierarchy.
+.IP
+See also \fB\-\-copy-dirlinks\fP for an analogous option for the sending
+side.
+.IP
+See the SYMBOLIC LINKS section for multi-option info.
+.IP "\fB\-\-hard-links\fP, \fB\-H\fP"
+This tells rsync to look for hard-linked files in the source and link
+together the corresponding files on the destination. Without this option,
+hard-linked files in the source are treated as though they were separate
+files.
+.IP
+This option does NOT necessarily ensure that the pattern of hard links on
+the destination exactly matches that on the source. Cases in which the
+destination may end up with extra hard links include the following:
+.IP
+.RS
+.IP o
+If the destination contains extraneous hard-links (more linking than what
+is present in the source file list), the copying algorithm will not break
+them explicitly. However, if one or more of the paths have content
+differences, the normal file-update process will break those extra links
+(unless you are using the \fB\-\-inplace\fP option).
+.IP o
+If you specify a \fB\-\-link-dest\fP directory that contains hard
+links, the linking of the destination files against the
+\fB\-\-link-dest\fP files can cause some paths in the destination to
+become linked together due to the \fB\-\-link-dest\fP associations.
+.RE
+.IP
+Note that rsync can only detect hard links between files that are inside
+the transfer set. If rsync updates a file that has extra hard-link
+connections to files outside the transfer, that linkage will be broken. If
+you are tempted to use the \fB\-\-inplace\fP option to avoid this breakage, be
+very careful that you know how your files are being updated so that you are
+certain that no unintended changes happen due to lingering hard links (and
+see the \fB\-\-inplace\fP option for more caveats).
+.IP
+If incremental recursion is active (see \fB\-\-inc-recursive\fP), rsync
+may transfer a missing hard-linked file before it finds that another link
+for that contents exists elsewhere in the hierarchy. This does not affect
+the accuracy of the transfer (i.e. which files are hard-linked together),
+just its efficiency (i.e. copying the data for a new, early copy of a
+hard-linked file that could have been found later in the transfer in
+another member of the hard-linked set of files). One way to avoid this
+inefficiency is to disable incremental recursion using the
+\fB\-\-no-inc-recursive\fP option.
+.IP "\fB\-\-perms\fP, \fB\-p\fP"
+This option causes the receiving rsync to set the destination permissions
+to be the same as the source permissions. (See also the \fB\-\-chmod\fP
+option for a way to modify what rsync considers to be the source
+permissions.)
+.IP
+When this option is \fIoff\fP, permissions are set as follows:
+.IP
+.RS
+.IP o
+Existing files (including updated files) retain their existing
+permissions, though the \fB\-\-executability\fP option might change
+just the execute permission for the file.
+.IP o
+New files get their "normal" permission bits set to the source file's
+permissions masked with the receiving directory's default permissions
+(either the receiving process's umask, or the permissions specified via
+the destination directory's default ACL), and their special permission
+bits disabled except in the case where a new directory inherits a setgid
+bit from its parent directory.
+.RE
+.IP
+Thus, when \fB\-\-perms\fP and \fB\-\-executability\fP are both disabled, rsync's
+behavior is the same as that of other file-copy utilities, such as \fBcp\fP(1)
+and \fBtar\fP(1).
+.IP
+In summary: to give destination files (both old and new) the source
+permissions, use \fB\-\-perms\fP. To give new files the destination-default
+permissions (while leaving existing files unchanged), make sure that the
+\fB\-\-perms\fP option is off and use \fB\-\-chmod=ugo=rwX\fP (which ensures
+that all non-masked bits get enabled). If you'd care to make this latter
+behavior easier to type, you could define a popt alias for it, such as
+putting this line in the file \fB~/.popt\fP (the following defines the \fB\-Z\fP
+option, and includes \fB\-\-no-g\fP to use the default group of the destination
+dir):
+.RS 4
+.IP
+.nf
+rsync alias -Z --no-p --no-g --chmod=ugo=rwX
+.fi
+.RE
+.IP
+You could then use this new option in a command such as this one:
+.RS 4
+.IP
+.nf
+rsync -avZ src/ dest/
+.fi
+.RE
+.IP
+(Caveat: make sure that \fB\-a\fP does not follow \fB\-Z\fP, or it will re-enable the
+two \fB\-\-no-*\fP options mentioned above.)
+.IP
+The preservation of the destination's setgid bit on newly-created
+directories when \fB\-\-perms\fP is off was added in rsync 2.6.7. Older rsync
+versions erroneously preserved the three special permission bits for
+newly-created files when \fB\-\-perms\fP was off, while overriding the
+destination's setgid bit setting on a newly-created directory. Default ACL
+observance was added to the ACL patch for rsync 2.6.7, so older (or
+non-ACL-enabled) rsyncs use the umask even if default ACLs are present.
+(Keep in mind that it is the version of the receiving rsync that affects
+these behaviors.)
+.IP "\fB\-\-executability\fP, \fB\-E\fP"
+This option causes rsync to preserve the executability (or
+non-executability) of regular files when \fB\-\-perms\fP is not enabled.
+A regular file is considered to be executable if at least one 'x' is turned
+on in its permissions. When an existing destination file's executability
+differs from that of the corresponding source file, rsync modifies the
+destination file's permissions as follows:
+.IP
+.RS
+.IP o
+To make a file non-executable, rsync turns off all its 'x' permissions.
+.IP o
+To make a file executable, rsync turns on each 'x' permission that has a
+corresponding 'r' permission enabled.
+.RE
+.IP
+If \fB\-\-perms\fP is enabled, this option is ignored.
+.IP "\fB\-\-acls\fP, \fB\-A\fP"
+This option causes rsync to update the destination ACLs to be the same as
+the source ACLs. The option also implies \fB\-\-perms\fP.
+.IP
+The source and destination systems must have compatible ACL entries for
+this option to work properly. See the \fB\-\-fake-super\fP option for a
+way to backup and restore ACLs that are not compatible.
+.IP "\fB\-\-xattrs\fP, \fB\-X\fP"
+This option causes rsync to update the destination extended attributes to
+be the same as the source ones.
+.IP
+For systems that support extended-attribute namespaces, a copy being done
+by a super-user copies all namespaces except system.*. A normal user only
+copies the user.* namespace. To be able to backup and restore non-user
+namespaces as a normal user, see the \fB\-\-fake-super\fP option.
+.IP
+The above name filtering can be overridden by using one or more filter
+options with the \fBx\fP modifier. When you specify an xattr-affecting
+filter rule, rsync requires that you do your own system/user filtering, as
+well as any additional filtering for what xattr names are copied and what
+names are allowed to be deleted. For example, to skip the system
+namespace, you could specify:
+.RS 4
+.IP
+.nf
+--filter='-x system.*'
+.fi
+.RE
+.IP
+To skip all namespaces except the user namespace, you could specify a
+negated-user match:
+.RS 4
+.IP
+.nf
+--filter='-x! user.*'
+.fi
+.RE
+.IP
+To prevent any attributes from being deleted, you could specify a
+receiver-only rule that excludes all names:
+.RS 4
+.IP
+.nf
+--filter='-xr *'
+.fi
+.RE
+.IP
+Note that the \fB\-X\fP option does not copy rsync's special xattr values (e.g.
+those used by \fB\-\-fake-super\fP) unless you repeat the option (e.g. \fB\-XX\fP).
+This "copy all xattrs" mode cannot be used with \fB\-\-fake-super\fP.
+.IP "\fB\-\-chmod=CHMOD\fP"
+This option tells rsync to apply one or more comma-separated "chmod" modes
+to the permission of the files in the transfer. The resulting value is
+treated as though it were the permissions that the sending side supplied
+for the file, which means that this option can seem to have no effect on
+existing files if \fB\-\-perms\fP is not enabled.
+.IP
+In addition to the normal parsing rules specified in the \fBchmod\fP(1)
+manpage, you can specify an item that should only apply to a directory by
+prefixing it with a 'D', or specify an item that should only apply to a
+file by prefixing it with a 'F'. For example, the following will ensure
+that all directories get marked set-gid, that no files are other-writable,
+that both are user-writable and group-writable, and that both have
+consistent executability across all bits:
+.RS 4
+.IP
+.nf
+--chmod=Dg+s,ug+w,Fo-w,+X
+.fi
+.RE
+.IP
+Using octal mode numbers is also allowed:
+.RS 4
+.IP
+.nf
+--chmod=D2775,F664
+.fi
+.RE
+.IP
+It is also legal to specify multiple \fB\-\-chmod\fP options, as each additional
+option is just appended to the list of changes to make.
+.IP
+See the \fB\-\-perms\fP and \fB\-\-executability\fP options for how the
+resulting permission value can be applied to the files in the transfer.
+.IP "\fB\-\-owner\fP, \fB\-o\fP"
+This option causes rsync to set the owner of the destination file to be the
+same as the source file, but only if the receiving rsync is being run as
+the super-user (see also the \fB\-\-super\fP and \fB\-\-fake-super\fP
+options). Without this option, the owner of new and/or transferred files
+are set to the invoking user on the receiving side.
+.IP
+The preservation of ownership will associate matching names by default, but
+may fall back to using the ID number in some circumstances (see also the
+\fB\-\-numeric-ids\fP option for a full discussion).
+.IP "\fB\-\-group\fP, \fB\-g\fP"
+This option causes rsync to set the group of the destination file to be the
+same as the source file. If the receiving program is not running as the
+super-user (or if \fB\-\-no-super\fP was specified), only groups that the
+invoking user on the receiving side is a member of will be preserved.
+Without this option, the group is set to the default group of the invoking
+user on the receiving side.
+.IP
+The preservation of group information will associate matching names by
+default, but may fall back to using the ID number in some circumstances
+(see also the \fB\-\-numeric-ids\fP option for a full discussion).
+.IP "\fB\-\-devices\fP"
+This option causes rsync to transfer character and block device files to
+the remote system to recreate these devices. If the receiving rsync is not
+being run as the super-user, rsync silently skips creating the device files
+(see also the \fB\-\-super\fP and \fB\-\-fake-super\fP options).
+.IP
+By default, rsync generates a "non-regular file" warning for each device
+file encountered when this option is not set. You can silence the warning
+by specifying \fB\-\-info=nonreg0\fP.
+.IP "\fB\-\-specials\fP"
+This option causes rsync to transfer special files, such as named sockets
+and fifos. If the receiving rsync is not being run as the super-user,
+rsync silently skips creating the special files (see also the
+\fB\-\-super\fP and \fB\-\-fake-super\fP options).
+.IP
+By default, rsync generates a "non-regular file" warning for each special
+file encountered when this option is not set. You can silence the warning
+by specifying \fB\-\-info=nonreg0\fP.
+.IP "\fB\-D\fP"
+The \fB\-D\fP option is equivalent to "\fB\-\-devices\fP
+\fB\-\-specials\fP".
+.IP "\fB\-\-copy-devices\fP"
+This tells rsync to treat a device on the sending side as a regular file,
+allowing it to be copied to a normal destination file (or another device
+if \fB\-\-write-devices\fP was also specified).
+.IP
+This option is refused by default by an rsync daemon.
+.IP "\fB\-\-write-devices\fP"
+This tells rsync to treat a device on the receiving side as a regular file,
+allowing the writing of file data into a device.
+.IP
+This option implies the \fB\-\-inplace\fP option.
+.IP
+Be careful using this, as you should know what devices are present on the
+receiving side of the transfer, especially when running rsync as root.
+.IP
+This option is refused by default by an rsync daemon.
+.IP "\fB\-\-times\fP, \fB\-t\fP"
+This tells rsync to transfer modification times along with the files and
+update them on the remote system. Note that if this option is not used,
+the optimization that excludes files that have not been modified cannot be
+effective; in other words, a missing \fB\-t\fP (or \fB\-a\fP) will cause the
+next transfer to behave as if it used \fB\-\-ignore-times\fP (\fB\-I\fP),
+causing all files to be updated (though rsync's delta-transfer algorithm
+will make the update fairly efficient if the files haven't actually
+changed, you're much better off using \fB\-t\fP).
+.IP
+A modern rsync that is using transfer protocol 30 or 31 conveys a modify
+time using up to 8-bytes. If rsync is forced to speak an older protocol
+(perhaps due to the remote rsync being older than 3.0.0) a modify time is
+conveyed using 4-bytes. Prior to 3.2.7, these shorter values could convey
+a date range of 13-Dec-1901 to 19-Jan-2038. Beginning with 3.2.7, these
+4-byte values now convey a date range of 1-Jan-1970 to 7-Feb-2106. If you
+have files dated older than 1970, make sure your rsync executables are
+upgraded so that the full range of dates can be conveyed.
+.IP "\fB\-\-atimes\fP, \fB\-U\fP"
+This tells rsync to set the access (use) times of the destination files to
+the same value as the source files.
+.IP
+If repeated, it also sets the \fB\-\-open-noatime\fP option, which can help you
+to make the sending and receiving systems have the same access times on the
+transferred files without needing to run rsync an extra time after a file
+is transferred.
+.IP
+Note that some older rsync versions (prior to 3.2.0) may have been built
+with a pre-release \fB\-\-atimes\fP patch that does not imply
+\fB\-\-open-noatime\fP when this option is repeated.
+.IP "\fB\-\-open-noatime\fP"
+This tells rsync to open files with the O_NOATIME flag (on systems that
+support it) to avoid changing the access time of the files that are being
+transferred. If your OS does not support the O_NOATIME flag then rsync
+will silently ignore this option. Note also that some filesystems are
+mounted to avoid updating the atime on read access even without the
+O_NOATIME flag being set.
+.IP "\fB\-\-crtimes\fP, \fB\-N,\fP"
+This tells rsync to set the create times (newness) of the destination
+files to the same value as the source files.
+.IP "\fB\-\-omit-dir-times\fP, \fB\-O\fP"
+This tells rsync to omit directories when it is preserving modification,
+access, and create times. If NFS is sharing the directories on the receiving
+side, it is a good idea to use \fB\-O\fP. This option is inferred if you use
+\fB\-\-backup\fP without \fB\-\-backup-dir\fP.
+.IP
+This option also has the side-effect of avoiding early creation of missing
+sub-directories when incremental recursion is enabled, as discussed in the
+\fB\-\-inc-recursive\fP section.
+.IP "\fB\-\-omit-link-times\fP, \fB\-J\fP"
+This tells rsync to omit symlinks when it is preserving modification,
+access, and create times.
+.IP "\fB\-\-super\fP"
+This tells the receiving side to attempt super-user activities even if the
+receiving rsync wasn't run by the super-user. These activities include:
+preserving users via the \fB\-\-owner\fP option, preserving all groups
+(not just the current user's groups) via the \fB\-\-group\fP option, and
+copying devices via the \fB\-\-devices\fP option. This is useful for
+systems that allow such activities without being the super-user, and also
+for ensuring that you will get errors if the receiving side isn't being run
+as the super-user. To turn off super-user activities, the super-user can
+use \fB\-\-no-super\fP.
+.IP "\fB\-\-fake-super\fP"
+When this option is enabled, rsync simulates super-user activities by
+saving/restoring the privileged attributes via special extended attributes
+that are attached to each file (as needed). This includes the file's owner
+and group (if it is not the default), the file's device info (device &
+special files are created as empty text files), and any permission bits
+that we won't allow to be set on the real file (e.g. the real file gets
+u-s,g-s,o-t for safety) or that would limit the owner's access (since the
+real super-user can always access/change a file, the files we create can
+always be accessed/changed by the creating user). This option also handles
+ACLs (if \fB\-\-acls\fP was specified) and non-user extended attributes
+(if \fB\-\-xattrs\fP was specified).
+.IP
+This is a good way to backup data without using a super-user, and to store
+ACLs from incompatible systems.
+.IP
+The \fB\-\-fake-super\fP option only affects the side where the option is used.
+To affect the remote side of a remote-shell connection, use the
+\fB\-\-remote-option\fP (\fB\-M\fP) option:
+.RS 4
+.IP
+.nf
+rsync -av -M--fake-super /src/ host:/dest/
+.fi
+.RE
+.IP
+For a local copy, this option affects both the source and the destination.
+If you wish a local copy to enable this option just for the destination
+files, specify \fB\-M\-\-fake-super\fP. If you wish a local copy to enable this
+option just for the source files, combine \fB\-\-fake-super\fP with \fB\-M\-\-super\fP.
+.IP
+This option is overridden by both \fB\-\-super\fP and \fB\-\-no-super\fP.
+.IP
+See also the \fBfake\ super\fP setting in the
+daemon's rsyncd.conf file.
+.IP "\fB\-\-sparse\fP, \fB\-S\fP"
+Try to handle sparse files efficiently so they take up less space on the
+destination. If combined with \fB\-\-inplace\fP the file created might
+not end up with sparse blocks with some combinations of kernel version
+and/or filesystem type. If \fB\-\-whole-file\fP is in effect (e.g. for a
+local copy) then it will always work because rsync truncates the file prior
+to writing out the updated version.
+.IP
+Note that versions of rsync older than 3.1.3 will reject the combination of
+\fB\-\-sparse\fP and \fB\-\-inplace\fP.
+.IP "\fB\-\-preallocate\fP"
+This tells the receiver to allocate each destination file to its eventual
+size before writing data to the file. Rsync will only use the real
+filesystem-level preallocation support provided by Linux's \fBfallocate\fP(2)
+system call or Cygwin's \fBposix_fallocate\fP(3), not the slow glibc
+implementation that writes a null byte into each block.
+.IP
+Without this option, larger files may not be entirely contiguous on the
+filesystem, but with this option rsync will probably copy more slowly. If
+the destination is not an extent-supporting filesystem (such as ext4, xfs,
+NTFS, etc.), this option may have no positive effect at all.
+.IP
+If combined with \fB\-\-sparse\fP, the file will only have sparse blocks
+(as opposed to allocated sequences of null bytes) if the kernel version and
+filesystem type support creating holes in the allocated data.
+.IP "\fB\-\-dry-run\fP, \fB\-n\fP"
+This makes rsync perform a trial run that doesn't make any changes (and
+produces mostly the same output as a real run). It is most commonly used
+in combination with the \fB\-\-verbose\fP (\fB\-v\fP) and/or
+\fB\-\-itemize-changes\fP (\fB\-i\fP) options to see what an rsync command is
+going to do before one actually runs it.
+.IP
+The output of \fB\-\-itemize-changes\fP is supposed to be exactly the
+same on a dry run and a subsequent real run (barring intentional trickery
+and system call failures); if it isn't, that's a bug. Other output should
+be mostly unchanged, but may differ in some areas. Notably, a dry run does
+not send the actual data for file transfers, so \fB\-\-progress\fP has no
+effect, the "bytes sent", "bytes received", "literal data", and "matched
+data" statistics are too small, and the "speedup" value is equivalent to a
+run where no file transfers were needed.
+.IP "\fB\-\-whole-file\fP, \fB\-W\fP"
+This option disables rsync's delta-transfer algorithm, which causes all
+transferred files to be sent whole. The transfer may be faster if this
+option is used when the bandwidth between the source and destination
+machines is higher than the bandwidth to disk (especially when the "disk"
+is actually a networked filesystem). This is the default when both the
+source and destination are specified as local paths, but only if no
+batch-writing option is in effect.
+.IP "\fB\-\-no-whole-file\fP, \fB\-\-no-W\fP"
+Disable whole-file updating when it is enabled by default for a local
+transfer. This usually slows rsync down, but it can be useful if you are
+trying to minimize the writes to the destination file (if combined with
+\fB\-\-inplace\fP) or for testing the checksum-based update algorithm.
+.IP
+See also the \fB\-\-whole-file\fP option.
+.IP "\fB\-\-checksum-choice=STR\fP, \fB\-\-cc=STR\fP"
+This option overrides the checksum algorithms. If one algorithm name is
+specified, it is used for both the transfer checksums and (assuming
+\fB\-\-checksum\fP is specified) the pre-transfer checksums. If two
+comma-separated names are supplied, the first name affects the transfer
+checksums, and the second name affects the pre-transfer checksums (\fB\-c\fP).
+.IP
+The checksum options that you may be able to use are:
+.IP
+.RS
+.IP o
+\fBauto\fP (the default automatic choice)
+.IP o
+\fBxxh128\fP
+.IP o
+\fBxxh3\fP
+.IP o
+\fBxxh64\fP (aka \fBxxhash\fP)
+.IP o
+\fBmd5\fP
+.IP o
+\fBmd4\fP
+.IP o
+\fBsha1\fP
+.IP o
+\fBnone\fP
+.RE
+.IP
+Run \fBrsync\ \-\-version\fP to see the default checksum list compiled into your
+version (which may differ from the list above).
+.IP
+If "none" is specified for the first (or only) name, the \fB\-\-whole-file\fP
+option is forced on and no checksum verification is performed on the
+transferred data. If "none" is specified for the second (or only) name,
+the \fB\-\-checksum\fP option cannot be used.
+.IP
+The "auto" option is the default, where rsync bases its algorithm choice on
+a negotiation between the client and the server as follows:
+.IP
+When both sides of the transfer are at least 3.2.0, rsync chooses the first
+algorithm in the client's list of choices that is also in the server's list
+of choices. If no common checksum choice is found, rsync exits with
+an error. If the remote rsync is too old to support checksum negotiation,
+a value is chosen based on the protocol version (which chooses between MD5
+and various flavors of MD4 based on protocol age).
+.IP
+The default order can be customized by setting the environment variable
+\fBRSYNC_CHECKSUM_LIST\fP to a space-separated list of acceptable checksum
+names. If the string contains a "\fB&\fP" character, it is separated into the
+"client string & server string", otherwise the same string applies to both.
+If the string (or string portion) contains no non-whitespace characters,
+the default checksum list is used. This method does not allow you to
+specify the transfer checksum separately from the pre-transfer checksum,
+and it discards "auto" and all unknown checksum names. A list with only
+invalid names results in a failed negotiation.
+.IP
+The use of the \fB\-\-checksum-choice\fP option overrides this environment list.
+.IP "\fB\-\-one-file-system\fP, \fB\-x\fP"
+This tells rsync to avoid crossing a filesystem boundary when recursing.
+This does not limit the user's ability to specify items to copy from
+multiple filesystems, just rsync's recursion through the hierarchy of each
+directory that the user specified, and also the analogous recursion on the
+receiving side during deletion. Also keep in mind that rsync treats a
+"bind" mount to the same device as being on the same filesystem.
+.IP
+If this option is repeated, rsync omits all mount-point directories from
+the copy. Otherwise, it includes an empty directory at each mount-point it
+encounters (using the attributes of the mounted directory because those of
+the underlying mount-point directory are inaccessible).
+.IP
+If rsync has been told to collapse symlinks (via \fB\-\-copy-links\fP or
+\fB\-\-copy-unsafe-links\fP), a symlink to a directory on another device
+is treated like a mount-point. Symlinks to non-directories are unaffected
+by this option.
+.IP "\fB\-\-ignore-non-existing\fP, \fB\-\-existing\fP"
+This tells rsync to skip creating files (including directories) that do not
+exist yet on the destination. If this option is combined with the
+\fB\-\-ignore-existing\fP option, no files will be updated (which can be
+useful if all you want to do is delete extraneous files).
+.IP
+This option is a TRANSFER RULE, so don't expect any
+exclude side effects.
+.IP "\fB\-\-ignore-existing\fP"
+This tells rsync to skip updating files that already exist on the
+destination (this does \fInot\fP ignore existing directories, or nothing would
+get done). See also \fB\-\-ignore-non-existing\fP.
+.IP
+This option is a TRANSFER RULE, so don't expect any
+exclude side effects.
+.IP
+This option can be useful for those doing backups using the
+\fB\-\-link-dest\fP option when they need to continue a backup run that
+got interrupted. Since a \fB\-\-link-dest\fP run is copied into a new
+directory hierarchy (when it is used properly), using [\fB\-\-ignore-existing\fP
+will ensure that the already-handled files don't get tweaked (which avoids
+a change in permissions on the hard-linked files). This does mean that
+this option is only looking at the existing files in the destination
+hierarchy itself.
+.IP
+When \fB\-\-info=skip2\fP is used rsync will output "FILENAME exists
+(INFO)" messages where the INFO indicates one of "type change", "sum
+change" (requires \fB\-c\fP), "file change" (based on the quick check),
+"attr change", or "uptodate". Using \fB\-\-info=skip1\fP (which is also
+implied by 2 \fB\-v\fP options) outputs the exists message without the
+INFO suffix.
+.IP "\fB\-\-remove-source-files\fP"
+This tells rsync to remove from the sending side the files (meaning
+non-directories) that are a part of the transfer and have been successfully
+duplicated on the receiving side.
+.IP
+Note that you should only use this option on source files that are
+quiescent. If you are using this to move files that show up in a
+particular directory over to another host, make sure that the finished
+files get renamed into the source directory, not directly written into it,
+so that rsync can't possibly transfer a file that is not yet fully written.
+If you can't first write the files into a different directory, you should
+use a naming idiom that lets rsync avoid transferring files that are not
+yet finished (e.g. name the file "foo.new" when it is written, rename it to
+"foo" when it is done, and then use the option \fB\-\-exclude='*.new'\fP
+for the rsync transfer).
+.IP
+Starting with 3.1.0, rsync will skip the sender-side removal (and output an
+error) if the file's size or modify time has not stayed unchanged.
+.IP
+Starting with 3.2.6, a local rsync copy will ensure that the sender does
+not remove a file the receiver just verified, such as when the user
+accidentally makes the source and destination directory the same path.
+.IP "\fB\-\-delete\fP"
+This tells rsync to delete extraneous files from the receiving side (ones
+that aren't on the sending side), but only for the directories that are
+being synchronized. You must have asked rsync to send the whole directory
+(e.g. "\fBdir\fP" or "\fBdir/\fP") without using a wildcard for the directory's
+contents (e.g. "\fBdir/*\fP") since the wildcard is expanded by the shell and
+rsync thus gets a request to transfer individual files, not the files'
+parent directory. Files that are excluded from the transfer are also
+excluded from being deleted unless you use the \fB\-\-delete-excluded\fP
+option or mark the rules as only matching on the sending side (see the
+include/exclude modifiers in the FILTER RULES section).
+.IP
+Prior to rsync 2.6.7, this option would have no effect unless
+\fB\-\-recursive\fP was enabled. Beginning with 2.6.7, deletions will
+also occur when \fB\-\-dirs\fP (\fB\-d\fP) is enabled, but only for
+directories whose contents are being copied.
+.IP
+This option can be dangerous if used incorrectly! It is a very good idea to
+first try a run using the \fB\-\-dry-run\fP (\fB\-n\fP) option to see what
+files are going to be deleted.
+.IP
+If the sending side detects any I/O errors, then the deletion of any files
+at the destination will be automatically disabled. This is to prevent
+temporary filesystem failures (such as NFS errors) on the sending side from
+causing a massive deletion of files on the destination. You can override
+this with the \fB\-\-ignore-errors\fP option.
+.IP
+The \fB\-\-delete\fP option may be combined with one of the \-\-delete-WHEN options
+without conflict, as well as \fB\-\-delete-excluded\fP. However, if none
+of the \fB\-\-delete-WHEN\fP options are specified, rsync will choose the
+\fB\-\-delete-during\fP algorithm when talking to rsync 3.0.0 or newer,
+or the \fB\-\-delete-before\fP algorithm when talking to an older rsync.
+See also \fB\-\-delete-delay\fP and \fB\-\-delete-after\fP.
+.IP "\fB\-\-delete-before\fP"
+Request that the file-deletions on the receiving side be done before the
+transfer starts. See \fB\-\-delete\fP (which is implied) for more
+details on file-deletion.
+.IP
+Deleting before the transfer is helpful if the filesystem is tight for
+space and removing extraneous files would help to make the transfer
+possible. However, it does introduce a delay before the start of the
+transfer, and this delay might cause the transfer to timeout (if
+\fB\-\-timeout\fP was specified). It also forces rsync to use the old,
+non-incremental recursion algorithm that requires rsync to scan all the
+files in the transfer into memory at once (see \fB\-\-recursive\fP).
+.IP "\fB\-\-delete-during\fP, \fB\-\-del\fP"
+Request that the file-deletions on the receiving side be done incrementally
+as the transfer happens. The per-directory delete scan is done right
+before each directory is checked for updates, so it behaves like a more
+efficient \fB\-\-delete-before\fP, including doing the deletions prior to
+any per-directory filter files being updated. This option was first added
+in rsync version 2.6.4. See \fB\-\-delete\fP (which is implied) for more
+details on file-deletion.
+.IP "\fB\-\-delete-delay\fP"
+Request that the file-deletions on the receiving side be computed during
+the transfer (like \fB\-\-delete-during\fP), and then removed after the
+transfer completes. This is useful when combined with
+\fB\-\-delay-updates\fP and/or \fB\-\-fuzzy\fP, and is more efficient
+than using \fB\-\-delete-after\fP (but can behave differently, since
+\fB\-\-delete-after\fP computes the deletions in a separate pass after
+all updates are done). If the number of removed files overflows an
+internal buffer, a temporary file will be created on the receiving side to
+hold the names (it is removed while open, so you shouldn't see it during
+the transfer). If the creation of the temporary file fails, rsync will try
+to fall back to using \fB\-\-delete-after\fP (which it cannot do if
+\fB\-\-recursive\fP is doing an incremental scan). See
+\fB\-\-delete\fP (which is implied) for more details on file-deletion.
+.IP "\fB\-\-delete-after\fP"
+Request that the file-deletions on the receiving side be done after the
+transfer has completed. This is useful if you are sending new
+per-directory merge files as a part of the transfer and you want their
+exclusions to take effect for the delete phase of the current transfer. It
+also forces rsync to use the old, non-incremental recursion algorithm that
+requires rsync to scan all the files in the transfer into memory at once
+(see \fB\-\-recursive\fP). See \fB\-\-delete\fP (which is implied) for
+more details on file-deletion.
+.IP
+See also the \fB\-\-delete-delay\fP option that might be a faster choice
+for those that just want the deletions to occur at the end of the transfer.
+.IP "\fB\-\-delete-excluded\fP"
+This option turns any unqualified exclude/include rules into server-side
+rules that do not affect the receiver's deletions.
+.IP
+By default, an exclude or include has both a server-side effect (to "hide"
+and "show" files when building the server's file list) and a receiver-side
+effect (to "protect" and "risk" files when deletions are occurring). Any
+rule that has no modifier to specify what sides it is executed on will be
+instead treated as if it were a server-side rule only, avoiding any
+"protect" effects of the rules.
+.IP
+A rule can still apply to both sides even with this option specified if the
+rule is given both the sender & receiver modifier letters (e.g., \fB\-f'\-sr\ foo'\fP). Receiver-side protect/risk rules can also be explicitly specified
+to limit the deletions. This saves you from having to edit a bunch of
+\fB\-f'\-\ foo'\fP rules into \fB\-f'\-s\ foo'\fP (aka \fB\-f'H\ foo'\fP) rules (not to mention
+the corresponding includes).
+.IP
+See the FILTER RULES section for more information. See
+\fB\-\-delete\fP (which is implied) for more details on deletion.
+.IP "\fB\-\-ignore-missing-args\fP"
+When rsync is first processing the explicitly requested source files (e.g.
+command-line arguments or \fB\-\-files-from\fP entries), it is normally
+an error if the file cannot be found. This option suppresses that error,
+and does not try to transfer the file. This does not affect subsequent
+vanished-file errors if a file was initially found to be present and later
+is no longer there.
+.IP "\fB\-\-delete-missing-args\fP"
+This option takes the behavior of the (implied)
+\fB\-\-ignore-missing-args\fP option a step farther: each missing arg
+will become a deletion request of the corresponding destination file on the
+receiving side (should it exist). If the destination file is a non-empty
+directory, it will only be successfully deleted if \fB\-\-force\fP or
+\fB\-\-delete\fP are in effect. Other than that, this option is
+independent of any other type of delete processing.
+.IP
+The missing source files are represented by special file-list entries which
+display as a "\fB*missing\fP" entry in the \fB\-\-list-only\fP output.
+.IP "\fB\-\-ignore-errors\fP"
+Tells \fB\-\-delete\fP to go ahead and delete files even when there are
+I/O errors.
+.IP "\fB\-\-force\fP"
+This option tells rsync to delete a non-empty directory when it is to be
+replaced by a non-directory. This is only relevant if deletions are not
+active (see \fB\-\-delete\fP for details).
+.IP
+Note for older rsync versions: \fB\-\-force\fP used to still be required when
+using \fB\-\-delete-after\fP, and it used to be non-functional unless the
+\fB\-\-recursive\fP option was also enabled.
+.IP "\fB\-\-max-delete=NUM\fP"
+This tells rsync not to delete more than NUM files or directories. If that
+limit is exceeded, all further deletions are skipped through the end of the
+transfer. At the end, rsync outputs a warning (including a count of the
+skipped deletions) and exits with an error code of 25 (unless some more
+important error condition also occurred).
+.IP
+Beginning with version 3.0.0, you may specify \fB\-\-max-delete=0\fP to be warned
+about any extraneous files in the destination without removing any of them.
+Older clients interpreted this as "unlimited", so if you don't know what
+version the client is, you can use the less obvious \fB\-\-max-delete=\-1\fP as a
+backward-compatible way to specify that no deletions be allowed (though
+really old versions didn't warn when the limit was exceeded).
+.IP "\fB\-\-max-size=SIZE\fP"
+This tells rsync to avoid transferring any file that is larger than the
+specified SIZE. A numeric value can be suffixed with a string to indicate
+the numeric units or left unqualified to specify bytes. Feel free to use a
+fractional value along with the units, such as \fB\-\-max-size=1.5m\fP.
+.IP
+This option is a TRANSFER RULE, so don't expect any
+exclude side effects.
+.IP
+The first letter of a units string can be \fBB\fP (bytes), \fBK\fP (kilo), \fBM\fP
+(mega), \fBG\fP (giga), \fBT\fP (tera), or \fBP\fP (peta). If the string is a single
+char or has "ib" added to it (e.g. "G" or "GiB") then the units are
+multiples of 1024. If you use a two-letter suffix that ends with a "B"
+(e.g. "kb") then you get units that are multiples of 1000. The string's
+letters can be any mix of upper and lower-case that you want to use.
+.IP
+Finally, if the string ends with either "+1" or "\-1", it is offset by one
+byte in the indicated direction. The largest possible value is usually
+\fB8192P-1\fP.
+.IP
+Examples: \fB\-\-max-size=1.5mb-1\fP is 1499999 bytes, and \fB\-\-max-size=2g+1\fP is
+2147483649 bytes.
+.IP
+Note that rsync versions prior to 3.1.0 did not allow \fB\-\-max-size=0\fP.
+.IP "\fB\-\-min-size=SIZE\fP"
+This tells rsync to avoid transferring any file that is smaller than the
+specified SIZE, which can help in not transferring small, junk files. See
+the \fB\-\-max-size\fP option for a description of SIZE and other info.
+.IP
+Note that rsync versions prior to 3.1.0 did not allow \fB\-\-min-size=0\fP.
+.IP "\fB\-\-max-alloc=SIZE\fP"
+By default rsync limits an individual malloc/realloc to about 1GB in size.
+For most people this limit works just fine and prevents a protocol error
+causing rsync to request massive amounts of memory. However, if you have
+many millions of files in a transfer, a large amount of server memory, and
+you don't want to split up your transfer into multiple parts, you can
+increase the per-allocation limit to something larger and rsync will
+consume more memory.
+.IP
+Keep in mind that this is not a limit on the total size of allocated
+memory. It is a sanity-check value for each individual allocation.
+.IP
+See the \fB\-\-max-size\fP option for a description of how SIZE can be
+specified. The default suffix if none is given is bytes.
+.IP
+Beginning in 3.2.3, a value of 0 specifies no limit.
+.IP
+You can set a default value using the environment variable
+\fBRSYNC_MAX_ALLOC\fP using the same SIZE values as supported by this
+option. If the remote rsync doesn't understand the \fB\-\-max-alloc\fP option,
+you can override an environmental value by specifying \fB\-\-max-alloc=1g\fP,
+which will make rsync avoid sending the option to the remote side (because
+"1G" is the default).
+.IP "\fB\-\-block-size=SIZE\fP, \fB\-B\fP"
+This forces the block size used in rsync's delta-transfer algorithm to a
+fixed value. It is normally selected based on the size of each file being
+updated. See the technical report for details.
+.IP
+Beginning in 3.2.3 the SIZE can be specified with a suffix as detailed in
+the \fB\-\-max-size\fP option. Older versions only accepted a byte count.
+.IP "\fB\-\-rsh=COMMAND\fP, \fB\-e\fP"
+This option allows you to choose an alternative remote shell program to use
+for communication between the local and remote copies of rsync. Typically,
+rsync is configured to use ssh by default, but you may prefer to use rsh on
+a local network.
+.IP
+If this option is used with \fB[user@]host::module/path\fP, then the remote
+shell \fICOMMAND\fP will be used to run an rsync daemon on the remote host, and
+all data will be transmitted through that remote shell connection, rather
+than through a direct socket connection to a running rsync daemon on the
+remote host. See the USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL
+CONNECTION section above.
+.IP
+Beginning with rsync 3.2.0, the \fBRSYNC_PORT\fP environment variable will
+be set when a daemon connection is being made via a remote-shell
+connection. It is set to 0 if the default daemon port is being assumed, or
+it is set to the value of the rsync port that was specified via either the
+\fB\-\-port\fP option or a non-empty port value in an \fBrsync://\fP URL.
+This allows the script to discern if a non-default port is being requested,
+allowing for things such as an SSL or stunnel helper script to connect to a
+default or alternate port.
+.IP
+Command-line arguments are permitted in COMMAND provided that COMMAND is
+presented to rsync as a single argument. You must use spaces (not tabs or
+other whitespace) to separate the command and args from each other, and you
+can use single- and/or double-quotes to preserve spaces in an argument (but
+not backslashes). Note that doubling a single-quote inside a single-quoted
+string gives you a single-quote; likewise for double-quotes (though you
+need to pay attention to which quotes your shell is parsing and which
+quotes rsync is parsing). Some examples:
+.RS 4
+.IP
+.nf
+-e 'ssh -p 2234'
+-e 'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"'
+.fi
+.RE
+.IP
+(Note that ssh users can alternately customize site-specific connect
+options in their .ssh/config file.)
+.IP
+You can also choose the remote shell program using the \fBRSYNC_RSH\fP
+environment variable, which accepts the same range of values as \fB\-e\fP.
+.IP
+See also the \fB\-\-blocking-io\fP option which is affected by this
+option.
+.IP "\fB\-\-rsync-path=PROGRAM\fP"
+Use this to specify what program is to be run on the remote machine to
+start-up rsync. Often used when rsync is not in the default remote-shell's
+path (e.g. \fB\-\-rsync-path=/usr/local/bin/rsync\fP). Note that PROGRAM is run
+with the help of a shell, so it can be any program, script, or command
+sequence you'd care to run, so long as it does not corrupt the standard-in
+& standard-out that rsync is using to communicate.
+.IP
+One tricky example is to set a different default directory on the remote
+machine for use with the \fB\-\-relative\fP option. For instance:
+.RS 4
+.IP
+.nf
+rsync -avR --rsync-path="cd /a/b && rsync" host:c/d /e/
+.fi
+.RE
+.IP "\fB\-\-remote-option=OPTION\fP, \fB\-M\fP"
+This option is used for more advanced situations where you want certain
+effects to be limited to one side of the transfer only. For instance, if
+you want to pass \fB\-\-log-file=FILE\fP and \fB\-\-fake-super\fP to
+the remote system, specify it like this:
+.RS 4
+.IP
+.nf
+rsync -av -M --log-file=foo -M--fake-super src/ dest/
+.fi
+.RE
+.IP
+If you want to have an option affect only the local side of a transfer when
+it normally affects both sides, send its negation to the remote side. Like
+this:
+.RS 4
+.IP
+.nf
+rsync -av -x -M--no-x src/ dest/
+.fi
+.RE
+.IP
+Be cautious using this, as it is possible to toggle an option that will
+cause rsync to have a different idea about what data to expect next over
+the socket, and that will make it fail in a cryptic fashion.
+.IP
+Note that you should use a separate \fB\-M\fP option for each remote option you
+want to pass. On older rsync versions, the presence of any spaces in the
+remote-option arg could cause it to be split into separate remote args, but
+this requires the use of \fB\-\-old-args\fP in a modern rsync.
+.IP
+When performing a local transfer, the "local" side is the sender and the
+"remote" side is the receiver.
+.IP
+Note some versions of the popt option-parsing library have a bug in them
+that prevents you from using an adjacent arg with an equal in it next to a
+short option letter (e.g. \fB\-M\-\-log-file=/tmp/foo\fP). If this bug affects
+your version of popt, you can use the version of popt that is included with
+rsync.
+.IP "\fB\-\-cvs-exclude\fP, \fB\-C\fP"
+This is a useful shorthand for excluding a broad range of files that you
+often don't want to transfer between systems. It uses a similar algorithm
+to CVS to determine if a file should be ignored.
+.IP
+The exclude list is initialized to exclude the following items (these
+initial items are marked as perishable\ \-\- see the FILTER RULES
+section):
+.RS 4
+.IP
+\fBRCS\fP
+\fBSCCS\fP
+\fBCVS\fP
+\fBCVS.adm\fP
+\fBRCSLOG\fP
+\fBcvslog.*\fP
+\fBtags\fP
+\fBTAGS\fP
+\fB.make.state\fP
+\fB.nse_depinfo\fP
+\fB*~\fP
+\fB#*\fP
+\fB.#*\fP
+\fB,*\fP
+\fB_$*\fP
+\fB*$\fP
+\fB*.old\fP
+\fB*.bak\fP
+\fB*.BAK\fP
+\fB*.orig\fP
+\fB*.rej\fP
+\fB.del-*\fP
+\fB*.a\fP
+\fB*.olb\fP
+\fB*.o\fP
+\fB*.obj\fP
+\fB*.so\fP
+\fB*.exe\fP
+\fB*.Z\fP
+\fB*.elc\fP
+\fB*.ln\fP
+\fBcore\fP
+\fB.svn/\fP
+\fB.git/\fP
+\fB.hg/\fP
+\fB.bzr/\fP
+.RE
+.IP
+then, files listed in a $HOME/.cvsignore are added to the list and any
+files listed in the CVSIGNORE environment variable (all cvsignore names are
+delimited by whitespace).
+.IP
+Finally, any file is ignored if it is in the same directory as a .cvsignore
+file and matches one of the patterns listed therein. Unlike rsync's
+filter/exclude files, these patterns are split on whitespace. See the
+\fBcvs\fP(1) manual for more information.
+.IP
+If you're combining \fB\-C\fP with your own \fB\-\-filter\fP rules, you should
+note that these CVS excludes are appended at the end of your own rules,
+regardless of where the \fB\-C\fP was placed on the command-line. This makes
+them a lower priority than any rules you specified explicitly. If you want
+to control where these CVS excludes get inserted into your filter rules,
+you should omit the \fB\-C\fP as a command-line option and use a combination of
+\fB\-\-filter=:C\fP and \fB\-\-filter=\-C\fP (either on your
+command-line or by putting the ":C" and "\-C" rules into a filter file with
+your other rules). The first option turns on the per-directory scanning
+for the .cvsignore file. The second option does a one-time import of the
+CVS excludes mentioned above.
+.IP "\fB\-\-filter=RULE\fP, \fB\-f\fP"
+This option allows you to add rules to selectively exclude certain files
+from the list of files to be transferred. This is most useful in
+combination with a recursive transfer.
+.IP
+You may use as many \fB\-\-filter\fP options on the command line as you like to
+build up the list of files to exclude. If the filter contains whitespace,
+be sure to quote it so that the shell gives the rule to rsync as a single
+argument. The text below also mentions that you can use an underscore to
+replace the space that separates a rule from its arg.
+.IP
+See the FILTER RULES section for detailed information on this option.
+.IP "\fB\-F\fP"
+The \fB\-F\fP option is a shorthand for adding two \fB\-\-filter\fP rules to
+your command. The first time it is used is a shorthand for this rule:
+.RS 4
+.IP
+.nf
+--filter='dir-merge /.rsync-filter'
+.fi
+.RE
+.IP
+This tells rsync to look for per-directory .rsync-filter files that have
+been sprinkled through the hierarchy and use their rules to filter the
+files in the transfer. If \fB\-F\fP is repeated, it is a shorthand for this
+rule:
+.RS 4
+.IP
+.nf
+--filter='exclude .rsync-filter'
+.fi
+.RE
+.IP
+This filters out the .rsync-filter files themselves from the transfer.
+.IP
+See the FILTER RULES section for detailed information on how these
+options work.
+.IP "\fB\-\-exclude=PATTERN\fP"
+This option is a simplified form of the \fB\-\-filter\fP option that
+specifies an exclude rule and does not allow the full rule-parsing syntax
+of normal filter rules. This is equivalent to specifying \fB\-f'\-\ PATTERN'\fP.
+.IP
+See the FILTER RULES section for detailed information on this option.
+.IP "\fB\-\-exclude-from=FILE\fP"
+This option is related to the \fB\-\-exclude\fP option, but it specifies
+a FILE that contains exclude patterns (one per line). Blank lines in the
+file are ignored, as are whole-line comments that start with '\fB;\fP' or '\fB#\fP'
+(filename rules that contain those characters are unaffected).
+.IP
+If a line begins with "\fB\-\ \fP" (dash, space) or "\fB+\ \fP" (plus, space), then
+the type of rule is being explicitly specified as an exclude or an include
+(respectively). Any rules without such a prefix are taken to be an exclude.
+.IP
+If a line consists of just "\fB!\fP", then the current filter rules are cleared
+before adding any further rules.
+.IP
+If \fIFILE\fP is '\fB\-\fP', the list will be read from standard input.
+.IP "\fB\-\-include=PATTERN\fP"
+This option is a simplified form of the \fB\-\-filter\fP option that
+specifies an include rule and does not allow the full rule-parsing syntax
+of normal filter rules. This is equivalent to specifying \fB\-f'+\ PATTERN'\fP.
+.IP
+See the FILTER RULES section for detailed information on this option.
+.IP "\fB\-\-include-from=FILE\fP"
+This option is related to the \fB\-\-include\fP option, but it specifies
+a FILE that contains include patterns (one per line). Blank lines in the
+file are ignored, as are whole-line comments that start with '\fB;\fP' or '\fB#\fP'
+(filename rules that contain those characters are unaffected).
+.IP
+If a line begins with "\fB\-\ \fP" (dash, space) or "\fB+\ \fP" (plus, space), then
+the type of rule is being explicitly specified as an exclude or an include
+(respectively). Any rules without such a prefix are taken to be an include.
+.IP
+If a line consists of just "\fB!\fP", then the current filter rules are cleared
+before adding any further rules.
+.IP
+If \fIFILE\fP is '\fB\-\fP', the list will be read from standard input.
+.IP "\fB\-\-files-from=FILE\fP"
+Using this option allows you to specify the exact list of files to transfer
+(as read from the specified FILE or '\fB\-\fP' for standard input). It also
+tweaks the default behavior of rsync to make transferring just the
+specified files and directories easier:
+.IP
+.RS
+.IP o
+The \fB\-\-relative\fP (\fB\-R\fP) option is implied, which preserves the
+path information that is specified for each item in the file (use
+\fB\-\-no-relative\fP or \fB\-\-no-R\fP if you want to turn that off).
+.IP o
+The \fB\-\-dirs\fP (\fB\-d\fP) option is implied, which will create
+directories specified in the list on the destination rather than noisily
+skipping them (use \fB\-\-no-dirs\fP or \fB\-\-no-d\fP if you want to turn that off).
+.IP o
+The \fB\-\-archive\fP (\fB\-a\fP) option's behavior does not imply
+\fB\-\-recursive\fP (\fB\-r\fP), so specify it explicitly, if you want it.
+.IP o
+These side-effects change the default state of rsync, so the position of
+the \fB\-\-files-from\fP option on the command-line has no bearing on how other
+options are parsed (e.g. \fB\-a\fP works the same before or after
+\fB\-\-files-from\fP, as does \fB\-\-no-R\fP and all other options).
+.RE
+.IP
+The filenames that are read from the FILE are all relative to the source
+dir\ \-\- any leading slashes are removed and no ".." references are allowed
+to go higher than the source dir. For example, take this command:
+.RS 4
+.IP
+.nf
+rsync -a --files-from=/tmp/foo /usr remote:/backup
+.fi
+.RE
+.IP
+If /tmp/foo contains the string "bin" (or even "/bin"), the /usr/bin
+directory will be created as /backup/bin on the remote host. If it
+contains "bin/" (note the trailing slash), the immediate contents of the
+directory would also be sent (without needing to be explicitly mentioned in
+the file\ \-\- this began in version 2.6.4). In both cases, if the
+\fB\-r\fP option was enabled, that dir's entire hierarchy would also be
+transferred (keep in mind that \fB\-r\fP needs to be specified
+explicitly with \fB\-\-files-from\fP, since it is not implied by \fB\-a\fP.
+Also note that the effect of the (enabled by default) \fB\-r\fP option
+is to duplicate only the path info that is read from the file\ \-\- it does
+not force the duplication of the source-spec path (/usr in this case).
+.IP
+In addition, the \fB\-\-files-from\fP file can be read from the remote host
+instead of the local host if you specify a "host:" in front of the file
+(the host must match one end of the transfer). As a short-cut, you can
+specify just a prefix of ":" to mean "use the remote end of the transfer".
+For example:
+.RS 4
+.IP
+.nf
+rsync -a --files-from=:/path/file-list src:/ /tmp/copy
+.fi
+.RE
+.IP
+This would copy all the files specified in the /path/file-list file that
+was located on the remote "src" host.
+.IP
+If the \fB\-\-iconv\fP and \fB\-\-secluded-args\fP options are specified
+and the \fB\-\-files-from\fP filenames are being sent from one host to another,
+the filenames will be translated from the sending host's charset to the
+receiving host's charset.
+.IP
+NOTE: sorting the list of files in the \fB\-\-files-from\fP input helps rsync to
+be more efficient, as it will avoid re-visiting the path elements that are
+shared between adjacent entries. If the input is not sorted, some path
+elements (implied directories) may end up being scanned multiple times, and
+rsync will eventually unduplicate them after they get turned into file-list
+elements.
+.IP "\fB\-\-from0\fP, \fB\-0\fP"
+This tells rsync that the rules/filenames it reads from a file are
+terminated by a null ('\\0') character, not a NL, CR, or CR+LF. This
+affects \fB\-\-exclude-from\fP, \fB\-\-include-from\fP,
+\fB\-\-files-from\fP, and any merged files specified in a
+\fB\-\-filter\fP rule. It does not affect \fB\-\-cvs-exclude\fP (since
+all names read from a .cvsignore file are split on whitespace).
+.IP "\fB\-\-old-args\fP"
+This option tells rsync to stop trying to protect the arg values on the
+remote side from unintended word-splitting or other misinterpretation.
+It also allows the client to treat an empty arg as a "." instead of
+generating an error.
+.IP
+The default in a modern rsync is for "shell-active" characters (including
+spaces) to be backslash-escaped in the args that are sent to the remote
+shell. The wildcard characters \fB*\fP, \fB?\fP, \fB[\fP, & \fB]\fP are not escaped in
+filename args (allowing them to expand into multiple filenames) while being
+protected in option args, such as \fB\-\-usermap\fP.
+.IP
+If you have a script that wants to use old-style arg splitting in its
+filenames, specify this option once. If the remote shell has a problem
+with any backslash escapes at all, specify this option twice.
+.IP
+You may also control this setting via the \fBRSYNC_OLD_ARGS\fP environment
+variable. If it has the value "1", rsync will default to a single-option
+setting. If it has the value "2" (or more), rsync will default to a
+repeated-option setting. If it is "0", you'll get the default escaping
+behavior. The environment is always overridden by manually specified
+positive or negative options (the negative is \fB\-\-no-old-args\fP).
+.IP
+Note that this option also disables the extra safety check added in 3.2.5
+that ensures that a remote sender isn't including extra top-level items in
+the file-list that you didn't request. This side-effect is necessary
+because we can't know for sure what names to expect when the remote shell
+is interpreting the args.
+.IP
+This option conflicts with the \fB\-\-secluded-args\fP option.
+.IP "\fB\-\-secluded-args\fP, \fB\-s\fP"
+This option sends all filenames and most options to the remote rsync via
+the protocol (not the remote shell command line) which avoids letting the
+remote shell modify them. Wildcards are expanded on the remote host by
+rsync instead of a shell.
+.IP
+This is similar to the default backslash-escaping of args that was added
+in 3.2.4 (see \fB\-\-old-args\fP) in that it prevents things like space
+splitting and unwanted special-character side-effects. However, it has the
+drawbacks of being incompatible with older rsync versions (prior to 3.0.0)
+and of being refused by restricted shells that want to be able to inspect
+all the option values for safety.
+.IP
+This option is useful for those times that you need the argument's
+character set to be converted for the remote host, if the remote shell is
+incompatible with the default backslash-escpaing method, or there is some
+other reason that you want the majority of the options and arguments to
+bypass the command-line of the remote shell.
+.IP
+If you combine this option with \fB\-\-iconv\fP, the args related to the
+remote side will be translated from the local to the remote character-set.
+The translation happens before wild-cards are expanded. See also the
+\fB\-\-files-from\fP option.
+.IP
+You may also control this setting via the \fBRSYNC_PROTECT_ARGS\fP
+environment variable. If it has a non-zero value, this setting will be
+enabled by default, otherwise it will be disabled by default. Either state
+is overridden by a manually specified positive or negative version of this
+option (note that \fB\-\-no-s\fP and \fB\-\-no-secluded-args\fP are the negative
+versions). This environment variable is also superseded by a non-zero
+\fBRSYNC_OLD_ARGS\fP export.
+.IP
+This option conflicts with the \fB\-\-old-args\fP option.
+.IP
+This option used to be called \fB\-\-protect-args\fP (before 3.2.6) and that
+older name can still be used (though specifying it as \fB\-s\fP is always the
+easiest and most compatible choice).
+.IP "\fB\-\-trust-sender\fP"
+This option disables two extra validation checks that a local client
+performs on the file list generated by a remote sender. This option should
+only be used if you trust the sender to not put something malicious in the
+file list (something that could possibly be done via a modified rsync, a
+modified shell, or some other similar manipulation).
+.IP
+Normally, the rsync client (as of version 3.2.5) runs two extra validation
+checks when pulling files from a remote rsync:
+.IP
+.RS
+.IP o
+It verifies that additional arg items didn't get added at the top of the
+transfer.
+.IP o
+It verifies that none of the items in the file list are names that should
+have been excluded (if filter rules were specified).
+.RE
+.IP
+Note that various options can turn off one or both of these checks if the
+option interferes with the validation. For instance:
+.IP
+.RS
+.IP o
+Using a per-directory filter file reads filter rules that only the server
+knows about, so the filter checking is disabled.
+.IP o
+Using the \fB\-\-old-args\fP option allows the sender to manipulate the
+requested args, so the arg checking is disabled.
+.IP o
+Reading the files-from list from the server side means that the client
+doesn't know the arg list, so the arg checking is disabled.
+.IP o
+Using \fB\-\-read-batch\fP disables both checks since the batch file's
+contents will have been verified when it was created.
+.RE
+.IP
+This option may help an under-powered client server if the extra pattern
+matching is slowing things down on a huge transfer. It can also be used to
+work around a currently-unknown bug in the verification logic for a transfer
+from a trusted sender.
+.IP
+When using this option it is a good idea to specify a dedicated destination
+directory, as discussed in the MULTI-HOST SECURITY section.
+.IP "\fB\-\-copy-as=USER[:GROUP]\fP"
+This option instructs rsync to use the USER and (if specified after a
+colon) the GROUP for the copy operations. This only works if the user that
+is running rsync has the ability to change users. If the group is not
+specified then the user's default groups are used.
+.IP
+This option can help to reduce the risk of an rsync being run as root into
+or out of a directory that might have live changes happening to it and you
+want to make sure that root-level read or write actions of system files are
+not possible. While you could alternatively run all of rsync as the
+specified user, sometimes you need the root-level host-access credentials
+to be used, so this allows rsync to drop root for the copying part of the
+operation after the remote-shell or daemon connection is established.
+.IP
+The option only affects one side of the transfer unless the transfer is
+local, in which case it affects both sides. Use the
+\fB\-\-remote-option\fP to affect the remote side, such as
+\fB\-M\-\-copy-as=joe\fP. For a local transfer, the lsh (or lsh.sh) support file
+provides a local-shell helper script that can be used to allow a
+"localhost:" or "lh:" host-spec to be specified without needing to setup
+any remote shells, allowing you to specify remote options that affect the
+side of the transfer that is using the host-spec (and using hostname "lh"
+avoids the overriding of the remote directory to the user's home dir).
+.IP
+For example, the following rsync writes the local files as user "joe":
+.RS 4
+.IP
+.nf
+sudo rsync -aiv --copy-as=joe host1:backups/joe/ /home/joe/
+.fi
+.RE
+.IP
+This makes all files owned by user "joe", limits the groups to those that
+are available to that user, and makes it impossible for the joe user to do
+a timed exploit of the path to induce a change to a file that the joe user
+has no permissions to change.
+.IP
+The following command does a local copy into the "dest/" dir as user "joe"
+(assuming you've installed support/lsh into a dir on your $PATH):
+.RS 4
+.IP
+.nf
+sudo rsync -aive lsh -M--copy-as=joe src/ lh:dest/
+.fi
+.RE
+.IP "\fB\-\-temp-dir=DIR\fP, \fB\-T\fP"
+This option instructs rsync to use DIR as a scratch directory when creating
+temporary copies of the files transferred on the receiving side. The
+default behavior is to create each temporary file in the same directory as
+the associated destination file. Beginning with rsync 3.1.1, the temp-file
+names inside the specified DIR will not be prefixed with an extra dot
+(though they will still have a random suffix added).
+.IP
+This option is most often used when the receiving disk partition does not
+have enough free space to hold a copy of the largest file in the transfer.
+In this case (i.e. when the scratch directory is on a different disk
+partition), rsync will not be able to rename each received temporary file
+over the top of the associated destination file, but instead must copy it
+into place. Rsync does this by copying the file over the top of the
+destination file, which means that the destination file will contain
+truncated data during this copy. If this were not done this way (even if
+the destination file were first removed, the data locally copied to a
+temporary file in the destination directory, and then renamed into place)
+it would be possible for the old file to continue taking up disk space (if
+someone had it open), and thus there might not be enough room to fit the
+new version on the disk at the same time.
+.IP
+If you are using this option for reasons other than a shortage of disk
+space, you may wish to combine it with the \fB\-\-delay-updates\fP
+option, which will ensure that all copied files get put into subdirectories
+in the destination hierarchy, awaiting the end of the transfer. If you
+don't have enough room to duplicate all the arriving files on the
+destination partition, another way to tell rsync that you aren't overly
+concerned about disk space is to use the \fB\-\-partial-dir\fP option
+with a relative path; because this tells rsync that it is OK to stash off a
+copy of a single file in a subdir in the destination hierarchy, rsync will
+use the partial-dir as a staging area to bring over the copied file, and
+then rename it into place from there. (Specifying a \fB\-\-partial-dir\fP
+with an absolute path does not have this side-effect.)
+.IP "\fB\-\-fuzzy\fP, \fB\-y\fP"
+This option tells rsync that it should look for a basis file for any
+destination file that is missing. The current algorithm looks in the same
+directory as the destination file for either a file that has an identical
+size and modified-time, or a similarly-named file. If found, rsync uses
+the fuzzy basis file to try to speed up the transfer.
+.IP
+If the option is repeated, the fuzzy scan will also be done in any matching
+alternate destination directories that are specified via
+\fB\-\-compare-dest\fP, \fB\-\-copy-dest\fP, or \fB\-\-link-dest\fP.
+.IP
+Note that the use of the \fB\-\-delete\fP option might get rid of any
+potential fuzzy-match files, so either use \fB\-\-delete-after\fP or
+specify some filename exclusions if you need to prevent this.
+.IP "\fB\-\-compare-dest=DIR\fP"
+This option instructs rsync to use \fIDIR\fP on the destination machine as an
+additional hierarchy to compare destination files against doing transfers
+(if the files are missing in the destination directory). If a file is
+found in \fIDIR\fP that is identical to the sender's file, the file will NOT be
+transferred to the destination directory. This is useful for creating a
+sparse backup of just files that have changed from an earlier backup. This
+option is typically used to copy into an empty (or newly created)
+directory.
+.IP
+Beginning in version 2.6.4, multiple \fB\-\-compare-dest\fP directories may be
+provided, which will cause rsync to search the list in the order specified
+for an exact match. If a match is found that differs only in attributes, a
+local copy is made and the attributes updated. If a match is not found, a
+basis file from one of the \fIDIRs\fP will be selected to try to speed up the
+transfer.
+.IP
+If \fIDIR\fP is a relative path, it is relative to the destination directory.
+See also \fB\-\-copy-dest\fP and \fB\-\-link-dest\fP.
+.IP
+NOTE: beginning with version 3.1.0, rsync will remove a file from a
+non-empty destination hierarchy if an exact match is found in one of the
+compare-dest hierarchies (making the end result more closely match a fresh
+copy).
+.IP "\fB\-\-copy-dest=DIR\fP"
+This option behaves like \fB\-\-compare-dest\fP, but rsync will also copy
+unchanged files found in \fIDIR\fP to the destination directory using a local
+copy. This is useful for doing transfers to a new destination while
+leaving existing files intact, and then doing a flash-cutover when all
+files have been successfully transferred.
+.IP
+Multiple \fB\-\-copy-dest\fP directories may be provided, which will cause rsync
+to search the list in the order specified for an unchanged file. If a
+match is not found, a basis file from one of the \fIDIRs\fP will be selected to
+try to speed up the transfer.
+.IP
+If \fIDIR\fP is a relative path, it is relative to the destination directory.
+See also \fB\-\-compare-dest\fP and \fB\-\-link-dest\fP.
+.IP "\fB\-\-link-dest=DIR\fP"
+This option behaves like \fB\-\-copy-dest\fP, but unchanged files are
+hard linked from \fIDIR\fP to the destination directory. The files must be
+identical in all preserved attributes (e.g. permissions, possibly
+ownership) in order for the files to be linked together. An example:
+.RS 4
+.IP
+.nf
+rsync -av --link-dest=$PWD/prior_dir host:src_dir/ new_dir/
+.fi
+.RE
+.IP
+If files aren't linking, double-check their attributes. Also check if
+some attributes are getting forced outside of rsync's control, such a mount
+option that squishes root to a single user, or mounts a removable drive
+with generic ownership (such as OS X's "Ignore ownership on this volume"
+option).
+.IP
+Beginning in version 2.6.4, multiple \fB\-\-link-dest\fP directories may be
+provided, which will cause rsync to search the list in the order specified
+for an exact match (there is a limit of 20 such directories). If a match
+is found that differs only in attributes, a local copy is made and the
+attributes updated. If a match is not found, a basis file from one of the
+\fIDIRs\fP will be selected to try to speed up the transfer.
+.IP
+This option works best when copying into an empty destination hierarchy, as
+existing files may get their attributes tweaked, and that can affect
+alternate destination files via hard-links. Also, itemizing of changes can
+get a bit muddled. Note that prior to version 3.1.0, an
+alternate-directory exact match would never be found (nor linked into the
+destination) when a destination file already exists.
+.IP
+Note that if you combine this option with \fB\-\-ignore-times\fP, rsync will not
+link any files together because it only links identical files together as a
+substitute for transferring the file, never as an additional check after
+the file is updated.
+.IP
+If \fIDIR\fP is a relative path, it is relative to the destination directory.
+See also \fB\-\-compare-dest\fP and \fB\-\-copy-dest\fP.
+.IP
+Note that rsync versions prior to 2.6.1 had a bug that could prevent
+\fB\-\-link-dest\fP from working properly for a non-super-user when
+\fB\-\-owner\fP (\fB\-o\fP) was specified (or implied). You can work-around
+this bug by avoiding the \fB\-o\fP option (or using \fB\-\-no-o\fP) when sending to an
+old rsync.
+.IP "\fB\-\-compress\fP, \fB\-z\fP"
+With this option, rsync compresses the file data as it is sent to the
+destination machine, which reduces the amount of data being transmitted\ \-\-
+something that is useful over a slow connection.
+.IP
+Rsync supports multiple compression methods and will choose one for you
+unless you force the choice using the \fB\-\-compress-choice\fP (\fB\-\-zc\fP)
+option.
+.IP
+Run \fBrsync\ \-\-version\fP to see the default compress list compiled into your
+version.
+.IP
+When both sides of the transfer are at least 3.2.0, rsync chooses the first
+algorithm in the client's list of choices that is also in the server's list
+of choices. If no common compress choice is found, rsync exits with
+an error. If the remote rsync is too old to support checksum negotiation,
+its list is assumed to be "zlib".
+.IP
+The default order can be customized by setting the environment variable
+\fBRSYNC_COMPRESS_LIST\fP to a space-separated list of acceptable
+compression names. If the string contains a "\fB&\fP" character, it is
+separated into the "client string & server string", otherwise the same
+string applies to both. If the string (or string portion) contains no
+non-whitespace characters, the default compress list is used. Any unknown
+compression names are discarded from the list, but a list with only invalid
+names results in a failed negotiation.
+.IP
+There are some older rsync versions that were configured to reject a \fB\-z\fP
+option and require the use of \fB\-zz\fP because their compression library was
+not compatible with the default zlib compression method. You can usually
+ignore this weirdness unless the rsync server complains and tells you to
+specify \fB\-zz\fP.
+.IP "\fB\-\-compress-choice=STR\fP, \fB\-\-zc=STR\fP"
+This option can be used to override the automatic negotiation of the
+compression algorithm that occurs when \fB\-\-compress\fP is used. The
+option implies \fB\-\-compress\fP unless "none" was specified, which
+instead implies \fB\-\-no-compress\fP.
+.IP
+The compression options that you may be able to use are:
+.IP
+.RS
+.IP o
+\fBzstd\fP
+.IP o
+\fBlz4\fP
+.IP o
+\fBzlibx\fP
+.IP o
+\fBzlib\fP
+.IP o
+\fBnone\fP
+.RE
+.IP
+Run \fBrsync\ \-\-version\fP to see the default compress list compiled into your
+version (which may differ from the list above).
+.IP
+Note that if you see an error about an option named \fB\-\-old-compress\fP or
+\fB\-\-new-compress\fP, this is rsync trying to send the \fB\-\-compress-choice=zlib\fP
+or \fB\-\-compress-choice=zlibx\fP option in a backward-compatible manner that
+more rsync versions understand. This error indicates that the older rsync
+version on the server will not allow you to force the compression type.
+.IP
+Note that the "zlibx" compression algorithm is just the "zlib" algorithm
+with matched data excluded from the compression stream (to try to make it
+more compatible with an external zlib implementation).
+.IP "\fB\-\-compress-level=NUM\fP, \fB\-\-zl=NUM\fP"
+Explicitly set the compression level to use (see \fB\-\-compress\fP,
+\fB\-z\fP) instead of letting it default. The \fB\-\-compress\fP option is
+implied as long as the level chosen is not a "don't compress" level for the
+compression algorithm that is in effect (e.g. zlib compression treats level
+0 as "off").
+.IP
+The level values vary depending on the checksum in effect. Because rsync
+will negotiate a checksum choice by default (when the remote rsync is new
+enough), it can be good to combine this option with a
+\fB\-\-compress-choice\fP (\fB\-\-zc\fP) option unless you're sure of the
+choice in effect. For example:
+.RS 4
+.IP
+.nf
+rsync -aiv --zc=zstd --zl=22 host:src/ dest/
+.fi
+.RE
+.IP
+For zlib & zlibx compression the valid values are from 1 to 9 with 6 being
+the default. Specifying \fB\-\-zl=0\fP turns compression off, and specifying
+\fB\-\-zl=\-1\fP chooses the default level of 6.
+.IP
+For zstd compression the valid values are from \-131072 to 22 with 3 being
+the default. Specifying 0 chooses the default of 3.
+.IP
+For lz4 compression there are no levels, so the value is always 0.
+.IP
+If you specify a too-large or too-small value, the number is silently
+limited to a valid value. This allows you to specify something like
+\fB\-\-zl=999999999\fP and be assured that you'll end up with the maximum
+compression level no matter what algorithm was chosen.
+.IP
+If you want to know the compression level that is in effect, specify
+\fB\-\-debug=nstr\fP to see the "negotiated string" results. This will
+report something like "\fBClient\ compress:\ zstd\ (level\ 3)\fP" (along with the
+checksum choice in effect).
+.IP "\fB\-\-skip-compress=LIST\fP"
+\fBNOTE:\fP no compression method currently supports per-file compression
+changes, so this option has no effect.
+.IP
+Override the list of file suffixes that will be compressed as little as
+possible. Rsync sets the compression level on a per-file basis based on
+the file's suffix. If the compression algorithm has an "off" level, then
+no compression occurs for those files. Other algorithms that support
+changing the streaming level on-the-fly will have the level minimized to
+reduces the CPU usage as much as possible for a matching file.
+.IP
+The \fBLIST\fP should be one or more file suffixes (without the dot) separated
+by slashes (\fB/\fP). You may specify an empty string to indicate that no files
+should be skipped.
+.IP
+Simple character-class matching is supported: each must consist of a list
+of letters inside the square brackets (e.g. no special classes, such as
+"[:alpha:]", are supported, and '\-' has no special meaning).
+.IP
+The characters asterisk (\fB*\fP) and question-mark (\fB?\fP) have no special meaning.
+.IP
+Here's an example that specifies 6 suffixes to skip (since 1 of the 5 rules
+matches 2 suffixes):
+.RS 4
+.IP
+.nf
+--skip-compress=gz/jpg/mp[34]/7z/bz2
+.fi
+.RE
+.IP
+The default file suffixes in the skip-compress list in this version of
+rsync are:
+.RS 4
+.IP
+3g2
+3gp
+7z
+aac
+ace
+apk
+avi
+bz2
+deb
+dmg
+ear
+f4v
+flac
+flv
+gpg
+gz
+iso
+jar
+jpeg
+jpg
+lrz
+lz
+lz4
+lzma
+lzo
+m1a
+m1v
+m2a
+m2ts
+m2v
+m4a
+m4b
+m4p
+m4r
+m4v
+mka
+mkv
+mov
+mp1
+mp2
+mp3
+mp4
+mpa
+mpeg
+mpg
+mpv
+mts
+odb
+odf
+odg
+odi
+odm
+odp
+ods
+odt
+oga
+ogg
+ogm
+ogv
+ogx
+opus
+otg
+oth
+otp
+ots
+ott
+oxt
+png
+qt
+rar
+rpm
+rz
+rzip
+spx
+squashfs
+sxc
+sxd
+sxg
+sxm
+sxw
+sz
+tbz
+tbz2
+tgz
+tlz
+ts
+txz
+tzo
+vob
+war
+webm
+webp
+xz
+z
+zip
+zst
+.RE
+.IP
+This list will be replaced by your \fB\-\-skip-compress\fP list in all but one
+situation: a copy from a daemon rsync will add your skipped suffixes to its
+list of non-compressing files (and its list may be configured to a
+different default).
+.IP "\fB\-\-numeric-ids\fP"
+With this option rsync will transfer numeric group and user IDs rather than
+using user and group names and mapping them at both ends.
+.IP
+By default rsync will use the username and groupname to determine what
+ownership to give files. The special uid 0 and the special group 0 are
+never mapped via user/group names even if the \fB\-\-numeric-ids\fP option is not
+specified.
+.IP
+If a user or group has no name on the source system or it has no match on
+the destination system, then the numeric ID from the source system is used
+instead. See also the \fBuse\ chroot\fP setting
+in the rsyncd.conf manpage for some comments on how the chroot setting
+affects rsync's ability to look up the names of the users and groups and
+what you can do about it.
+.IP "\fB\-\-usermap=STRING\fP, \fB\-\-groupmap=STRING\fP"
+These options allow you to specify users and groups that should be mapped
+to other values by the receiving side. The \fBSTRING\fP is one or more
+\fBFROM\fP:\fBTO\fP pairs of values separated by commas. Any matching \fBFROM\fP
+value from the sender is replaced with a \fBTO\fP value from the receiver.
+You may specify usernames or user IDs for the \fBFROM\fP and \fBTO\fP values,
+and the \fBFROM\fP value may also be a wild-card string, which will be
+matched against the sender's names (wild-cards do NOT match against ID
+numbers, though see below for why a '\fB*\fP' matches everything). You may
+instead specify a range of ID numbers via an inclusive range: LOW-HIGH.
+For example:
+.RS 4
+.IP
+.nf
+--usermap=0-99:nobody,wayne:admin,*:normal --groupmap=usr:1,1:usr
+.fi
+.RE
+.IP
+The first match in the list is the one that is used. You should specify
+all your user mappings using a single \fB\-\-usermap\fP option, and/or all your
+group mappings using a single \fB\-\-groupmap\fP option.
+.IP
+Note that the sender's name for the 0 user and group are not transmitted to
+the receiver, so you should either match these values using a 0, or use the
+names in effect on the receiving side (typically "root"). All other
+\fBFROM\fP names match those in use on the sending side. All \fBTO\fP names
+match those in use on the receiving side.
+.IP
+Any IDs that do not have a name on the sending side are treated as having
+an empty name for the purpose of matching. This allows them to be matched
+via a "\fB*\fP" or using an empty name. For instance:
+.RS 4
+.IP
+.nf
+--usermap=:nobody --groupmap=*:nobody
+.fi
+.RE
+.IP
+When the \fB\-\-numeric-ids\fP option is used, the sender does not send any
+names, so all the IDs are treated as having an empty name. This means that
+you will need to specify numeric \fBFROM\fP values if you want to map these
+nameless IDs to different values.
+.IP
+For the \fB\-\-usermap\fP option to work, the receiver will need to be running as
+a super-user (see also the \fB\-\-super\fP and \fB\-\-fake-super\fP
+options). For the \fB\-\-groupmap\fP option to work, the receiver will need to
+have permissions to set that group.
+.IP
+Starting with rsync 3.2.4, the \fB\-\-usermap\fP option implies the
+\fB\-\-owner\fP (\fB\-o\fP) option while the \fB\-\-groupmap\fP option implies the
+\fB\-\-group\fP (\fB\-g\fP) option (since rsync needs to have those options
+enabled for the mapping options to work).
+.IP
+An older rsync client may need to use \fB\-s\fP to avoid a complaint
+about wildcard characters, but a modern rsync handles this automatically.
+.IP "\fB\-\-chown=USER:GROUP\fP"
+This option forces all files to be owned by USER with group GROUP. This is
+a simpler interface than using \fB\-\-usermap\fP & \fB\-\-groupmap\fP
+directly, but it is implemented using those options internally so they
+cannot be mixed. If either the USER or GROUP is empty, no mapping for the
+omitted user/group will occur. If GROUP is empty, the trailing colon may
+be omitted, but if USER is empty, a leading colon must be supplied.
+.IP
+If you specify "\fB\-\-chown=foo:bar\fP", this is exactly the same as specifying
+"\fB\-\-usermap=*:foo\ \-\-groupmap=*:bar\fP", only easier (and with the same
+implied \fB\-\-owner\fP and/or \fB\-\-group\fP options).
+.IP
+An older rsync client may need to use \fB\-s\fP to avoid a complaint
+about wildcard characters, but a modern rsync handles this automatically.
+.IP "\fB\-\-timeout=SECONDS\fP"
+This option allows you to set a maximum I/O timeout in seconds. If no data
+is transferred for the specified time then rsync will exit. The default is
+0, which means no timeout.
+.IP "\fB\-\-contimeout=SECONDS\fP"
+This option allows you to set the amount of time that rsync will wait for
+its connection to an rsync daemon to succeed. If the timeout is reached,
+rsync exits with an error.
+.IP "\fB\-\-address=ADDRESS\fP"
+By default rsync will bind to the wildcard address when connecting to an
+rsync daemon. The \fB\-\-address\fP option allows you to specify a specific IP
+address (or hostname) to bind to.
+.IP
+See also the daemon version of the \fB\-\-address\fP option.
+.IP "\fB\-\-port=PORT\fP"
+This specifies an alternate TCP port number to use rather than the default
+of 873. This is only needed if you are using the double-colon (::) syntax
+to connect with an rsync daemon (since the URL syntax has a way to specify
+the port as a part of the URL).
+.IP
+See also the daemon version of the \fB\-\-port\fP option.
+.IP "\fB\-\-sockopts=OPTIONS\fP"
+This option can provide endless fun for people who like to tune their
+systems to the utmost degree. You can set all sorts of socket options
+which may make transfers faster (or slower!). Read the manpage for the
+\fBsetsockopt()\fP system call for details on some of the options you may be
+able to set. By default no special socket options are set. This only
+affects direct socket connections to a remote rsync daemon.
+.IP
+See also the daemon version of the \fB\-\-sockopts\fP option.
+.IP "\fB\-\-blocking-io\fP"
+This tells rsync to use blocking I/O when launching a remote shell
+transport. If the remote shell is either rsh or remsh, rsync defaults to
+using blocking I/O, otherwise it defaults to using non-blocking I/O. (Note
+that ssh prefers non-blocking I/O.)
+.IP "\fB\-\-outbuf=MODE\fP"
+This sets the output buffering mode. The mode can be None (aka
+Unbuffered), Line, or Block (aka Full). You may specify as little as a
+single letter for the mode, and use upper or lower case.
+.IP
+The main use of this option is to change Full buffering to Line buffering
+when rsync's output is going to a file or pipe.
+.IP "\fB\-\-itemize-changes\fP, \fB\-i\fP"
+Requests a simple itemized list of the changes that are being made to each
+file, including attribute changes. This is exactly the same as specifying
+\fB\-\-out-format='%i\ %n%L'\fP. If you repeat the option, unchanged
+files will also be output, but only if the receiving rsync is at least
+version 2.6.7 (you can use \fB\-vv\fP with older versions of rsync, but that
+also turns on the output of other verbose messages).
+.IP
+The "%i" escape has a cryptic output that is 11 letters long. The general
+format is like the string \fBYXcstpoguax\fP, where \fBY\fP is replaced by the type
+of update being done, \fBX\fP is replaced by the file-type, and the other
+letters represent attributes that may be output if they are being modified.
+.IP
+The update types that replace the \fBY\fP are as follows:
+.IP
+.RS
+.IP o
+A \fB<\fP means that a file is being transferred to the remote host (sent).
+.IP o
+A \fB>\fP means that a file is being transferred to the local host
+(received).
+.IP o
+A \fBc\fP means that a local change/creation is occurring for the item (such
+as the creation of a directory or the changing of a symlink, etc.).
+.IP o
+A \fBh\fP means that the item is a hard link to another item (requires
+\fB\-\-hard-links\fP).
+.IP o
+A \fB.\fP means that the item is not being updated (though it might have
+attributes that are being modified).
+.IP o
+A \fB*\fP means that the rest of the itemized-output area contains a message
+(e.g. "deleting").
+.RE
+.IP
+The file-types that replace the \fBX\fP are: \fBf\fP for a file, a \fBd\fP for a
+directory, an \fBL\fP for a symlink, a \fBD\fP for a device, and a \fBS\fP for a
+special file (e.g. named sockets and fifos).
+.IP
+The other letters in the string indicate if some attributes of the file
+have changed, as follows:
+.IP
+.RS
+.IP o
+"\fB.\fP" \- the attribute is unchanged.
+.IP o
+"\fB+\fP" \- the file is newly created.
+.IP o
+"\fB\ \fP" \- all the attributes are unchanged (all dots turn to spaces).
+.IP o
+"\fB?\fP" \- the change is unknown (when the remote rsync is old).
+.IP o
+A letter indicates an attribute is being updated.
+.RE
+.IP
+The attribute that is associated with each letter is as follows:
+.IP
+.RS
+.IP o
+A \fBc\fP means either that a regular file has a different checksum (requires
+\fB\-\-checksum\fP) or that a symlink, device, or special file has a
+changed value. Note that if you are sending files to an rsync prior to
+3.0.1, this change flag will be present only for checksum-differing
+regular files.
+.IP o
+A \fBs\fP means the size of a regular file is different and will be updated
+by the file transfer.
+.IP o
+A \fBt\fP means the modification time is different and is being updated to
+the sender's value (requires \fB\-\-times\fP). An alternate value of
+\fBT\fP means that the modification time will be set to the transfer time,
+which happens when a file/symlink/device is updated without
+\fB\-\-times\fP and when a symlink is changed and the receiver can't
+set its time. (Note: when using an rsync 3.0.0 client, you might see the
+\fBs\fP flag combined with \fBt\fP instead of the proper \fBT\fP flag for this
+time-setting failure.)
+.IP o
+A \fBp\fP means the permissions are different and are being updated to the
+sender's value (requires \fB\-\-perms\fP).
+.IP o
+An \fBo\fP means the owner is different and is being updated to the sender's
+value (requires \fB\-\-owner\fP and super-user privileges).
+.IP o
+A \fBg\fP means the group is different and is being updated to the sender's
+value (requires \fB\-\-group\fP and the authority to set the group).
+.IP o
+.IP
+.RS
+.IP o
+A \fBu\fP|\fBn\fP|\fBb\fP indicates the following information:
+
+\fBu\fP means the access (use) time is different and is being updated to
+the sender's value (requires \fB\-\-atimes\fP)
+.IP o
+\fBn\fP means the create time (newness) is different and is being updated
+to the sender's value (requires \fB\-\-crtimes\fP)
+.IP o
+\fBb\fP means that both the access and create times are being updated
+.RE
+.IP o
+The \fBa\fP means that the ACL information is being changed.
+.IP o
+The \fBx\fP means that the extended attribute information is being changed.
+.RE
+.IP
+One other output is possible: when deleting files, the "%i" will output the
+string "\fB*deleting\fP" for each item that is being removed (assuming that you
+are talking to a recent enough rsync that it logs deletions instead of
+outputting them as a verbose message).
+.IP "\fB\-\-out-format=FORMAT\fP"
+This allows you to specify exactly what the rsync client outputs to the
+user on a per-update basis. The format is a text string containing
+embedded single-character escape sequences prefixed with a percent (%)
+character. A default format of "%n%L" is assumed if either
+\fB\-\-info=name\fP or \fB\-v\fP is specified (this tells you just the
+name of the file and, if the item is a link, where it points). For a full
+list of the possible escape characters, see the \fBlog\ format\fP setting in the rsyncd.conf manpage.
+.IP
+Specifying the \fB\-\-out-format\fP option implies the \fB\-\-info=name\fP
+option, which will mention each file, dir, etc. that gets updated in a
+significant way (a transferred file, a recreated symlink/device, or a
+touched directory). In addition, if the itemize-changes escape (%i) is
+included in the string (e.g. if the \fB\-\-itemize-changes\fP option was
+used), the logging of names increases to mention any item that is changed
+in any way (as long as the receiving side is at least 2.6.4). See the
+\fB\-\-itemize-changes\fP option for a description of the output of "%i".
+.IP
+Rsync will output the out-format string prior to a file's transfer unless
+one of the transfer-statistic escapes is requested, in which case the
+logging is done at the end of the file's transfer. When this late logging
+is in effect and \fB\-\-progress\fP is also specified, rsync will also
+output the name of the file being transferred prior to its progress
+information (followed, of course, by the out-format output).
+.IP "\fB\-\-log-file=FILE\fP"
+This option causes rsync to log what it is doing to a file. This is
+similar to the logging that a daemon does, but can be requested for the
+client side and/or the server side of a non-daemon transfer. If specified
+as a client option, transfer logging will be enabled with a default format
+of "%i %n%L". See the \fB\-\-log-file-format\fP option if you wish to
+override this.
+.IP
+Here's an example command that requests the remote side to log what is
+happening:
+.RS 4
+.IP
+.nf
+rsync -av --remote-option=--log-file=/tmp/rlog src/ dest/
+.fi
+.RE
+.IP
+This is very useful if you need to debug why a connection is closing
+unexpectedly.
+.IP
+See also the daemon version of the \fB\-\-log-file\fP option.
+.IP "\fB\-\-log-file-format=FORMAT\fP"
+This allows you to specify exactly what per-update logging is put into the
+file specified by the \fB\-\-log-file\fP option (which must also be
+specified for this option to have any effect). If you specify an empty
+string, updated files will not be mentioned in the log file. For a list of
+the possible escape characters, see the \fBlog\ format\fP
+setting in the rsyncd.conf manpage.
+.IP
+The default FORMAT used if \fB\-\-log-file\fP is specified and this
+option is not is '%i %n%L'.
+.IP
+See also the daemon version of the \fB\-\-log-file-format\fP
+option.
+.IP "\fB\-\-stats\fP"
+This tells rsync to print a verbose set of statistics on the file transfer,
+allowing you to tell how effective rsync's delta-transfer algorithm is for
+your data. This option is equivalent to \fB\-\-info=stats2\fP if
+combined with 0 or 1 \fB\-v\fP options, or \fB\-\-info=stats3\fP if
+combined with 2 or more \fB\-v\fP options.
+.IP
+The current statistics are as follows:
+.IP
+.RS
+.IP o
+\fBNumber\ of\ files\fP is the count of all "files" (in the generic sense),
+which includes directories, symlinks, etc. The total count will be
+followed by a list of counts by filetype (if the total is non-zero). For
+example: "(reg: 5, dir: 3, link: 2, dev: 1, special: 1)" lists the totals
+for regular files, directories, symlinks, devices, and special files. If
+any of value is 0, it is completely omitted from the list.
+.IP o
+\fBNumber\ of\ created\ files\fP is the count of how many "files" (generic
+sense) were created (as opposed to updated). The total count will be
+followed by a list of counts by filetype (if the total is non-zero).
+.IP o
+\fBNumber\ of\ deleted\ files\fP is the count of how many "files" (generic
+sense) were deleted. The total count will be
+followed by a list of counts by filetype (if the total is non-zero).
+Note that this line is only output if deletions are in effect, and only
+if protocol 31 is being used (the default for rsync 3.1.x).
+.IP o
+\fBNumber\ of\ regular\ files\ transferred\fP is the count of normal files that
+were updated via rsync's delta-transfer algorithm, which does not include
+dirs, symlinks, etc. Note that rsync 3.1.0 added the word "regular" into
+this heading.
+.IP o
+\fBTotal\ file\ size\fP is the total sum of all file sizes in the transfer.
+This does not count any size for directories or special files, but does
+include the size of symlinks.
+.IP o
+\fBTotal\ transferred\ file\ size\fP is the total sum of all files sizes for
+just the transferred files.
+.IP o
+\fBLiteral\ data\fP is how much unmatched file-update data we had to send to
+the receiver for it to recreate the updated files.
+.IP o
+\fBMatched\ data\fP is how much data the receiver got locally when recreating
+the updated files.
+.IP o
+\fBFile\ list\ size\fP is how big the file-list data was when the sender sent
+it to the receiver. This is smaller than the in-memory size for the file
+list due to some compressing of duplicated data when rsync sends the
+list.
+.IP o
+\fBFile\ list\ generation\ time\fP is the number of seconds that the sender
+spent creating the file list. This requires a modern rsync on the
+sending side for this to be present.
+.IP o
+\fBFile\ list\ transfer\ time\fP is the number of seconds that the sender spent
+sending the file list to the receiver.
+.IP o
+\fBTotal\ bytes\ sent\fP is the count of all the bytes that rsync sent from the
+client side to the server side.
+.IP o
+\fBTotal\ bytes\ received\fP is the count of all non-message bytes that rsync
+received by the client side from the server side. "Non-message" bytes
+means that we don't count the bytes for a verbose message that the server
+sent to us, which makes the stats more consistent.
+.RE
+.IP "\fB\-\-8-bit-output\fP, \fB\-8\fP"
+This tells rsync to leave all high-bit characters unescaped in the output
+instead of trying to test them to see if they're valid in the current
+locale and escaping the invalid ones. All control characters (but never
+tabs) are always escaped, regardless of this option's setting.
+.IP
+The escape idiom that started in 2.6.7 is to output a literal backslash
+(\fB\\\fP) and a hash (\fB#\fP), followed by exactly 3 octal digits. For example, a
+newline would output as "\fB\\#012\fP". A literal backslash that is in a
+filename is not escaped unless it is followed by a hash and 3 digits (0-9).
+.IP "\fB\-\-human-readable\fP, \fB\-h\fP"
+Output numbers in a more human-readable format. There are 3 possible levels:
+.RS
+.IP
+.IP 1.
+output numbers with a separator between each set of 3 digits (either a
+comma or a period, depending on if the decimal point is represented by a
+period or a comma).
+.IP 2.
+output numbers in units of 1000 (with a character suffix for larger
+units\ \-\- see below).
+.IP 3.
+output numbers in units of 1024.
+.RE
+.IP
+The default is human-readable level 1. Each \fB\-h\fP option increases the
+level by one. You can take the level down to 0 (to output numbers as pure
+digits) by specifying the \fB\-\-no-human-readable\fP (\fB\-\-no-h\fP) option.
+.IP
+The unit letters that are appended in levels 2 and 3 are: \fBK\fP (kilo), \fBM\fP
+(mega), \fBG\fP (giga), \fBT\fP (tera), or \fBP\fP (peta). For example, a 1234567-byte
+file would output as 1.23M in level-2 (assuming that a period is your local
+decimal point).
+.IP
+Backward compatibility note: versions of rsync prior to 3.1.0 do not
+support human-readable level 1, and they default to level 0. Thus,
+specifying one or two \fB\-h\fP options will behave in a comparable manner in
+old and new versions as long as you didn't specify a \fB\-\-no-h\fP option prior
+to one or more \fB\-h\fP options. See the \fB\-\-list-only\fP option for one
+difference.
+.IP "\fB\-\-partial\fP"
+By default, rsync will delete any partially transferred file if the
+transfer is interrupted. In some circumstances it is more desirable to
+keep partially transferred files. Using the \fB\-\-partial\fP option tells rsync
+to keep the partial file which should make a subsequent transfer of the
+rest of the file much faster.
+.IP "\fB\-\-partial-dir=DIR\fP"
+This option modifies the behavior of the \fB\-\-partial\fP option while
+also implying that it be enabled. This enhanced partial-file method puts
+any partially transferred files into the specified \fIDIR\fP instead of writing
+the partial file out to the destination file. On the next transfer, rsync
+will use a file found in this dir as data to speed up the resumption of the
+transfer and then delete it after it has served its purpose.
+.IP
+Note that if \fB\-\-whole-file\fP is specified (or implied), any
+partial-dir files that are found for a file that is being updated will
+simply be removed (since rsync is sending files without using rsync's
+delta-transfer algorithm).
+.IP
+Rsync will create the \fIDIR\fP if it is missing, but just the last dir\ \-\- not
+the whole path. This makes it easy to use a relative path (such as
+"\fB\-\-partial-dir=.rsync-partial\fP") to have rsync create the
+partial-directory in the destination file's directory when it is needed,
+and then remove it again when the partial file is deleted. Note that this
+directory removal is only done for a relative pathname, as it is expected
+that an absolute path is to a directory that is reserved for partial-dir
+work.
+.IP
+If the partial-dir value is not an absolute path, rsync will add an exclude
+rule at the end of all your existing excludes. This will prevent the
+sending of any partial-dir files that may exist on the sending side, and
+will also prevent the untimely deletion of partial-dir items on the
+receiving side. An example: the above \fB\-\-partial-dir\fP option would add the
+equivalent of this "perishable" exclude at the end of any other filter
+rules: \fB\-f\ '\-p\ .rsync-partial/'\fP
+.IP
+If you are supplying your own exclude rules, you may need to add your own
+exclude/hide/protect rule for the partial-dir because:
+.RS
+.IP
+.IP 1.
+the auto-added rule may be ineffective at the end of your other rules, or
+.IP 2.
+you may wish to override rsync's exclude choice.
+.RE
+.IP
+For instance, if you want to make rsync clean-up any left-over partial-dirs
+that may be lying around, you should specify \fB\-\-delete-after\fP and
+add a "risk" filter rule, e.g. \fB\-f\ 'R\ .rsync-partial/'\fP. Avoid using
+\fB\-\-delete-before\fP or \fB\-\-delete-during\fP unless you don't
+need rsync to use any of the left-over partial-dir data during the current
+run.
+.IP
+IMPORTANT: the \fB\-\-partial-dir\fP should not be writable by other users or it
+is a security risk! E.g. AVOID "/tmp"!
+.IP
+You can also set the partial-dir value the \fBRSYNC_PARTIAL_DIR\fP
+environment variable. Setting this in the environment does not force
+\fB\-\-partial\fP to be enabled, but rather it affects where partial
+files go when \fB\-\-partial\fP is specified. For instance, instead of
+using \fB\-\-partial-dir=.rsync-tmp\fP along with \fB\-\-progress\fP, you could
+set \fBRSYNC_PARTIAL_DIR=.rsync-tmp\fP in your environment and then use
+the \fB\-P\fP option to turn on the use of the .rsync-tmp dir for
+partial transfers. The only times that the \fB\-\-partial\fP option does
+not look for this environment value are:
+.RS
+.IP
+.IP 1.
+when \fB\-\-inplace\fP was specified (since \fB\-\-inplace\fP
+conflicts with \fB\-\-partial-dir\fP), and
+.IP 2.
+when \fB\-\-delay-updates\fP was specified (see below).
+.RE
+.IP
+When a modern rsync resumes the transfer of a file in the partial-dir, that
+partial file is now updated in-place instead of creating yet another
+tmp-file copy (so it maxes out at dest + tmp instead of dest + partial +
+tmp). This requires both ends of the transfer to be at least version
+3.2.0.
+.IP
+For the purposes of the daemon-config's "\fBrefuse\ options\fP" setting,
+\fB\-\-partial-dir\fP does \fInot\fP imply \fB\-\-partial\fP. This is so that a
+refusal of the \fB\-\-partial\fP option can be used to disallow the
+overwriting of destination files with a partial transfer, while still
+allowing the safer idiom provided by \fB\-\-partial-dir\fP.
+.IP "\fB\-\-delay-updates\fP"
+This option puts the temporary file from each updated file into a holding
+directory until the end of the transfer, at which time all the files are
+renamed into place in rapid succession. This attempts to make the updating
+of the files a little more atomic. By default the files are placed into a
+directory named \fB.~tmp~\fP in each file's destination directory, but if
+you've specified the \fB\-\-partial-dir\fP option, that directory will be
+used instead. See the comments in the \fB\-\-partial-dir\fP section for
+a discussion of how this \fB.~tmp~\fP dir will be excluded from the transfer,
+and what you can do if you want rsync to cleanup old \fB.~tmp~\fP dirs that
+might be lying around. Conflicts with \fB\-\-inplace\fP and
+\fB\-\-append\fP.
+.IP
+This option implies \fB\-\-no-inc-recursive\fP since it needs the full
+file list in memory in order to be able to iterate over it at the end.
+.IP
+This option uses more memory on the receiving side (one bit per file
+transferred) and also requires enough free disk space on the receiving side
+to hold an additional copy of all the updated files. Note also that you
+should not use an absolute path to \fB\-\-partial-dir\fP unless:
+.RS
+.IP
+.IP 1.
+there is no chance of any of the files in the transfer having the same
+name (since all the updated files will be put into a single directory if
+the path is absolute), and
+.IP 2.
+there are no mount points in the hierarchy (since the delayed updates
+will fail if they can't be renamed into place).
+.RE
+.IP
+See also the "atomic-rsync" python script in the "support" subdir for an
+update algorithm that is even more atomic (it uses \fB\-\-link-dest\fP
+and a parallel hierarchy of files).
+.IP "\fB\-\-prune-empty-dirs\fP, \fB\-m\fP"
+This option tells the receiving rsync to get rid of empty directories from
+the file-list, including nested directories that have no non-directory
+children. This is useful for avoiding the creation of a bunch of useless
+directories when the sending rsync is recursively scanning a hierarchy of
+files using include/exclude/filter rules.
+.IP
+This option can still leave empty directories on the receiving side if you
+make use of TRANSFER_RULES.
+.IP
+Because the file-list is actually being pruned, this option also affects
+what directories get deleted when a delete is active. However, keep in
+mind that excluded files and directories can prevent existing items from
+being deleted due to an exclude both hiding source files and protecting
+destination files. See the perishable filter-rule option for how to avoid
+this.
+.IP
+You can prevent the pruning of certain empty directories from the file-list
+by using a global "protect" filter. For instance, this option would ensure
+that the directory "emptydir" was kept in the file-list:
+.RS 4
+.IP
+.nf
+--filter 'protect emptydir/'
+.fi
+.RE
+.IP
+Here's an example that copies all .pdf files in a hierarchy, only creating
+the necessary destination directories to hold the .pdf files, and ensures
+that any superfluous files and directories in the destination are removed
+(note the hide filter of non-directories being used instead of an exclude):
+.RS 4
+.IP
+.nf
+rsync -avm --del --include='*.pdf' -f 'hide,! */' src/ dest
+.fi
+.RE
+.IP
+If you didn't want to remove superfluous destination files, the more
+time-honored options of \fB\-\-include='*/'\ \-\-exclude='*'\fP would work
+fine in place of the hide-filter (if that is more natural to you).
+.IP "\fB\-\-progress\fP"
+This option tells rsync to print information showing the progress of the
+transfer. This gives a bored user something to watch. With a modern rsync
+this is the same as specifying \fB\-\-info=flist2,name,progress\fP, but
+any user-supplied settings for those info flags takes precedence (e.g.
+\fB\-\-info=flist0\ \-\-progress\fP).
+.IP
+While rsync is transferring a regular file, it updates a progress line that
+looks like this:
+.RS 4
+.IP
+.nf
+782448 63% 110.64kB/s 0:00:04
+.fi
+.RE
+.IP
+In this example, the receiver has reconstructed 782448 bytes or 63% of the
+sender's file, which is being reconstructed at a rate of 110.64 kilobytes
+per second, and the transfer will finish in 4 seconds if the current rate
+is maintained until the end.
+.IP
+These statistics can be misleading if rsync's delta-transfer algorithm is
+in use. For example, if the sender's file consists of the basis file
+followed by additional data, the reported rate will probably drop
+dramatically when the receiver gets to the literal data, and the transfer
+will probably take much longer to finish than the receiver estimated as it
+was finishing the matched part of the file.
+.IP
+When the file transfer finishes, rsync replaces the progress line with a
+summary line that looks like this:
+.RS 4
+.IP
+.nf
+1,238,099 100% 146.38kB/s 0:00:08 (xfr#5, to-chk=169/396)
+.fi
+.RE
+.IP
+In this example, the file was 1,238,099 bytes long in total, the average
+rate of transfer for the whole file was 146.38 kilobytes per second over
+the 8 seconds that it took to complete, it was the 5th transfer of a
+regular file during the current rsync session, and there are 169 more files
+for the receiver to check (to see if they are up-to-date or not) remaining
+out of the 396 total files in the file-list.
+.IP
+In an incremental recursion scan, rsync won't know the total number of
+files in the file-list until it reaches the ends of the scan, but since it
+starts to transfer files during the scan, it will display a line with the
+text "ir-chk" (for incremental recursion check) instead of "to-chk" until
+the point that it knows the full size of the list, at which point it will
+switch to using "to-chk". Thus, seeing "ir-chk" lets you know that the
+total count of files in the file list is still going to increase (and each
+time it does, the count of files left to check will increase by the number
+of the files added to the list).
+.IP "\fB\-P\fP"
+The \fB\-P\fP option is equivalent to "\fB\-\-partial\fP
+\fB\-\-progress\fP". Its purpose is to make it much easier to specify
+these two options for a long transfer that may be interrupted.
+.IP
+There is also a \fB\-\-info=progress2\fP option that outputs statistics
+based on the whole transfer, rather than individual files. Use this flag
+without outputting a filename (e.g. avoid \fB\-v\fP or specify
+\fB\-\-info=name0\fP) if you want to see how the transfer is doing
+without scrolling the screen with a lot of names. (You don't need to
+specify the \fB\-\-progress\fP option in order to use
+\fB\-\-info=progress2\fP.)
+.IP
+Finally, you can get an instant progress report by sending rsync a signal
+of either SIGINFO or SIGVTALRM. On BSD systems, a SIGINFO is generated by
+typing a Ctrl+T (Linux doesn't currently support a SIGINFO signal). When
+the client-side process receives one of those signals, it sets a flag to
+output a single progress report which is output when the current file
+transfer finishes (so it may take a little time if a big file is being
+handled when the signal arrives). A filename is output (if needed)
+followed by the \fB\-\-info=progress2\fP format of progress info. If you
+don't know which of the 3 rsync processes is the client process, it's OK to
+signal all of them (since the non-client processes ignore the signal).
+.IP
+CAUTION: sending SIGVTALRM to an older rsync (pre-3.2.0) will kill it.
+.IP "\fB\-\-password-file=FILE\fP"
+This option allows you to provide a password for accessing an rsync daemon
+via a file or via standard input if \fBFILE\fP is \fB\-\fP. The file should
+contain just the password on the first line (all other lines are ignored).
+Rsync will exit with an error if \fBFILE\fP is world readable or if a
+root-run rsync command finds a non-root-owned file.
+.IP
+This option does not supply a password to a remote shell transport such as
+ssh; to learn how to do that, consult the remote shell's documentation.
+When accessing an rsync daemon using a remote shell as the transport, this
+option only comes into effect after the remote shell finishes its
+authentication (i.e. if you have also specified a password in the daemon's
+config file).
+.IP "\fB\-\-early-input=FILE\fP"
+This option allows rsync to send up to 5K of data to the "early exec"
+script on its stdin. One possible use of this data is to give the script a
+secret that can be used to mount an encrypted filesystem (which you should
+unmount in the the "post-xfer exec" script).
+.IP
+The daemon must be at least version 3.2.1.
+.IP "\fB\-\-list-only\fP"
+This option will cause the source files to be listed instead of
+transferred. This option is inferred if there is a single source arg and
+no destination specified, so its main uses are:
+.RS
+.IP
+.IP 1.
+to turn a copy command that includes a destination arg into a
+file-listing command, or
+.IP 2.
+to be able to specify more than one source arg. Note: be sure to
+include the destination.
+.RE
+.IP
+CAUTION: keep in mind that a source arg with a wild-card is expanded by the
+shell into multiple args, so it is never safe to try to specify a single
+wild-card arg to try to infer this option. A safe example is:
+.RS 4
+.IP
+.nf
+rsync -av --list-only foo* dest/
+.fi
+.RE
+.IP
+This option always uses an output format that looks similar to this:
+.RS 4
+.IP
+.nf
+drwxrwxr-x 4,096 2022/09/30 12:53:11 support
+-rw-rw-r-- 80 2005/01/11 10:37:37 support/Makefile
+.fi
+.RE
+.IP
+The only option that affects this output style is (as of 3.1.0) the
+\fB\-\-human-readable\fP (\fB\-h\fP) option. The default is to output sizes
+as byte counts with digit separators (in a 14-character-width column).
+Specifying at least one \fB\-h\fP option makes the sizes output with unit
+suffixes. If you want old-style bytecount sizes without digit separators
+(and an 11-character-width column) use \fB\-\-no-h\fP.
+.IP
+Compatibility note: when requesting a remote listing of files from an rsync
+that is version 2.6.3 or older, you may encounter an error if you ask for a
+non-recursive listing. This is because a file listing implies the
+\fB\-\-dirs\fP option w/o \fB\-\-recursive\fP, and older rsyncs don't
+have that option. To avoid this problem, either specify the \fB\-\-no-dirs\fP
+option (if you don't need to expand a directory's content), or turn on
+recursion and exclude the content of subdirectories: \fB\-r\ \-\-exclude='/*/*'\fP.
+.IP "\fB\-\-bwlimit=RATE\fP"
+This option allows you to specify the maximum transfer rate for the data
+sent over the socket, specified in units per second. The RATE value can be
+suffixed with a string to indicate a size multiplier, and may be a
+fractional value (e.g. \fB\-\-bwlimit=1.5m\fP). If no suffix is specified, the
+value will be assumed to be in units of 1024 bytes (as if "K" or "KiB" had
+been appended). See the \fB\-\-max-size\fP option for a description of
+all the available suffixes. A value of 0 specifies no limit.
+.IP
+For backward-compatibility reasons, the rate limit will be rounded to the
+nearest KiB unit, so no rate smaller than 1024 bytes per second is
+possible.
+.IP
+Rsync writes data over the socket in blocks, and this option both limits
+the size of the blocks that rsync writes, and tries to keep the average
+transfer rate at the requested limit. Some burstiness may be seen where
+rsync writes out a block of data and then sleeps to bring the average rate
+into compliance.
+.IP
+Due to the internal buffering of data, the \fB\-\-progress\fP option may
+not be an accurate reflection on how fast the data is being sent. This is
+because some files can show up as being rapidly sent when the data is
+quickly buffered, while other can show up as very slow when the flushing of
+the output buffer occurs. This may be fixed in a future version.
+.IP
+See also the daemon version of the \fB\-\-bwlimit\fP option.
+.IP "\fB\-\-stop-after=MINS\fP, (\fB\-\-time-limit=MINS\fP)"
+This option tells rsync to stop copying when the specified number of
+minutes has elapsed.
+.IP
+For maximal flexibility, rsync does not communicate this option to the
+remote rsync since it is usually enough that one side of the connection
+quits as specified. This allows the option's use even when only one side
+of the connection supports it. You can tell the remote side about the time
+limit using \fB\-\-remote-option\fP (\fB\-M\fP), should the need arise.
+.IP
+The \fB\-\-time-limit\fP version of this option is deprecated.
+.IP "\fB\-\-stop-at=y-m-dTh:m\fP"
+This option tells rsync to stop copying when the specified point in time
+has been reached. The date & time can be fully specified in a numeric
+format of year-month-dayThour:minute (e.g. 2000-12-31T23:59) in the local
+timezone. You may choose to separate the date numbers using slashes
+instead of dashes.
+.IP
+The value can also be abbreviated in a variety of ways, such as specifying
+a 2-digit year and/or leaving off various values. In all cases, the value
+will be taken to be the next possible point in time where the supplied
+information matches. If the value specifies the current time or a past
+time, rsync exits with an error.
+.IP
+For example, "1-30" specifies the next January 30th (at midnight local
+time), "14:00" specifies the next 2 P.M., "1" specifies the next 1st of the
+month at midnight, "31" specifies the next month where we can stop on its
+31st day, and ":59" specifies the next 59th minute after the hour.
+.IP
+For maximal flexibility, rsync does not communicate this option to the
+remote rsync since it is usually enough that one side of the connection
+quits as specified. This allows the option's use even when only one side
+of the connection supports it. You can tell the remote side about the time
+limit using \fB\-\-remote-option\fP (\fB\-M\fP), should the need arise. Do
+keep in mind that the remote host may have a different default timezone
+than your local host.
+.IP "\fB\-\-fsync\fP"
+Cause the receiving side to fsync each finished file. This may slow down
+the transfer, but can help to provide peace of mind when updating critical
+files.
+.IP "\fB\-\-write-batch=FILE\fP"
+Record a file that can later be applied to another identical destination
+with \fB\-\-read-batch\fP. See the "BATCH MODE" section for details, and
+also the \fB\-\-only-write-batch\fP option.
+.IP
+This option overrides the negotiated checksum & compress lists and always
+negotiates a choice based on old-school md5/md4/zlib choices. If you want
+a more modern choice, use the \fB\-\-checksum-choice\fP (\fB\-\-cc\fP) and/or
+\fB\-\-compress-choice\fP (\fB\-\-zc\fP) options.
+.IP "\fB\-\-only-write-batch=FILE\fP"
+Works like \fB\-\-write-batch\fP, except that no updates are made on the
+destination system when creating the batch. This lets you transport the
+changes to the destination system via some other means and then apply the
+changes via \fB\-\-read-batch\fP.
+.IP
+Note that you can feel free to write the batch directly to some portable
+media: if this media fills to capacity before the end of the transfer, you
+can just apply that partial transfer to the destination and repeat the
+whole process to get the rest of the changes (as long as you don't mind a
+partially updated destination system while the multi-update cycle is
+happening).
+.IP
+Also note that you only save bandwidth when pushing changes to a remote
+system because this allows the batched data to be diverted from the sender
+into the batch file without having to flow over the wire to the receiver
+(when pulling, the sender is remote, and thus can't write the batch).
+.IP "\fB\-\-read-batch=FILE\fP"
+Apply all of the changes stored in FILE, a file previously generated by
+\fB\-\-write-batch\fP. If \fIFILE\fP is \fB\-\fP, the batch data will be read
+from standard input. See the "BATCH MODE" section for details.
+.IP "\fB\-\-protocol=NUM\fP"
+Force an older protocol version to be used. This is useful for creating a
+batch file that is compatible with an older version of rsync. For
+instance, if rsync 2.6.4 is being used with the \fB\-\-write-batch\fP
+option, but rsync 2.6.3 is what will be used to run the
+\fB\-\-read-batch\fP option, you should use "\-\-protocol=28" when creating
+the batch file to force the older protocol version to be used in the batch
+file (assuming you can't upgrade the rsync on the reading system).
+.IP "\fB\-\-iconv=CONVERT_SPEC\fP"
+Rsync can convert filenames between character sets using this option.
+Using a CONVERT_SPEC of "." tells rsync to look up the default
+character-set via the locale setting. Alternately, you can fully specify
+what conversion to do by giving a local and a remote charset separated by a
+comma in the order \fB\-\-iconv=LOCAL,REMOTE\fP, e.g. \fB\-\-iconv=utf8,iso88591\fP.
+This order ensures that the option will stay the same whether you're
+pushing or pulling files. Finally, you can specify either \fB\-\-no-iconv\fP or
+a CONVERT_SPEC of "\-" to turn off any conversion. The default setting of
+this option is site-specific, and can also be affected via the
+\fBRSYNC_ICONV\fP environment variable.
+.IP
+For a list of what charset names your local iconv library supports, you can
+run "\fBiconv\ \-\-list\fP".
+.IP
+If you specify the \fB\-\-secluded-args\fP (\fB\-s\fP) option, rsync will
+translate the filenames you specify on the command-line that are being sent
+to the remote host. See also the \fB\-\-files-from\fP option.
+.IP
+Note that rsync does not do any conversion of names in filter files
+(including include/exclude files). It is up to you to ensure that you're
+specifying matching rules that can match on both sides of the transfer.
+For instance, you can specify extra include/exclude rules if there are
+filename differences on the two sides that need to be accounted for.
+.IP
+When you pass an \fB\-\-iconv\fP option to an rsync daemon that allows it, the
+daemon uses the charset specified in its "charset" configuration parameter
+regardless of the remote charset you actually pass. Thus, you may feel
+free to specify just the local charset for a daemon transfer (e.g.
+\fB\-\-iconv=utf8\fP).
+.IP "\fB\-\-ipv4\fP, \fB\-4\fP or \fB\-\-ipv6\fP, \fB\-6\fP"
+Tells rsync to prefer IPv4/IPv6 when creating sockets or running ssh. This
+affects sockets that rsync has direct control over, such as the outgoing
+socket when directly contacting an rsync daemon, as well as the forwarding
+of the \fB\-4\fP or \fB\-6\fP option to ssh when rsync can deduce that ssh is being
+used as the remote shell. For other remote shells you'll need to specify
+the "\fB\-\-rsh\ SHELL\ \-4\fP" option directly (or whatever IPv4/IPv6 hint options
+it uses).
+.IP
+See also the daemon version of these options.
+.IP
+If rsync was compiled without support for IPv6, the \fB\-\-ipv6\fP option will
+have no effect. The \fBrsync\ \-\-version\fP output will contain "\fBno\ IPv6\fP" if
+is the case.
+.IP "\fB\-\-checksum-seed=NUM\fP"
+Set the checksum seed to the integer NUM. This 4 byte checksum seed is
+included in each block and MD4 file checksum calculation (the more modern
+MD5 file checksums don't use a seed). By default the checksum seed is
+generated by the server and defaults to the current \fBtime\fP(). This
+option is used to set a specific checksum seed, which is useful for
+applications that want repeatable block checksums, or in the case where the
+user wants a more random checksum seed. Setting NUM to 0 causes rsync to
+use the default of \fBtime\fP() for checksum seed.
+.P
+.SH "DAEMON OPTIONS"
+.P
+The options allowed when starting an rsync daemon are as follows:
+.P
+.IP "\fB\-\-daemon\fP"
+This tells rsync that it is to run as a daemon. The daemon you start
+running may be accessed using an rsync client using the \fBhost::module\fP or
+\fBrsync://host/module/\fP syntax.
+.IP
+If standard input is a socket then rsync will assume that it is being run
+via inetd, otherwise it will detach from the current terminal and become a
+background daemon. The daemon will read the config file (rsyncd.conf) on
+each connect made by a client and respond to requests accordingly.
+.IP
+See the \fBrsyncd.conf\fP(5) manpage for more details.
+.IP "\fB\-\-address=ADDRESS\fP"
+By default rsync will bind to the wildcard address when run as a daemon
+with the \fB\-\-daemon\fP option. The \fB\-\-address\fP option allows you to specify a
+specific IP address (or hostname) to bind to. This makes virtual hosting
+possible in conjunction with the \fB\-\-config\fP option.
+.IP
+See also the address global option in the
+rsyncd.conf manpage and the client version of the \fB\-\-address\fP
+option.
+.IP "\fB\-\-bwlimit=RATE\fP"
+This option allows you to specify the maximum transfer rate for the data
+the daemon sends over the socket. The client can still specify a smaller
+\fB\-\-bwlimit\fP value, but no larger value will be allowed.
+.IP
+See the client version of the \fB\-\-bwlimit\fP option for some
+extra details.
+.IP "\fB\-\-config=FILE\fP"
+This specifies an alternate config file than the default. This is only
+relevant when \fB\-\-daemon\fP is specified. The default is
+/etc/rsyncd.conf unless the daemon is running over a remote shell program
+and the remote user is not the super-user; in that case the default is
+rsyncd.conf in the current directory (typically $HOME).
+.IP "\fB\-\-dparam=OVERRIDE\fP, \fB\-M\fP"
+This option can be used to set a daemon-config parameter when starting up
+rsync in daemon mode. It is equivalent to adding the parameter at the end
+of the global settings prior to the first module's definition. The
+parameter names can be specified without spaces, if you so desire. For
+instance:
+.RS 4
+.IP
+.nf
+rsync --daemon -M pidfile=/path/rsync.pid
+.fi
+.RE
+.IP "\fB\-\-no-detach\fP"
+When running as a daemon, this option instructs rsync to not detach itself
+and become a background process. This option is required when running as a
+service on Cygwin, and may also be useful when rsync is supervised by a
+program such as \fBdaemontools\fP or AIX's \fBSystem\ Resource\ Controller\fP.
+\fB\-\-no-detach\fP is also recommended when rsync is run under a debugger. This
+option has no effect if rsync is run from inetd or sshd.
+.IP "\fB\-\-port=PORT\fP"
+This specifies an alternate TCP port number for the daemon to listen on
+rather than the default of 873.
+.IP
+See also the client version of the \fB\-\-port\fP option and the
+port global setting in the rsyncd.conf manpage.
+.IP "\fB\-\-log-file=FILE\fP"
+This option tells the rsync daemon to use the given log-file name instead
+of using the "\fBlog\ file\fP" setting in the config file.
+.IP
+See also the client version of the \fB\-\-log-file\fP option.
+.IP "\fB\-\-log-file-format=FORMAT\fP"
+This option tells the rsync daemon to use the given FORMAT string instead
+of using the "\fBlog\ format\fP" setting in the config file. It also enables
+"\fBtransfer\ logging\fP" unless the string is empty, in which case transfer
+logging is turned off.
+.IP
+See also the client version of the \fB\-\-log-file-format\fP
+option.
+.IP "\fB\-\-sockopts\fP"
+This overrides the \fBsocket\ options\fP
+setting in the rsyncd.conf file and has the same syntax.
+.IP
+See also the client version of the \fB\-\-sockopts\fP option.
+.IP "\fB\-\-verbose\fP, \fB\-v\fP"
+This option increases the amount of information the daemon logs during its
+startup phase. After the client connects, the daemon's verbosity level
+will be controlled by the options that the client used and the
+"\fBmax\ verbosity\fP" setting in the module's config section.
+.IP
+See also the client version of the \fB\-\-verbose\fP option.
+.IP "\fB\-\-ipv4\fP, \fB\-4\fP or \fB\-\-ipv6\fP, \fB\-6\fP"
+Tells rsync to prefer IPv4/IPv6 when creating the incoming sockets that the
+rsync daemon will use to listen for connections. One of these options may
+be required in older versions of Linux to work around an IPv6 bug in the
+kernel (if you see an "address already in use" error when nothing else is
+using the port, try specifying \fB\-\-ipv6\fP or \fB\-\-ipv4\fP when starting the
+daemon).
+.IP
+See also the client version of these options.
+.IP
+If rsync was compiled without support for IPv6, the \fB\-\-ipv6\fP option will
+have no effect. The \fBrsync\ \-\-version\fP output will contain "\fBno\ IPv6\fP" if
+is the case.
+.IP "\fB\-\-help\fP, \fB\-h\fP"
+When specified after \fB\-\-daemon\fP, print a short help page describing the
+options available for starting an rsync daemon.
+.P
+.SH "FILTER RULES"
+.P
+The filter rules allow for custom control of several aspects of how files are
+handled:
+.P
+.IP o
+Control which files the sending side puts into the file list that describes
+the transfer hierarchy
+.IP o
+Control which files the receiving side protects from deletion when the file
+is not in the sender's file list
+.IP o
+Control which extended attribute names are skipped when copying xattrs
+.P
+The rules are either directly specified via option arguments or they can be
+read in from one or more files. The filter-rule files can even be a part of
+the hierarchy of files being copied, affecting different parts of the tree in
+different ways.
+.P
+.SS "SIMPLE INCLUDE/EXCLUDE RULES"
+.P
+We will first cover the basics of how include & exclude rules affect what files
+are transferred, ignoring any deletion side-effects. Filter rules mainly
+affect the contents of directories that rsync is "recursing" into, but they can
+also affect a top-level item in the transfer that was specified as a argument.
+.P
+The default for any unmatched file/dir is for it to be included in the
+transfer, which puts the file/dir into the sender's file list. The use of an
+exclude rule causes one or more matching files/dirs to be left out of the
+sender's file list. An include rule can be used to limit the effect of an
+exclude rule that is matching too many files.
+.P
+The order of the rules is important because the first rule that matches is the
+one that takes effect. Thus, if an early rule excludes a file, no include rule
+that comes after it can have any effect. This means that you must place any
+include overrides somewhere prior to the exclude that it is intended to limit.
+.P
+When a directory is excluded, all its contents and sub-contents are also
+excluded. The sender doesn't scan through any of it at all, which can save a
+lot of time when skipping large unneeded sub-trees.
+.P
+It is also important to understand that the include/exclude rules are applied
+to every file and directory that the sender is recursing into. Thus, if you
+want a particular deep file to be included, you have to make sure that none of
+the directories that must be traversed on the way down to that file are
+excluded or else the file will never be discovered to be included. As an
+example, if the directory "\fBa/path\fP" was given as a transfer argument and you
+want to ensure that the file "\fBa/path/down/deep/wanted.txt\fP" is a part of the
+transfer, then the sender must not exclude the directories "\fBa/path\fP",
+"\fBa/path/down\fP", or "\fBa/path/down/deep\fP" as it makes it way scanning through
+the file tree.
+.P
+When you are working on the rules, it can be helpful to ask rsync to tell you
+what is being excluded/included and why. Specifying \fB\-\-debug=FILTER\fP or (when
+pulling files) \fB\-M\-\-debug=FILTER\fP turns on level 1 of the FILTER debug
+information that will output a message any time that a file or directory is
+included or excluded and which rule it matched. Beginning in 3.2.4 it will
+also warn if a filter rule has trailing whitespace, since an exclude of "foo\ "
+(with a trailing space) will not exclude a file named "foo".
+.P
+Exclude and include rules can specify wildcard PATTERN MATCHING RULES
+(similar to shell wildcards) that allow you to match things like a file suffix
+or a portion of a filename.
+.P
+A rule can be limited to only affecting a directory by putting a trailing slash
+onto the filename.
+.P
+.SS "SIMPLE INCLUDE/EXCLUDE EXAMPLE"
+.P
+With the following file tree created on the sending side:
+.RS 4
+.P
+.nf
+mkdir x/
+touch x/file.txt
+mkdir x/y/
+touch x/y/file.txt
+touch x/y/zzz.txt
+mkdir x/z/
+touch x/z/file.txt
+.fi
+.RE
+.P
+Then the following rsync command will transfer the file "\fBx/y/file.txt\fP" and
+the directories needed to hold it, resulting in the path "\fB/tmp/x/y/file.txt\fP"
+existing on the remote host:
+.RS 4
+.P
+.nf
+rsync -ai -f'+ x/' -f'+ x/y/' -f'+ x/y/file.txt' -f'- *' x host:/tmp/
+.fi
+.RE
+.P
+Aside: this copy could also have been accomplished using the \fB\-R\fP
+option (though the 2 commands behave differently if deletions are enabled):
+.RS 4
+.P
+.nf
+rsync -aiR x/y/file.txt host:/tmp/
+.fi
+.RE
+.P
+The following command does not need an include of the "x" directory because it
+is not a part of the transfer (note the traililng slash). Running this command
+would copy just "\fB/tmp/x/file.txt\fP" because the "y" and "z" dirs get excluded:
+.RS 4
+.P
+.nf
+rsync -ai -f'+ file.txt' -f'- *' x/ host:/tmp/x/
+.fi
+.RE
+.P
+This command would omit the zzz.txt file while copying "x" and everything else
+it contains:
+.RS 4
+.P
+.nf
+rsync -ai -f'- zzz.txt' x host:/tmp/
+.fi
+.RE
+.P
+.SS "FILTER RULES WHEN DELETING"
+.P
+By default the include & exclude filter rules affect both the sender
+(as it creates its file list)
+and the receiver (as it creates its file lists for calculating deletions). If
+no delete option is in effect, the receiver skips creating the delete-related
+file lists. This two-sided default can be manually overridden so that you are
+only specifying sender rules or receiver rules, as described in the FILTER
+RULES IN DEPTH section.
+.P
+When deleting, an exclude protects a file from being removed on the receiving
+side while an include overrides that protection (putting the file at risk of
+deletion). The default is for a file to be at risk\ \-\- its safety depends on it
+matching a corresponding file from the sender.
+.P
+An example of the two-sided exclude effect can be illustrated by the copying of
+a C development directory between 2 systems. When doing a touch-up copy, you
+might want to skip copying the built executable and the \fB.o\fP files (sender
+hide) so that the receiving side can build their own and not lose any object
+files that are already correct (receiver protect). For instance:
+.RS 4
+.P
+.nf
+rsync -ai --del -f'- *.o' -f'- cmd' src host:/dest/
+.fi
+.RE
+.P
+Note that using \fB\-f'\-p\ *.o'\fP is even better than \fB\-f'\-\ *.o'\fP if there is a
+chance that the directory structure may have changed. The "p" modifier is
+discussed in FILTER RULE MODIFIERS.
+.P
+One final note, if your shell doesn't mind unexpanded wildcards, you could
+simplify the typing of the filter options by using an underscore in place of
+the space and leaving off the quotes. For instance, \fB\-f\ \-_*.o\ \-f\ \-_cmd\fP (and
+similar) could be used instead of the filter options above.
+.P
+.SS "FILTER RULES IN DEPTH"
+.P
+Rsync supports old-style include/exclude rules and new-style filter rules. The
+older rules are specified using \fB\-\-include\fP and \fB\-\-exclude\fP as
+well as the \fB\-\-include-from\fP and \fB\-\-exclude-from\fP. These are
+limited in behavior but they don't require a "\-" or "+" prefix. An old-style
+exclude rule is turned into a "\fB\-\ name\fP" filter rule (with no modifiers) and an
+old-style include rule is turned into a "\fB+\ name\fP" filter rule (with no
+modifiers).
+.P
+Rsync builds an ordered list of filter rules as specified on the command-line
+and/or read-in from files. New style filter rules have the following syntax:
+.RS 4
+.P
+.nf
+RULE [PATTERN_OR_FILENAME]
+RULE,MODIFIERS [PATTERN_OR_FILENAME]
+.fi
+.RE
+.P
+You have your choice of using either short or long RULE names, as described
+below. If you use a short-named rule, the ',' separating the RULE from the
+MODIFIERS is optional. The PATTERN or FILENAME that follows (when present)
+must come after either a single space or an underscore (_). Any additional
+spaces and/or underscores are considered to be a part of the pattern name.
+Here are the available rule prefixes:
+.P
+.IP "\fBexclude,\ '\-'\fP"
+specifies an exclude pattern that (by default) is both a
+\fBhide\fP and a \fBprotect\fP.
+.IP "\fBinclude,\ '+'\fP"
+specifies an include pattern that (by default) is both a
+\fBshow\fP and a \fBrisk\fP.
+.IP "\fBmerge,\ '.'\fP"
+specifies a merge-file on the client side to read for more
+rules.
+.IP "\fBdir-merge,\ ':'\fP"
+specifies a per-directory merge-file. Using this kind of
+filter rule requires that you trust the sending side's filter checking, so
+it has the side-effect mentioned under the \fB\-\-trust-sender\fP option.
+.IP "\fBhide,\ 'H'\fP"
+specifies a pattern for hiding files from the transfer.
+Equivalent to a sender-only exclude, so \fB\-f'H\ foo'\fP could also be specified
+as \fB\-f'\-s\ foo'\fP.
+.IP "\fBshow,\ 'S'\fP"
+files that match the pattern are not hidden. Equivalent to a
+sender-only include, so \fB\-f'S\ foo'\fP could also be specified as \fB\-f'+s\ foo'\fP.
+.IP "\fBprotect,\ 'P'\fP"
+specifies a pattern for protecting files from deletion.
+Equivalent to a receiver-only exclude, so \fB\-f'P\ foo'\fP could also be
+specified as \fB\-f'\-r\ foo'\fP.
+.IP "\fBrisk,\ 'R'\fP"
+files that match the pattern are not protected. Equivalent to a
+receiver-only include, so \fB\-f'R\ foo'\fP could also be specified as \fB\-f'+r\ foo'\fP.
+.IP "\fBclear,\ '!'\fP"
+clears the current include/exclude list (takes no arg)
+.P
+When rules are being read from a file (using merge or dir-merge), empty lines
+are ignored, as are whole-line comments that start with a '\fB#\fP' (filename rules
+that contain a hash character are unaffected).
+.P
+Note also that the \fB\-\-filter\fP, \fB\-\-include\fP, and
+\fB\-\-exclude\fP options take one rule/pattern each. To add multiple ones,
+you can repeat the options on the command-line, use the merge-file syntax of
+the \fB\-\-filter\fP option, or the \fB\-\-include-from\fP /
+\fB\-\-exclude-from\fP options.
+.P
+.SS "PATTERN MATCHING RULES"
+.P
+Most of the rules mentioned above take an argument that specifies what the rule
+should match. If rsync is recursing through a directory hierarchy, keep in
+mind that each pattern is matched against the name of every directory in the
+descent path as rsync finds the filenames to send.
+.P
+The matching rules for the pattern argument take several forms:
+.P
+.IP o
+If a pattern contains a \fB/\fP (not counting a trailing slash) or a "\fB**\fP"
+(which can match a slash), then the pattern is matched against the full
+pathname, including any leading directories within the transfer. If the
+pattern doesn't contain a (non-trailing) \fB/\fP or a "\fB**\fP", then it is matched
+only against the final component of the filename or pathname. For example,
+\fBfoo\fP means that the final path component must be "foo" while \fBfoo/bar\fP would
+match the last 2 elements of the path (as long as both elements are within
+the transfer).
+.IP o
+A pattern that ends with a \fB/\fP only matches a directory, not a regular file,
+symlink, or device.
+.IP o
+A pattern that starts with a \fB/\fP is anchored to the start of the transfer
+path instead of the end. For example, \fB/foo/**\fP or \fB/foo/bar/**\fP match only
+leading elements in the path. If the rule is read from a per-directory
+filter file, the transfer path being matched will begin at the level of the
+filter file instead of the top of the transfer. See the section on
+ANCHORING INCLUDE/EXCLUDE PATTERNS for a full discussion of how to
+specify a pattern that matches at the root of the transfer.
+.P
+Rsync chooses between doing a simple string match and wildcard matching by
+checking if the pattern contains one of these three wildcard characters: '\fB*\fP',
+\&'\fB?\fP', and '\fB[\fP' :
+.P
+.IP o
+a '\fB?\fP' matches any single character except a slash (\fB/\fP).
+.IP o
+a '\fB*\fP' matches zero or more non-slash characters.
+.IP o
+a '\fB**\fP' matches zero or more characters, including slashes.
+.IP o
+a '\fB[\fP' introduces a character class, such as \fB[a-z]\fP or \fB[[:alpha:]]\fP, that
+must match one character.
+.IP o
+a trailing \fB***\fP in the pattern is a shorthand that allows you to match a
+directory and all its contents using a single rule. For example, specifying
+"\fBdir_name/***\fP" will match both the "dir_name" directory (as if "\fBdir_name/\fP"
+had been specified) and everything in the directory (as if "\fBdir_name/**\fP"
+had been specified).
+.IP o
+a backslash can be used to escape a wildcard character, but it is only
+interpreted as an escape character if at least one wildcard character is
+present in the match pattern. For instance, the pattern "\fBfoo\\bar\fP" matches
+that single backslash literally, while the pattern "\fBfoo\\bar*\fP" would need to
+be changed to "\fBfoo\\\\bar*\fP" to avoid the "\fB\\b\fP" becoming just "b".
+.P
+Here are some examples of exclude/include matching:
+.P
+.IP o
+Option \fB\-f'\-\ *.o'\fP would exclude all filenames ending with \fB.o\fP
+.IP o
+Option \fB\-f'\-\ /foo'\fP would exclude a file (or directory) named foo in the
+transfer-root directory
+.IP o
+Option \fB\-f'\-\ foo/'\fP would exclude any directory named foo
+.IP o
+Option \fB\-f'\-\ foo/*/bar'\fP would exclude any file/dir named bar which is at two
+levels below a directory named foo (if foo is in the transfer)
+.IP o
+Option \fB\-f'\-\ /foo/**/bar'\fP would exclude any file/dir named bar that was two
+or more levels below a top-level directory named foo (note that /foo/bar is
+\fBnot\fP excluded by this)
+.IP o
+Options \fB\-f'+\ */'\ \-f'+\ *.c'\ \-f'\-\ *'\fP would include all directories and .c
+source files but nothing else
+.IP o
+Options \fB\-f'+\ foo/'\ \-f'+\ foo/bar.c'\ \-f'\-\ *'\fP would include only the foo
+directory and foo/bar.c (the foo directory must be explicitly included or it
+would be excluded by the "\fB\-\ *\fP")
+.P
+.SS "FILTER RULE MODIFIERS"
+.P
+The following modifiers are accepted after an include (+) or exclude (\-) rule:
+.P
+.IP o
+A \fB/\fP specifies that the include/exclude rule should be matched against the
+absolute pathname of the current item. For example, \fB\-f'\-/\ /etc/passwd'\fP
+would exclude the passwd file any time the transfer was sending files from
+the "/etc" directory, and "\-/ subdir/foo" would always exclude "foo" when it
+is in a dir named "subdir", even if "foo" is at the root of the current
+transfer.
+.IP o
+A \fB!\fP specifies that the include/exclude should take effect if the pattern
+fails to match. For instance, \fB\-f'\-!\ */'\fP would exclude all non-directories.
+.IP o
+A \fBC\fP is used to indicate that all the global CVS-exclude rules should be
+inserted as excludes in place of the "\-C". No arg should follow.
+.IP o
+An \fBs\fP is used to indicate that the rule applies to the sending side. When a
+rule affects the sending side, it affects what files are put into the
+sender's file list. The default is for a rule to affect both sides unless
+\fB\-\-delete-excluded\fP was specified, in which case default rules become
+sender-side only. See also the hide (H) and show (S) rules, which are an
+alternate way to specify sending-side includes/excludes.
+.IP o
+An \fBr\fP is used to indicate that the rule applies to the receiving side. When
+a rule affects the receiving side, it prevents files from being deleted. See
+the \fBs\fP modifier for more info. See also the protect (P) and risk (R) rules,
+which are an alternate way to specify receiver-side includes/excludes.
+.IP o
+A \fBp\fP indicates that a rule is perishable, meaning that it is ignored in
+directories that are being deleted. For instance, the
+\fB\-\-cvs-exclude\fP (\fB\-C\fP) option's default rules that exclude things
+like "CVS" and "\fB*.o\fP" are marked as perishable, and will not prevent a
+directory that was removed on the source from being deleted on the
+destination.
+.IP o
+An \fBx\fP indicates that a rule affects xattr names in xattr copy/delete
+operations (and is thus ignored when matching file/dir names). If no
+xattr-matching rules are specified, a default xattr filtering rule is used
+(see the \fB\-\-xattrs\fP option).
+.P
+.SS "MERGE-FILE FILTER RULES"
+.P
+You can merge whole files into your filter rules by specifying either a merge
+(.) or a dir-merge (:) filter rule (as introduced in the FILTER RULES
+section above).
+.P
+There are two kinds of merged files\ \-\- single-instance ('.') and per-directory
+(':'). A single-instance merge file is read one time, and its rules are
+incorporated into the filter list in the place of the "." rule. For
+per-directory merge files, rsync will scan every directory that it traverses
+for the named file, merging its contents when the file exists into the current
+list of inherited rules. These per-directory rule files must be created on the
+sending side because it is the sending side that is being scanned for the
+available files to transfer. These rule files may also need to be transferred
+to the receiving side if you want them to affect what files don't get deleted
+(see PER-DIRECTORY RULES AND DELETE below).
+.P
+Some examples:
+.RS 4
+.P
+.nf
+merge /etc/rsync/default.rules
+\&. /etc/rsync/default.rules
+dir-merge .per-dir-filter
+dir-merge,n- .non-inherited-per-dir-excludes
+:n- .non-inherited-per-dir-excludes
+.fi
+.RE
+.P
+The following modifiers are accepted after a merge or dir-merge rule:
+.P
+.IP o
+A \fB\-\fP specifies that the file should consist of only exclude patterns, with
+no other rule-parsing except for in-file comments.
+.IP o
+A \fB+\fP specifies that the file should consist of only include patterns, with
+no other rule-parsing except for in-file comments.
+.IP o
+A \fBC\fP is a way to specify that the file should be read in a CVS-compatible
+manner. This turns on 'n', 'w', and '\-', but also allows the list-clearing
+token (!) to be specified. If no filename is provided, ".cvsignore" is
+assumed.
+.IP o
+A \fBe\fP will exclude the merge-file name from the transfer; e.g. "dir-merge,e
+\&.rules" is like "dir-merge .rules" and "\- .rules".
+.IP o
+An \fBn\fP specifies that the rules are not inherited by subdirectories.
+.IP o
+A \fBw\fP specifies that the rules are word-split on whitespace instead of the
+normal line-splitting. This also turns off comments. Note: the space that
+separates the prefix from the rule is treated specially, so "\- foo + bar" is
+parsed as two rules (assuming that prefix-parsing wasn't also disabled).
+.IP o
+You may also specify any of the modifiers for the "+" or "\-" rules (above) in
+order to have the rules that are read in from the file default to having that
+modifier set (except for the \fB!\fP modifier, which would not be useful). For
+instance, "merge,\-/ .excl" would treat the contents of .excl as absolute-path
+excludes, while "dir-merge,s .filt" and ":sC" would each make all their
+per-directory rules apply only on the sending side. If the merge rule
+specifies sides to affect (via the \fBs\fP or \fBr\fP modifier or both), then the
+rules in the file must not specify sides (via a modifier or a rule prefix
+such as \fBhide\fP).
+.P
+Per-directory rules are inherited in all subdirectories of the directory where
+the merge-file was found unless the 'n' modifier was used. Each subdirectory's
+rules are prefixed to the inherited per-directory rules from its parents, which
+gives the newest rules a higher priority than the inherited rules. The entire
+set of dir-merge rules are grouped together in the spot where the merge-file
+was specified, so it is possible to override dir-merge rules via a rule that
+got specified earlier in the list of global rules. When the list-clearing rule
+("!") is read from a per-directory file, it only clears the inherited rules for
+the current merge file.
+.P
+Another way to prevent a single rule from a dir-merge file from being inherited
+is to anchor it with a leading slash. Anchored rules in a per-directory
+merge-file are relative to the merge-file's directory, so a pattern "/foo"
+would only match the file "foo" in the directory where the dir-merge filter
+file was found.
+.P
+Here's an example filter file which you'd specify via \fB\-\-filter=".\ file":\fP
+.RS 4
+.P
+.nf
+merge /home/user/.global-filter
+- *.gz
+dir-merge .rules
++ *.[ch]
+- *.o
+- foo*
+.fi
+.RE
+.P
+This will merge the contents of the /home/user/.global-filter file at the start
+of the list and also turns the ".rules" filename into a per-directory filter
+file. All rules read in prior to the start of the directory scan follow the
+global anchoring rules (i.e. a leading slash matches at the root of the
+transfer).
+.P
+If a per-directory merge-file is specified with a path that is a parent
+directory of the first transfer directory, rsync will scan all the parent dirs
+from that starting point to the transfer directory for the indicated
+per-directory file. For instance, here is a common filter (see \fB\-F\fP):
+.RS 4
+.P
+.nf
+--filter=': /.rsync-filter'
+.fi
+.RE
+.P
+That rule tells rsync to scan for the file .rsync-filter in all directories
+from the root down through the parent directory of the transfer prior to the
+start of the normal directory scan of the file in the directories that are sent
+as a part of the transfer. (Note: for an rsync daemon, the root is always the
+same as the module's "path".)
+.P
+Some examples of this pre-scanning for per-directory files:
+.RS 4
+.P
+.nf
+rsync -avF /src/path/ /dest/dir
+rsync -av --filter=': ../../.rsync-filter' /src/path/ /dest/dir
+rsync -av --filter=': .rsync-filter' /src/path/ /dest/dir
+.fi
+.RE
+.P
+The first two commands above will look for ".rsync-filter" in "/" and "/src"
+before the normal scan begins looking for the file in "/src/path" and its
+subdirectories. The last command avoids the parent-dir scan and only looks for
+the ".rsync-filter" files in each directory that is a part of the transfer.
+.P
+If you want to include the contents of a ".cvsignore" in your patterns, you
+should use the rule ":C", which creates a dir-merge of the .cvsignore file, but
+parsed in a CVS-compatible manner. You can use this to affect where the
+\fB\-\-cvs-exclude\fP (\fB\-C\fP) option's inclusion of the per-directory
+\&.cvsignore file gets placed into your rules by putting the ":C" wherever you
+like in your filter rules. Without this, rsync would add the dir-merge rule
+for the .cvsignore file at the end of all your other rules (giving it a lower
+priority than your command-line rules). For example:
+.RS 4
+.P
+.nf
+cat <<EOT | rsync -avC --filter='. -' a/ b
++ foo.o
+:C
+- *.old
+EOT
+rsync -avC --include=foo.o -f :C --exclude='*.old' a/ b
+.fi
+.RE
+.P
+Both of the above rsync commands are identical. Each one will merge all the
+per-directory .cvsignore rules in the middle of the list rather than at the
+end. This allows their dir-specific rules to supersede the rules that follow
+the :C instead of being subservient to all your rules. To affect the other CVS
+exclude rules (i.e. the default list of exclusions, the contents of
+$HOME/.cvsignore, and the value of $CVSIGNORE) you should omit the \fB\-C\fP
+command-line option and instead insert a "\-C" rule into your filter rules; e.g.
+"\fB\-\-filter=\-C\fP".
+.P
+.SS "LIST-CLEARING FILTER RULE"
+.P
+You can clear the current include/exclude list by using the "!" filter rule (as
+introduced in the FILTER RULES section above). The "current" list is either
+the global list of rules (if the rule is encountered while parsing the filter
+options) or a set of per-directory rules (which are inherited in their own
+sub-list, so a subdirectory can use this to clear out the parent's rules).
+.P
+.SS "ANCHORING INCLUDE/EXCLUDE PATTERNS"
+.P
+As mentioned earlier, global include/exclude patterns are anchored at the "root
+of the transfer" (as opposed to per-directory patterns, which are anchored at
+the merge-file's directory). If you think of the transfer as a subtree of
+names that are being sent from sender to receiver, the transfer-root is where
+the tree starts to be duplicated in the destination directory. This root
+governs where patterns that start with a / match.
+.P
+Because the matching is relative to the transfer-root, changing the trailing
+slash on a source path or changing your use of the \fB\-\-relative\fP option
+affects the path you need to use in your matching (in addition to changing how
+much of the file tree is duplicated on the destination host). The following
+examples demonstrate this.
+.P
+Let's say that we want to match two source files, one with an absolute
+path of "/home/me/foo/bar", and one with a path of "/home/you/bar/baz".
+Here is how the various command choices differ for a 2-source transfer:
+.RS 4
+.P
+.nf
+Example cmd: rsync -a /home/me /home/you /dest
++/- pattern: /me/foo/bar
++/- pattern: /you/bar/baz
+Target file: /dest/me/foo/bar
+Target file: /dest/you/bar/baz
+.fi
+.RE
+.RS 4
+.P
+.nf
+Example cmd: rsync -a /home/me/ /home/you/ /dest
++/- pattern: /foo/bar (note missing "me")
++/- pattern: /bar/baz (note missing "you")
+Target file: /dest/foo/bar
+Target file: /dest/bar/baz
+.fi
+.RE
+.RS 4
+.P
+.nf
+Example cmd: rsync -a --relative /home/me/ /home/you /dest
++/- pattern: /home/me/foo/bar (note full path)
++/- pattern: /home/you/bar/baz (ditto)
+Target file: /dest/home/me/foo/bar
+Target file: /dest/home/you/bar/baz
+.fi
+.RE
+.RS 4
+.P
+.nf
+Example cmd: cd /home; rsync -a --relative me/foo you/ /dest
++/- pattern: /me/foo/bar (starts at specified path)
++/- pattern: /you/bar/baz (ditto)
+Target file: /dest/me/foo/bar
+Target file: /dest/you/bar/baz
+.fi
+.RE
+.P
+The easiest way to see what name you should filter is to just look at the
+output when using \fB\-\-verbose\fP and put a / in front of the name (use the
+\fB\-\-dry-run\fP option if you're not yet ready to copy any files).
+.P
+.SS "PER-DIRECTORY RULES AND DELETE"
+.P
+Without a delete option, per-directory rules are only relevant on the sending
+side, so you can feel free to exclude the merge files themselves without
+affecting the transfer. To make this easy, the 'e' modifier adds this exclude
+for you, as seen in these two equivalent commands:
+.RS 4
+.P
+.nf
+rsync -av --filter=': .excl' --exclude=.excl host:src/dir /dest
+rsync -av --filter=':e .excl' host:src/dir /dest
+.fi
+.RE
+.P
+However, if you want to do a delete on the receiving side AND you want some
+files to be excluded from being deleted, you'll need to be sure that the
+receiving side knows what files to exclude. The easiest way is to include the
+per-directory merge files in the transfer and use \fB\-\-delete-after\fP,
+because this ensures that the receiving side gets all the same exclude rules as
+the sending side before it tries to delete anything:
+.RS 4
+.P
+.nf
+rsync -avF --delete-after host:src/dir /dest
+.fi
+.RE
+.P
+However, if the merge files are not a part of the transfer, you'll need to
+either specify some global exclude rules (i.e. specified on the command line),
+or you'll need to maintain your own per-directory merge files on the receiving
+side. An example of the first is this (assume that the remote .rules files
+exclude themselves):
+.RS 4
+.P
+.nf
+rsync -av --filter=': .rules' --filter='. /my/extra.rules'
+ --delete host:src/dir /dest
+.fi
+.RE
+.P
+In the above example the extra.rules file can affect both sides of the
+transfer, but (on the sending side) the rules are subservient to the rules
+merged from the .rules files because they were specified after the
+per-directory merge rule.
+.P
+In one final example, the remote side is excluding the .rsync-filter files from
+the transfer, but we want to use our own .rsync-filter files to control what
+gets deleted on the receiving side. To do this we must specifically exclude
+the per-directory merge files (so that they don't get deleted) and then put
+rules into the local files to control what else should not get deleted. Like
+one of these commands:
+.RS 4
+.P
+.nf
+rsync -av --filter=':e /.rsync-filter' --delete \\
+ host:src/dir /dest
+rsync -avFF --delete host:src/dir /dest
+.fi
+.RE
+.P
+.SH "TRANSFER RULES"
+.P
+In addition to the FILTER RULES that affect the recursive file scans that
+generate the file list on the sending and (when deleting) receiving sides,
+there are transfer rules. These rules affect which files the generator decides
+need to be transferred without the side effects of an exclude filter rule.
+Transfer rules affect only files and never directories.
+.P
+Because a transfer rule does not affect what goes into the sender's (and
+receiver's) file list, it cannot have any effect on which files get deleted on
+the receiving side. For example, if the file "foo" is present in the sender's
+list but its size is such that it is omitted due to a transfer rule, the
+receiving side does not request the file. However, its presence in the file
+list means that a delete pass will not remove a matching file named "foo" on
+the receiving side. On the other hand, a server-side exclude (hide) of the
+file "foo" leaves the file out of the server's file list, and absent a
+receiver-side exclude (protect) the receiver will remove a matching file named
+"foo" if deletions are requested.
+.P
+Given that the files are still in the sender's file list, the
+\fB\-\-prune-empty-dirs\fP option will not judge a directory as being empty
+even if it contains only files that the transfer rules omitted.
+.P
+Similarly, a transfer rule does not have any extra effect on which files are
+deleted on the receiving side, so setting a maximum file size for the transfer
+does not prevent big files from being deleted.
+.P
+Examples of transfer rules include the default "quick check" algorithm (which
+compares size & modify time), the \fB\-\-update\fP option, the
+\fB\-\-max-size\fP option, the \fB\-\-ignore-non-existing\fP option, and a
+few others.
+.P
+.SH "BATCH MODE"
+.P
+Batch mode can be used to apply the same set of updates to many identical
+systems. Suppose one has a tree which is replicated on a number of hosts. Now
+suppose some changes have been made to this source tree and those changes need
+to be propagated to the other hosts. In order to do this using batch mode,
+rsync is run with the write-batch option to apply the changes made to the
+source tree to one of the destination trees. The write-batch option causes the
+rsync client to store in a "batch file" all the information needed to repeat
+this operation against other, identical destination trees.
+.P
+Generating the batch file once saves having to perform the file status,
+checksum, and data block generation more than once when updating multiple
+destination trees. Multicast transport protocols can be used to transfer the
+batch update files in parallel to many hosts at once, instead of sending the
+same data to every host individually.
+.P
+To apply the recorded changes to another destination tree, run rsync with the
+read-batch option, specifying the name of the same batch file, and the
+destination tree. Rsync updates the destination tree using the information
+stored in the batch file.
+.P
+For your convenience, a script file is also created when the write-batch option
+is used: it will be named the same as the batch file with ".sh" appended. This
+script file contains a command-line suitable for updating a destination tree
+using the associated batch file. It can be executed using a Bourne (or
+Bourne-like) shell, optionally passing in an alternate destination tree
+pathname which is then used instead of the original destination path. This is
+useful when the destination tree path on the current host differs from the one
+used to create the batch file.
+.P
+Examples:
+.RS 4
+.P
+.nf
+$ rsync --write-batch=foo -a host:/source/dir/ /adest/dir/
+$ scp foo* remote:
+$ ssh remote ./foo.sh /bdest/dir/
+.fi
+.RE
+.RS 4
+.P
+.nf
+$ rsync --write-batch=foo -a /source/dir/ /adest/dir/
+$ ssh remote rsync --read-batch=- -a /bdest/dir/ <foo
+.fi
+.RE
+.P
+In these examples, rsync is used to update /adest/dir/ from /source/dir/ and
+the information to repeat this operation is stored in "foo" and "foo.sh". The
+host "remote" is then updated with the batched data going into the directory
+/bdest/dir. The differences between the two examples reveals some of the
+flexibility you have in how you deal with batches:
+.P
+.IP o
+The first example shows that the initial copy doesn't have to be local\ \-\- you
+can push or pull data to/from a remote host using either the remote-shell
+syntax or rsync daemon syntax, as desired.
+.IP o
+The first example uses the created "foo.sh" file to get the right rsync
+options when running the read-batch command on the remote host.
+.IP o
+The second example reads the batch data via standard input so that the batch
+file doesn't need to be copied to the remote machine first. This example
+avoids the foo.sh script because it needed to use a modified
+\fB\-\-read-batch\fP option, but you could edit the script file if you
+wished to make use of it (just be sure that no other option is trying to use
+standard input, such as the \fB\-\-exclude-from=\-\fP option).
+.P
+Caveats:
+.P
+The read-batch option expects the destination tree that it is updating to be
+identical to the destination tree that was used to create the batch update
+fileset. When a difference between the destination trees is encountered the
+update might be discarded with a warning (if the file appears to be up-to-date
+already) or the file-update may be attempted and then, if the file fails to
+verify, the update discarded with an error. This means that it should be safe
+to re-run a read-batch operation if the command got interrupted. If you wish
+to force the batched-update to always be attempted regardless of the file's
+size and date, use the \fB\-I\fP option (when reading the batch). If an
+error occurs, the destination tree will probably be in a partially updated
+state. In that case, rsync can be used in its regular (non-batch) mode of
+operation to fix up the destination tree.
+.P
+The rsync version used on all destinations must be at least as new as the one
+used to generate the batch file. Rsync will die with an error if the protocol
+version in the batch file is too new for the batch-reading rsync to handle.
+See also the \fB\-\-protocol\fP option for a way to have the creating rsync
+generate a batch file that an older rsync can understand. (Note that batch
+files changed format in version 2.6.3, so mixing versions older than that with
+newer versions will not work.)
+.P
+When reading a batch file, rsync will force the value of certain options to
+match the data in the batch file if you didn't set them to the same as the
+batch-writing command. Other options can (and should) be changed. For
+instance \fB\-\-write-batch\fP changes to \fB\-\-read-batch\fP,
+\fB\-\-files-from\fP is dropped, and the \fB\-\-filter\fP /
+\fB\-\-include\fP / \fB\-\-exclude\fP options are not needed unless one of
+the \fB\-\-delete\fP options is specified.
+.P
+The code that creates the BATCH.sh file transforms any filter/include/exclude
+options into a single list that is appended as a "here" document to the shell
+script file. An advanced user can use this to modify the exclude list if a
+change in what gets deleted by \fB\-\-delete\fP is desired. A normal user
+can ignore this detail and just use the shell script as an easy way to run the
+appropriate \fB\-\-read-batch\fP command for the batched data.
+.P
+The original batch mode in rsync was based on "rsync+", but the latest
+version uses a new implementation.
+.P
+.SH "SYMBOLIC LINKS"
+.P
+Three basic behaviors are possible when rsync encounters a symbolic
+link in the source directory.
+.P
+By default, symbolic links are not transferred at all. A message "skipping
+non-regular" file is emitted for any symlinks that exist.
+.P
+If \fB\-\-links\fP is specified, then symlinks are added to the transfer
+(instead of being noisily ignored), and the default handling is to recreate
+them with the same target on the destination. Note that \fB\-\-archive\fP
+implies \fB\-\-links\fP.
+.P
+If \fB\-\-copy-links\fP is specified, then symlinks are "collapsed" by
+copying their referent, rather than the symlink.
+.P
+Rsync can also distinguish "safe" and "unsafe" symbolic links. An example
+where this might be used is a web site mirror that wishes to ensure that the
+rsync module that is copied does not include symbolic links to \fB/etc/passwd\fP in
+the public section of the site. Using \fB\-\-copy-unsafe-links\fP will cause
+any links to be copied as the file they point to on the destination. Using
+\fB\-\-safe-links\fP will cause unsafe links to be omitted by the receiver.
+(Note that you must specify or imply \fB\-\-links\fP for
+\fB\-\-safe-links\fP to have any effect.)
+.P
+Symbolic links are considered unsafe if they are absolute symlinks (start with
+\fB/\fP), empty, or if they contain enough ".." components to ascend from the top
+of the transfer.
+.P
+Here's a summary of how the symlink options are interpreted. The list is in
+order of precedence, so if your combination of options isn't mentioned, use the
+first line that is a complete subset of your options:
+.P
+.IP "\fB\-\-copy-links\fP"
+Turn all symlinks into normal files and directories
+(leaving no symlinks in the transfer for any other options to affect).
+.IP "\fB\-\-copy-dirlinks\fP"
+Turn just symlinks to directories into real
+directories, leaving all other symlinks to be handled as described below.
+.IP "\fB\-\-links\ \-\-copy-unsafe-links\fP"
+Turn all unsafe symlinks
+into files and create all safe symlinks.
+.IP "\fB\-\-copy-unsafe-links\fP"
+Turn all unsafe symlinks into files, noisily
+skip all safe symlinks.
+.IP "\fB\-\-links\ \-\-safe-links\fP"
+The receiver skips creating
+unsafe symlinks found in the transfer and creates the safe ones.
+.IP "\fB\-\-links\fP"
+Create all symlinks.
+.P
+For the effect of \fB\-\-munge-links\fP, see the discussion in that option's
+section.
+.P
+Note that the \fB\-\-keep-dirlinks\fP option does not effect symlinks in the
+transfer but instead affects how rsync treats a symlink to a directory that
+already exists on the receiving side. See that option's section for a warning.
+.P
+.SH "DIAGNOSTICS"
+.P
+Rsync occasionally produces error messages that may seem a little cryptic. The
+one that seems to cause the most confusion is "protocol version mismatch\ \-\- is
+your shell clean?".
+.P
+This message is usually caused by your startup scripts or remote shell facility
+producing unwanted garbage on the stream that rsync is using for its transport.
+The way to diagnose this problem is to run your remote shell like this:
+.RS 4
+.P
+.nf
+ssh remotehost /bin/true > out.dat
+.fi
+.RE
+.P
+then look at out.dat. If everything is working correctly then out.dat should
+be a zero length file. If you are getting the above error from rsync then you
+will probably find that out.dat contains some text or data. Look at the
+contents and try to work out what is producing it. The most common cause is
+incorrectly configured shell startup scripts (such as .cshrc or .profile) that
+contain output statements for non-interactive logins.
+.P
+If you are having trouble debugging filter patterns, then try specifying the
+\fB\-vv\fP option. At this level of verbosity rsync will show why each individual
+file is included or excluded.
+.P
+.SH "EXIT VALUES"
+.P
+.IP o
+\fB0\fP \- Success
+.IP o
+\fB1\fP \- Syntax or usage error
+.IP o
+\fB2\fP \- Protocol incompatibility
+.IP o
+\fB3\fP \- Errors selecting input/output files, dirs
+.IP o
+.P
+.RS
+.IP o
+\fB4\fP \- Requested action not supported. Either:
+
+an attempt was made to manipulate 64-bit files on a platform that cannot support them
+.IP o
+an option was specified that is supported by the client and not by the server
+.RE
+.IP o
+\fB5\fP \- Error starting client-server protocol
+.IP o
+\fB6\fP \- Daemon unable to append to log-file
+.IP o
+\fB10\fP \- Error in socket I/O
+.IP o
+\fB11\fP \- Error in file I/O
+.IP o
+\fB12\fP \- Error in rsync protocol data stream
+.IP o
+\fB13\fP \- Errors with program diagnostics
+.IP o
+\fB14\fP \- Error in IPC code
+.IP o
+\fB20\fP \- Received SIGUSR1 or SIGINT
+.IP o
+\fB21\fP \- Some error returned by \fBwaitpid()\fP
+.IP o
+\fB22\fP \- Error allocating core memory buffers
+.IP o
+\fB23\fP \- Partial transfer due to error
+.IP o
+\fB24\fP \- Partial transfer due to vanished source files
+.IP o
+\fB25\fP \- The \-\-max-delete limit stopped deletions
+.IP o
+\fB30\fP \- Timeout in data send/receive
+.IP o
+\fB35\fP \- Timeout waiting for daemon connection
+.P
+.SH "ENVIRONMENT VARIABLES"
+.P
+.IP "\fBCVSIGNORE\fP"
+The CVSIGNORE environment variable supplements any ignore patterns in
+\&.cvsignore files. See the \fB\-\-cvs-exclude\fP option for more details.
+.IP "\fBRSYNC_ICONV\fP"
+Specify a default \fB\-\-iconv\fP setting using this environment
+variable. First supported in 3.0.0.
+.IP "\fBRSYNC_OLD_ARGS\fP"
+Specify a "1" if you want the \fB\-\-old-args\fP option to be enabled by
+default, a "2" (or more) if you want it to be enabled in the
+repeated-option state, or a "0" to make sure that it is disabled by
+default. When this environment variable is set to a non-zero value, it
+supersedes the \fBRSYNC_PROTECT_ARGS\fP variable.
+.IP
+This variable is ignored if \fB\-\-old-args\fP, \fB\-\-no-old-args\fP, or
+\fB\-\-secluded-args\fP is specified on the command line.
+.IP
+First supported in 3.2.4.
+.IP "\fBRSYNC_PROTECT_ARGS\fP"
+Specify a non-zero numeric value if you want the \fB\-\-secluded-args\fP
+option to be enabled by default, or a zero value to make sure that it is
+disabled by default.
+.IP
+This variable is ignored if \fB\-\-secluded-args\fP, \fB\-\-no-secluded-args\fP,
+or \fB\-\-old-args\fP is specified on the command line.
+.IP
+First supported in 3.1.0. Starting in 3.2.4, this variable is ignored if
+\fBRSYNC_OLD_ARGS\fP is set to a non-zero value.
+.IP "\fBRSYNC_RSH\fP"
+This environment variable allows you to override the default shell used as
+the transport for rsync. Command line options are permitted after the
+command name, just as in the \fB\-\-rsh\fP (\fB\-e\fP) option.
+.IP "\fBRSYNC_PROXY\fP"
+This environment variable allows you to redirect your rsync
+client to use a web proxy when connecting to an rsync daemon. You should
+set \fBRSYNC_PROXY\fP to a hostname:port pair.
+.IP "\fBRSYNC_PASSWORD\fP"
+This environment variable allows you to set the password for an rsync
+\fBdaemon\fP connection, which avoids the password prompt. Note that this
+does \fBnot\fP supply a password to a remote shell transport such as ssh
+(consult its documentation for how to do that).
+.IP "\fBUSER\fP or \fBLOGNAME\fP"
+The USER or LOGNAME environment variables are used to determine the default
+username sent to an rsync daemon. If neither is set, the username defaults
+to "nobody". If both are set, \fBUSER\fP takes precedence.
+.IP "\fBRSYNC_PARTIAL_DIR\fP"
+This environment variable specifies the directory to use for a
+\fB\-\-partial\fP transfer without implying that partial transfers be
+enabled. See the \fB\-\-partial-dir\fP option for full details.
+.IP "\fBRSYNC_COMPRESS_LIST\fP"
+This environment variable allows you to customize the negotiation of the
+compression algorithm by specifying an alternate order or a reduced list of
+names. Use the command \fBrsync\ \-\-version\fP to see the available compression
+names. See the \fB\-\-compress\fP option for full details.
+.IP "\fBRSYNC_CHECKSUM_LIST\fP"
+This environment variable allows you to customize the negotiation of the
+checksum algorithm by specifying an alternate order or a reduced list of
+names. Use the command \fBrsync\ \-\-version\fP to see the available checksum
+names. See the \fB\-\-checksum-choice\fP option for full details.
+.IP "\fBRSYNC_MAX_ALLOC\fP"
+This environment variable sets an allocation maximum as if you had used the
+\fB\-\-max-alloc\fP option.
+.IP "\fBRSYNC_PORT\fP"
+This environment variable is not read by rsync, but is instead set in
+its sub-environment when rsync is running the remote shell in combination
+with a daemon connection. This allows a script such as
+\fBrsync-ssl\fP to be able to know the port number that the user
+specified on the command line.
+.IP "\fBHOME\fP"
+This environment variable is used to find the user's default .cvsignore
+file.
+.IP "\fBRSYNC_CONNECT_PROG\fP"
+This environment variable is mainly used in debug setups to set the program
+to use when making a daemon connection. See CONNECTING TO AN RSYNC
+DAEMON for full details.
+.IP "\fBRSYNC_SHELL\fP"
+This environment variable is mainly used in debug setups to set the program
+to use to run the program specified by \fBRSYNC_CONNECT_PROG\fP. See
+CONNECTING TO AN RSYNC DAEMON for full details.
+.P
+.SH "FILES"
+.P
+/etc/rsyncd.conf or rsyncd.conf
+.P
+.SH "SEE ALSO"
+.P
+\fBrsync-ssl\fP(1), \fBrsyncd.conf\fP(5), \fBrrsync\fP(1)
+.P
+.SH "BUGS"
+.P
+.IP o
+Times are transferred as *nix time_t values.
+.IP o
+When transferring to FAT filesystems rsync may re-sync unmodified files. See
+the comments on the \fB\-\-modify-window\fP option.
+.IP o
+File permissions, devices, etc. are transferred as native numerical values.
+.IP o
+See also the comments on the \fB\-\-delete\fP option.
+.P
+Please report bugs! See the web site at https://rsync.samba.org/.
+.P
+.SH "VERSION"
+.P
+This manpage is current for version 3.2.7 of rsync.
+.P
+.SH "INTERNAL OPTIONS"
+.P
+The options \fB\-\-server\fP and \fB\-\-sender\fP are used internally by rsync, and should
+never be typed by a user under normal circumstances. Some awareness of these
+options may be needed in certain scenarios, such as when setting up a login
+that can only run an rsync command. For instance, the support directory of the
+rsync distribution has an example script named rrsync (for restricted rsync)
+that can be used with a restricted ssh login.
+.P
+.SH "CREDITS"
+.P
+Rsync is distributed under the GNU General Public License. See the file
+COPYING for details.
+.P
+An rsync web site is available at https://rsync.samba.org/. The site
+includes an FAQ-O-Matic which may cover questions unanswered by this manual
+page.
+.P
+The rsync github project is https://github.com/WayneD/rsync.
+.P
+We would be delighted to hear from you if you like this program. Please
+contact the mailing-list at rsync@lists.samba.org.
+.P
+This program uses the excellent zlib compression library written by Jean-loup
+Gailly and Mark Adler.
+.P
+.SH "THANKS"
+.P
+Special thanks go out to: John Van Essen, Matt McCutchen, Wesley W. Terpstra,
+David Dykstra, Jos Backus, Sebastian Krahmer, Martin Pool, and our
+gone-but-not-forgotten compadre, J.W. Schultz.
+.P
+Thanks also to Richard Brent, Brendan Mackay, Bill Waite, Stephen Rothwell and
+David Bell. I've probably missed some people, my apologies if I have.
+.P
+.SH "AUTHOR"
+.P
+Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people have later contributed to it. It is currently maintained by Wayne
+Davison.
+.P
+Mailing lists for support and development are available at
+https://lists.samba.org/.
diff --git a/rsync.1.html b/rsync.1.html
new file mode 100644
index 0000000..453b2cd
--- /dev/null
+++ b/rsync.1.html
@@ -0,0 +1,4511 @@
+<html><head>
+<title>rsync(1) manpage</title>
+<meta charset="UTF-8"/>
+<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
+<style>
+body {
+ max-width: 50em;
+ margin: auto;
+}
+body, b, strong, u {
+ font-family: 'Roboto', sans-serif;
+}
+a.tgt { font-face: symbol; font-weight: 400; font-size: 70%; visibility: hidden; text-decoration: none; color: #ddd; padding: 0 4px; border: 0; }
+a.tgt:after { content: '🔗'; }
+a.tgt:hover { color: #444; background-color: #eaeaea; }
+h1:hover > a.tgt, h2:hover > a.tgt, h3:hover > a.tgt, dt:hover > a.tgt { visibility: visible; }
+code {
+ font-family: 'Roboto Mono', monospace;
+ font-weight: bold;
+ white-space: pre;
+}
+pre code {
+ display: block;
+ font-weight: normal;
+}
+blockquote pre code {
+ background: #f1f1f1;
+}
+dd p:first-of-type {
+ margin-block-start: 0em;
+}
+</style>
+</head><body>
+<h2 id="NAME">NAME<a href="#NAME" class="tgt"></a></h2>
+<p>rsync -&#8288; a fast, versatile, remote (and local) file-copying tool</p>
+<h2 id="SYNOPSIS">SYNOPSIS<a href="#SYNOPSIS" class="tgt"></a></h2>
+<pre><code>Local:
+ rsync [OPTION...] SRC... [DEST]
+
+Access via remote shell:
+ Pull:
+ rsync [OPTION...] [USER@]HOST:SRC... [DEST]
+ Push:
+ rsync [OPTION...] SRC... [USER@]HOST:DEST
+
+Access via rsync daemon:
+ Pull:
+ rsync [OPTION...] [USER@]HOST::SRC... [DEST]
+ rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
+ Push:
+ rsync [OPTION...] SRC... [USER@]HOST::DEST
+ rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST)
+</code></pre>
+<p>Usages with just one SRC arg and no DEST arg will list the source files instead
+of copying.</p>
+<p>The online version of this manpage (that includes cross-linking of topics)
+is available at <a href="https://download.samba.org/pub/rsync/rsync.1">https://download.samba.org/pub/rsync/rsync.1</a>.</p>
+<h2 id="DESCRIPTION">DESCRIPTION<a href="#DESCRIPTION" class="tgt"></a></h2>
+<p>Rsync is a fast and extraordinarily versatile file copying tool. It can copy
+locally, to/from another host over any remote shell, or to/from a remote rsync
+daemon. It offers a large number of options that control every aspect of its
+behavior and permit very flexible specification of the set of files to be
+copied. It is famous for its delta-transfer algorithm, which reduces the
+amount of data sent over the network by sending only the differences between
+the source files and the existing files in the destination. Rsync is widely
+used for backups and mirroring and as an improved copy command for everyday
+use.</p>
+<p>Rsync finds files that need to be transferred using a &quot;quick check&quot; algorithm
+(by default) that looks for files that have changed in size or in last-modified
+time. Any changes in the other preserved attributes (as requested by options)
+are made on the destination file directly when the quick check indicates that
+the file's data does not need to be updated.</p>
+<p>Some of the additional features of rsync are:</p>
+<ul>
+<li>support for copying links, devices, owners, groups, and permissions</li>
+<li>exclude and exclude-from options similar to GNU tar</li>
+<li>a CVS exclude mode for ignoring the same files that CVS would ignore</li>
+<li>can use any transparent remote shell, including ssh or rsh</li>
+<li>does not require super-user privileges</li>
+<li>pipelining of file transfers to minimize latency costs</li>
+<li>support for anonymous or authenticated rsync daemons (ideal for mirroring)</li>
+</ul>
+<h2 id="GENERAL">GENERAL<a href="#GENERAL" class="tgt"></a></h2>
+<p>Rsync copies files either to or from a remote host, or locally on the current
+host (it does not support copying files between two remote hosts).</p>
+<p>There are two different ways for rsync to contact a remote system: using a
+remote-shell program as the transport (such as ssh or rsh) or contacting an
+rsync daemon directly via TCP. The remote-shell transport is used whenever the
+source or destination path contains a single colon (:) separator after a host
+specification. Contacting an rsync daemon directly happens when the source or
+destination path contains a double colon (::) separator after a host
+specification, OR when an rsync:// URL is specified (see also the <a href="#USING_RSYNC-DAEMON_FEATURES_VIA_A_REMOTE-SHELL_CONNECTION">USING
+RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION</a> section for an
+exception to this latter rule).</p>
+<p>As a special case, if a single source arg is specified without a destination,
+the files are listed in an output format similar to &quot;<code>ls -l</code>&quot;.</p>
+<p>As expected, if neither the source or destination path specify a remote host,
+the copy occurs locally (see also the <a href="#opt--list-only"><code>--list-only</code></a> option).</p>
+<p>Rsync refers to the local side as the client and the remote side as the server.
+Don't confuse server with an rsync daemon. A daemon is always a server, but a
+server can be either a daemon or a remote-shell spawned process.</p>
+<h2 id="SETUP">SETUP<a href="#SETUP" class="tgt"></a></h2>
+<p>See the file README.md for installation instructions.</p>
+<p>Once installed, you can use rsync to any machine that you can access via a
+remote shell (as well as some that you can access using the rsync daemon-mode
+protocol). For remote transfers, a modern rsync uses ssh for its
+communications, but it may have been configured to use a different remote shell
+by default, such as rsh or remsh.</p>
+<p>You can also specify any remote shell you like, either by using the <a href="#opt-e"><code>-e</code></a>
+command line option, or by setting the <a href="#RSYNC_RSH"><code>RSYNC_RSH</code></a> environment variable.</p>
+<p>Note that rsync must be installed on both the source and destination machines.</p>
+<h2 id="USAGE">USAGE<a href="#USAGE" class="tgt"></a></h2>
+<p>You use rsync in the same way you use rcp. You must specify a source and a
+destination, one of which may be remote.</p>
+<p>Perhaps the best way to explain the syntax is with some examples:</p>
+<blockquote>
+<pre><code>rsync -t *.c foo:src/
+</code></pre>
+</blockquote>
+<p>This would transfer all files matching the pattern <code>*.c</code> from the current
+directory to the directory src on the machine foo. If any of the files already
+exist on the remote system then the rsync remote-update protocol is used to
+update the file by sending only the differences in the data. Note that the
+expansion of wildcards on the command-line (<code>*.c</code>) into a list of files is
+handled by the shell before it runs rsync and not by rsync itself (exactly the
+same as all other Posix-style programs).</p>
+<blockquote>
+<pre><code>rsync -avz foo:src/bar /data/tmp
+</code></pre>
+</blockquote>
+<p>This would recursively transfer all files from the directory src/bar on the
+machine foo into the /data/tmp/bar directory on the local machine. The files
+are transferred in archive mode, which ensures that symbolic links, devices,
+attributes, permissions, ownerships, etc. are preserved in the transfer.
+Additionally, compression will be used to reduce the size of data portions of
+the transfer.</p>
+<blockquote>
+<pre><code>rsync -avz foo:src/bar/ /data/tmp
+</code></pre>
+</blockquote>
+<p>A trailing slash on the source changes this behavior to avoid creating an
+additional directory level at the destination. You can think of a trailing /
+on a source as meaning &quot;copy the contents of this directory&quot; as opposed to
+&quot;copy the directory by name&quot;, but in both cases the attributes of the
+containing directory are transferred to the containing directory on the
+destination. In other words, each of the following commands copies the files
+in the same way, including their setting of the attributes of /dest/foo:</p>
+<blockquote>
+<pre><code>rsync -av /src/foo /dest
+rsync -av /src/foo/ /dest/foo
+</code></pre>
+</blockquote>
+<p>Note also that host and module references don't require a trailing slash to
+copy the contents of the default directory. For example, both of these copy
+the remote directory's contents into &quot;/dest&quot;:</p>
+<blockquote>
+<pre><code>rsync -av host: /dest
+rsync -av host::module /dest
+</code></pre>
+</blockquote>
+<p>You can also use rsync in local-only mode, where both the source and
+destination don't have a ':' in the name. In this case it behaves like an
+improved copy command.</p>
+<p>Finally, you can list all the (listable) modules available from a particular
+rsync daemon by leaving off the module name:</p>
+<blockquote>
+<pre><code>rsync somehost.mydomain.com::
+</code></pre>
+</blockquote>
+<h2 id="COPYING_TO_A_DIFFERENT_NAME">COPYING TO A DIFFERENT NAME<a href="#COPYING_TO_A_DIFFERENT_NAME" class="tgt"></a></h2>
+<p>When you want to copy a directory to a different name, use a trailing slash on
+the source directory to put the contents of the directory into any destination
+directory you like:</p>
+<blockquote>
+<pre><code>rsync -ai foo/ bar/
+</code></pre>
+</blockquote>
+<p>Rsync also has the ability to customize a destination file's name when copying
+a single item. The rules for this are:</p>
+<ul>
+<li>The transfer list must consist of a single item (either a file or an empty
+directory)</li>
+<li>The final element of the destination path must not exist as a directory</li>
+<li>The destination path must not have been specified with a trailing slash</li>
+</ul>
+<p>Under those circumstances, rsync will set the name of the destination's single
+item to the last element of the destination path. Keep in mind that it is best
+to only use this idiom when copying a file and use the above trailing-slash
+idiom when copying a directory.</p>
+<p>The following example copies the <code>foo.c</code> file as <code>bar.c</code> in the <code>save</code> dir
+(assuming that <code>bar.c</code> isn't a directory):</p>
+<blockquote>
+<pre><code>rsync -ai src/foo.c save/bar.c
+</code></pre>
+</blockquote>
+<p>The single-item copy rule might accidentally bite you if you unknowingly copy a
+single item and specify a destination dir that doesn't exist (without using a
+trailing slash). For example, if <code>src/*.c</code> matches one file and <code>save/dir</code>
+doesn't exist, this will confuse you by naming the destination file <code>save/dir</code>:</p>
+<blockquote>
+<pre><code>rsync -ai src/*.c save/dir
+</code></pre>
+</blockquote>
+<p>To prevent such an accident, either make sure the destination dir exists or
+specify the destination path with a trailing slash:</p>
+<blockquote>
+<pre><code>rsync -ai src/*.c save/dir/
+</code></pre>
+</blockquote>
+<h2 id="SORTED_TRANSFER_ORDER">SORTED TRANSFER ORDER<a href="#SORTED_TRANSFER_ORDER" class="tgt"></a></h2>
+<p>Rsync always sorts the specified filenames into its internal transfer list.
+This handles the merging together of the contents of identically named
+directories, makes it easy to remove duplicate filenames. It can, however,
+confuse someone when the files are transferred in a different order than what
+was given on the command-line.</p>
+<p>If you need a particular file to be transferred prior to another, either
+separate the files into different rsync calls, or consider using
+<a href="#opt--delay-updates"><code>--delay-updates</code></a> (which doesn't affect the sorted transfer order, but
+does make the final file-updating phase happen much more rapidly).</p>
+<h2 id="MULTI-HOST_SECURITY">MULTI-HOST SECURITY<a href="#MULTI-HOST_SECURITY" class="tgt"></a></h2>
+<p>Rsync takes steps to ensure that the file requests that are shared in a
+transfer are protected against various security issues. Most of the potential
+problems arise on the receiving side where rsync takes steps to ensure that the
+list of files being transferred remains within the bounds of what was
+requested.</p>
+<p>Toward this end, rsync 3.1.2 and later have aborted when a file list contains
+an absolute or relative path that tries to escape out of the top of the
+transfer. Also, beginning with version 3.2.5, rsync does two more safety
+checks of the file list to (1) ensure that no extra source arguments were added
+into the transfer other than those that the client requested and (2) ensure
+that the file list obeys the exclude rules that were sent to the sender.</p>
+<p>For those that don't yet have a 3.2.5 client rsync (or those that want to be
+extra careful), it is safest to do a copy into a dedicated destination
+directory for the remote files when you don't trust the remote host. For
+example, instead of doing an rsync copy into your home directory:</p>
+<blockquote>
+<pre><code>rsync -aiv host1:dir1 ~
+</code></pre>
+</blockquote>
+<p>Dedicate a &quot;host1-files&quot; dir to the remote content:</p>
+<blockquote>
+<pre><code>rsync -aiv host1:dir1 ~/host1-files
+</code></pre>
+</blockquote>
+<p>See the <a href="#opt--trust-sender"><code>--trust-sender</code></a> option for additional details.</p>
+<p>CAUTION: it is not particularly safe to use rsync to copy files from a
+case-preserving filesystem to a case-ignoring filesystem. If you must perform
+such a copy, you should either disable symlinks via <code>--no-links</code> or enable the
+munging of symlinks via <a href="#opt--munge-links"><code>--munge-links</code></a> (and make sure you use the
+right local or remote option). This will prevent rsync from doing potentially
+dangerous things if a symlink name overlaps with a file or directory. It does
+not, however, ensure that you get a full copy of all the files (since that may
+not be possible when the names overlap). A potentially better solution is to
+list all the source files and create a safe list of filenames that you pass to
+the <a href="#opt--files-from"><code>--files-from</code></a> option. Any files that conflict in name would need
+to be copied to different destination directories using more than one copy.</p>
+<p>While a copy of a case-ignoring filesystem to a case-ignoring filesystem can
+work out fairly well, if no <code>--delete-during</code> or <code>--delete-before</code> option is
+active, rsync can potentially update an existing file on the receiveing side
+without noticing that the upper-/lower-case of the filename should be changed
+to match the sender.</p>
+<h2 id="ADVANCED_USAGE">ADVANCED USAGE<a href="#ADVANCED_USAGE" class="tgt"></a></h2>
+<p>The syntax for requesting multiple files from a remote host is done by
+specifying additional remote-host args in the same style as the first, or with
+the hostname omitted. For instance, all these work:</p>
+<blockquote>
+<pre><code>rsync -aiv host:file1 :file2 host:file{3,4} /dest/
+rsync -aiv host::modname/file{1,2} host::modname/extra /dest/
+rsync -aiv host::modname/first ::extra-file{1,2} /dest/
+</code></pre>
+</blockquote>
+<p>Note that a daemon connection only supports accessing one module per copy
+command, so if the start of a follow-up path doesn't begin with the
+modname of the first path, it is assumed to be a path in the module (such as
+the extra-file1 &amp; extra-file2 that are grabbed above).</p>
+<p>Really old versions of rsync (2.6.9 and before) only allowed specifying one
+remote-source arg, so some people have instead relied on the remote-shell
+performing space splitting to break up an arg into multiple paths. Such
+unintuitive behavior is no longer supported by default (though you can request
+it, as described below).</p>
+<p>Starting in 3.2.4, filenames are passed to a remote shell in such a way as to
+preserve the characters you give it. Thus, if you ask for a file with spaces
+in the name, that's what the remote rsync looks for:</p>
+<blockquote>
+<pre><code>rsync -aiv host:'a simple file.pdf' /dest/
+</code></pre>
+</blockquote>
+<p>If you use scripts that have been written to manually apply extra quoting to
+the remote rsync args (or to require remote arg splitting), you can ask rsync
+to let your script handle the extra escaping. This is done by either adding
+the <a href="#opt--old-args"><code>--old-args</code></a> option to the rsync runs in the script (which requires
+a new rsync) or exporting <a href="#RSYNC_OLD_ARGS">RSYNC_OLD_ARGS</a>=1 and <a href="#RSYNC_PROTECT_ARGS">RSYNC_PROTECT_ARGS</a>=0
+(which works with old or new rsync versions).</p>
+<h2 id="CONNECTING_TO_AN_RSYNC_DAEMON">CONNECTING TO AN RSYNC DAEMON<a href="#CONNECTING_TO_AN_RSYNC_DAEMON" class="tgt"></a></h2>
+<p>It is also possible to use rsync without a remote shell as the transport. In
+this case you will directly connect to a remote rsync daemon, typically using
+TCP port 873. (This obviously requires the daemon to be running on the remote
+system, so refer to the <a href="#STARTING_AN_RSYNC_DAEMON_TO_ACCEPT_CONNECTIONS">STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS</a>
+section below for information on that.)</p>
+<p>Using rsync in this way is the same as using it with a remote shell except
+that:</p>
+<ul>
+<li>Use either double-colon syntax or rsync:// URL syntax instead of the
+single-colon (remote shell) syntax.</li>
+<li>The first element of the &quot;path&quot; is actually a module name.</li>
+<li>Additional remote source args can use an abbreviated syntax that omits the
+hostname and/or the module name, as discussed in <a href="#ADVANCED_USAGE">ADVANCED USAGE</a>.</li>
+<li>The remote daemon may print a &quot;message of the day&quot; when you connect.</li>
+<li>If you specify only the host (with no module or path) then a list of
+accessible modules on the daemon is output.</li>
+<li>If you specify a remote source path but no destination, a listing of the
+matching files on the remote daemon is output.</li>
+<li>The <a href="#opt--rsh"><code>--rsh</code></a> (<code>-e</code>) option must be omitted to avoid changing the
+connection style from using a socket connection to <a href="#USING_RSYNC-DAEMON_FEATURES_VIA_A_REMOTE-SHELL_CONNECTION">USING RSYNC-DAEMON
+FEATURES VIA A REMOTE-SHELL CONNECTION</a>.</li>
+</ul>
+<p>An example that copies all the files in a remote module named &quot;src&quot;:</p>
+<blockquote>
+<pre><code>rsync -av host::src /dest
+</code></pre>
+</blockquote>
+<p>Some modules on the remote daemon may require authentication. If so, you will
+receive a password prompt when you connect. You can avoid the password prompt
+by setting the environment variable <a href="#RSYNC_PASSWORD"><code>RSYNC_PASSWORD</code></a> to the password you
+want to use or using the <a href="#opt--password-file"><code>--password-file</code></a> option. This may be useful
+when scripting rsync.</p>
+<p>WARNING: On some systems environment variables are visible to all users. On
+those systems using <a href="#opt--password-file"><code>--password-file</code></a> is recommended.</p>
+<p>You may establish the connection via a web proxy by setting the environment
+variable <a href="#RSYNC_PROXY"><code>RSYNC_PROXY</code></a> to a hostname:port pair pointing to your web proxy.
+Note that your web proxy's configuration must support proxy connections to port
+873.</p>
+<p>You may also establish a daemon connection using a program as a proxy by
+setting the environment variable <a href="#RSYNC_CONNECT_PROG"><code>RSYNC_CONNECT_PROG</code></a> to the commands you
+wish to run in place of making a direct socket connection. The string may
+contain the escape &quot;%H&quot; to represent the hostname specified in the rsync
+command (so use &quot;%%&quot; if you need a single &quot;%&quot; in your string). For example:</p>
+<blockquote>
+<pre><code>export RSYNC_CONNECT_PROG='ssh proxyhost nc %H 873'
+rsync -av targethost1::module/src/ /dest/
+rsync -av rsync://targethost2/module/src/ /dest/
+</code></pre>
+</blockquote>
+<p>The command specified above uses ssh to run nc (netcat) on a proxyhost, which
+forwards all data to port 873 (the rsync daemon) on the targethost (%H).</p>
+<p>Note also that if the <a href="#RSYNC_SHELL"><code>RSYNC_SHELL</code></a> environment variable is set, that
+program will be used to run the <code>RSYNC_CONNECT_PROG</code> command instead of using
+the default shell of the <strong>system()</strong> call.</p>
+<h2 id="USING_RSYNC-DAEMON_FEATURES_VIA_A_REMOTE-SHELL_CONNECTION">USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION<a href="#USING_RSYNC-DAEMON_FEATURES_VIA_A_REMOTE-SHELL_CONNECTION" class="tgt"></a></h2>
+<p>It is sometimes useful to use various features of an rsync daemon (such as
+named modules) without actually allowing any new socket connections into a
+system (other than what is already required to allow remote-shell access).
+Rsync supports connecting to a host using a remote shell and then spawning a
+single-use &quot;daemon&quot; server that expects to read its config file in the home dir
+of the remote user. This can be useful if you want to encrypt a daemon-style
+transfer's data, but since the daemon is started up fresh by the remote user,
+you may not be able to use features such as chroot or change the uid used by
+the daemon. (For another way to encrypt a daemon transfer, consider using ssh
+to tunnel a local port to a remote machine and configure a normal rsync daemon
+on that remote host to only allow connections from &quot;localhost&quot;.)</p>
+<p>From the user's perspective, a daemon transfer via a remote-shell connection
+uses nearly the same command-line syntax as a normal rsync-daemon transfer,
+with the only exception being that you must explicitly set the remote shell
+program on the command-line with the <a href="#opt--rsh"><code>--rsh=COMMAND</code></a> option. (Setting the
+RSYNC_RSH in the environment will not turn on this functionality.) For example:</p>
+<blockquote>
+<pre><code>rsync -av --rsh=ssh host::module /dest
+</code></pre>
+</blockquote>
+<p>If you need to specify a different remote-shell user, keep in mind that the
+user@ prefix in front of the host is specifying the rsync-user value (for a
+module that requires user-based authentication). This means that you must give
+the '-&#8288;l user' option to ssh when specifying the remote-shell, as in this
+example that uses the short version of the <a href="#opt--rsh"><code>--rsh</code></a> option:</p>
+<blockquote>
+<pre><code>rsync -av -e &quot;ssh -l ssh-user&quot; rsync-user@host::module /dest
+</code></pre>
+</blockquote>
+<p>The &quot;ssh-user&quot; will be used at the ssh level; the &quot;rsync-user&quot; will be used to
+log-in to the &quot;module&quot;.</p>
+<p>In this setup, the daemon is started by the ssh command that is accessing the
+system (which can be forced via the <code>~/.ssh/authorized_keys</code> file, if desired).
+However, when accessing a daemon directly, it needs to be started beforehand.</p>
+<h2 id="STARTING_AN_RSYNC_DAEMON_TO_ACCEPT_CONNECTIONS">STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS<a href="#STARTING_AN_RSYNC_DAEMON_TO_ACCEPT_CONNECTIONS" class="tgt"></a></h2>
+<p>In order to connect to an rsync daemon, the remote system needs to have a
+daemon already running (or it needs to have configured something like inetd to
+spawn an rsync daemon for incoming connections on a particular port). For full
+information on how to start a daemon that will handling incoming socket
+connections, see the <a href="rsyncd.conf.5"><strong>rsyncd.conf</strong>(5)</a> manpage&nbsp;-&#8288;-&#8288; that is
+the config file for the daemon, and it contains the full details for how to run
+the daemon (including stand-alone and inetd configurations).</p>
+<p>If you're using one of the remote-shell transports for the transfer, there is
+no need to manually start an rsync daemon.</p>
+<h2 id="EXAMPLES">EXAMPLES<a href="#EXAMPLES" class="tgt"></a></h2>
+<p>Here are some examples of how rsync can be used.</p>
+<p>To backup a home directory, which consists of large MS Word files and mail
+folders, a per-user cron job can be used that runs this each day:</p>
+<blockquote>
+<pre><code>rsync -aiz . bkhost:backup/joe/
+</code></pre>
+</blockquote>
+<p>To move some files from a remote host to the local host, you could run:</p>
+<blockquote>
+<pre><code>rsync -aiv --remove-source-files rhost:/tmp/{file1,file2}.c ~/src/
+</code></pre>
+</blockquote>
+<h2 id="OPTION_SUMMARY">OPTION SUMMARY<a href="#OPTION_SUMMARY" class="tgt"></a></h2>
+<p>Here is a short summary of the options available in rsync. Each option also
+has its own detailed description later in this manpage.</p>
+<pre><code>--verbose, -v increase verbosity
+--info=FLAGS fine-grained informational verbosity
+--debug=FLAGS fine-grained debug verbosity
+--stderr=e|a|c change stderr output mode (default: errors)
+--quiet, -q suppress non-error messages
+--no-motd suppress daemon-mode MOTD
+--checksum, -c skip based on checksum, not mod-time &amp; size
+--archive, -a archive mode is -rlptgoD (no -A,-X,-U,-N,-H)
+--no-OPTION turn off an implied OPTION (e.g. --no-D)
+--recursive, -r recurse into directories
+--relative, -R use relative path names
+--no-implied-dirs don't send implied dirs with --relative
+--backup, -b make backups (see --suffix &amp; --backup-dir)
+--backup-dir=DIR make backups into hierarchy based in DIR
+--suffix=SUFFIX backup suffix (default ~ w/o --backup-dir)
+--update, -u skip files that are newer on the receiver
+--inplace update destination files in-place
+--append append data onto shorter files
+--append-verify --append w/old data in file checksum
+--dirs, -d transfer directories without recursing
+--old-dirs, --old-d works like --dirs when talking to old rsync
+--mkpath create destination's missing path components
+--links, -l copy symlinks as symlinks
+--copy-links, -L transform symlink into referent file/dir
+--copy-unsafe-links only &quot;unsafe&quot; symlinks are transformed
+--safe-links ignore symlinks that point outside the tree
+--munge-links munge symlinks to make them safe &amp; unusable
+--copy-dirlinks, -k transform symlink to dir into referent dir
+--keep-dirlinks, -K treat symlinked dir on receiver as dir
+--hard-links, -H preserve hard links
+--perms, -p preserve permissions
+--executability, -E preserve executability
+--chmod=CHMOD affect file and/or directory permissions
+--acls, -A preserve ACLs (implies --perms)
+--xattrs, -X preserve extended attributes
+--owner, -o preserve owner (super-user only)
+--group, -g preserve group
+--devices preserve device files (super-user only)
+--copy-devices copy device contents as a regular file
+--write-devices write to devices as files (implies --inplace)
+--specials preserve special files
+-D same as --devices --specials
+--times, -t preserve modification times
+--atimes, -U preserve access (use) times
+--open-noatime avoid changing the atime on opened files
+--crtimes, -N preserve create times (newness)
+--omit-dir-times, -O omit directories from --times
+--omit-link-times, -J omit symlinks from --times
+--super receiver attempts super-user activities
+--fake-super store/recover privileged attrs using xattrs
+--sparse, -S turn sequences of nulls into sparse blocks
+--preallocate allocate dest files before writing them
+--dry-run, -n perform a trial run with no changes made
+--whole-file, -W copy files whole (w/o delta-xfer algorithm)
+--checksum-choice=STR choose the checksum algorithm (aka --cc)
+--one-file-system, -x don't cross filesystem boundaries
+--block-size=SIZE, -B force a fixed checksum block-size
+--rsh=COMMAND, -e specify the remote shell to use
+--rsync-path=PROGRAM specify the rsync to run on remote machine
+--existing skip creating new files on receiver
+--ignore-existing skip updating files that exist on receiver
+--remove-source-files sender removes synchronized files (non-dir)
+--del an alias for --delete-during
+--delete delete extraneous files from dest dirs
+--delete-before receiver deletes before xfer, not during
+--delete-during receiver deletes during the transfer
+--delete-delay find deletions during, delete after
+--delete-after receiver deletes after transfer, not during
+--delete-excluded also delete excluded files from dest dirs
+--ignore-missing-args ignore missing source args without error
+--delete-missing-args delete missing source args from destination
+--ignore-errors delete even if there are I/O errors
+--force force deletion of dirs even if not empty
+--max-delete=NUM don't delete more than NUM files
+--max-size=SIZE don't transfer any file larger than SIZE
+--min-size=SIZE don't transfer any file smaller than SIZE
+--max-alloc=SIZE change a limit relating to memory alloc
+--partial keep partially transferred files
+--partial-dir=DIR put a partially transferred file into DIR
+--delay-updates put all updated files into place at end
+--prune-empty-dirs, -m prune empty directory chains from file-list
+--numeric-ids don't map uid/gid values by user/group name
+--usermap=STRING custom username mapping
+--groupmap=STRING custom groupname mapping
+--chown=USER:GROUP simple username/groupname mapping
+--timeout=SECONDS set I/O timeout in seconds
+--contimeout=SECONDS set daemon connection timeout in seconds
+--ignore-times, -I don't skip files that match size and time
+--size-only skip files that match in size
+--modify-window=NUM, -@ set the accuracy for mod-time comparisons
+--temp-dir=DIR, -T create temporary files in directory DIR
+--fuzzy, -y find similar file for basis if no dest file
+--compare-dest=DIR also compare destination files relative to DIR
+--copy-dest=DIR ... and include copies of unchanged files
+--link-dest=DIR hardlink to files in DIR when unchanged
+--compress, -z compress file data during the transfer
+--compress-choice=STR choose the compression algorithm (aka --zc)
+--compress-level=NUM explicitly set compression level (aka --zl)
+--skip-compress=LIST skip compressing files with suffix in LIST
+--cvs-exclude, -C auto-ignore files in the same way CVS does
+--filter=RULE, -f add a file-filtering RULE
+-F same as --filter='dir-merge /.rsync-filter'
+ repeated: --filter='- .rsync-filter'
+--exclude=PATTERN exclude files matching PATTERN
+--exclude-from=FILE read exclude patterns from FILE
+--include=PATTERN don't exclude files matching PATTERN
+--include-from=FILE read include patterns from FILE
+--files-from=FILE read list of source-file names from FILE
+--from0, -0 all *-from/filter files are delimited by 0s
+--old-args disable the modern arg-protection idiom
+--secluded-args, -s use the protocol to safely send the args
+--trust-sender trust the remote sender's file list
+--copy-as=USER[:GROUP] specify user &amp; optional group for the copy
+--address=ADDRESS bind address for outgoing socket to daemon
+--port=PORT specify double-colon alternate port number
+--sockopts=OPTIONS specify custom TCP options
+--blocking-io use blocking I/O for the remote shell
+--outbuf=N|L|B set out buffering to None, Line, or Block
+--stats give some file-transfer stats
+--8-bit-output, -8 leave high-bit chars unescaped in output
+--human-readable, -h output numbers in a human-readable format
+--progress show progress during transfer
+-P same as --partial --progress
+--itemize-changes, -i output a change-summary for all updates
+--remote-option=OPT, -M send OPTION to the remote side only
+--out-format=FORMAT output updates using the specified FORMAT
+--log-file=FILE log what we're doing to the specified FILE
+--log-file-format=FMT log updates using the specified FMT
+--password-file=FILE read daemon-access password from FILE
+--early-input=FILE use FILE for daemon's early exec input
+--list-only list the files instead of copying them
+--bwlimit=RATE limit socket I/O bandwidth
+--stop-after=MINS Stop rsync after MINS minutes have elapsed
+--stop-at=y-m-dTh:m Stop rsync at the specified point in time
+--fsync fsync every written file
+--write-batch=FILE write a batched update to FILE
+--only-write-batch=FILE like --write-batch but w/o updating dest
+--read-batch=FILE read a batched update from FILE
+--protocol=NUM force an older protocol version to be used
+--iconv=CONVERT_SPEC request charset conversion of filenames
+--checksum-seed=NUM set block/file checksum seed (advanced)
+--ipv4, -4 prefer IPv4
+--ipv6, -6 prefer IPv6
+--version, -V print the version + other info and exit
+--help, -h (*) show this help (* -h is help only on its own)
+</code></pre>
+<p>Rsync can also be run as a daemon, in which case the following options are
+accepted:</p>
+<pre><code>--daemon run as an rsync daemon
+--address=ADDRESS bind to the specified address
+--bwlimit=RATE limit socket I/O bandwidth
+--config=FILE specify alternate rsyncd.conf file
+--dparam=OVERRIDE, -M override global daemon config parameter
+--no-detach do not detach from the parent
+--port=PORT listen on alternate port number
+--log-file=FILE override the &quot;log file&quot; setting
+--log-file-format=FMT override the &quot;log format&quot; setting
+--sockopts=OPTIONS specify custom TCP options
+--verbose, -v increase verbosity
+--ipv4, -4 prefer IPv4
+--ipv6, -6 prefer IPv6
+--help, -h show this help (when used with --daemon)
+</code></pre>
+<h2 id="OPTIONS">OPTIONS<a href="#OPTIONS" class="tgt"></a></h2>
+<p>Rsync accepts both long (double-dash + word) and short (single-dash + letter)
+options. The full list of the available options are described below. If an
+option can be specified in more than one way, the choices are comma-separated.
+Some options only have a long variant, not a short.</p>
+<p>If the option takes a parameter, the parameter is only listed after the long
+variant, even though it must also be specified for the short. When specifying
+a parameter, you can either use the form <code>--option=param</code>, <code>--option param</code>,
+<code>-o=param</code>, <code>-o param</code>, or <code>-oparam</code> (the latter choices assume that your
+option has a short variant).</p>
+<p>The parameter may need to be quoted in some manner for it to survive the
+shell's command-line parsing. Also keep in mind that a leading tilde (<code>~</code>) in
+a pathname is substituted by your shell, so make sure that you separate the
+option name from the pathname using a space if you want the local shell to
+expand it.</p>
+<dl>
+
+<dt id="opt--help"><code>--help</code><a href="#opt--help" class="tgt"></a></dt><dd>
+<p>Print a short help page describing the options available in rsync and exit.
+You can also use <code>-h</code> for <code>--help</code> when it is used without any other
+options (since it normally means <a href="#opt--human-readable"><code>--human-readable</code></a>).</p>
+</dd>
+
+<span id="opt-V"></span><dt id="opt--version"><code>--version</code>, <code>-V</code><a href="#opt--version" class="tgt"></a></dt><dd>
+<p>Print the rsync version plus other info and exit. When repeated, the
+information is output is a JSON format that is still fairly readable
+(client side only).</p>
+<p>The output includes a list of compiled-in capabilities, a list of
+optimizations, the default list of checksum algorithms, the default list of
+compression algorithms, the default list of daemon auth digests, a link to
+the rsync web site, and a few other items.</p>
+</dd>
+
+<span id="opt-v"></span><dt id="opt--verbose"><code>--verbose</code>, <code>-v</code><a href="#opt--verbose" class="tgt"></a></dt><dd>
+<p>This option increases the amount of information you are given during the
+transfer. By default, rsync works silently. A single <code>-v</code> will give you
+information about what files are being transferred and a brief summary at
+the end. Two <code>-v</code> options will give you information on what files are
+being skipped and slightly more information at the end. More than two <code>-v</code>
+options should only be used if you are debugging rsync.</p>
+<p>The end-of-run summary tells you the number of bytes sent to the remote
+rsync (which is the receiving side on a local copy), the number of bytes
+received from the remote host, and the average bytes per second of the
+transferred data computed over the entire length of the rsync run. The
+second line shows the total size (in bytes), which is the sum of all the
+file sizes that rsync considered transferring. It also shows a &quot;speedup&quot;
+value, which is a ratio of the total file size divided by the sum of the
+sent and received bytes (which is really just a feel-good bigger-is-better
+number). Note that these byte values can be made more (or less)
+human-readable by using the <a href="#opt--human-readable"><code>--human-readable</code></a> (or
+<code>--no-human-readable</code>) options.</p>
+<p>In a modern rsync, the <code>-v</code> option is equivalent to the setting of groups
+of <a href="#opt--info"><code>--info</code></a> and <a href="#opt--debug"><code>--debug</code></a> options. You can choose to use
+these newer options in addition to, or in place of using <code>--verbose</code>, as
+any fine-grained settings override the implied settings of <code>-v</code>. Both
+<a href="#opt--info"><code>--info</code></a> and <a href="#opt--debug"><code>--debug</code></a> have a way to ask for help that
+tells you exactly what flags are set for each increase in verbosity.</p>
+<p>However, do keep in mind that a daemon's &quot;<code>max verbosity</code>&quot; setting will limit
+how high of a level the various individual flags can be set on the daemon
+side. For instance, if the max is 2, then any info and/or debug flag that
+is set to a higher value than what would be set by <code>-vv</code> will be downgraded
+to the <code>-vv</code> level in the daemon's logging.</p>
+</dd>
+
+<dt id="opt--info"><code>--info=FLAGS</code><a href="#opt--info" class="tgt"></a></dt><dd>
+<p>This option lets you have fine-grained control over the information output
+you want to see. An individual flag name may be followed by a level
+number, with 0 meaning to silence that output, 1 being the default output
+level, and higher numbers increasing the output of that flag (for those
+that support higher levels). Use <code>--info=help</code> to see all the available
+flag names, what they output, and what flag names are added for each
+increase in the verbose level. Some examples:</p>
+<blockquote>
+<pre><code>rsync -a --info=progress2 src/ dest/
+rsync -avv --info=stats2,misc1,flist0 src/ dest/
+</code></pre>
+</blockquote>
+<p>Note that <code>--info=name</code>'s output is affected by the <a href="#opt--out-format"><code>--out-format</code></a>
+and <a href="#opt--itemize-changes"><code>--itemize-changes</code></a> (<code>-i</code>) options. See those options for more
+information on what is output and when.</p>
+<p>This option was added to 3.1.0, so an older rsync on the server side might
+reject your attempts at fine-grained control (if one or more flags needed
+to be send to the server and the server was too old to understand them).
+See also the &quot;<code>max verbosity</code>&quot; caveat above when dealing with a daemon.</p>
+</dd>
+
+<dt id="opt--debug"><code>--debug=FLAGS</code><a href="#opt--debug" class="tgt"></a></dt><dd>
+<p>This option lets you have fine-grained control over the debug output you
+want to see. An individual flag name may be followed by a level number,
+with 0 meaning to silence that output, 1 being the default output level,
+and higher numbers increasing the output of that flag (for those that
+support higher levels). Use <code>--debug=help</code> to see all the available flag
+names, what they output, and what flag names are added for each increase in
+the verbose level. Some examples:</p>
+<blockquote>
+<pre><code>rsync -avvv --debug=none src/ dest/
+rsync -avA --del --debug=del2,acl src/ dest/
+</code></pre>
+</blockquote>
+<p>Note that some debug messages will only be output when the <a href="#opt--stderr"><code>--stderr=all</code></a>
+option is specified, especially those pertaining to I/O and buffer debugging.</p>
+<p>Beginning in 3.2.0, this option is no longer auto-forwarded to the server
+side in order to allow you to specify different debug values for each side
+of the transfer, as well as to specify a new debug option that is only
+present in one of the rsync versions. If you want to duplicate the same
+option on both sides, using brace expansion is an easy way to save you some
+typing. This works in zsh and bash:</p>
+<blockquote>
+<pre><code>rsync -aiv {-M,}--debug=del2 src/ dest/
+</code></pre>
+</blockquote>
+</dd>
+
+<dt id="opt--stderr"><code>--stderr=errors|all|client</code><a href="#opt--stderr" class="tgt"></a></dt><dd>
+<p>This option controls which processes output to stderr and if info messages
+are also changed to stderr. The mode strings can be abbreviated, so feel
+free to use a single letter value. The 3 possible choices are:</p>
+<ul>
+<li>
+<p><code>errors</code> -&#8288; (the default) causes all the rsync processes to send an
+error directly to stderr, even if the process is on the remote side of
+the transfer. Info messages are sent to the client side via the protocol
+stream. If stderr is not available (i.e. when directly connecting with a
+daemon via a socket) errors fall back to being sent via the protocol
+stream.</p>
+</li>
+<li>
+<p><code>all</code> -&#8288; causes all rsync messages (info and error) to get written
+directly to stderr from all (possible) processes. This causes stderr to
+become line-buffered (instead of raw) and eliminates the ability to
+divide up the info and error messages by file handle. For those doing
+debugging or using several levels of verbosity, this option can help to
+avoid clogging up the transfer stream (which should prevent any chance of
+a deadlock bug hanging things up). It also allows <a href="#opt--debug"><code>--debug</code></a> to
+enable some extra I/O related messages.</p>
+</li>
+<li>
+<p><code>client</code> -&#8288; causes all rsync messages to be sent to the client side
+via the protocol stream. One client process outputs all messages, with
+errors on stderr and info messages on stdout. This <strong>was</strong> the default
+in older rsync versions, but can cause error delays when a lot of
+transfer data is ahead of the messages. If you're pushing files to an
+older rsync, you may want to use <code>--stderr=all</code> since that idiom has
+been around for several releases.</p>
+</li>
+</ul>
+<p>This option was added in rsync 3.2.3. This version also began the
+forwarding of a non-default setting to the remote side, though rsync uses
+the backward-compatible options <code>--msgs2stderr</code> and <code>--no-msgs2stderr</code> to
+represent the <code>all</code> and <code>client</code> settings, respectively. A newer rsync
+will continue to accept these older option names to maintain compatibility.</p>
+</dd>
+
+<span id="opt-q"></span><dt id="opt--quiet"><code>--quiet</code>, <code>-q</code><a href="#opt--quiet" class="tgt"></a></dt><dd>
+<p>This option decreases the amount of information you are given during the
+transfer, notably suppressing information messages from the remote server.
+This option is useful when invoking rsync from cron.</p>
+</dd>
+
+<dt id="opt--no-motd"><code>--no-motd</code><a href="#opt--no-motd" class="tgt"></a></dt><dd>
+<p>This option affects the information that is output by the client at the
+start of a daemon transfer. This suppresses the message-of-the-day (MOTD)
+text, but it also affects the list of modules that the daemon sends in
+response to the &quot;rsync host::&quot; request (due to a limitation in the rsync
+protocol), so omit this option if you want to request the list of modules
+from the daemon.</p>
+</dd>
+
+<span id="opt-I"></span><dt id="opt--ignore-times"><code>--ignore-times</code>, <code>-I</code><a href="#opt--ignore-times" class="tgt"></a></dt><dd>
+<p>Normally rsync will skip any files that are already the same size and have
+the same modification timestamp. This option turns off this &quot;quick check&quot;
+behavior, causing all files to be updated.</p>
+<p>This option can be confusing compared to <a href="#opt--ignore-existing"><code>--ignore-existing</code></a> and
+<a href="#opt--ignore-non-existing"><code>--ignore-non-existing</code></a> in that that they cause rsync to transfer
+fewer files, while this option causes rsync to transfer more files.</p>
+</dd>
+
+<dt id="opt--size-only"><code>--size-only</code><a href="#opt--size-only" class="tgt"></a></dt><dd>
+<p>This modifies rsync's &quot;quick check&quot; algorithm for finding files that need
+to be transferred, changing it from the default of transferring files with
+either a changed size or a changed last-modified time to just looking for
+files that have changed in size. This is useful when starting to use rsync
+after using another mirroring system which may not preserve timestamps
+exactly.</p>
+</dd>
+
+<span id="opt-_"></span><dt id="opt--modify-window"><code>--modify-window=NUM</code>, <code>-@</code><a href="#opt--modify-window" class="tgt"></a></dt><dd>
+<p>When comparing two timestamps, rsync treats the timestamps as being equal
+if they differ by no more than the modify-window value. The default is 0,
+which matches just integer seconds. If you specify a negative value (and
+the receiver is at least version 3.1.3) then nanoseconds will also be taken
+into account. Specifying 1 is useful for copies to/from MS Windows FAT
+filesystems, because FAT represents times with a 2-second resolution
+(allowing times to differ from the original by up to 1 second).</p>
+<p>If you want all your transfers to default to comparing nanoseconds, you can
+create a <code>~/.popt</code> file and put these lines in it:</p>
+<blockquote>
+<pre><code>rsync alias -a -a@-1
+rsync alias -t -t@-1
+</code></pre>
+</blockquote>
+<p>With that as the default, you'd need to specify <code>--modify-window=0</code> (aka
+<code>-@0</code>) to override it and ignore nanoseconds, e.g. if you're copying
+between ext3 and ext4, or if the receiving rsync is older than 3.1.3.</p>
+</dd>
+
+<span id="opt-c"></span><dt id="opt--checksum"><code>--checksum</code>, <code>-c</code><a href="#opt--checksum" class="tgt"></a></dt><dd>
+<p>This changes the way rsync checks if the files have been changed and are in
+need of a transfer. Without this option, rsync uses a &quot;quick check&quot; that
+(by default) checks if each file's size and time of last modification match
+between the sender and receiver. This option changes this to compare a
+128-bit checksum for each file that has a matching size. Generating the
+checksums means that both sides will expend a lot of disk I/O reading all
+the data in the files in the transfer, so this can slow things down
+significantly (and this is prior to any reading that will be done to
+transfer changed files)</p>
+<p>The sending side generates its checksums while it is doing the file-system
+scan that builds the list of the available files. The receiver generates
+its checksums when it is scanning for changed files, and will checksum any
+file that has the same size as the corresponding sender's file: files with
+either a changed size or a changed checksum are selected for transfer.</p>
+<p>Note that rsync always verifies that each <u>transferred</u> file was correctly
+reconstructed on the receiving side by checking a whole-file checksum that
+is generated as the file is transferred, but that automatic
+after-the-transfer verification has nothing to do with this option's
+before-the-transfer &quot;Does this file need to be updated?&quot; check.</p>
+<p>The checksum used is auto-negotiated between the client and the server, but
+can be overridden using either the <a href="#opt--checksum-choice"><code>--checksum-choice</code></a> (<code>--cc</code>)
+option or an environment variable that is discussed in that option's
+section.</p>
+</dd>
+
+<span id="opt-a"></span><dt id="opt--archive"><code>--archive</code>, <code>-a</code><a href="#opt--archive" class="tgt"></a></dt><dd>
+<p>This is equivalent to <code>-rlptgoD</code>. It is a quick way of saying you want
+recursion and want to preserve almost everything. Be aware that it does
+<strong>not</strong> include preserving ACLs (<code>-A</code>), xattrs (<code>-X</code>), atimes (<code>-U</code>),
+crtimes (<code>-N</code>), nor the finding and preserving of hardlinks (<code>-H</code>).</p>
+<p>The only exception to the above equivalence is when <a href="#opt--files-from"><code>--files-from</code></a>
+is specified, in which case <a href="#opt-r"><code>-r</code></a> is not implied.</p>
+</dd>
+
+<dt id="opt--no-OPTION"><code>--no-OPTION</code><a href="#opt--no-OPTION" class="tgt"></a></dt><dd>
+<p>You may turn off one or more implied options by prefixing the option name
+with &quot;no-&quot;. Not all positive options have a negated opposite, but a lot
+do, including those that can be used to disable an implied option (e.g.
+<code>--no-D</code>, <code>--no-perms</code>) or have different defaults in various circumstances
+(e.g. <a href="#opt--no-whole-file"><code>--no-whole-file</code></a>, <code>--no-blocking-io</code>, <code>--no-dirs</code>). Every
+valid negated option accepts both the short and the long option name after
+the &quot;no-&quot; prefix (e.g. <code>--no-R</code> is the same as <code>--no-relative</code>).</p>
+<p>As an example, if you want to use <a href="#opt--archive"><code>--archive</code></a> (<code>-a</code>) but don't want
+<a href="#opt--owner"><code>--owner</code></a> (<code>-o</code>), instead of converting <code>-a</code> into <code>-rlptgD</code>, you
+can specify <code>-a --no-o</code> (aka <code>--archive --no-owner</code>).</p>
+<p>The order of the options is important: if you specify <code>--no-r -a</code>, the <code>-r</code>
+option would end up being turned on, the opposite of <code>-a --no-r</code>. Note
+also that the side-effects of the <a href="#opt--files-from"><code>--files-from</code></a> option are NOT
+positional, as it affects the default state of several options and slightly
+changes the meaning of <a href="#opt-a"><code>-a</code></a> (see the <a href="#opt--files-from"><code>--files-from</code></a> option
+for more details).</p>
+</dd>
+
+<span id="opt-r"></span><dt id="opt--recursive"><code>--recursive</code>, <code>-r</code><a href="#opt--recursive" class="tgt"></a></dt><dd>
+<p>This tells rsync to copy directories recursively. See also
+<a href="#opt--dirs"><code>--dirs</code></a> (<code>-d</code>) for an option that allows the scanning of a single
+directory.</p>
+<p>See the <a href="#opt--inc-recursive"><code>--inc-recursive</code></a> option for a discussion of the
+incremental recursion for creating the list of files to transfer.</p>
+</dd>
+
+<span id="opt--i-r"></span><dt id="opt--inc-recursive"><code>--inc-recursive</code>, <code>--i-r</code><a href="#opt--inc-recursive" class="tgt"></a></dt><dd>
+<p>This option explicitly enables on incremental recursion when scanning for
+files, which is enabled by default when using the <a href="#opt--recursive"><code>--recursive</code></a>
+option and both sides of the transfer are running rsync 3.0.0 or newer.</p>
+<p>Incremental recursion uses much less memory than non-incremental, while
+also beginning the transfer more quickly (since it doesn't need to scan the
+entire transfer hierarchy before it starts transferring files). If no
+recursion is enabled in the source files, this option has no effect.</p>
+<p>Some options require rsync to know the full file list, so these options
+disable the incremental recursion mode. These include:</p>
+<ul>
+<li><a href="#opt--delete-before"><code>--delete-before</code></a> (the old default of <a href="#opt--delete"><code>--delete</code></a>)</li>
+<li><a href="#opt--delete-after"><code>--delete-after</code></a></li>
+<li><a href="#opt--prune-empty-dirs"><code>--prune-empty-dirs</code></a></li>
+<li><a href="#opt--delay-updates"><code>--delay-updates</code></a></li>
+</ul>
+<p>In order to make <a href="#opt--delete"><code>--delete</code></a> compatible with incremental recursion,
+rsync 3.0.0 made <a href="#opt--delete-during"><code>--delete-during</code></a> the default delete mode (which
+was first added in 2.6.4).</p>
+<p>One side-effect of incremental recursion is that any missing
+sub-directories inside a recursively-scanned directory are (by default)
+created prior to recursing into the sub-dirs. This earlier creation point
+(compared to a non-incremental recursion) allows rsync to then set the
+modify time of the finished directory right away (without having to delay
+that until a bunch of recursive copying has finished). However, these
+early directories don't yet have their completed mode, mtime, or ownership
+set&nbsp;-&#8288;-&#8288; they have more restrictive rights until the subdirectory's copying
+actually begins. This early-creation idiom can be avoided by using the
+<a href="#opt--omit-dir-times"><code>--omit-dir-times</code></a> option.</p>
+<p>Incremental recursion can be disabled using the
+<a href="#opt--no-inc-recursive"><code>--no-inc-recursive</code></a> (<code>--no-i-r</code>) option.</p>
+</dd>
+
+<span id="opt--no-i-r"></span><dt id="opt--no-inc-recursive"><code>--no-inc-recursive</code>, <code>--no-i-r</code><a href="#opt--no-inc-recursive" class="tgt"></a></dt><dd>
+<p>Disables the new incremental recursion algorithm of the
+<a href="#opt--recursive"><code>--recursive</code></a> option. This makes rsync scan the full file list
+before it begins to transfer files. See <a href="#opt--inc-recursive"><code>--inc-recursive</code></a> for more
+info.</p>
+</dd>
+
+<span id="opt-R"></span><dt id="opt--relative"><code>--relative</code>, <code>-R</code><a href="#opt--relative" class="tgt"></a></dt><dd>
+<p>Use relative paths. This means that the full path names specified on the
+command line are sent to the server rather than just the last parts of the
+filenames. This is particularly useful when you want to send several
+different directories at the same time. For example, if you used this
+command:</p>
+<blockquote>
+<pre><code>rsync -av /foo/bar/baz.c remote:/tmp/
+</code></pre>
+</blockquote>
+<p>would create a file named baz.c in /tmp/ on the remote machine. If instead
+you used</p>
+<blockquote>
+<pre><code>rsync -avR /foo/bar/baz.c remote:/tmp/
+</code></pre>
+</blockquote>
+<p>then a file named /tmp/foo/bar/baz.c would be created on the remote
+machine, preserving its full path. These extra path elements are called
+&quot;implied directories&quot; (i.e. the &quot;foo&quot; and the &quot;foo/bar&quot; directories in the
+above example).</p>
+<p>Beginning with rsync 3.0.0, rsync always sends these implied directories as
+real directories in the file list, even if a path element is really a
+symlink on the sending side. This prevents some really unexpected behaviors
+when copying the full path of a file that you didn't realize had a symlink
+in its path. If you want to duplicate a server-side symlink, include both
+the symlink via its path, and referent directory via its real path. If
+you're dealing with an older rsync on the sending side, you may need to use
+the <a href="#opt--no-implied-dirs"><code>--no-implied-dirs</code></a> option.</p>
+<p>It is also possible to limit the amount of path information that is sent as
+implied directories for each path you specify. With a modern rsync on the
+sending side (beginning with 2.6.7), you can insert a dot and a slash into
+the source path, like this:</p>
+<blockquote>
+<pre><code>rsync -avR /foo/./bar/baz.c remote:/tmp/
+</code></pre>
+</blockquote>
+<p>That would create /tmp/bar/baz.c on the remote machine. (Note that the dot
+must be followed by a slash, so &quot;/foo/.&quot; would not be abbreviated.) For
+older rsync versions, you would need to use a chdir to limit the source
+path. For example, when pushing files:</p>
+<blockquote>
+<pre><code>(cd /foo; rsync -avR bar/baz.c remote:/tmp/)
+</code></pre>
+</blockquote>
+<p>(Note that the parens put the two commands into a sub-shell, so that the
+&quot;cd&quot; command doesn't remain in effect for future commands.) If you're
+pulling files from an older rsync, use this idiom (but only for a
+non-daemon transfer):</p>
+<blockquote>
+<pre><code>rsync -avR --rsync-path=&quot;cd /foo; rsync&quot; \
+ remote:bar/baz.c /tmp/
+</code></pre>
+</blockquote>
+</dd>
+
+<dt id="opt--no-implied-dirs"><code>--no-implied-dirs</code><a href="#opt--no-implied-dirs" class="tgt"></a></dt><dd>
+<p>This option affects the default behavior of the <a href="#opt--relative"><code>--relative</code></a> option. When
+it is specified, the attributes of the implied directories from the source
+names are not included in the transfer. This means that the corresponding
+path elements on the destination system are left unchanged if they exist,
+and any missing implied directories are created with default attributes.
+This even allows these implied path elements to have big differences, such
+as being a symlink to a directory on the receiving side.</p>
+<p>For instance, if a command-line arg or a files-from entry told rsync to
+transfer the file &quot;path/foo/file&quot;, the directories &quot;path&quot; and &quot;path/foo&quot;
+are implied when <a href="#opt--relative"><code>--relative</code></a> is used. If &quot;path/foo&quot; is a symlink to &quot;bar&quot;
+on the destination system, the receiving rsync would ordinarily delete
+&quot;path/foo&quot;, recreate it as a directory, and receive the file into the new
+directory. With <code>--no-implied-dirs</code>, the receiving rsync updates
+&quot;path/foo/file&quot; using the existing path elements, which means that the file
+ends up being created in &quot;path/bar&quot;. Another way to accomplish this link
+preservation is to use the <a href="#opt--keep-dirlinks"><code>--keep-dirlinks</code></a> option (which will also affect
+symlinks to directories in the rest of the transfer).</p>
+<p>When pulling files from an rsync older than 3.0.0, you may need to use this
+option if the sending side has a symlink in the path you request and you
+wish the implied directories to be transferred as normal directories.</p>
+</dd>
+
+<span id="opt-b"></span><dt id="opt--backup"><code>--backup</code>, <code>-b</code><a href="#opt--backup" class="tgt"></a></dt><dd>
+<p>With this option, preexisting destination files are renamed as each file is
+transferred or deleted. You can control where the backup file goes and
+what (if any) suffix gets appended using the <a href="#opt--backup-dir"><code>--backup-dir</code></a> and
+<a href="#opt--suffix"><code>--suffix</code></a> options.</p>
+<p>If you don't specify <a href="#opt--backup-dir"><code>--backup-dir</code></a>:</p>
+<ol>
+<li>the <a href="#opt--omit-dir-times"><code>--omit-dir-times</code></a> option will be forced on</li>
+<li>the use of <a href="#opt--delete"><code>--delete</code></a> (without <a href="#opt--delete-excluded"><code>--delete-excluded</code></a>),
+causes rsync to add a &quot;protect&quot; <a href="#FILTER_RULES">filter-rule</a> for the
+backup suffix to the end of all your existing filters that looks like
+this: <code>-f &quot;P *~&quot;</code>. This rule prevents previously backed-up files from
+being deleted.</li>
+</ol>
+<p>Note that if you are supplying your own filter rules, you may need to
+manually insert your own exclude/protect rule somewhere higher up in the
+list so that it has a high enough priority to be effective (e.g. if your
+rules specify a trailing inclusion/exclusion of <code>*</code>, the auto-added rule
+would never be reached).</p>
+</dd>
+
+<dt id="opt--backup-dir"><code>--backup-dir=DIR</code><a href="#opt--backup-dir" class="tgt"></a></dt><dd>
+<p>This implies the <a href="#opt--backup"><code>--backup</code></a> option, and tells rsync to store all
+backups in the specified directory on the receiving side. This can be used
+for incremental backups. You can additionally specify a backup suffix
+using the <a href="#opt--suffix"><code>--suffix</code></a> option (otherwise the files backed up in the
+specified directory will keep their original filenames).</p>
+<p>Note that if you specify a relative path, the backup directory will be
+relative to the destination directory, so you probably want to specify
+either an absolute path or a path that starts with &quot;../&quot;. If an rsync
+daemon is the receiver, the backup dir cannot go outside the module's path
+hierarchy, so take extra care not to delete it or copy into it.</p>
+</dd>
+
+<dt id="opt--suffix"><code>--suffix=SUFFIX</code><a href="#opt--suffix" class="tgt"></a></dt><dd>
+<p>This option allows you to override the default backup suffix used with the
+<a href="#opt--backup"><code>--backup</code></a> (<code>-b</code>) option. The default suffix is a <code>~</code> if no
+<a href="#opt--backup-dir"><code>--backup-dir</code></a> was specified, otherwise it is an empty string.</p>
+</dd>
+
+<span id="opt-u"></span><dt id="opt--update"><code>--update</code>, <code>-u</code><a href="#opt--update" class="tgt"></a></dt><dd>
+<p>This forces rsync to skip any files which exist on the destination and have
+a modified time that is newer than the source file. (If an existing
+destination file has a modification time equal to the source file's, it
+will be updated if the sizes are different.)</p>
+<p>Note that this does not affect the copying of dirs, symlinks, or other
+special files. Also, a difference of file format between the sender and
+receiver is always considered to be important enough for an update, no
+matter what date is on the objects. In other words, if the source has a
+directory where the destination has a file, the transfer would occur
+regardless of the timestamps.</p>
+<p>This option is a <a href="#TRANSFER_RULES">TRANSFER RULE</a>, so don't expect any
+exclude side effects.</p>
+<p>A caution for those that choose to combine <a href="#opt--inplace"><code>--inplace</code></a> with
+<code>--update</code>: an interrupted transfer will leave behind a partial file on the
+receiving side that has a very recent modified time, so re-running the
+transfer will probably <strong>not</strong> continue the interrupted file. As such, it
+is usually best to avoid combining this with<a href="#opt--inplace"> <code>--inplace</code></a> unless you
+have implemented manual steps to handle any interrupted in-progress files.</p>
+</dd>
+
+<dt id="opt--inplace"><code>--inplace</code><a href="#opt--inplace" class="tgt"></a></dt><dd>
+<p>This option changes how rsync transfers a file when its data needs to be
+updated: instead of the default method of creating a new copy of the file
+and moving it into place when it is complete, rsync instead writes the
+updated data directly to the destination file.</p>
+<p>This has several effects:</p>
+<ul>
+<li>Hard links are not broken. This means the new data will be visible
+through other hard links to the destination file. Moreover, attempts to
+copy differing source files onto a multiply-linked destination file will
+result in a &quot;tug of war&quot; with the destination data changing back and
+forth.</li>
+<li>In-use binaries cannot be updated (either the OS will prevent this from
+happening, or binaries that attempt to swap-in their data will misbehave
+or crash).</li>
+<li>The file's data will be in an inconsistent state during the transfer and
+will be left that way if the transfer is interrupted or if an update
+fails.</li>
+<li>A file that rsync cannot write to cannot be updated. While a super user
+can update any file, a normal user needs to be granted write permission
+for the open of the file for writing to be successful.</li>
+<li>The efficiency of rsync's delta-transfer algorithm may be reduced if some
+data in the destination file is overwritten before it can be copied to a
+position later in the file. This does not apply if you use <a href="#opt--backup"><code>--backup</code></a>,
+since rsync is smart enough to use the backup file as the basis file for
+the transfer.</li>
+</ul>
+<p>WARNING: you should not use this option to update files that are being
+accessed by others, so be careful when choosing to use this for a copy.</p>
+<p>This option is useful for transferring large files with block-based changes
+or appended data, and also on systems that are disk bound, not network
+bound. It can also help keep a copy-on-write filesystem snapshot from
+diverging the entire contents of a file that only has minor changes.</p>
+<p>The option implies <a href="#opt--partial"><code>--partial</code></a> (since an interrupted transfer does
+not delete the file), but conflicts with <a href="#opt--partial-dir"><code>--partial-dir</code></a> and
+<a href="#opt--delay-updates"><code>--delay-updates</code></a>. Prior to rsync 2.6.4 <code>--inplace</code> was also
+incompatible with <a href="#opt--compare-dest"><code>--compare-dest</code></a> and <a href="#opt--link-dest"><code>--link-dest</code></a>.</p>
+</dd>
+
+<dt id="opt--append"><code>--append</code><a href="#opt--append" class="tgt"></a></dt><dd>
+<p>This special copy mode only works to efficiently update files that are
+known to be growing larger where any existing content on the receiving side
+is also known to be the same as the content on the sender. The use of
+<code>--append</code> <strong>can be dangerous</strong> if you aren't 100% sure that all the files
+in the transfer are shared, growing files. You should thus use filter
+rules to ensure that you weed out any files that do not fit this criteria.</p>
+<p>Rsync updates these growing file in-place without verifying any of the
+existing content in the file (it only verifies the content that it is
+appending). Rsync skips any files that exist on the receiving side that
+are not shorter than the associated file on the sending side (which means
+that new files are transferred). It also skips any files whose size on the
+sending side gets shorter during the send negotiations (rsync warns about a
+&quot;diminished&quot; file when this happens).</p>
+<p>This does not interfere with the updating of a file's non-content
+attributes (e.g. permissions, ownership, etc.) when the file does not need
+to be transferred, nor does it affect the updating of any directories or
+non-regular files.</p>
+</dd>
+
+<dt id="opt--append-verify"><code>--append-verify</code><a href="#opt--append-verify" class="tgt"></a></dt><dd>
+<p>This special copy mode works like <a href="#opt--append"><code>--append</code></a> except that all the
+data in the file is included in the checksum verification (making it less
+efficient but also potentially safer). This option <strong>can be dangerous</strong> if
+you aren't 100% sure that all the files in the transfer are shared, growing
+files. See the <a href="#opt--append"><code>--append</code></a> option for more details.</p>
+<p>Note: prior to rsync 3.0.0, the <a href="#opt--append"><code>--append</code></a> option worked like
+<code>--append-verify</code>, so if you are interacting with an older rsync (or the
+transfer is using a protocol prior to 30), specifying either append option
+will initiate an <code>--append-verify</code> transfer.</p>
+</dd>
+
+<span id="opt-d"></span><dt id="opt--dirs"><code>--dirs</code>, <code>-d</code><a href="#opt--dirs" class="tgt"></a></dt><dd>
+<p>Tell the sending side to include any directories that are encountered.
+Unlike <a href="#opt--recursive"><code>--recursive</code></a>, a directory's contents are not copied unless
+the directory name specified is &quot;.&quot; or ends with a trailing slash (e.g.
+&quot;.&quot;, &quot;dir/.&quot;, &quot;dir/&quot;, etc.). Without this option or the
+<a href="#opt--recursive"><code>--recursive</code></a> option, rsync will skip all directories it encounters
+(and output a message to that effect for each one). If you specify both
+<code>--dirs</code> and <a href="#opt--recursive"><code>--recursive</code></a>, <code>--recursive</code> takes precedence.</p>
+<p>The <code>--dirs</code> option is implied by the <a href="#opt--files-from"><code>--files-from</code></a> option or the
+<a href="#opt--list-only"><code>--list-only</code></a> option (including an implied <a href="#opt--list-only"><code>--list-only</code></a>
+usage) if <a href="#opt--recursive"><code>--recursive</code></a> wasn't specified (so that directories are
+seen in the listing). Specify <code>--no-dirs</code> (or <code>--no-d</code>) if you want to
+turn this off.</p>
+<p>There is also a backward-compatibility helper option, <code>--old-dirs</code>
+(<code>--old-d</code>) that tells rsync to use a hack of <code>-r --exclude='/*/*'</code> to get
+an older rsync to list a single directory without recursing.</p>
+</dd>
+
+<dt id="opt--mkpath"><code>--mkpath</code><a href="#opt--mkpath" class="tgt"></a></dt><dd>
+<p>Create all missing path components of the destination path.</p>
+<p>By default, rsync allows only the final component of the destination path
+to not exist, which is an attempt to help you to validate your destination
+path. With this option, rsync creates all the missing destination-path
+components, just as if <code>mkdir -p $DEST_PATH</code> had been run on the receiving
+side.</p>
+<p>When specifying a destination path, including a trailing slash ensures that
+the whole path is treated as directory names to be created, even when the
+file list has a single item. See the <a href="#COPYING_TO_A_DIFFERENT_NAME">COPYING TO A DIFFERENT NAME</a>
+section for full details on how rsync decides if a final destination-path
+component should be created as a directory or not.</p>
+<p>If you would like the newly-created destination dirs to match the dirs on
+the sending side, you should be using <a href="#opt--relative"><code>--relative</code></a> (<code>-R</code>) instead
+of <code>--mkpath</code>. For instance, the following two commands result in the same
+destination tree, but only the second command ensures that the
+&quot;some/extra/path&quot; components match the dirs on the sending side:</p>
+<blockquote>
+<pre><code>rsync -ai --mkpath host:some/extra/path/*.c some/extra/path/
+rsync -aiR host:some/extra/path/*.c ./
+</code></pre>
+</blockquote>
+</dd>
+
+<span id="opt-l"></span><dt id="opt--links"><code>--links</code>, <code>-l</code><a href="#opt--links" class="tgt"></a></dt><dd>
+<p>Add symlinks to the transferred files instead of noisily ignoring them with
+a &quot;non-regular file&quot; warning for each symlink encountered. You can
+alternately silence the warning by specifying <a href="#opt--info"><code>--info=nonreg0</code></a>.</p>
+<p>The default handling of symlinks is to recreate each symlink's unchanged
+value on the receiving side.</p>
+<p>See the <a href="#SYMBOLIC_LINKS">SYMBOLIC LINKS</a> section for multi-option info.</p>
+</dd>
+
+<span id="opt-L"></span><dt id="opt--copy-links"><code>--copy-links</code>, <code>-L</code><a href="#opt--copy-links" class="tgt"></a></dt><dd>
+<p>The sender transforms each symlink encountered in the transfer into the
+referent item, following the symlink chain to the file or directory that it
+references. If a symlink chain is broken, an error is output and the file
+is dropped from the transfer.</p>
+<p>This option supersedes any other options that affect symlinks in the
+transfer, since there are no symlinks left in the transfer.</p>
+<p>This option does not change the handling of existing symlinks on the
+receiving side, unlike versions of rsync prior to 2.6.3 which had the
+side-effect of telling the receiving side to also follow symlinks. A
+modern rsync won't forward this option to a remote receiver (since only the
+sender needs to know about it), so this caveat should only affect someone
+using an rsync client older than 2.6.7 (which is when <code>-L</code> stopped being
+forwarded to the receiver).</p>
+<p>See the <a href="#opt--keep-dirlinks"><code>--keep-dirlinks</code></a> (<code>-K</code>) if you need a symlink to a
+directory to be treated as a real directory on the receiving side.</p>
+<p>See the <a href="#SYMBOLIC_LINKS">SYMBOLIC LINKS</a> section for multi-option info.</p>
+</dd>
+
+<dt id="opt--copy-unsafe-links"><code>--copy-unsafe-links</code><a href="#opt--copy-unsafe-links" class="tgt"></a></dt><dd>
+<p>This tells rsync to copy the referent of symbolic links that point outside
+the copied tree. Absolute symlinks are also treated like ordinary files,
+and so are any symlinks in the source path itself when <a href="#opt--relative"><code>--relative</code></a>
+is used.</p>
+<p>Note that the cut-off point is the top of the transfer, which is the part
+of the path that rsync isn't mentioning in the verbose output. If you copy
+&quot;/src/subdir&quot; to &quot;/dest/&quot; then the &quot;subdir&quot; directory is a name inside the
+transfer tree, not the top of the transfer (which is /src) so it is legal
+for created relative symlinks to refer to other names inside the /src and
+/dest directories. If you instead copy &quot;/src/subdir/&quot; (with a trailing
+slash) to &quot;/dest/subdir&quot; that would not allow symlinks to any files outside
+of &quot;subdir&quot;.</p>
+<p>Note that safe symlinks are only copied if <a href="#opt--links"><code>--links</code></a> was also
+specified or implied. The <code>--copy-unsafe-links</code> option has no extra effect
+when combined with <a href="#opt--copy-links"><code>--copy-links</code></a>.</p>
+<p>See the <a href="#SYMBOLIC_LINKS">SYMBOLIC LINKS</a> section for multi-option info.</p>
+</dd>
+
+<dt id="opt--safe-links"><code>--safe-links</code><a href="#opt--safe-links" class="tgt"></a></dt><dd>
+<p>This tells the receiving rsync to ignore any symbolic links in the transfer
+which point outside the copied tree. All absolute symlinks are also
+ignored.</p>
+<p>Since this ignoring is happening on the receiving side, it will still be
+effective even when the sending side has munged symlinks (when it is using
+<a href="#opt--munge-links"><code>--munge-links</code></a>). It also affects deletions, since the file being
+present in the transfer prevents any matching file on the receiver from
+being deleted when the symlink is deemed to be unsafe and is skipped.</p>
+<p>This option must be combined with <a href="#opt--links"><code>--links</code></a> (or
+<a href="#opt--archive"><code>--archive</code></a>) to have any symlinks in the transfer to conditionally
+ignore. Its effect is superseded by <a href="#opt--copy-unsafe-links"><code>--copy-unsafe-links</code></a>.</p>
+<p>Using this option in conjunction with <a href="#opt--relative"><code>--relative</code></a> may give
+unexpected results.</p>
+<p>See the <a href="#SYMBOLIC_LINKS">SYMBOLIC LINKS</a> section for multi-option info.</p>
+</dd>
+
+<dt id="opt--munge-links"><code>--munge-links</code><a href="#opt--munge-links" class="tgt"></a></dt><dd>
+<p>This option affects just one side of the transfer and tells rsync to munge
+symlink values when it is receiving files or unmunge symlink values when it
+is sending files. The munged values make the symlinks unusable on disk but
+allows the original contents of the symlinks to be recovered.</p>
+<p>The server-side rsync often enables this option without the client's
+knowledge, such as in an rsync daemon's configuration file or by an option
+given to the rrsync (restricted rsync) script. When specified on the
+client side, specify the option normally if it is the client side that
+has/needs the munged symlinks, or use <code>-M--munge-links</code> to give the option
+to the server when it has/needs the munged symlinks. Note that on a local
+transfer, the client is the sender, so specifying the option directly
+unmunges symlinks while specifying it as a remote option munges symlinks.</p>
+<p>This option has no effect when sent to a daemon via <a href="#opt--remote-option"><code>--remote-option</code></a>
+because the daemon configures whether it wants munged symlinks via its
+&quot;<code>munge symlinks</code>&quot; parameter.</p>
+<p>The symlink value is munged/unmunged once it is in the transfer, so any
+option that transforms symlinks into non-symlinks occurs prior to the
+munging/unmunging <strong>except</strong> for <a href="#opt--safe-links"><code>--safe-links</code></a>, which is a choice
+that the receiver makes, so it bases its decision on the munged/unmunged
+value. This does mean that if a receiver has munging enabled, that using
+<a href="#opt--safe-links"><code>--safe-links</code></a> will cause all symlinks to be ignored (since they
+are all absolute).</p>
+<p>The method that rsync uses to munge the symlinks is to prefix each one's
+value with the string &quot;/rsyncd-munged/&quot;. This prevents the links from
+being used as long as the directory does not exist. When this option is
+enabled, rsync will refuse to run if that path is a directory or a symlink
+to a directory (though it only checks at startup). See also the
+&quot;munge-symlinks&quot; python script in the support directory of the source code
+for a way to munge/unmunge one or more symlinks in-place.</p>
+</dd>
+
+<span id="opt-k"></span><dt id="opt--copy-dirlinks"><code>--copy-dirlinks</code>, <code>-k</code><a href="#opt--copy-dirlinks" class="tgt"></a></dt><dd>
+<p>This option causes the sending side to treat a symlink to a directory as
+though it were a real directory. This is useful if you don't want symlinks
+to non-directories to be affected, as they would be using
+<a href="#opt--copy-links"><code>--copy-links</code></a>.</p>
+<p>Without this option, if the sending side has replaced a directory with a
+symlink to a directory, the receiving side will delete anything that is in
+the way of the new symlink, including a directory hierarchy (as long as
+<a href="#opt--force"><code>--force</code></a> or <a href="#opt--delete"><code>--delete</code></a> is in effect).</p>
+<p>See also <a href="#opt--keep-dirlinks"><code>--keep-dirlinks</code></a> for an analogous option for the
+receiving side.</p>
+<p><code>--copy-dirlinks</code> applies to all symlinks to directories in the source. If
+you want to follow only a few specified symlinks, a trick you can use is to
+pass them as additional source args with a trailing slash, using
+<a href="#opt--relative"><code>--relative</code></a> to make the paths match up right. For example:</p>
+<blockquote>
+<pre><code>rsync -r --relative src/./ src/./follow-me/ dest/
+</code></pre>
+</blockquote>
+<p>This works because rsync calls <strong>lstat</strong>(2) on the source arg as given, and
+the trailing slash makes <strong>lstat</strong>(2) follow the symlink, giving rise to a
+directory in the file-list which overrides the symlink found during the
+scan of &quot;src/./&quot;.</p>
+<p>See the <a href="#SYMBOLIC_LINKS">SYMBOLIC LINKS</a> section for multi-option info.</p>
+</dd>
+
+<span id="opt-K"></span><dt id="opt--keep-dirlinks"><code>--keep-dirlinks</code>, <code>-K</code><a href="#opt--keep-dirlinks" class="tgt"></a></dt><dd>
+<p>This option causes the receiving side to treat a symlink to a directory as
+though it were a real directory, but only if it matches a real directory
+from the sender. Without this option, the receiver's symlink would be
+deleted and replaced with a real directory.</p>
+<p>For example, suppose you transfer a directory &quot;foo&quot; that contains a file
+&quot;file&quot;, but &quot;foo&quot; is a symlink to directory &quot;bar&quot; on the receiver. Without
+<code>--keep-dirlinks</code>, the receiver deletes symlink &quot;foo&quot;, recreates it as a
+directory, and receives the file into the new directory. With
+<code>--keep-dirlinks</code>, the receiver keeps the symlink and &quot;file&quot; ends up in
+&quot;bar&quot;.</p>
+<p>One note of caution: if you use <code>--keep-dirlinks</code>, you must trust all the
+symlinks in the copy or enable the <a href="#opt--munge-links"><code>--munge-links</code></a> option on the
+receiving side! If it is possible for an untrusted user to create their
+own symlink to any real directory, the user could then (on a subsequent
+copy) replace the symlink with a real directory and affect the content of
+whatever directory the symlink references. For backup copies, you are
+better off using something like a bind mount instead of a symlink to modify
+your receiving hierarchy.</p>
+<p>See also <a href="#opt--copy-dirlinks"><code>--copy-dirlinks</code></a> for an analogous option for the sending
+side.</p>
+<p>See the <a href="#SYMBOLIC_LINKS">SYMBOLIC LINKS</a> section for multi-option info.</p>
+</dd>
+
+<span id="opt-H"></span><dt id="opt--hard-links"><code>--hard-links</code>, <code>-H</code><a href="#opt--hard-links" class="tgt"></a></dt><dd>
+<p>This tells rsync to look for hard-linked files in the source and link
+together the corresponding files on the destination. Without this option,
+hard-linked files in the source are treated as though they were separate
+files.</p>
+<p>This option does NOT necessarily ensure that the pattern of hard links on
+the destination exactly matches that on the source. Cases in which the
+destination may end up with extra hard links include the following:</p>
+<ul>
+<li>If the destination contains extraneous hard-links (more linking than what
+is present in the source file list), the copying algorithm will not break
+them explicitly. However, if one or more of the paths have content
+differences, the normal file-update process will break those extra links
+(unless you are using the <a href="#opt--inplace"><code>--inplace</code></a> option).</li>
+<li>If you specify a <a href="#opt--link-dest"><code>--link-dest</code></a> directory that contains hard
+links, the linking of the destination files against the
+<a href="#opt--link-dest"><code>--link-dest</code></a> files can cause some paths in the destination to
+become linked together due to the <a href="#opt--link-dest"><code>--link-dest</code></a> associations.</li>
+</ul>
+<p>Note that rsync can only detect hard links between files that are inside
+the transfer set. If rsync updates a file that has extra hard-link
+connections to files outside the transfer, that linkage will be broken. If
+you are tempted to use the <a href="#opt--inplace"><code>--inplace</code></a> option to avoid this breakage, be
+very careful that you know how your files are being updated so that you are
+certain that no unintended changes happen due to lingering hard links (and
+see the <a href="#opt--inplace"><code>--inplace</code></a> option for more caveats).</p>
+<p>If incremental recursion is active (see <a href="#opt--inc-recursive"><code>--inc-recursive</code></a>), rsync
+may transfer a missing hard-linked file before it finds that another link
+for that contents exists elsewhere in the hierarchy. This does not affect
+the accuracy of the transfer (i.e. which files are hard-linked together),
+just its efficiency (i.e. copying the data for a new, early copy of a
+hard-linked file that could have been found later in the transfer in
+another member of the hard-linked set of files). One way to avoid this
+inefficiency is to disable incremental recursion using the
+<a href="#opt--no-inc-recursive"><code>--no-inc-recursive</code></a> option.</p>
+</dd>
+
+<span id="opt-p"></span><dt id="opt--perms"><code>--perms</code>, <code>-p</code><a href="#opt--perms" class="tgt"></a></dt><dd>
+<p>This option causes the receiving rsync to set the destination permissions
+to be the same as the source permissions. (See also the <a href="#opt--chmod"><code>--chmod</code></a>
+option for a way to modify what rsync considers to be the source
+permissions.)</p>
+<p>When this option is <u>off</u>, permissions are set as follows:</p>
+<ul>
+<li>Existing files (including updated files) retain their existing
+permissions, though the <a href="#opt--executability"><code>--executability</code></a> option might change
+just the execute permission for the file.</li>
+<li>New files get their &quot;normal&quot; permission bits set to the source file's
+permissions masked with the receiving directory's default permissions
+(either the receiving process's umask, or the permissions specified via
+the destination directory's default ACL), and their special permission
+bits disabled except in the case where a new directory inherits a setgid
+bit from its parent directory.</li>
+</ul>
+<p>Thus, when <code>--perms</code> and <a href="#opt--executability"><code>--executability</code></a> are both disabled, rsync's
+behavior is the same as that of other file-copy utilities, such as <strong>cp</strong>(1)
+and <strong>tar</strong>(1).</p>
+<p>In summary: to give destination files (both old and new) the source
+permissions, use <code>--perms</code>. To give new files the destination-default
+permissions (while leaving existing files unchanged), make sure that the
+<code>--perms</code> option is off and use <a href="#opt--chmod"><code>--chmod=ugo=rwX</code></a> (which ensures
+that all non-masked bits get enabled). If you'd care to make this latter
+behavior easier to type, you could define a popt alias for it, such as
+putting this line in the file <code>~/.popt</code> (the following defines the <code>-Z</code>
+option, and includes <code>--no-g</code> to use the default group of the destination
+dir):</p>
+<blockquote>
+<pre><code> rsync alias -Z --no-p --no-g --chmod=ugo=rwX
+</code></pre>
+</blockquote>
+<p>You could then use this new option in a command such as this one:</p>
+<blockquote>
+<pre><code> rsync -avZ src/ dest/
+</code></pre>
+</blockquote>
+<p>(Caveat: make sure that <code>-a</code> does not follow <code>-Z</code>, or it will re-enable the
+two <code>--no-*</code> options mentioned above.)</p>
+<p>The preservation of the destination's setgid bit on newly-created
+directories when <code>--perms</code> is off was added in rsync 2.6.7. Older rsync
+versions erroneously preserved the three special permission bits for
+newly-created files when <code>--perms</code> was off, while overriding the
+destination's setgid bit setting on a newly-created directory. Default ACL
+observance was added to the ACL patch for rsync 2.6.7, so older (or
+non-ACL-enabled) rsyncs use the umask even if default ACLs are present.
+(Keep in mind that it is the version of the receiving rsync that affects
+these behaviors.)</p>
+</dd>
+
+<span id="opt-E"></span><dt id="opt--executability"><code>--executability</code>, <code>-E</code><a href="#opt--executability" class="tgt"></a></dt><dd>
+<p>This option causes rsync to preserve the executability (or
+non-executability) of regular files when <a href="#opt--perms"><code>--perms</code></a> is not enabled.
+A regular file is considered to be executable if at least one 'x' is turned
+on in its permissions. When an existing destination file's executability
+differs from that of the corresponding source file, rsync modifies the
+destination file's permissions as follows:</p>
+<ul>
+<li>To make a file non-executable, rsync turns off all its 'x' permissions.</li>
+<li>To make a file executable, rsync turns on each 'x' permission that has a
+corresponding 'r' permission enabled.</li>
+</ul>
+<p>If <a href="#opt--perms"><code>--perms</code></a> is enabled, this option is ignored.</p>
+</dd>
+
+<span id="opt-A"></span><dt id="opt--acls"><code>--acls</code>, <code>-A</code><a href="#opt--acls" class="tgt"></a></dt><dd>
+<p>This option causes rsync to update the destination ACLs to be the same as
+the source ACLs. The option also implies <a href="#opt--perms"><code>--perms</code></a>.</p>
+<p>The source and destination systems must have compatible ACL entries for
+this option to work properly. See the <a href="#opt--fake-super"><code>--fake-super</code></a> option for a
+way to backup and restore ACLs that are not compatible.</p>
+</dd>
+
+<span id="opt-X"></span><dt id="opt--xattrs"><code>--xattrs</code>, <code>-X</code><a href="#opt--xattrs" class="tgt"></a></dt><dd>
+<p>This option causes rsync to update the destination extended attributes to
+be the same as the source ones.</p>
+<p>For systems that support extended-attribute namespaces, a copy being done
+by a super-user copies all namespaces except system.*. A normal user only
+copies the user.* namespace. To be able to backup and restore non-user
+namespaces as a normal user, see the <a href="#opt--fake-super"><code>--fake-super</code></a> option.</p>
+<p>The above name filtering can be overridden by using one or more filter
+options with the <strong>x</strong> modifier. When you specify an xattr-affecting
+filter rule, rsync requires that you do your own system/user filtering, as
+well as any additional filtering for what xattr names are copied and what
+names are allowed to be deleted. For example, to skip the system
+namespace, you could specify:</p>
+<blockquote>
+<pre><code>--filter='-x system.*'
+</code></pre>
+</blockquote>
+<p>To skip all namespaces except the user namespace, you could specify a
+negated-user match:</p>
+<blockquote>
+<pre><code>--filter='-x! user.*'
+</code></pre>
+</blockquote>
+<p>To prevent any attributes from being deleted, you could specify a
+receiver-only rule that excludes all names:</p>
+<blockquote>
+<pre><code>--filter='-xr *'
+</code></pre>
+</blockquote>
+<p>Note that the <code>-X</code> option does not copy rsync's special xattr values (e.g.
+those used by <a href="#opt--fake-super"><code>--fake-super</code></a>) unless you repeat the option (e.g. <code>-XX</code>).
+This &quot;copy all xattrs&quot; mode cannot be used with <a href="#opt--fake-super"><code>--fake-super</code></a>.</p>
+</dd>
+
+<dt id="opt--chmod"><code>--chmod=CHMOD</code><a href="#opt--chmod" class="tgt"></a></dt><dd>
+<p>This option tells rsync to apply one or more comma-separated &quot;chmod&quot; modes
+to the permission of the files in the transfer. The resulting value is
+treated as though it were the permissions that the sending side supplied
+for the file, which means that this option can seem to have no effect on
+existing files if <a href="#opt--perms"><code>--perms</code></a> is not enabled.</p>
+<p>In addition to the normal parsing rules specified in the <strong>chmod</strong>(1)
+manpage, you can specify an item that should only apply to a directory by
+prefixing it with a 'D', or specify an item that should only apply to a
+file by prefixing it with a 'F'. For example, the following will ensure
+that all directories get marked set-gid, that no files are other-writable,
+that both are user-writable and group-writable, and that both have
+consistent executability across all bits:</p>
+<blockquote>
+<pre><code>--chmod=Dg+s,ug+w,Fo-w,+X
+</code></pre>
+</blockquote>
+<p>Using octal mode numbers is also allowed:</p>
+<blockquote>
+<pre><code>--chmod=D2775,F664
+</code></pre>
+</blockquote>
+<p>It is also legal to specify multiple <code>--chmod</code> options, as each additional
+option is just appended to the list of changes to make.</p>
+<p>See the <a href="#opt--perms"><code>--perms</code></a> and <a href="#opt--executability"><code>--executability</code></a> options for how the
+resulting permission value can be applied to the files in the transfer.</p>
+</dd>
+
+<span id="opt-o"></span><dt id="opt--owner"><code>--owner</code>, <code>-o</code><a href="#opt--owner" class="tgt"></a></dt><dd>
+<p>This option causes rsync to set the owner of the destination file to be the
+same as the source file, but only if the receiving rsync is being run as
+the super-user (see also the <a href="#opt--super"><code>--super</code></a> and <a href="#opt--fake-super"><code>--fake-super</code></a>
+options). Without this option, the owner of new and/or transferred files
+are set to the invoking user on the receiving side.</p>
+<p>The preservation of ownership will associate matching names by default, but
+may fall back to using the ID number in some circumstances (see also the
+<a href="#opt--numeric-ids"><code>--numeric-ids</code></a> option for a full discussion).</p>
+</dd>
+
+<span id="opt-g"></span><dt id="opt--group"><code>--group</code>, <code>-g</code><a href="#opt--group" class="tgt"></a></dt><dd>
+<p>This option causes rsync to set the group of the destination file to be the
+same as the source file. If the receiving program is not running as the
+super-user (or if <code>--no-super</code> was specified), only groups that the
+invoking user on the receiving side is a member of will be preserved.
+Without this option, the group is set to the default group of the invoking
+user on the receiving side.</p>
+<p>The preservation of group information will associate matching names by
+default, but may fall back to using the ID number in some circumstances
+(see also the <a href="#opt--numeric-ids"><code>--numeric-ids</code></a> option for a full discussion).</p>
+</dd>
+
+<dt id="opt--devices"><code>--devices</code><a href="#opt--devices" class="tgt"></a></dt><dd>
+<p>This option causes rsync to transfer character and block device files to
+the remote system to recreate these devices. If the receiving rsync is not
+being run as the super-user, rsync silently skips creating the device files
+(see also the <a href="#opt--super"><code>--super</code></a> and <a href="#opt--fake-super"><code>--fake-super</code></a> options).</p>
+<p>By default, rsync generates a &quot;non-regular file&quot; warning for each device
+file encountered when this option is not set. You can silence the warning
+by specifying <a href="#opt--info"><code>--info=nonreg0</code></a>.</p>
+</dd>
+
+<dt id="opt--specials"><code>--specials</code><a href="#opt--specials" class="tgt"></a></dt><dd>
+<p>This option causes rsync to transfer special files, such as named sockets
+and fifos. If the receiving rsync is not being run as the super-user,
+rsync silently skips creating the special files (see also the
+<a href="#opt--super"><code>--super</code></a> and <a href="#opt--fake-super"><code>--fake-super</code></a> options).</p>
+<p>By default, rsync generates a &quot;non-regular file&quot; warning for each special
+file encountered when this option is not set. You can silence the warning
+by specifying <a href="#opt--info"><code>--info=nonreg0</code></a>.</p>
+</dd>
+
+<dt id="opt-D"><code>-D</code><a href="#opt-D" class="tgt"></a></dt><dd>
+<p>The <code>-D</code> option is equivalent to &quot;<a href="#opt--devices"><code>--devices</code></a>
+<a href="#opt--specials"><code>--specials</code></a>&quot;.</p>
+</dd>
+
+<dt id="opt--copy-devices"><code>--copy-devices</code><a href="#opt--copy-devices" class="tgt"></a></dt><dd>
+<p>This tells rsync to treat a device on the sending side as a regular file,
+allowing it to be copied to a normal destination file (or another device
+if <code>--write-devices</code> was also specified).</p>
+<p>This option is refused by default by an rsync daemon.</p>
+</dd>
+
+<dt id="opt--write-devices"><code>--write-devices</code><a href="#opt--write-devices" class="tgt"></a></dt><dd>
+<p>This tells rsync to treat a device on the receiving side as a regular file,
+allowing the writing of file data into a device.</p>
+<p>This option implies the <a href="#opt--inplace"><code>--inplace</code></a> option.</p>
+<p>Be careful using this, as you should know what devices are present on the
+receiving side of the transfer, especially when running rsync as root.</p>
+<p>This option is refused by default by an rsync daemon.</p>
+</dd>
+
+<span id="opt-t"></span><dt id="opt--times"><code>--times</code>, <code>-t</code><a href="#opt--times" class="tgt"></a></dt><dd>
+<p>This tells rsync to transfer modification times along with the files and
+update them on the remote system. Note that if this option is not used,
+the optimization that excludes files that have not been modified cannot be
+effective; in other words, a missing <code>-t</code> (or <a href="#opt-a"><code>-a</code></a>) will cause the
+next transfer to behave as if it used <a href="#opt--ignore-times"><code>--ignore-times</code></a> (<code>-I</code>),
+causing all files to be updated (though rsync's delta-transfer algorithm
+will make the update fairly efficient if the files haven't actually
+changed, you're much better off using <code>-t</code>).</p>
+<p>A modern rsync that is using transfer protocol 30 or 31 conveys a modify
+time using up to 8-bytes. If rsync is forced to speak an older protocol
+(perhaps due to the remote rsync being older than 3.0.0) a modify time is
+conveyed using 4-bytes. Prior to 3.2.7, these shorter values could convey
+a date range of 13-Dec-1901 to 19-Jan-2038. Beginning with 3.2.7, these
+4-byte values now convey a date range of 1-Jan-1970 to 7-Feb-2106. If you
+have files dated older than 1970, make sure your rsync executables are
+upgraded so that the full range of dates can be conveyed.</p>
+</dd>
+
+<span id="opt-U"></span><dt id="opt--atimes"><code>--atimes</code>, <code>-U</code><a href="#opt--atimes" class="tgt"></a></dt><dd>
+<p>This tells rsync to set the access (use) times of the destination files to
+the same value as the source files.</p>
+<p>If repeated, it also sets the <a href="#opt--open-noatime"><code>--open-noatime</code></a> option, which can help you
+to make the sending and receiving systems have the same access times on the
+transferred files without needing to run rsync an extra time after a file
+is transferred.</p>
+<p>Note that some older rsync versions (prior to 3.2.0) may have been built
+with a pre-release <code>--atimes</code> patch that does not imply
+<a href="#opt--open-noatime"><code>--open-noatime</code></a> when this option is repeated.</p>
+</dd>
+
+<dt id="opt--open-noatime"><code>--open-noatime</code><a href="#opt--open-noatime" class="tgt"></a></dt><dd>
+<p>This tells rsync to open files with the O_NOATIME flag (on systems that
+support it) to avoid changing the access time of the files that are being
+transferred. If your OS does not support the O_NOATIME flag then rsync
+will silently ignore this option. Note also that some filesystems are
+mounted to avoid updating the atime on read access even without the
+O_NOATIME flag being set.</p>
+</dd>
+
+<span id="opt-N_"></span><dt id="opt--crtimes"><code>--crtimes</code>, <code>-N,</code><a href="#opt--crtimes" class="tgt"></a></dt><dd>
+<p>This tells rsync to set the create times (newness) of the destination
+files to the same value as the source files.</p>
+</dd>
+
+<span id="opt-O"></span><dt id="opt--omit-dir-times"><code>--omit-dir-times</code>, <code>-O</code><a href="#opt--omit-dir-times" class="tgt"></a></dt><dd>
+<p>This tells rsync to omit directories when it is preserving modification,
+access, and create times. If NFS is sharing the directories on the receiving
+side, it is a good idea to use <code>-O</code>. This option is inferred if you use
+<a href="#opt--backup"><code>--backup</code></a> without <a href="#opt--backup-dir"><code>--backup-dir</code></a>.</p>
+<p>This option also has the side-effect of avoiding early creation of missing
+sub-directories when incremental recursion is enabled, as discussed in the
+<a href="#opt--inc-recursive"><code>--inc-recursive</code></a> section.</p>
+</dd>
+
+<span id="opt-J"></span><dt id="opt--omit-link-times"><code>--omit-link-times</code>, <code>-J</code><a href="#opt--omit-link-times" class="tgt"></a></dt><dd>
+<p>This tells rsync to omit symlinks when it is preserving modification,
+access, and create times.</p>
+</dd>
+
+<dt id="opt--super"><code>--super</code><a href="#opt--super" class="tgt"></a></dt><dd>
+<p>This tells the receiving side to attempt super-user activities even if the
+receiving rsync wasn't run by the super-user. These activities include:
+preserving users via the <a href="#opt--owner"><code>--owner</code></a> option, preserving all groups
+(not just the current user's groups) via the <a href="#opt--group"><code>--group</code></a> option, and
+copying devices via the <a href="#opt--devices"><code>--devices</code></a> option. This is useful for
+systems that allow such activities without being the super-user, and also
+for ensuring that you will get errors if the receiving side isn't being run
+as the super-user. To turn off super-user activities, the super-user can
+use <code>--no-super</code>.</p>
+</dd>
+
+<dt id="opt--fake-super"><code>--fake-super</code><a href="#opt--fake-super" class="tgt"></a></dt><dd>
+<p>When this option is enabled, rsync simulates super-user activities by
+saving/restoring the privileged attributes via special extended attributes
+that are attached to each file (as needed). This includes the file's owner
+and group (if it is not the default), the file's device info (device &amp;
+special files are created as empty text files), and any permission bits
+that we won't allow to be set on the real file (e.g. the real file gets
+u-s,g-s,o-t for safety) or that would limit the owner's access (since the
+real super-user can always access/change a file, the files we create can
+always be accessed/changed by the creating user). This option also handles
+ACLs (if <a href="#opt--acls"><code>--acls</code></a> was specified) and non-user extended attributes
+(if <a href="#opt--xattrs"><code>--xattrs</code></a> was specified).</p>
+<p>This is a good way to backup data without using a super-user, and to store
+ACLs from incompatible systems.</p>
+<p>The <code>--fake-super</code> option only affects the side where the option is used.
+To affect the remote side of a remote-shell connection, use the
+<a href="#opt--remote-option"><code>--remote-option</code></a> (<code>-M</code>) option:</p>
+<blockquote>
+<pre><code>rsync -av -M--fake-super /src/ host:/dest/
+</code></pre>
+</blockquote>
+<p>For a local copy, this option affects both the source and the destination.
+If you wish a local copy to enable this option just for the destination
+files, specify <code>-M--fake-super</code>. If you wish a local copy to enable this
+option just for the source files, combine <code>--fake-super</code> with <code>-M--super</code>.</p>
+<p>This option is overridden by both <a href="#opt--super"><code>--super</code></a> and <code>--no-super</code>.</p>
+<p>See also the <a href="rsyncd.conf.5#fake_super"><code>fake super</code></a> setting in the
+daemon's rsyncd.conf file.</p>
+</dd>
+
+<span id="opt-S"></span><dt id="opt--sparse"><code>--sparse</code>, <code>-S</code><a href="#opt--sparse" class="tgt"></a></dt><dd>
+<p>Try to handle sparse files efficiently so they take up less space on the
+destination. If combined with <a href="#opt--inplace"><code>--inplace</code></a> the file created might
+not end up with sparse blocks with some combinations of kernel version
+and/or filesystem type. If <a href="#opt--whole-file"><code>--whole-file</code></a> is in effect (e.g. for a
+local copy) then it will always work because rsync truncates the file prior
+to writing out the updated version.</p>
+<p>Note that versions of rsync older than 3.1.3 will reject the combination of
+<code>--sparse</code> and <a href="#opt--inplace"><code>--inplace</code></a>.</p>
+</dd>
+
+<dt id="opt--preallocate"><code>--preallocate</code><a href="#opt--preallocate" class="tgt"></a></dt><dd>
+<p>This tells the receiver to allocate each destination file to its eventual
+size before writing data to the file. Rsync will only use the real
+filesystem-level preallocation support provided by Linux's <strong>fallocate</strong>(2)
+system call or Cygwin's <strong>posix_fallocate</strong>(3), not the slow glibc
+implementation that writes a null byte into each block.</p>
+<p>Without this option, larger files may not be entirely contiguous on the
+filesystem, but with this option rsync will probably copy more slowly. If
+the destination is not an extent-supporting filesystem (such as ext4, xfs,
+NTFS, etc.), this option may have no positive effect at all.</p>
+<p>If combined with <a href="#opt--sparse"><code>--sparse</code></a>, the file will only have sparse blocks
+(as opposed to allocated sequences of null bytes) if the kernel version and
+filesystem type support creating holes in the allocated data.</p>
+</dd>
+
+<span id="opt-n"></span><dt id="opt--dry-run"><code>--dry-run</code>, <code>-n</code><a href="#opt--dry-run" class="tgt"></a></dt><dd>
+<p>This makes rsync perform a trial run that doesn't make any changes (and
+produces mostly the same output as a real run). It is most commonly used
+in combination with the <a href="#opt--verbose"><code>--verbose</code></a> (<code>-v</code>) and/or
+<a href="#opt--itemize-changes"><code>--itemize-changes</code></a> (<code>-i</code>) options to see what an rsync command is
+going to do before one actually runs it.</p>
+<p>The output of <a href="#opt--itemize-changes"><code>--itemize-changes</code></a> is supposed to be exactly the
+same on a dry run and a subsequent real run (barring intentional trickery
+and system call failures); if it isn't, that's a bug. Other output should
+be mostly unchanged, but may differ in some areas. Notably, a dry run does
+not send the actual data for file transfers, so <a href="#opt--progress"><code>--progress</code></a> has no
+effect, the &quot;bytes sent&quot;, &quot;bytes received&quot;, &quot;literal data&quot;, and &quot;matched
+data&quot; statistics are too small, and the &quot;speedup&quot; value is equivalent to a
+run where no file transfers were needed.</p>
+</dd>
+
+<span id="opt-W"></span><dt id="opt--whole-file"><code>--whole-file</code>, <code>-W</code><a href="#opt--whole-file" class="tgt"></a></dt><dd>
+<p>This option disables rsync's delta-transfer algorithm, which causes all
+transferred files to be sent whole. The transfer may be faster if this
+option is used when the bandwidth between the source and destination
+machines is higher than the bandwidth to disk (especially when the &quot;disk&quot;
+is actually a networked filesystem). This is the default when both the
+source and destination are specified as local paths, but only if no
+batch-writing option is in effect.</p>
+</dd>
+
+<span id="opt--no-W"></span><dt id="opt--no-whole-file"><code>--no-whole-file</code>, <code>--no-W</code><a href="#opt--no-whole-file" class="tgt"></a></dt><dd>
+<p>Disable whole-file updating when it is enabled by default for a local
+transfer. This usually slows rsync down, but it can be useful if you are
+trying to minimize the writes to the destination file (if combined with
+<a href="#opt--inplace"><code>--inplace</code></a>) or for testing the checksum-based update algorithm.</p>
+<p>See also the <a href="#opt--whole-file"><code>--whole-file</code></a> option.</p>
+</dd>
+
+<span id="opt--cc"></span><dt id="opt--checksum-choice"><code>--checksum-choice=STR</code>, <code>--cc=STR</code><a href="#opt--checksum-choice" class="tgt"></a></dt><dd>
+<p>This option overrides the checksum algorithms. If one algorithm name is
+specified, it is used for both the transfer checksums and (assuming
+<a href="#opt--checksum"><code>--checksum</code></a> is specified) the pre-transfer checksums. If two
+comma-separated names are supplied, the first name affects the transfer
+checksums, and the second name affects the pre-transfer checksums (<code>-c</code>).</p>
+<p>The checksum options that you may be able to use are:</p>
+<ul>
+<li><code>auto</code> (the default automatic choice)</li>
+<li><code>xxh128</code></li>
+<li><code>xxh3</code></li>
+<li><code>xxh64</code> (aka <code>xxhash</code>)</li>
+<li><code>md5</code></li>
+<li><code>md4</code></li>
+<li><code>sha1</code></li>
+<li><code>none</code></li>
+</ul>
+<p>Run <code>rsync --version</code> to see the default checksum list compiled into your
+version (which may differ from the list above).</p>
+<p>If &quot;none&quot; is specified for the first (or only) name, the <a href="#opt--whole-file"><code>--whole-file</code></a>
+option is forced on and no checksum verification is performed on the
+transferred data. If &quot;none&quot; is specified for the second (or only) name,
+the <a href="#opt--checksum"><code>--checksum</code></a> option cannot be used.</p>
+<p>The &quot;auto&quot; option is the default, where rsync bases its algorithm choice on
+a negotiation between the client and the server as follows:</p>
+<p>When both sides of the transfer are at least 3.2.0, rsync chooses the first
+algorithm in the client's list of choices that is also in the server's list
+of choices. If no common checksum choice is found, rsync exits with
+an error. If the remote rsync is too old to support checksum negotiation,
+a value is chosen based on the protocol version (which chooses between MD5
+and various flavors of MD4 based on protocol age).</p>
+<p>The default order can be customized by setting the environment variable
+<a href="#RSYNC_CHECKSUM_LIST"><code>RSYNC_CHECKSUM_LIST</code></a> to a space-separated list of acceptable checksum
+names. If the string contains a &quot;<code>&amp;</code>&quot; character, it is separated into the
+&quot;client string &amp; server string&quot;, otherwise the same string applies to both.
+If the string (or string portion) contains no non-whitespace characters,
+the default checksum list is used. This method does not allow you to
+specify the transfer checksum separately from the pre-transfer checksum,
+and it discards &quot;auto&quot; and all unknown checksum names. A list with only
+invalid names results in a failed negotiation.</p>
+<p>The use of the <code>--checksum-choice</code> option overrides this environment list.</p>
+</dd>
+
+<span id="opt-x"></span><dt id="opt--one-file-system"><code>--one-file-system</code>, <code>-x</code><a href="#opt--one-file-system" class="tgt"></a></dt><dd>
+<p>This tells rsync to avoid crossing a filesystem boundary when recursing.
+This does not limit the user's ability to specify items to copy from
+multiple filesystems, just rsync's recursion through the hierarchy of each
+directory that the user specified, and also the analogous recursion on the
+receiving side during deletion. Also keep in mind that rsync treats a
+&quot;bind&quot; mount to the same device as being on the same filesystem.</p>
+<p>If this option is repeated, rsync omits all mount-point directories from
+the copy. Otherwise, it includes an empty directory at each mount-point it
+encounters (using the attributes of the mounted directory because those of
+the underlying mount-point directory are inaccessible).</p>
+<p>If rsync has been told to collapse symlinks (via <a href="#opt--copy-links"><code>--copy-links</code></a> or
+<a href="#opt--copy-unsafe-links"><code>--copy-unsafe-links</code></a>), a symlink to a directory on another device
+is treated like a mount-point. Symlinks to non-directories are unaffected
+by this option.</p>
+</dd>
+
+<span id="opt--existing"></span><dt id="opt--ignore-non-existing"><code>--ignore-non-existing</code>, <code>--existing</code><a href="#opt--ignore-non-existing" class="tgt"></a></dt><dd>
+<p>This tells rsync to skip creating files (including directories) that do not
+exist yet on the destination. If this option is combined with the
+<a href="#opt--ignore-existing"><code>--ignore-existing</code></a> option, no files will be updated (which can be
+useful if all you want to do is delete extraneous files).</p>
+<p>This option is a <a href="#TRANSFER_RULES">TRANSFER RULE</a>, so don't expect any
+exclude side effects.</p>
+</dd>
+
+<dt id="opt--ignore-existing"><code>--ignore-existing</code><a href="#opt--ignore-existing" class="tgt"></a></dt><dd>
+<p>This tells rsync to skip updating files that already exist on the
+destination (this does <u>not</u> ignore existing directories, or nothing would
+get done). See also <a href="#opt--ignore-non-existing"><code>--ignore-non-existing</code></a>.</p>
+<p>This option is a <a href="#TRANSFER_RULES">TRANSFER RULE</a>, so don't expect any
+exclude side effects.</p>
+<p>This option can be useful for those doing backups using the
+<a href="#opt--link-dest"><code>--link-dest</code></a> option when they need to continue a backup run that
+got interrupted. Since a <a href="#opt--link-dest"><code>--link-dest</code></a> run is copied into a new
+directory hierarchy (when it is used properly), using [<code>--ignore-existing</code>
+will ensure that the already-handled files don't get tweaked (which avoids
+a change in permissions on the hard-linked files). This does mean that
+this option is only looking at the existing files in the destination
+hierarchy itself.</p>
+<p>When <a href="#opt--info"><code>--info=skip2</code></a> is used rsync will output &quot;FILENAME exists
+(INFO)&quot; messages where the INFO indicates one of &quot;type change&quot;, &quot;sum
+change&quot; (requires <a href="#opt-c"><code>-c</code></a>), &quot;file change&quot; (based on the quick check),
+&quot;attr change&quot;, or &quot;uptodate&quot;. Using <a href="#opt--info"><code>--info=skip1</code></a> (which is also
+implied by 2 <a href="#opt-v"><code>-v</code></a> options) outputs the exists message without the
+INFO suffix.</p>
+</dd>
+
+<dt id="opt--remove-source-files"><code>--remove-source-files</code><a href="#opt--remove-source-files" class="tgt"></a></dt><dd>
+<p>This tells rsync to remove from the sending side the files (meaning
+non-directories) that are a part of the transfer and have been successfully
+duplicated on the receiving side.</p>
+<p>Note that you should only use this option on source files that are
+quiescent. If you are using this to move files that show up in a
+particular directory over to another host, make sure that the finished
+files get renamed into the source directory, not directly written into it,
+so that rsync can't possibly transfer a file that is not yet fully written.
+If you can't first write the files into a different directory, you should
+use a naming idiom that lets rsync avoid transferring files that are not
+yet finished (e.g. name the file &quot;foo.new&quot; when it is written, rename it to
+&quot;foo&quot; when it is done, and then use the option <a href="#opt--exclude"><code>--exclude='*.new'</code></a>
+for the rsync transfer).</p>
+<p>Starting with 3.1.0, rsync will skip the sender-side removal (and output an
+error) if the file's size or modify time has not stayed unchanged.</p>
+<p>Starting with 3.2.6, a local rsync copy will ensure that the sender does
+not remove a file the receiver just verified, such as when the user
+accidentally makes the source and destination directory the same path.</p>
+</dd>
+
+<dt id="opt--delete"><code>--delete</code><a href="#opt--delete" class="tgt"></a></dt><dd>
+<p>This tells rsync to delete extraneous files from the receiving side (ones
+that aren't on the sending side), but only for the directories that are
+being synchronized. You must have asked rsync to send the whole directory
+(e.g. &quot;<code>dir</code>&quot; or &quot;<code>dir/</code>&quot;) without using a wildcard for the directory's
+contents (e.g. &quot;<code>dir/*</code>&quot;) since the wildcard is expanded by the shell and
+rsync thus gets a request to transfer individual files, not the files'
+parent directory. Files that are excluded from the transfer are also
+excluded from being deleted unless you use the <a href="#opt--delete-excluded"><code>--delete-excluded</code></a>
+option or mark the rules as only matching on the sending side (see the
+include/exclude modifiers in the <a href="#FILTER_RULES">FILTER RULES</a> section).</p>
+<p>Prior to rsync 2.6.7, this option would have no effect unless
+<a href="#opt--recursive"><code>--recursive</code></a> was enabled. Beginning with 2.6.7, deletions will
+also occur when <a href="#opt--dirs"><code>--dirs</code></a> (<code>-d</code>) is enabled, but only for
+directories whose contents are being copied.</p>
+<p>This option can be dangerous if used incorrectly! It is a very good idea to
+first try a run using the <a href="#opt--dry-run"><code>--dry-run</code></a> (<code>-n</code>) option to see what
+files are going to be deleted.</p>
+<p>If the sending side detects any I/O errors, then the deletion of any files
+at the destination will be automatically disabled. This is to prevent
+temporary filesystem failures (such as NFS errors) on the sending side from
+causing a massive deletion of files on the destination. You can override
+this with the <a href="#opt--ignore-errors"><code>--ignore-errors</code></a> option.</p>
+<p>The <code>--delete</code> option may be combined with one of the -&#8288;-&#8288;delete-WHEN options
+without conflict, as well as <a href="#opt--delete-excluded"><code>--delete-excluded</code></a>. However, if none
+of the <code>--delete-WHEN</code> options are specified, rsync will choose the
+<a href="#opt--delete-during"><code>--delete-during</code></a> algorithm when talking to rsync 3.0.0 or newer,
+or the <a href="#opt--delete-before"><code>--delete-before</code></a> algorithm when talking to an older rsync.
+See also <a href="#opt--delete-delay"><code>--delete-delay</code></a> and <a href="#opt--delete-after"><code>--delete-after</code></a>.</p>
+</dd>
+
+<dt id="opt--delete-before"><code>--delete-before</code><a href="#opt--delete-before" class="tgt"></a></dt><dd>
+<p>Request that the file-deletions on the receiving side be done before the
+transfer starts. See <a href="#opt--delete"><code>--delete</code></a> (which is implied) for more
+details on file-deletion.</p>
+<p>Deleting before the transfer is helpful if the filesystem is tight for
+space and removing extraneous files would help to make the transfer
+possible. However, it does introduce a delay before the start of the
+transfer, and this delay might cause the transfer to timeout (if
+<a href="#opt--timeout"><code>--timeout</code></a> was specified). It also forces rsync to use the old,
+non-incremental recursion algorithm that requires rsync to scan all the
+files in the transfer into memory at once (see <a href="#opt--recursive"><code>--recursive</code></a>).</p>
+</dd>
+
+<span id="opt--del"></span><dt id="opt--delete-during"><code>--delete-during</code>, <code>--del</code><a href="#opt--delete-during" class="tgt"></a></dt><dd>
+<p>Request that the file-deletions on the receiving side be done incrementally
+as the transfer happens. The per-directory delete scan is done right
+before each directory is checked for updates, so it behaves like a more
+efficient <a href="#opt--delete-before"><code>--delete-before</code></a>, including doing the deletions prior to
+any per-directory filter files being updated. This option was first added
+in rsync version 2.6.4. See <a href="#opt--delete"><code>--delete</code></a> (which is implied) for more
+details on file-deletion.</p>
+</dd>
+
+<dt id="opt--delete-delay"><code>--delete-delay</code><a href="#opt--delete-delay" class="tgt"></a></dt><dd>
+<p>Request that the file-deletions on the receiving side be computed during
+the transfer (like <a href="#opt--delete-during"><code>--delete-during</code></a>), and then removed after the
+transfer completes. This is useful when combined with
+<a href="#opt--delay-updates"><code>--delay-updates</code></a> and/or <a href="#opt--fuzzy"><code>--fuzzy</code></a>, and is more efficient
+than using <a href="#opt--delete-after"><code>--delete-after</code></a> (but can behave differently, since
+<a href="#opt--delete-after"><code>--delete-after</code></a> computes the deletions in a separate pass after
+all updates are done). If the number of removed files overflows an
+internal buffer, a temporary file will be created on the receiving side to
+hold the names (it is removed while open, so you shouldn't see it during
+the transfer). If the creation of the temporary file fails, rsync will try
+to fall back to using <a href="#opt--delete-after"><code>--delete-after</code></a> (which it cannot do if
+<a href="#opt--recursive"><code>--recursive</code></a> is doing an incremental scan). See
+<a href="#opt--delete"><code>--delete</code></a> (which is implied) for more details on file-deletion.</p>
+</dd>
+
+<dt id="opt--delete-after"><code>--delete-after</code><a href="#opt--delete-after" class="tgt"></a></dt><dd>
+<p>Request that the file-deletions on the receiving side be done after the
+transfer has completed. This is useful if you are sending new
+per-directory merge files as a part of the transfer and you want their
+exclusions to take effect for the delete phase of the current transfer. It
+also forces rsync to use the old, non-incremental recursion algorithm that
+requires rsync to scan all the files in the transfer into memory at once
+(see <a href="#opt--recursive"><code>--recursive</code></a>). See <a href="#opt--delete"><code>--delete</code></a> (which is implied) for
+more details on file-deletion.</p>
+<p>See also the <a href="#opt--delete-delay"><code>--delete-delay</code></a> option that might be a faster choice
+for those that just want the deletions to occur at the end of the transfer.</p>
+</dd>
+
+<dt id="opt--delete-excluded"><code>--delete-excluded</code><a href="#opt--delete-excluded" class="tgt"></a></dt><dd>
+<p>This option turns any unqualified exclude/include rules into server-side
+rules that do not affect the receiver's deletions.</p>
+<p>By default, an exclude or include has both a server-side effect (to &quot;hide&quot;
+and &quot;show&quot; files when building the server's file list) and a receiver-side
+effect (to &quot;protect&quot; and &quot;risk&quot; files when deletions are occurring). Any
+rule that has no modifier to specify what sides it is executed on will be
+instead treated as if it were a server-side rule only, avoiding any
+&quot;protect&quot; effects of the rules.</p>
+<p>A rule can still apply to both sides even with this option specified if the
+rule is given both the sender &amp; receiver modifier letters (e.g., <code>-f'-sr foo'</code>). Receiver-side protect/risk rules can also be explicitly specified
+to limit the deletions. This saves you from having to edit a bunch of
+<code>-f'- foo'</code> rules into <code>-f'-s foo'</code> (aka <code>-f'H foo'</code>) rules (not to mention
+the corresponding includes).</p>
+<p>See the <a href="#FILTER_RULES">FILTER RULES</a> section for more information. See
+<a href="#opt--delete"><code>--delete</code></a> (which is implied) for more details on deletion.</p>
+</dd>
+
+<dt id="opt--ignore-missing-args"><code>--ignore-missing-args</code><a href="#opt--ignore-missing-args" class="tgt"></a></dt><dd>
+<p>When rsync is first processing the explicitly requested source files (e.g.
+command-line arguments or <a href="#opt--files-from"><code>--files-from</code></a> entries), it is normally
+an error if the file cannot be found. This option suppresses that error,
+and does not try to transfer the file. This does not affect subsequent
+vanished-file errors if a file was initially found to be present and later
+is no longer there.</p>
+</dd>
+
+<dt id="opt--delete-missing-args"><code>--delete-missing-args</code><a href="#opt--delete-missing-args" class="tgt"></a></dt><dd>
+<p>This option takes the behavior of the (implied)
+<a href="#opt--ignore-missing-args"><code>--ignore-missing-args</code></a> option a step farther: each missing arg
+will become a deletion request of the corresponding destination file on the
+receiving side (should it exist). If the destination file is a non-empty
+directory, it will only be successfully deleted if <a href="#opt--force"><code>--force</code></a> or
+<a href="#opt--delete"><code>--delete</code></a> are in effect. Other than that, this option is
+independent of any other type of delete processing.</p>
+<p>The missing source files are represented by special file-list entries which
+display as a &quot;<code>*missing</code>&quot; entry in the <a href="#opt--list-only"><code>--list-only</code></a> output.</p>
+</dd>
+
+<dt id="opt--ignore-errors"><code>--ignore-errors</code><a href="#opt--ignore-errors" class="tgt"></a></dt><dd>
+<p>Tells <a href="#opt--delete"><code>--delete</code></a> to go ahead and delete files even when there are
+I/O errors.</p>
+</dd>
+
+<dt id="opt--force"><code>--force</code><a href="#opt--force" class="tgt"></a></dt><dd>
+<p>This option tells rsync to delete a non-empty directory when it is to be
+replaced by a non-directory. This is only relevant if deletions are not
+active (see <a href="#opt--delete"><code>--delete</code></a> for details).</p>
+<p>Note for older rsync versions: <code>--force</code> used to still be required when
+using <a href="#opt--delete-after"><code>--delete-after</code></a>, and it used to be non-functional unless the
+<a href="#opt--recursive"><code>--recursive</code></a> option was also enabled.</p>
+</dd>
+
+<dt id="opt--max-delete"><code>--max-delete=NUM</code><a href="#opt--max-delete" class="tgt"></a></dt><dd>
+<p>This tells rsync not to delete more than NUM files or directories. If that
+limit is exceeded, all further deletions are skipped through the end of the
+transfer. At the end, rsync outputs a warning (including a count of the
+skipped deletions) and exits with an error code of 25 (unless some more
+important error condition also occurred).</p>
+<p>Beginning with version 3.0.0, you may specify <code>--max-delete=0</code> to be warned
+about any extraneous files in the destination without removing any of them.
+Older clients interpreted this as &quot;unlimited&quot;, so if you don't know what
+version the client is, you can use the less obvious <code>--max-delete=-1</code> as a
+backward-compatible way to specify that no deletions be allowed (though
+really old versions didn't warn when the limit was exceeded).</p>
+</dd>
+
+<dt id="opt--max-size"><code>--max-size=SIZE</code><a href="#opt--max-size" class="tgt"></a></dt><dd>
+<p>This tells rsync to avoid transferring any file that is larger than the
+specified SIZE. A numeric value can be suffixed with a string to indicate
+the numeric units or left unqualified to specify bytes. Feel free to use a
+fractional value along with the units, such as <code>--max-size=1.5m</code>.</p>
+<p>This option is a <a href="#TRANSFER_RULES">TRANSFER RULE</a>, so don't expect any
+exclude side effects.</p>
+<p>The first letter of a units string can be <code>B</code> (bytes), <code>K</code> (kilo), <code>M</code>
+(mega), <code>G</code> (giga), <code>T</code> (tera), or <code>P</code> (peta). If the string is a single
+char or has &quot;ib&quot; added to it (e.g. &quot;G&quot; or &quot;GiB&quot;) then the units are
+multiples of 1024. If you use a two-letter suffix that ends with a &quot;B&quot;
+(e.g. &quot;kb&quot;) then you get units that are multiples of 1000. The string's
+letters can be any mix of upper and lower-case that you want to use.</p>
+<p>Finally, if the string ends with either &quot;+1&quot; or &quot;-&#8288;1&quot;, it is offset by one
+byte in the indicated direction. The largest possible value is usually
+<code>8192P-1</code>.</p>
+<p>Examples: <code>--max-size=1.5mb-1</code> is 1499999 bytes, and <code>--max-size=2g+1</code> is
+2147483649 bytes.</p>
+<p>Note that rsync versions prior to 3.1.0 did not allow <code>--max-size=0</code>.</p>
+</dd>
+
+<dt id="opt--min-size"><code>--min-size=SIZE</code><a href="#opt--min-size" class="tgt"></a></dt><dd>
+<p>This tells rsync to avoid transferring any file that is smaller than the
+specified SIZE, which can help in not transferring small, junk files. See
+the <a href="#opt--max-size"><code>--max-size</code></a> option for a description of SIZE and other info.</p>
+<p>Note that rsync versions prior to 3.1.0 did not allow <code>--min-size=0</code>.</p>
+</dd>
+
+<dt id="opt--max-alloc"><code>--max-alloc=SIZE</code><a href="#opt--max-alloc" class="tgt"></a></dt><dd>
+<p>By default rsync limits an individual malloc/realloc to about 1GB in size.
+For most people this limit works just fine and prevents a protocol error
+causing rsync to request massive amounts of memory. However, if you have
+many millions of files in a transfer, a large amount of server memory, and
+you don't want to split up your transfer into multiple parts, you can
+increase the per-allocation limit to something larger and rsync will
+consume more memory.</p>
+<p>Keep in mind that this is not a limit on the total size of allocated
+memory. It is a sanity-check value for each individual allocation.</p>
+<p>See the <a href="#opt--max-size"><code>--max-size</code></a> option for a description of how SIZE can be
+specified. The default suffix if none is given is bytes.</p>
+<p>Beginning in 3.2.3, a value of 0 specifies no limit.</p>
+<p>You can set a default value using the environment variable
+<a href="#RSYNC_MAX_ALLOC"><code>RSYNC_MAX_ALLOC</code></a> using the same SIZE values as supported by this
+option. If the remote rsync doesn't understand the <code>--max-alloc</code> option,
+you can override an environmental value by specifying <code>--max-alloc=1g</code>,
+which will make rsync avoid sending the option to the remote side (because
+&quot;1G&quot; is the default).</p>
+</dd>
+
+<span id="opt-B"></span><dt id="opt--block-size"><code>--block-size=SIZE</code>, <code>-B</code><a href="#opt--block-size" class="tgt"></a></dt><dd>
+<p>This forces the block size used in rsync's delta-transfer algorithm to a
+fixed value. It is normally selected based on the size of each file being
+updated. See the technical report for details.</p>
+<p>Beginning in 3.2.3 the SIZE can be specified with a suffix as detailed in
+the <a href="#opt--max-size"><code>--max-size</code></a> option. Older versions only accepted a byte count.</p>
+</dd>
+
+<span id="opt-e"></span><dt id="opt--rsh"><code>--rsh=COMMAND</code>, <code>-e</code><a href="#opt--rsh" class="tgt"></a></dt><dd>
+<p>This option allows you to choose an alternative remote shell program to use
+for communication between the local and remote copies of rsync. Typically,
+rsync is configured to use ssh by default, but you may prefer to use rsh on
+a local network.</p>
+<p>If this option is used with <code>[user@]host::module/path</code>, then the remote
+shell <u>COMMAND</u> will be used to run an rsync daemon on the remote host, and
+all data will be transmitted through that remote shell connection, rather
+than through a direct socket connection to a running rsync daemon on the
+remote host. See the <a href="#USING_RSYNC-DAEMON_FEATURES_VIA_A_REMOTE-SHELL_CONNECTION">USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL
+CONNECTION</a> section above.</p>
+<p>Beginning with rsync 3.2.0, the <a href="#RSYNC_PORT"><code>RSYNC_PORT</code></a> environment variable will
+be set when a daemon connection is being made via a remote-shell
+connection. It is set to 0 if the default daemon port is being assumed, or
+it is set to the value of the rsync port that was specified via either the
+<a href="#opt--port"><code>--port</code></a> option or a non-empty port value in an <code>rsync://</code> URL.
+This allows the script to discern if a non-default port is being requested,
+allowing for things such as an SSL or stunnel helper script to connect to a
+default or alternate port.</p>
+<p>Command-line arguments are permitted in COMMAND provided that COMMAND is
+presented to rsync as a single argument. You must use spaces (not tabs or
+other whitespace) to separate the command and args from each other, and you
+can use single- and/or double-quotes to preserve spaces in an argument (but
+not backslashes). Note that doubling a single-quote inside a single-quoted
+string gives you a single-quote; likewise for double-quotes (though you
+need to pay attention to which quotes your shell is parsing and which
+quotes rsync is parsing). Some examples:</p>
+<blockquote>
+<pre><code>-e 'ssh -p 2234'
+-e 'ssh -o &quot;ProxyCommand nohup ssh firewall nc -w1 %h %p&quot;'
+</code></pre>
+</blockquote>
+<p>(Note that ssh users can alternately customize site-specific connect
+options in their .ssh/config file.)</p>
+<p>You can also choose the remote shell program using the <a href="#RSYNC_RSH"><code>RSYNC_RSH</code></a>
+environment variable, which accepts the same range of values as <code>-e</code>.</p>
+<p>See also the <a href="#opt--blocking-io"><code>--blocking-io</code></a> option which is affected by this
+option.</p>
+</dd>
+
+<dt id="opt--rsync-path"><code>--rsync-path=PROGRAM</code><a href="#opt--rsync-path" class="tgt"></a></dt><dd>
+<p>Use this to specify what program is to be run on the remote machine to
+start-up rsync. Often used when rsync is not in the default remote-shell's
+path (e.g. <code>--rsync-path=/usr/local/bin/rsync</code>). Note that PROGRAM is run
+with the help of a shell, so it can be any program, script, or command
+sequence you'd care to run, so long as it does not corrupt the standard-in
+&amp; standard-out that rsync is using to communicate.</p>
+<p>One tricky example is to set a different default directory on the remote
+machine for use with the <a href="#opt--relative"><code>--relative</code></a> option. For instance:</p>
+<blockquote>
+<pre><code>rsync -avR --rsync-path=&quot;cd /a/b &amp;&amp; rsync&quot; host:c/d /e/
+</code></pre>
+</blockquote>
+</dd>
+
+<span id="opt-M"></span><dt id="opt--remote-option"><code>--remote-option=OPTION</code>, <code>-M</code><a href="#opt--remote-option" class="tgt"></a></dt><dd>
+<p>This option is used for more advanced situations where you want certain
+effects to be limited to one side of the transfer only. For instance, if
+you want to pass <a href="#opt--log-file"><code>--log-file=FILE</code></a> and <a href="#opt--fake-super"><code>--fake-super</code></a> to
+the remote system, specify it like this:</p>
+<blockquote>
+<pre><code>rsync -av -M --log-file=foo -M--fake-super src/ dest/
+</code></pre>
+</blockquote>
+<p>If you want to have an option affect only the local side of a transfer when
+it normally affects both sides, send its negation to the remote side. Like
+this:</p>
+<blockquote>
+<pre><code>rsync -av -x -M--no-x src/ dest/
+</code></pre>
+</blockquote>
+<p>Be cautious using this, as it is possible to toggle an option that will
+cause rsync to have a different idea about what data to expect next over
+the socket, and that will make it fail in a cryptic fashion.</p>
+<p>Note that you should use a separate <code>-M</code> option for each remote option you
+want to pass. On older rsync versions, the presence of any spaces in the
+remote-option arg could cause it to be split into separate remote args, but
+this requires the use of <a href="#opt--old-args"><code>--old-args</code></a> in a modern rsync.</p>
+<p>When performing a local transfer, the &quot;local&quot; side is the sender and the
+&quot;remote&quot; side is the receiver.</p>
+<p>Note some versions of the popt option-parsing library have a bug in them
+that prevents you from using an adjacent arg with an equal in it next to a
+short option letter (e.g. <code>-M--log-file=/tmp/foo</code>). If this bug affects
+your version of popt, you can use the version of popt that is included with
+rsync.</p>
+</dd>
+
+<span id="opt-C"></span><dt id="opt--cvs-exclude"><code>--cvs-exclude</code>, <code>-C</code><a href="#opt--cvs-exclude" class="tgt"></a></dt><dd>
+<p>This is a useful shorthand for excluding a broad range of files that you
+often don't want to transfer between systems. It uses a similar algorithm
+to CVS to determine if a file should be ignored.</p>
+<p>The exclude list is initialized to exclude the following items (these
+initial items are marked as perishable&nbsp;-&#8288;-&#8288; see the <a href="#FILTER_RULES">FILTER RULES</a>
+section):</p>
+<blockquote>
+<p><code>RCS</code>
+<code>SCCS</code>
+<code>CVS</code>
+<code>CVS.adm</code>
+<code>RCSLOG</code>
+<code>cvslog.*</code>
+<code>tags</code>
+<code>TAGS</code>
+<code>.make.state</code>
+<code>.nse_depinfo</code>
+<code>*~</code>
+<code>#*</code>
+<code>.#*</code>
+<code>,*</code>
+<code>_$*</code>
+<code>*$</code>
+<code>*.old</code>
+<code>*.bak</code>
+<code>*.BAK</code>
+<code>*.orig</code>
+<code>*.rej</code>
+<code>.del-*</code>
+<code>*.a</code>
+<code>*.olb</code>
+<code>*.o</code>
+<code>*.obj</code>
+<code>*.so</code>
+<code>*.exe</code>
+<code>*.Z</code>
+<code>*.elc</code>
+<code>*.ln</code>
+<code>core</code>
+<code>.svn/</code>
+<code>.git/</code>
+<code>.hg/</code>
+<code>.bzr/</code></p>
+</blockquote>
+<p>then, files listed in a $HOME/.cvsignore are added to the list and any
+files listed in the CVSIGNORE environment variable (all cvsignore names are
+delimited by whitespace).</p>
+<p>Finally, any file is ignored if it is in the same directory as a .cvsignore
+file and matches one of the patterns listed therein. Unlike rsync's
+filter/exclude files, these patterns are split on whitespace. See the
+<strong>cvs</strong>(1) manual for more information.</p>
+<p>If you're combining <code>-C</code> with your own <a href="#opt--filter"><code>--filter</code></a> rules, you should
+note that these CVS excludes are appended at the end of your own rules,
+regardless of where the <code>-C</code> was placed on the command-line. This makes
+them a lower priority than any rules you specified explicitly. If you want
+to control where these CVS excludes get inserted into your filter rules,
+you should omit the <code>-C</code> as a command-line option and use a combination of
+<a href="#opt--filter"><code>--filter=:C</code></a> and <a href="#opt--filter"><code>--filter=-C</code></a> (either on your
+command-line or by putting the &quot;:C&quot; and &quot;-&#8288;C&quot; rules into a filter file with
+your other rules). The first option turns on the per-directory scanning
+for the .cvsignore file. The second option does a one-time import of the
+CVS excludes mentioned above.</p>
+</dd>
+
+<span id="opt-f"></span><dt id="opt--filter"><code>--filter=RULE</code>, <code>-f</code><a href="#opt--filter" class="tgt"></a></dt><dd>
+<p>This option allows you to add rules to selectively exclude certain files
+from the list of files to be transferred. This is most useful in
+combination with a recursive transfer.</p>
+<p>You may use as many <code>--filter</code> options on the command line as you like to
+build up the list of files to exclude. If the filter contains whitespace,
+be sure to quote it so that the shell gives the rule to rsync as a single
+argument. The text below also mentions that you can use an underscore to
+replace the space that separates a rule from its arg.</p>
+<p>See the <a href="#FILTER_RULES">FILTER RULES</a> section for detailed information on this option.</p>
+</dd>
+
+<dt id="opt-F"><code>-F</code><a href="#opt-F" class="tgt"></a></dt><dd>
+<p>The <code>-F</code> option is a shorthand for adding two <a href="#opt--filter"><code>--filter</code></a> rules to
+your command. The first time it is used is a shorthand for this rule:</p>
+<blockquote>
+<pre><code>--filter='dir-merge /.rsync-filter'
+</code></pre>
+</blockquote>
+<p>This tells rsync to look for per-directory .rsync-filter files that have
+been sprinkled through the hierarchy and use their rules to filter the
+files in the transfer. If <code>-F</code> is repeated, it is a shorthand for this
+rule:</p>
+<blockquote>
+<pre><code>--filter='exclude .rsync-filter'
+</code></pre>
+</blockquote>
+<p>This filters out the .rsync-filter files themselves from the transfer.</p>
+<p>See the <a href="#FILTER_RULES">FILTER RULES</a> section for detailed information on how these
+options work.</p>
+</dd>
+
+<dt id="opt--exclude"><code>--exclude=PATTERN</code><a href="#opt--exclude" class="tgt"></a></dt><dd>
+<p>This option is a simplified form of the <a href="#opt--filter"><code>--filter</code></a> option that
+specifies an exclude rule and does not allow the full rule-parsing syntax
+of normal filter rules. This is equivalent to specifying <code>-f'- PATTERN'</code>.</p>
+<p>See the <a href="#FILTER_RULES">FILTER RULES</a> section for detailed information on this option.</p>
+</dd>
+
+<dt id="opt--exclude-from"><code>--exclude-from=FILE</code><a href="#opt--exclude-from" class="tgt"></a></dt><dd>
+<p>This option is related to the <a href="#opt--exclude"><code>--exclude</code></a> option, but it specifies
+a FILE that contains exclude patterns (one per line). Blank lines in the
+file are ignored, as are whole-line comments that start with '<code>;</code>' or '<code>#</code>'
+(filename rules that contain those characters are unaffected).</p>
+<p>If a line begins with &quot;<code>- </code>&quot; (dash, space) or &quot;<code>+ </code>&quot; (plus, space), then
+the type of rule is being explicitly specified as an exclude or an include
+(respectively). Any rules without such a prefix are taken to be an exclude.</p>
+<p>If a line consists of just &quot;<code>!</code>&quot;, then the current filter rules are cleared
+before adding any further rules.</p>
+<p>If <u>FILE</u> is '<code>-</code>', the list will be read from standard input.</p>
+</dd>
+
+<dt id="opt--include"><code>--include=PATTERN</code><a href="#opt--include" class="tgt"></a></dt><dd>
+<p>This option is a simplified form of the <a href="#opt--filter"><code>--filter</code></a> option that
+specifies an include rule and does not allow the full rule-parsing syntax
+of normal filter rules. This is equivalent to specifying <code>-f'+ PATTERN'</code>.</p>
+<p>See the <a href="#FILTER_RULES">FILTER RULES</a> section for detailed information on this option.</p>
+</dd>
+
+<dt id="opt--include-from"><code>--include-from=FILE</code><a href="#opt--include-from" class="tgt"></a></dt><dd>
+<p>This option is related to the <a href="#opt--include"><code>--include</code></a> option, but it specifies
+a FILE that contains include patterns (one per line). Blank lines in the
+file are ignored, as are whole-line comments that start with '<code>;</code>' or '<code>#</code>'
+(filename rules that contain those characters are unaffected).</p>
+<p>If a line begins with &quot;<code>- </code>&quot; (dash, space) or &quot;<code>+ </code>&quot; (plus, space), then
+the type of rule is being explicitly specified as an exclude or an include
+(respectively). Any rules without such a prefix are taken to be an include.</p>
+<p>If a line consists of just &quot;<code>!</code>&quot;, then the current filter rules are cleared
+before adding any further rules.</p>
+<p>If <u>FILE</u> is '<code>-</code>', the list will be read from standard input.</p>
+</dd>
+
+<dt id="opt--files-from"><code>--files-from=FILE</code><a href="#opt--files-from" class="tgt"></a></dt><dd>
+<p>Using this option allows you to specify the exact list of files to transfer
+(as read from the specified FILE or '<code>-</code>' for standard input). It also
+tweaks the default behavior of rsync to make transferring just the
+specified files and directories easier:</p>
+<ul>
+<li>The <a href="#opt--relative"><code>--relative</code></a> (<code>-R</code>) option is implied, which preserves the
+path information that is specified for each item in the file (use
+<code>--no-relative</code> or <code>--no-R</code> if you want to turn that off).</li>
+<li>The <a href="#opt--dirs"><code>--dirs</code></a> (<code>-d</code>) option is implied, which will create
+directories specified in the list on the destination rather than noisily
+skipping them (use <code>--no-dirs</code> or <code>--no-d</code> if you want to turn that off).</li>
+<li>The <a href="#opt--archive"><code>--archive</code></a> (<code>-a</code>) option's behavior does not imply
+<a href="#opt--recursive"><code>--recursive</code></a> (<code>-r</code>), so specify it explicitly, if you want it.</li>
+<li>These side-effects change the default state of rsync, so the position of
+the <code>--files-from</code> option on the command-line has no bearing on how other
+options are parsed (e.g. <a href="#opt-a"><code>-a</code></a> works the same before or after
+<code>--files-from</code>, as does <code>--no-R</code> and all other options).</li>
+</ul>
+<p>The filenames that are read from the FILE are all relative to the source
+dir&nbsp;-&#8288;-&#8288; any leading slashes are removed and no &quot;..&quot; references are allowed
+to go higher than the source dir. For example, take this command:</p>
+<blockquote>
+<pre><code>rsync -a --files-from=/tmp/foo /usr remote:/backup
+</code></pre>
+</blockquote>
+<p>If /tmp/foo contains the string &quot;bin&quot; (or even &quot;/bin&quot;), the /usr/bin
+directory will be created as /backup/bin on the remote host. If it
+contains &quot;bin/&quot; (note the trailing slash), the immediate contents of the
+directory would also be sent (without needing to be explicitly mentioned in
+the file&nbsp;-&#8288;-&#8288; this began in version 2.6.4). In both cases, if the
+<a href="#opt-r"><code>-r</code></a> option was enabled, that dir's entire hierarchy would also be
+transferred (keep in mind that <a href="#opt-r"><code>-r</code></a> needs to be specified
+explicitly with <code>--files-from</code>, since it is not implied by <a href="#opt-a"><code>-a</code></a>.
+Also note that the effect of the (enabled by default) <a href="#opt-r"><code>-r</code></a> option
+is to duplicate only the path info that is read from the file&nbsp;-&#8288;-&#8288; it does
+not force the duplication of the source-spec path (/usr in this case).</p>
+<p>In addition, the <code>--files-from</code> file can be read from the remote host
+instead of the local host if you specify a &quot;host:&quot; in front of the file
+(the host must match one end of the transfer). As a short-cut, you can
+specify just a prefix of &quot;:&quot; to mean &quot;use the remote end of the transfer&quot;.
+For example:</p>
+<blockquote>
+<pre><code>rsync -a --files-from=:/path/file-list src:/ /tmp/copy
+</code></pre>
+</blockquote>
+<p>This would copy all the files specified in the /path/file-list file that
+was located on the remote &quot;src&quot; host.</p>
+<p>If the <a href="#opt--iconv"><code>--iconv</code></a> and <a href="#opt--secluded-args"><code>--secluded-args</code></a> options are specified
+and the <code>--files-from</code> filenames are being sent from one host to another,
+the filenames will be translated from the sending host's charset to the
+receiving host's charset.</p>
+<p>NOTE: sorting the list of files in the <code>--files-from</code> input helps rsync to
+be more efficient, as it will avoid re-visiting the path elements that are
+shared between adjacent entries. If the input is not sorted, some path
+elements (implied directories) may end up being scanned multiple times, and
+rsync will eventually unduplicate them after they get turned into file-list
+elements.</p>
+</dd>
+
+<span id="opt-0"></span><dt id="opt--from0"><code>--from0</code>, <code>-0</code><a href="#opt--from0" class="tgt"></a></dt><dd>
+<p>This tells rsync that the rules/filenames it reads from a file are
+terminated by a null ('\0') character, not a NL, CR, or CR+LF. This
+affects <a href="#opt--exclude-from"><code>--exclude-from</code></a>, <a href="#opt--include-from"><code>--include-from</code></a>,
+<a href="#opt--files-from"><code>--files-from</code></a>, and any merged files specified in a
+<a href="#opt--filter"><code>--filter</code></a> rule. It does not affect <a href="#opt--cvs-exclude"><code>--cvs-exclude</code></a> (since
+all names read from a .cvsignore file are split on whitespace).</p>
+</dd>
+
+<dt id="opt--old-args"><code>--old-args</code><a href="#opt--old-args" class="tgt"></a></dt><dd>
+<p>This option tells rsync to stop trying to protect the arg values on the
+remote side from unintended word-splitting or other misinterpretation.
+It also allows the client to treat an empty arg as a &quot;.&quot; instead of
+generating an error.</p>
+<p>The default in a modern rsync is for &quot;shell-active&quot; characters (including
+spaces) to be backslash-escaped in the args that are sent to the remote
+shell. The wildcard characters <code>*</code>, <code>?</code>, <code>[</code>, &amp; <code>]</code> are not escaped in
+filename args (allowing them to expand into multiple filenames) while being
+protected in option args, such as <a href="#opt--usermap"><code>--usermap</code></a>.</p>
+<p>If you have a script that wants to use old-style arg splitting in its
+filenames, specify this option once. If the remote shell has a problem
+with any backslash escapes at all, specify this option twice.</p>
+<p>You may also control this setting via the <a href="#RSYNC_OLD_ARGS"><code>RSYNC_OLD_ARGS</code></a> environment
+variable. If it has the value &quot;1&quot;, rsync will default to a single-option
+setting. If it has the value &quot;2&quot; (or more), rsync will default to a
+repeated-option setting. If it is &quot;0&quot;, you'll get the default escaping
+behavior. The environment is always overridden by manually specified
+positive or negative options (the negative is <code>--no-old-args</code>).</p>
+<p>Note that this option also disables the extra safety check added in 3.2.5
+that ensures that a remote sender isn't including extra top-level items in
+the file-list that you didn't request. This side-effect is necessary
+because we can't know for sure what names to expect when the remote shell
+is interpreting the args.</p>
+<p>This option conflicts with the <a href="#opt--secluded-args"><code>--secluded-args</code></a> option.</p>
+</dd>
+
+<span id="opt-s"></span><dt id="opt--secluded-args"><code>--secluded-args</code>, <code>-s</code><a href="#opt--secluded-args" class="tgt"></a></dt><dd>
+<p>This option sends all filenames and most options to the remote rsync via
+the protocol (not the remote shell command line) which avoids letting the
+remote shell modify them. Wildcards are expanded on the remote host by
+rsync instead of a shell.</p>
+<p>This is similar to the default backslash-escaping of args that was added
+in 3.2.4 (see <a href="#opt--old-args"><code>--old-args</code></a>) in that it prevents things like space
+splitting and unwanted special-character side-effects. However, it has the
+drawbacks of being incompatible with older rsync versions (prior to 3.0.0)
+and of being refused by restricted shells that want to be able to inspect
+all the option values for safety.</p>
+<p>This option is useful for those times that you need the argument's
+character set to be converted for the remote host, if the remote shell is
+incompatible with the default backslash-escpaing method, or there is some
+other reason that you want the majority of the options and arguments to
+bypass the command-line of the remote shell.</p>
+<p>If you combine this option with <a href="#opt--iconv"><code>--iconv</code></a>, the args related to the
+remote side will be translated from the local to the remote character-set.
+The translation happens before wild-cards are expanded. See also the
+<a href="#opt--files-from"><code>--files-from</code></a> option.</p>
+<p>You may also control this setting via the <a href="#RSYNC_PROTECT_ARGS"><code>RSYNC_PROTECT_ARGS</code></a>
+environment variable. If it has a non-zero value, this setting will be
+enabled by default, otherwise it will be disabled by default. Either state
+is overridden by a manually specified positive or negative version of this
+option (note that <code>--no-s</code> and <code>--no-secluded-args</code> are the negative
+versions). This environment variable is also superseded by a non-zero
+<a href="#RSYNC_OLD_ARGS"><code>RSYNC_OLD_ARGS</code></a> export.</p>
+<p>This option conflicts with the <a href="#opt--old-args"><code>--old-args</code></a> option.</p>
+<p>This option used to be called <code>--protect-args</code> (before 3.2.6) and that
+older name can still be used (though specifying it as <code>-s</code> is always the
+easiest and most compatible choice).</p>
+</dd>
+
+<dt id="opt--trust-sender"><code>--trust-sender</code><a href="#opt--trust-sender" class="tgt"></a></dt><dd>
+<p>This option disables two extra validation checks that a local client
+performs on the file list generated by a remote sender. This option should
+only be used if you trust the sender to not put something malicious in the
+file list (something that could possibly be done via a modified rsync, a
+modified shell, or some other similar manipulation).</p>
+<p>Normally, the rsync client (as of version 3.2.5) runs two extra validation
+checks when pulling files from a remote rsync:</p>
+<ul>
+<li>It verifies that additional arg items didn't get added at the top of the
+transfer.</li>
+<li>It verifies that none of the items in the file list are names that should
+have been excluded (if filter rules were specified).</li>
+</ul>
+<p>Note that various options can turn off one or both of these checks if the
+option interferes with the validation. For instance:</p>
+<ul>
+<li>Using a per-directory filter file reads filter rules that only the server
+knows about, so the filter checking is disabled.</li>
+<li>Using the <a href="#opt--old-args"><code>--old-args</code></a> option allows the sender to manipulate the
+requested args, so the arg checking is disabled.</li>
+<li>Reading the files-from list from the server side means that the client
+doesn't know the arg list, so the arg checking is disabled.</li>
+<li>Using <a href="#opt--read-batch"><code>--read-batch</code></a> disables both checks since the batch file's
+contents will have been verified when it was created.</li>
+</ul>
+<p>This option may help an under-powered client server if the extra pattern
+matching is slowing things down on a huge transfer. It can also be used to
+work around a currently-unknown bug in the verification logic for a transfer
+from a trusted sender.</p>
+<p>When using this option it is a good idea to specify a dedicated destination
+directory, as discussed in the <a href="#MULTI-HOST_SECURITY">MULTI-HOST SECURITY</a> section.</p>
+</dd>
+
+<dt id="opt--copy-as"><code>--copy-as=USER[:GROUP]</code><a href="#opt--copy-as" class="tgt"></a></dt><dd>
+<p>This option instructs rsync to use the USER and (if specified after a
+colon) the GROUP for the copy operations. This only works if the user that
+is running rsync has the ability to change users. If the group is not
+specified then the user's default groups are used.</p>
+<p>This option can help to reduce the risk of an rsync being run as root into
+or out of a directory that might have live changes happening to it and you
+want to make sure that root-level read or write actions of system files are
+not possible. While you could alternatively run all of rsync as the
+specified user, sometimes you need the root-level host-access credentials
+to be used, so this allows rsync to drop root for the copying part of the
+operation after the remote-shell or daemon connection is established.</p>
+<p>The option only affects one side of the transfer unless the transfer is
+local, in which case it affects both sides. Use the
+<a href="#opt--remote-option"><code>--remote-option</code></a> to affect the remote side, such as
+<code>-M--copy-as=joe</code>. For a local transfer, the lsh (or lsh.sh) support file
+provides a local-shell helper script that can be used to allow a
+&quot;localhost:&quot; or &quot;lh:&quot; host-spec to be specified without needing to setup
+any remote shells, allowing you to specify remote options that affect the
+side of the transfer that is using the host-spec (and using hostname &quot;lh&quot;
+avoids the overriding of the remote directory to the user's home dir).</p>
+<p>For example, the following rsync writes the local files as user &quot;joe&quot;:</p>
+<blockquote>
+<pre><code>sudo rsync -aiv --copy-as=joe host1:backups/joe/ /home/joe/
+</code></pre>
+</blockquote>
+<p>This makes all files owned by user &quot;joe&quot;, limits the groups to those that
+are available to that user, and makes it impossible for the joe user to do
+a timed exploit of the path to induce a change to a file that the joe user
+has no permissions to change.</p>
+<p>The following command does a local copy into the &quot;dest/&quot; dir as user &quot;joe&quot;
+(assuming you've installed support/lsh into a dir on your $PATH):</p>
+<blockquote>
+<pre><code>sudo rsync -aive lsh -M--copy-as=joe src/ lh:dest/
+</code></pre>
+</blockquote>
+</dd>
+
+<span id="opt-T"></span><dt id="opt--temp-dir"><code>--temp-dir=DIR</code>, <code>-T</code><a href="#opt--temp-dir" class="tgt"></a></dt><dd>
+<p>This option instructs rsync to use DIR as a scratch directory when creating
+temporary copies of the files transferred on the receiving side. The
+default behavior is to create each temporary file in the same directory as
+the associated destination file. Beginning with rsync 3.1.1, the temp-file
+names inside the specified DIR will not be prefixed with an extra dot
+(though they will still have a random suffix added).</p>
+<p>This option is most often used when the receiving disk partition does not
+have enough free space to hold a copy of the largest file in the transfer.
+In this case (i.e. when the scratch directory is on a different disk
+partition), rsync will not be able to rename each received temporary file
+over the top of the associated destination file, but instead must copy it
+into place. Rsync does this by copying the file over the top of the
+destination file, which means that the destination file will contain
+truncated data during this copy. If this were not done this way (even if
+the destination file were first removed, the data locally copied to a
+temporary file in the destination directory, and then renamed into place)
+it would be possible for the old file to continue taking up disk space (if
+someone had it open), and thus there might not be enough room to fit the
+new version on the disk at the same time.</p>
+<p>If you are using this option for reasons other than a shortage of disk
+space, you may wish to combine it with the <a href="#opt--delay-updates"><code>--delay-updates</code></a>
+option, which will ensure that all copied files get put into subdirectories
+in the destination hierarchy, awaiting the end of the transfer. If you
+don't have enough room to duplicate all the arriving files on the
+destination partition, another way to tell rsync that you aren't overly
+concerned about disk space is to use the <a href="#opt--partial-dir"><code>--partial-dir</code></a> option
+with a relative path; because this tells rsync that it is OK to stash off a
+copy of a single file in a subdir in the destination hierarchy, rsync will
+use the partial-dir as a staging area to bring over the copied file, and
+then rename it into place from there. (Specifying a <a href="#opt--partial-dir"><code>--partial-dir</code></a>
+with an absolute path does not have this side-effect.)</p>
+</dd>
+
+<span id="opt-y"></span><dt id="opt--fuzzy"><code>--fuzzy</code>, <code>-y</code><a href="#opt--fuzzy" class="tgt"></a></dt><dd>
+<p>This option tells rsync that it should look for a basis file for any
+destination file that is missing. The current algorithm looks in the same
+directory as the destination file for either a file that has an identical
+size and modified-time, or a similarly-named file. If found, rsync uses
+the fuzzy basis file to try to speed up the transfer.</p>
+<p>If the option is repeated, the fuzzy scan will also be done in any matching
+alternate destination directories that are specified via
+<a href="#opt--compare-dest"><code>--compare-dest</code></a>, <a href="#opt--copy-dest"><code>--copy-dest</code></a>, or <a href="#opt--link-dest"><code>--link-dest</code></a>.</p>
+<p>Note that the use of the <a href="#opt--delete"><code>--delete</code></a> option might get rid of any
+potential fuzzy-match files, so either use <a href="#opt--delete-after"><code>--delete-after</code></a> or
+specify some filename exclusions if you need to prevent this.</p>
+</dd>
+
+<dt id="opt--compare-dest"><code>--compare-dest=DIR</code><a href="#opt--compare-dest" class="tgt"></a></dt><dd>
+<p>This option instructs rsync to use <u>DIR</u> on the destination machine as an
+additional hierarchy to compare destination files against doing transfers
+(if the files are missing in the destination directory). If a file is
+found in <u>DIR</u> that is identical to the sender's file, the file will NOT be
+transferred to the destination directory. This is useful for creating a
+sparse backup of just files that have changed from an earlier backup. This
+option is typically used to copy into an empty (or newly created)
+directory.</p>
+<p>Beginning in version 2.6.4, multiple <code>--compare-dest</code> directories may be
+provided, which will cause rsync to search the list in the order specified
+for an exact match. If a match is found that differs only in attributes, a
+local copy is made and the attributes updated. If a match is not found, a
+basis file from one of the <u>DIRs</u> will be selected to try to speed up the
+transfer.</p>
+<p>If <u>DIR</u> is a relative path, it is relative to the destination directory.
+See also <a href="#opt--copy-dest"><code>--copy-dest</code></a> and <a href="#opt--link-dest"><code>--link-dest</code></a>.</p>
+<p>NOTE: beginning with version 3.1.0, rsync will remove a file from a
+non-empty destination hierarchy if an exact match is found in one of the
+compare-dest hierarchies (making the end result more closely match a fresh
+copy).</p>
+</dd>
+
+<dt id="opt--copy-dest"><code>--copy-dest=DIR</code><a href="#opt--copy-dest" class="tgt"></a></dt><dd>
+<p>This option behaves like <a href="#opt--compare-dest"><code>--compare-dest</code></a>, but rsync will also copy
+unchanged files found in <u>DIR</u> to the destination directory using a local
+copy. This is useful for doing transfers to a new destination while
+leaving existing files intact, and then doing a flash-cutover when all
+files have been successfully transferred.</p>
+<p>Multiple <code>--copy-dest</code> directories may be provided, which will cause rsync
+to search the list in the order specified for an unchanged file. If a
+match is not found, a basis file from one of the <u>DIRs</u> will be selected to
+try to speed up the transfer.</p>
+<p>If <u>DIR</u> is a relative path, it is relative to the destination directory.
+See also <a href="#opt--compare-dest"><code>--compare-dest</code></a> and <a href="#opt--link-dest"><code>--link-dest</code></a>.</p>
+</dd>
+
+<dt id="opt--link-dest"><code>--link-dest=DIR</code><a href="#opt--link-dest" class="tgt"></a></dt><dd>
+<p>This option behaves like <a href="#opt--copy-dest"><code>--copy-dest</code></a>, but unchanged files are
+hard linked from <u>DIR</u> to the destination directory. The files must be
+identical in all preserved attributes (e.g. permissions, possibly
+ownership) in order for the files to be linked together. An example:</p>
+<blockquote>
+<pre><code>rsync -av --link-dest=$PWD/prior_dir host:src_dir/ new_dir/
+</code></pre>
+</blockquote>
+<p>If files aren't linking, double-check their attributes. Also check if
+some attributes are getting forced outside of rsync's control, such a mount
+option that squishes root to a single user, or mounts a removable drive
+with generic ownership (such as OS X's &quot;Ignore ownership on this volume&quot;
+option).</p>
+<p>Beginning in version 2.6.4, multiple <code>--link-dest</code> directories may be
+provided, which will cause rsync to search the list in the order specified
+for an exact match (there is a limit of 20 such directories). If a match
+is found that differs only in attributes, a local copy is made and the
+attributes updated. If a match is not found, a basis file from one of the
+<u>DIRs</u> will be selected to try to speed up the transfer.</p>
+<p>This option works best when copying into an empty destination hierarchy, as
+existing files may get their attributes tweaked, and that can affect
+alternate destination files via hard-links. Also, itemizing of changes can
+get a bit muddled. Note that prior to version 3.1.0, an
+alternate-directory exact match would never be found (nor linked into the
+destination) when a destination file already exists.</p>
+<p>Note that if you combine this option with <a href="#opt--ignore-times"><code>--ignore-times</code></a>, rsync will not
+link any files together because it only links identical files together as a
+substitute for transferring the file, never as an additional check after
+the file is updated.</p>
+<p>If <u>DIR</u> is a relative path, it is relative to the destination directory.
+See also <a href="#opt--compare-dest"><code>--compare-dest</code></a> and <a href="#opt--copy-dest"><code>--copy-dest</code></a>.</p>
+<p>Note that rsync versions prior to 2.6.1 had a bug that could prevent
+<code>--link-dest</code> from working properly for a non-super-user when
+<a href="#opt--owner"><code>--owner</code></a> (<code>-o</code>) was specified (or implied). You can work-around
+this bug by avoiding the <code>-o</code> option (or using <code>--no-o</code>) when sending to an
+old rsync.</p>
+</dd>
+
+<span id="opt-z"></span><dt id="opt--compress"><code>--compress</code>, <code>-z</code><a href="#opt--compress" class="tgt"></a></dt><dd>
+<p>With this option, rsync compresses the file data as it is sent to the
+destination machine, which reduces the amount of data being transmitted&nbsp;-&#8288;-&#8288;
+something that is useful over a slow connection.</p>
+<p>Rsync supports multiple compression methods and will choose one for you
+unless you force the choice using the <a href="#opt--compress-choice"><code>--compress-choice</code></a> (<code>--zc</code>)
+option.</p>
+<p>Run <code>rsync --version</code> to see the default compress list compiled into your
+version.</p>
+<p>When both sides of the transfer are at least 3.2.0, rsync chooses the first
+algorithm in the client's list of choices that is also in the server's list
+of choices. If no common compress choice is found, rsync exits with
+an error. If the remote rsync is too old to support checksum negotiation,
+its list is assumed to be &quot;zlib&quot;.</p>
+<p>The default order can be customized by setting the environment variable
+<a href="#RSYNC_COMPRESS_LIST"><code>RSYNC_COMPRESS_LIST</code></a> to a space-separated list of acceptable
+compression names. If the string contains a &quot;<code>&amp;</code>&quot; character, it is
+separated into the &quot;client string &amp; server string&quot;, otherwise the same
+string applies to both. If the string (or string portion) contains no
+non-whitespace characters, the default compress list is used. Any unknown
+compression names are discarded from the list, but a list with only invalid
+names results in a failed negotiation.</p>
+<p>There are some older rsync versions that were configured to reject a <code>-z</code>
+option and require the use of <code>-zz</code> because their compression library was
+not compatible with the default zlib compression method. You can usually
+ignore this weirdness unless the rsync server complains and tells you to
+specify <code>-zz</code>.</p>
+</dd>
+
+<span id="opt--zc"></span><dt id="opt--compress-choice"><code>--compress-choice=STR</code>, <code>--zc=STR</code><a href="#opt--compress-choice" class="tgt"></a></dt><dd>
+<p>This option can be used to override the automatic negotiation of the
+compression algorithm that occurs when <a href="#opt--compress"><code>--compress</code></a> is used. The
+option implies <a href="#opt--compress"><code>--compress</code></a> unless &quot;none&quot; was specified, which
+instead implies <code>--no-compress</code>.</p>
+<p>The compression options that you may be able to use are:</p>
+<ul>
+<li><code>zstd</code></li>
+<li><code>lz4</code></li>
+<li><code>zlibx</code></li>
+<li><code>zlib</code></li>
+<li><code>none</code></li>
+</ul>
+<p>Run <code>rsync --version</code> to see the default compress list compiled into your
+version (which may differ from the list above).</p>
+<p>Note that if you see an error about an option named <code>--old-compress</code> or
+<code>--new-compress</code>, this is rsync trying to send the <code>--compress-choice=zlib</code>
+or <code>--compress-choice=zlibx</code> option in a backward-compatible manner that
+more rsync versions understand. This error indicates that the older rsync
+version on the server will not allow you to force the compression type.</p>
+<p>Note that the &quot;zlibx&quot; compression algorithm is just the &quot;zlib&quot; algorithm
+with matched data excluded from the compression stream (to try to make it
+more compatible with an external zlib implementation).</p>
+</dd>
+
+<span id="opt--zl"></span><dt id="opt--compress-level"><code>--compress-level=NUM</code>, <code>--zl=NUM</code><a href="#opt--compress-level" class="tgt"></a></dt><dd>
+<p>Explicitly set the compression level to use (see <a href="#opt--compress"><code>--compress</code></a>,
+<code>-z</code>) instead of letting it default. The <a href="#opt--compress"><code>--compress</code></a> option is
+implied as long as the level chosen is not a &quot;don't compress&quot; level for the
+compression algorithm that is in effect (e.g. zlib compression treats level
+0 as &quot;off&quot;).</p>
+<p>The level values vary depending on the checksum in effect. Because rsync
+will negotiate a checksum choice by default (when the remote rsync is new
+enough), it can be good to combine this option with a
+<a href="#opt--compress-choice"><code>--compress-choice</code></a> (<code>--zc</code>) option unless you're sure of the
+choice in effect. For example:</p>
+<blockquote>
+<pre><code>rsync -aiv --zc=zstd --zl=22 host:src/ dest/
+</code></pre>
+</blockquote>
+<p>For zlib &amp; zlibx compression the valid values are from 1 to 9 with 6 being
+the default. Specifying <code>--zl=0</code> turns compression off, and specifying
+<code>--zl=-1</code> chooses the default level of 6.</p>
+<p>For zstd compression the valid values are from -&#8288;131072 to 22 with 3 being
+the default. Specifying 0 chooses the default of 3.</p>
+<p>For lz4 compression there are no levels, so the value is always 0.</p>
+<p>If you specify a too-large or too-small value, the number is silently
+limited to a valid value. This allows you to specify something like
+<code>--zl=999999999</code> and be assured that you'll end up with the maximum
+compression level no matter what algorithm was chosen.</p>
+<p>If you want to know the compression level that is in effect, specify
+<a href="#opt--debug"><code>--debug=nstr</code></a> to see the &quot;negotiated string&quot; results. This will
+report something like &quot;<code>Client compress: zstd (level 3)</code>&quot; (along with the
+checksum choice in effect).</p>
+</dd>
+
+<dt id="opt--skip-compress"><code>--skip-compress=LIST</code><a href="#opt--skip-compress" class="tgt"></a></dt><dd>
+<p><strong>NOTE:</strong> no compression method currently supports per-file compression
+changes, so this option has no effect.</p>
+<p>Override the list of file suffixes that will be compressed as little as
+possible. Rsync sets the compression level on a per-file basis based on
+the file's suffix. If the compression algorithm has an &quot;off&quot; level, then
+no compression occurs for those files. Other algorithms that support
+changing the streaming level on-the-fly will have the level minimized to
+reduces the CPU usage as much as possible for a matching file.</p>
+<p>The <strong>LIST</strong> should be one or more file suffixes (without the dot) separated
+by slashes (<code>/</code>). You may specify an empty string to indicate that no files
+should be skipped.</p>
+<p>Simple character-class matching is supported: each must consist of a list
+of letters inside the square brackets (e.g. no special classes, such as
+&quot;[:alpha:]&quot;, are supported, and '-&#8288;' has no special meaning).</p>
+<p>The characters asterisk (<code>*</code>) and question-mark (<code>?</code>) have no special meaning.</p>
+<p>Here's an example that specifies 6 suffixes to skip (since 1 of the 5 rules
+matches 2 suffixes):</p>
+<blockquote>
+<pre><code>--skip-compress=gz/jpg/mp[34]/7z/bz2
+</code></pre>
+</blockquote>
+<p>The default file suffixes in the skip-compress list in this version of
+rsync are:</p>
+<blockquote>
+<p>3g2
+3gp
+7z
+aac
+ace
+apk
+avi
+bz2
+deb
+dmg
+ear
+f4v
+flac
+flv
+gpg
+gz
+iso
+jar
+jpeg
+jpg
+lrz
+lz
+lz4
+lzma
+lzo
+m1a
+m1v
+m2a
+m2ts
+m2v
+m4a
+m4b
+m4p
+m4r
+m4v
+mka
+mkv
+mov
+mp1
+mp2
+mp3
+mp4
+mpa
+mpeg
+mpg
+mpv
+mts
+odb
+odf
+odg
+odi
+odm
+odp
+ods
+odt
+oga
+ogg
+ogm
+ogv
+ogx
+opus
+otg
+oth
+otp
+ots
+ott
+oxt
+png
+qt
+rar
+rpm
+rz
+rzip
+spx
+squashfs
+sxc
+sxd
+sxg
+sxm
+sxw
+sz
+tbz
+tbz2
+tgz
+tlz
+ts
+txz
+tzo
+vob
+war
+webm
+webp
+xz
+z
+zip
+zst</p>
+</blockquote>
+<p>This list will be replaced by your <code>--skip-compress</code> list in all but one
+situation: a copy from a daemon rsync will add your skipped suffixes to its
+list of non-compressing files (and its list may be configured to a
+different default).</p>
+</dd>
+
+<dt id="opt--numeric-ids"><code>--numeric-ids</code><a href="#opt--numeric-ids" class="tgt"></a></dt><dd>
+<p>With this option rsync will transfer numeric group and user IDs rather than
+using user and group names and mapping them at both ends.</p>
+<p>By default rsync will use the username and groupname to determine what
+ownership to give files. The special uid 0 and the special group 0 are
+never mapped via user/group names even if the <code>--numeric-ids</code> option is not
+specified.</p>
+<p>If a user or group has no name on the source system or it has no match on
+the destination system, then the numeric ID from the source system is used
+instead. See also the <a href="rsyncd.conf.5#use_chroot"><code>use chroot</code></a> setting
+in the rsyncd.conf manpage for some comments on how the chroot setting
+affects rsync's ability to look up the names of the users and groups and
+what you can do about it.</p>
+</dd>
+
+<span id="opt--groupmap"></span><dt id="opt--usermap"><code>--usermap=STRING</code>, <code>--groupmap=STRING</code><a href="#opt--usermap" class="tgt"></a></dt><dd>
+<p>These options allow you to specify users and groups that should be mapped
+to other values by the receiving side. The <strong>STRING</strong> is one or more
+<strong>FROM</strong>:<strong>TO</strong> pairs of values separated by commas. Any matching <strong>FROM</strong>
+value from the sender is replaced with a <strong>TO</strong> value from the receiver.
+You may specify usernames or user IDs for the <strong>FROM</strong> and <strong>TO</strong> values,
+and the <strong>FROM</strong> value may also be a wild-card string, which will be
+matched against the sender's names (wild-cards do NOT match against ID
+numbers, though see below for why a '<code>*</code>' matches everything). You may
+instead specify a range of ID numbers via an inclusive range: LOW-HIGH.
+For example:</p>
+<blockquote>
+<pre><code>--usermap=0-99:nobody,wayne:admin,*:normal --groupmap=usr:1,1:usr
+</code></pre>
+</blockquote>
+<p>The first match in the list is the one that is used. You should specify
+all your user mappings using a single <code>--usermap</code> option, and/or all your
+group mappings using a single <code>--groupmap</code> option.</p>
+<p>Note that the sender's name for the 0 user and group are not transmitted to
+the receiver, so you should either match these values using a 0, or use the
+names in effect on the receiving side (typically &quot;root&quot;). All other
+<strong>FROM</strong> names match those in use on the sending side. All <strong>TO</strong> names
+match those in use on the receiving side.</p>
+<p>Any IDs that do not have a name on the sending side are treated as having
+an empty name for the purpose of matching. This allows them to be matched
+via a &quot;<code>*</code>&quot; or using an empty name. For instance:</p>
+<blockquote>
+<pre><code>--usermap=:nobody --groupmap=*:nobody
+</code></pre>
+</blockquote>
+<p>When the <a href="#opt--numeric-ids"><code>--numeric-ids</code></a> option is used, the sender does not send any
+names, so all the IDs are treated as having an empty name. This means that
+you will need to specify numeric <strong>FROM</strong> values if you want to map these
+nameless IDs to different values.</p>
+<p>For the <code>--usermap</code> option to work, the receiver will need to be running as
+a super-user (see also the <a href="#opt--super"><code>--super</code></a> and <a href="#opt--fake-super"><code>--fake-super</code></a>
+options). For the <code>--groupmap</code> option to work, the receiver will need to
+have permissions to set that group.</p>
+<p>Starting with rsync 3.2.4, the <code>--usermap</code> option implies the
+<a href="#opt--owner"><code>--owner</code></a> (<code>-o</code>) option while the <code>--groupmap</code> option implies the
+<a href="#opt--group"><code>--group</code></a> (<code>-g</code>) option (since rsync needs to have those options
+enabled for the mapping options to work).</p>
+<p>An older rsync client may need to use <a href="#opt-s"><code>-s</code></a> to avoid a complaint
+about wildcard characters, but a modern rsync handles this automatically.</p>
+</dd>
+
+<dt id="opt--chown"><code>--chown=USER:GROUP</code><a href="#opt--chown" class="tgt"></a></dt><dd>
+<p>This option forces all files to be owned by USER with group GROUP. This is
+a simpler interface than using <a href="#opt--usermap"><code>--usermap</code></a> &amp; <a href="#opt--groupmap"><code>--groupmap</code></a>
+directly, but it is implemented using those options internally so they
+cannot be mixed. If either the USER or GROUP is empty, no mapping for the
+omitted user/group will occur. If GROUP is empty, the trailing colon may
+be omitted, but if USER is empty, a leading colon must be supplied.</p>
+<p>If you specify &quot;<code>--chown=foo:bar</code>&quot;, this is exactly the same as specifying
+&quot;<code>--usermap=*:foo --groupmap=*:bar</code>&quot;, only easier (and with the same
+implied <a href="#opt--owner"><code>--owner</code></a> and/or <a href="#opt--group"><code>--group</code></a> options).</p>
+<p>An older rsync client may need to use <a href="#opt-s"><code>-s</code></a> to avoid a complaint
+about wildcard characters, but a modern rsync handles this automatically.</p>
+</dd>
+
+<dt id="opt--timeout"><code>--timeout=SECONDS</code><a href="#opt--timeout" class="tgt"></a></dt><dd>
+<p>This option allows you to set a maximum I/O timeout in seconds. If no data
+is transferred for the specified time then rsync will exit. The default is
+0, which means no timeout.</p>
+</dd>
+
+<dt id="opt--contimeout"><code>--contimeout=SECONDS</code><a href="#opt--contimeout" class="tgt"></a></dt><dd>
+<p>This option allows you to set the amount of time that rsync will wait for
+its connection to an rsync daemon to succeed. If the timeout is reached,
+rsync exits with an error.</p>
+</dd>
+
+<dt id="opt--address"><code>--address=ADDRESS</code><a href="#opt--address" class="tgt"></a></dt><dd>
+<p>By default rsync will bind to the wildcard address when connecting to an
+rsync daemon. The <code>--address</code> option allows you to specify a specific IP
+address (or hostname) to bind to.</p>
+<p>See also <a href="#dopt--address">the daemon version of the <code>--address</code> option</a>.</p>
+</dd>
+
+<dt id="opt--port"><code>--port=PORT</code><a href="#opt--port" class="tgt"></a></dt><dd>
+<p>This specifies an alternate TCP port number to use rather than the default
+of 873. This is only needed if you are using the double-colon (::) syntax
+to connect with an rsync daemon (since the URL syntax has a way to specify
+the port as a part of the URL).</p>
+<p>See also <a href="#dopt--port">the daemon version of the <code>--port</code> option</a>.</p>
+</dd>
+
+<dt id="opt--sockopts"><code>--sockopts=OPTIONS</code><a href="#opt--sockopts" class="tgt"></a></dt><dd>
+<p>This option can provide endless fun for people who like to tune their
+systems to the utmost degree. You can set all sorts of socket options
+which may make transfers faster (or slower!). Read the manpage for the
+<code>setsockopt()</code> system call for details on some of the options you may be
+able to set. By default no special socket options are set. This only
+affects direct socket connections to a remote rsync daemon.</p>
+<p>See also <a href="#dopt--sockopts">the daemon version of the <code>--sockopts</code> option</a>.</p>
+</dd>
+
+<dt id="opt--blocking-io"><code>--blocking-io</code><a href="#opt--blocking-io" class="tgt"></a></dt><dd>
+<p>This tells rsync to use blocking I/O when launching a remote shell
+transport. If the remote shell is either rsh or remsh, rsync defaults to
+using blocking I/O, otherwise it defaults to using non-blocking I/O. (Note
+that ssh prefers non-blocking I/O.)</p>
+</dd>
+
+<dt id="opt--outbuf"><code>--outbuf=MODE</code><a href="#opt--outbuf" class="tgt"></a></dt><dd>
+<p>This sets the output buffering mode. The mode can be None (aka
+Unbuffered), Line, or Block (aka Full). You may specify as little as a
+single letter for the mode, and use upper or lower case.</p>
+<p>The main use of this option is to change Full buffering to Line buffering
+when rsync's output is going to a file or pipe.</p>
+</dd>
+
+<span id="opt-i"></span><dt id="opt--itemize-changes"><code>--itemize-changes</code>, <code>-i</code><a href="#opt--itemize-changes" class="tgt"></a></dt><dd>
+<p>Requests a simple itemized list of the changes that are being made to each
+file, including attribute changes. This is exactly the same as specifying
+<a href="#opt--out-format"><code>--out-format='%i %n%L'</code></a>. If you repeat the option, unchanged
+files will also be output, but only if the receiving rsync is at least
+version 2.6.7 (you can use <code>-vv</code> with older versions of rsync, but that
+also turns on the output of other verbose messages).</p>
+<p>The &quot;%i&quot; escape has a cryptic output that is 11 letters long. The general
+format is like the string <code>YXcstpoguax</code>, where <strong>Y</strong> is replaced by the type
+of update being done, <strong>X</strong> is replaced by the file-type, and the other
+letters represent attributes that may be output if they are being modified.</p>
+<p>The update types that replace the <strong>Y</strong> are as follows:</p>
+<ul>
+<li>A <code>&lt;</code> means that a file is being transferred to the remote host (sent).</li>
+<li>A <code>&gt;</code> means that a file is being transferred to the local host
+(received).</li>
+<li>A <code>c</code> means that a local change/creation is occurring for the item (such
+as the creation of a directory or the changing of a symlink, etc.).</li>
+<li>A <code>h</code> means that the item is a hard link to another item (requires
+<a href="#opt--hard-links"><code>--hard-links</code></a>).</li>
+<li>A <code>.</code> means that the item is not being updated (though it might have
+attributes that are being modified).</li>
+<li>A <code>*</code> means that the rest of the itemized-output area contains a message
+(e.g. &quot;deleting&quot;).</li>
+</ul>
+<p>The file-types that replace the <strong>X</strong> are: <code>f</code> for a file, a <code>d</code> for a
+directory, an <code>L</code> for a symlink, a <code>D</code> for a device, and a <code>S</code> for a
+special file (e.g. named sockets and fifos).</p>
+<p>The other letters in the string indicate if some attributes of the file
+have changed, as follows:</p>
+<ul>
+<li>&quot;<code>.</code>&quot; -&#8288; the attribute is unchanged.</li>
+<li>&quot;<code>+</code>&quot; -&#8288; the file is newly created.</li>
+<li>&quot;<code> </code>&quot; -&#8288; all the attributes are unchanged (all dots turn to spaces).</li>
+<li>&quot;<code>?</code>&quot; -&#8288; the change is unknown (when the remote rsync is old).</li>
+<li>A letter indicates an attribute is being updated.</li>
+</ul>
+<p>The attribute that is associated with each letter is as follows:</p>
+<ul>
+<li>A <code>c</code> means either that a regular file has a different checksum (requires
+<a href="#opt--checksum"><code>--checksum</code></a>) or that a symlink, device, or special file has a
+changed value. Note that if you are sending files to an rsync prior to
+3.0.1, this change flag will be present only for checksum-differing
+regular files.</li>
+<li>A <code>s</code> means the size of a regular file is different and will be updated
+by the file transfer.</li>
+<li>A <code>t</code> means the modification time is different and is being updated to
+the sender's value (requires <a href="#opt--times"><code>--times</code></a>). An alternate value of
+<code>T</code> means that the modification time will be set to the transfer time,
+which happens when a file/symlink/device is updated without
+<a href="#opt--times"><code>--times</code></a> and when a symlink is changed and the receiver can't
+set its time. (Note: when using an rsync 3.0.0 client, you might see the
+<code>s</code> flag combined with <code>t</code> instead of the proper <code>T</code> flag for this
+time-setting failure.)</li>
+<li>A <code>p</code> means the permissions are different and are being updated to the
+sender's value (requires <a href="#opt--perms"><code>--perms</code></a>).</li>
+<li>An <code>o</code> means the owner is different and is being updated to the sender's
+value (requires <a href="#opt--owner"><code>--owner</code></a> and super-user privileges).</li>
+<li>A <code>g</code> means the group is different and is being updated to the sender's
+value (requires <a href="#opt--group"><code>--group</code></a> and the authority to set the group).</li>
+<li>A <code>u</code>|<code>n</code>|<code>b</code> indicates the following information:
+<ul>
+<li><code>u</code> means the access (use) time is different and is being updated to
+the sender's value (requires <a href="#opt--atimes"><code>--atimes</code></a>)</li>
+<li><code>n</code> means the create time (newness) is different and is being updated
+to the sender's value (requires <a href="#opt--crtimes"><code>--crtimes</code></a>)</li>
+<li><code>b</code> means that both the access and create times are being updated</li>
+</ul>
+</li>
+<li>The <code>a</code> means that the ACL information is being changed.</li>
+<li>The <code>x</code> means that the extended attribute information is being changed.</li>
+</ul>
+<p>One other output is possible: when deleting files, the &quot;%i&quot; will output the
+string &quot;<code>*deleting</code>&quot; for each item that is being removed (assuming that you
+are talking to a recent enough rsync that it logs deletions instead of
+outputting them as a verbose message).</p>
+</dd>
+
+<dt id="opt--out-format"><code>--out-format=FORMAT</code><a href="#opt--out-format" class="tgt"></a></dt><dd>
+<p>This allows you to specify exactly what the rsync client outputs to the
+user on a per-update basis. The format is a text string containing
+embedded single-character escape sequences prefixed with a percent (%)
+character. A default format of &quot;%n%L&quot; is assumed if either
+<a href="#opt--info"><code>--info=name</code></a> or <a href="#opt-v"><code>-v</code></a> is specified (this tells you just the
+name of the file and, if the item is a link, where it points). For a full
+list of the possible escape characters, see the <a href="rsyncd.conf.5#log_format"><code>log format</code></a> setting in the rsyncd.conf manpage.</p>
+<p>Specifying the <code>--out-format</code> option implies the <a href="#opt--info"><code>--info=name</code></a>
+option, which will mention each file, dir, etc. that gets updated in a
+significant way (a transferred file, a recreated symlink/device, or a
+touched directory). In addition, if the itemize-changes escape (%i) is
+included in the string (e.g. if the <a href="#opt--itemize-changes"><code>--itemize-changes</code></a> option was
+used), the logging of names increases to mention any item that is changed
+in any way (as long as the receiving side is at least 2.6.4). See the
+<a href="#opt--itemize-changes"><code>--itemize-changes</code></a> option for a description of the output of &quot;%i&quot;.</p>
+<p>Rsync will output the out-format string prior to a file's transfer unless
+one of the transfer-statistic escapes is requested, in which case the
+logging is done at the end of the file's transfer. When this late logging
+is in effect and <a href="#opt--progress"><code>--progress</code></a> is also specified, rsync will also
+output the name of the file being transferred prior to its progress
+information (followed, of course, by the out-format output).</p>
+</dd>
+
+<dt id="opt--log-file"><code>--log-file=FILE</code><a href="#opt--log-file" class="tgt"></a></dt><dd>
+<p>This option causes rsync to log what it is doing to a file. This is
+similar to the logging that a daemon does, but can be requested for the
+client side and/or the server side of a non-daemon transfer. If specified
+as a client option, transfer logging will be enabled with a default format
+of &quot;%i %n%L&quot;. See the <a href="#opt--log-file-format"><code>--log-file-format</code></a> option if you wish to
+override this.</p>
+<p>Here's an example command that requests the remote side to log what is
+happening:</p>
+<blockquote>
+<pre><code>rsync -av --remote-option=--log-file=/tmp/rlog src/ dest/
+</code></pre>
+</blockquote>
+<p>This is very useful if you need to debug why a connection is closing
+unexpectedly.</p>
+<p>See also <a href="#dopt--log-file">the daemon version of the <code>--log-file</code> option</a>.</p>
+</dd>
+
+<dt id="opt--log-file-format"><code>--log-file-format=FORMAT</code><a href="#opt--log-file-format" class="tgt"></a></dt><dd>
+<p>This allows you to specify exactly what per-update logging is put into the
+file specified by the <a href="#opt--log-file"><code>--log-file</code></a> option (which must also be
+specified for this option to have any effect). If you specify an empty
+string, updated files will not be mentioned in the log file. For a list of
+the possible escape characters, see the <a href="rsyncd.conf.5#log_format"><code>log format</code></a>
+setting in the rsyncd.conf manpage.</p>
+<p>The default FORMAT used if <a href="#opt--log-file"><code>--log-file</code></a> is specified and this
+option is not is '%i %n%L'.</p>
+<p>See also <a href="#dopt--log-file-format">the daemon version of the <code>--log-file-format</code>
+option</a>.</p>
+</dd>
+
+<dt id="opt--stats"><code>--stats</code><a href="#opt--stats" class="tgt"></a></dt><dd>
+<p>This tells rsync to print a verbose set of statistics on the file transfer,
+allowing you to tell how effective rsync's delta-transfer algorithm is for
+your data. This option is equivalent to <a href="#opt--info"><code>--info=stats2</code></a> if
+combined with 0 or 1 <a href="#opt-v"><code>-v</code></a> options, or <a href="#opt--info"><code>--info=stats3</code></a> if
+combined with 2 or more <a href="#opt-v"><code>-v</code></a> options.</p>
+<p>The current statistics are as follows:</p>
+<ul>
+<li><code>Number of files</code> is the count of all &quot;files&quot; (in the generic sense),
+which includes directories, symlinks, etc. The total count will be
+followed by a list of counts by filetype (if the total is non-zero). For
+example: &quot;(reg: 5, dir: 3, link: 2, dev: 1, special: 1)&quot; lists the totals
+for regular files, directories, symlinks, devices, and special files. If
+any of value is 0, it is completely omitted from the list.</li>
+<li><code>Number of created files</code> is the count of how many &quot;files&quot; (generic
+sense) were created (as opposed to updated). The total count will be
+followed by a list of counts by filetype (if the total is non-zero).</li>
+<li><code>Number of deleted files</code> is the count of how many &quot;files&quot; (generic
+sense) were deleted. The total count will be
+followed by a list of counts by filetype (if the total is non-zero).
+Note that this line is only output if deletions are in effect, and only
+if protocol 31 is being used (the default for rsync 3.1.x).</li>
+<li><code>Number of regular files transferred</code> is the count of normal files that
+were updated via rsync's delta-transfer algorithm, which does not include
+dirs, symlinks, etc. Note that rsync 3.1.0 added the word &quot;regular&quot; into
+this heading.</li>
+<li><code>Total file size</code> is the total sum of all file sizes in the transfer.
+This does not count any size for directories or special files, but does
+include the size of symlinks.</li>
+<li><code>Total transferred file size</code> is the total sum of all files sizes for
+just the transferred files.</li>
+<li><code>Literal data</code> is how much unmatched file-update data we had to send to
+the receiver for it to recreate the updated files.</li>
+<li><code>Matched data</code> is how much data the receiver got locally when recreating
+the updated files.</li>
+<li><code>File list size</code> is how big the file-list data was when the sender sent
+it to the receiver. This is smaller than the in-memory size for the file
+list due to some compressing of duplicated data when rsync sends the
+list.</li>
+<li><code>File list generation time</code> is the number of seconds that the sender
+spent creating the file list. This requires a modern rsync on the
+sending side for this to be present.</li>
+<li><code>File list transfer time</code> is the number of seconds that the sender spent
+sending the file list to the receiver.</li>
+<li><code>Total bytes sent</code> is the count of all the bytes that rsync sent from the
+client side to the server side.</li>
+<li><code>Total bytes received</code> is the count of all non-message bytes that rsync
+received by the client side from the server side. &quot;Non-message&quot; bytes
+means that we don't count the bytes for a verbose message that the server
+sent to us, which makes the stats more consistent.</li>
+</ul>
+</dd>
+
+<span id="opt-8"></span><dt id="opt--8-bit-output"><code>--8-bit-output</code>, <code>-8</code><a href="#opt--8-bit-output" class="tgt"></a></dt><dd>
+<p>This tells rsync to leave all high-bit characters unescaped in the output
+instead of trying to test them to see if they're valid in the current
+locale and escaping the invalid ones. All control characters (but never
+tabs) are always escaped, regardless of this option's setting.</p>
+<p>The escape idiom that started in 2.6.7 is to output a literal backslash
+(<code>\</code>) and a hash (<code>#</code>), followed by exactly 3 octal digits. For example, a
+newline would output as &quot;<code>\#012</code>&quot;. A literal backslash that is in a
+filename is not escaped unless it is followed by a hash and 3 digits (0-9).</p>
+</dd>
+
+<span id="opt-h"></span><dt id="opt--human-readable"><code>--human-readable</code>, <code>-h</code><a href="#opt--human-readable" class="tgt"></a></dt><dd>
+<p>Output numbers in a more human-readable format. There are 3 possible levels:</p>
+<ol>
+<li>output numbers with a separator between each set of 3 digits (either a
+comma or a period, depending on if the decimal point is represented by a
+period or a comma).</li>
+<li>output numbers in units of 1000 (with a character suffix for larger
+units&nbsp;-&#8288;-&#8288; see below).</li>
+<li>output numbers in units of 1024.</li>
+</ol>
+<p>The default is human-readable level 1. Each <code>-h</code> option increases the
+level by one. You can take the level down to 0 (to output numbers as pure
+digits) by specifying the <code>--no-human-readable</code> (<code>--no-h</code>) option.</p>
+<p>The unit letters that are appended in levels 2 and 3 are: <code>K</code> (kilo), <code>M</code>
+(mega), <code>G</code> (giga), <code>T</code> (tera), or <code>P</code> (peta). For example, a 1234567-byte
+file would output as 1.23M in level-2 (assuming that a period is your local
+decimal point).</p>
+<p>Backward compatibility note: versions of rsync prior to 3.1.0 do not
+support human-readable level 1, and they default to level 0. Thus,
+specifying one or two <code>-h</code> options will behave in a comparable manner in
+old and new versions as long as you didn't specify a <code>--no-h</code> option prior
+to one or more <code>-h</code> options. See the <a href="#opt--list-only"><code>--list-only</code></a> option for one
+difference.</p>
+</dd>
+
+<dt id="opt--partial"><code>--partial</code><a href="#opt--partial" class="tgt"></a></dt><dd>
+<p>By default, rsync will delete any partially transferred file if the
+transfer is interrupted. In some circumstances it is more desirable to
+keep partially transferred files. Using the <code>--partial</code> option tells rsync
+to keep the partial file which should make a subsequent transfer of the
+rest of the file much faster.</p>
+</dd>
+
+<dt id="opt--partial-dir"><code>--partial-dir=DIR</code><a href="#opt--partial-dir" class="tgt"></a></dt><dd>
+<p>This option modifies the behavior of the <a href="#opt--partial"><code>--partial</code></a> option while
+also implying that it be enabled. This enhanced partial-file method puts
+any partially transferred files into the specified <u>DIR</u> instead of writing
+the partial file out to the destination file. On the next transfer, rsync
+will use a file found in this dir as data to speed up the resumption of the
+transfer and then delete it after it has served its purpose.</p>
+<p>Note that if <a href="#opt--whole-file"><code>--whole-file</code></a> is specified (or implied), any
+partial-dir files that are found for a file that is being updated will
+simply be removed (since rsync is sending files without using rsync's
+delta-transfer algorithm).</p>
+<p>Rsync will create the <u>DIR</u> if it is missing, but just the last dir&nbsp;-&#8288;-&#8288; not
+the whole path. This makes it easy to use a relative path (such as
+&quot;<code>--partial-dir=.rsync-partial</code>&quot;) to have rsync create the
+partial-directory in the destination file's directory when it is needed,
+and then remove it again when the partial file is deleted. Note that this
+directory removal is only done for a relative pathname, as it is expected
+that an absolute path is to a directory that is reserved for partial-dir
+work.</p>
+<p>If the partial-dir value is not an absolute path, rsync will add an exclude
+rule at the end of all your existing excludes. This will prevent the
+sending of any partial-dir files that may exist on the sending side, and
+will also prevent the untimely deletion of partial-dir items on the
+receiving side. An example: the above <code>--partial-dir</code> option would add the
+equivalent of this &quot;perishable&quot; exclude at the end of any other filter
+rules: <code>-f '-p .rsync-partial/'</code></p>
+<p>If you are supplying your own exclude rules, you may need to add your own
+exclude/hide/protect rule for the partial-dir because:</p>
+<ol>
+<li>the auto-added rule may be ineffective at the end of your other rules, or</li>
+<li>you may wish to override rsync's exclude choice.</li>
+</ol>
+<p>For instance, if you want to make rsync clean-up any left-over partial-dirs
+that may be lying around, you should specify <a href="#opt--delete-after"><code>--delete-after</code></a> and
+add a &quot;risk&quot; filter rule, e.g. <code>-f 'R .rsync-partial/'</code>. Avoid using
+<a href="#opt--delete-before"><code>--delete-before</code></a> or <a href="#opt--delete-during"><code>--delete-during</code></a> unless you don't
+need rsync to use any of the left-over partial-dir data during the current
+run.</p>
+<p>IMPORTANT: the <code>--partial-dir</code> should not be writable by other users or it
+is a security risk! E.g. AVOID &quot;/tmp&quot;!</p>
+<p>You can also set the partial-dir value the <a href="#RSYNC_PARTIAL_DIR"><code>RSYNC_PARTIAL_DIR</code></a>
+environment variable. Setting this in the environment does not force
+<a href="#opt--partial"><code>--partial</code></a> to be enabled, but rather it affects where partial
+files go when <a href="#opt--partial"><code>--partial</code></a> is specified. For instance, instead of
+using <code>--partial-dir=.rsync-tmp</code> along with <a href="#opt--progress"><code>--progress</code></a>, you could
+set <a href="#RSYNC_PARTIAL_DIR"><code>RSYNC_PARTIAL_DIR=.rsync-tmp</code></a> in your environment and then use
+the <a href="#opt-P"><code>-P</code></a> option to turn on the use of the .rsync-tmp dir for
+partial transfers. The only times that the <a href="#opt--partial"><code>--partial</code></a> option does
+not look for this environment value are:</p>
+<ol>
+<li>when <a href="#opt--inplace"><code>--inplace</code></a> was specified (since <a href="#opt--inplace"><code>--inplace</code></a>
+conflicts with <code>--partial-dir</code>), and</li>
+<li>when <a href="#opt--delay-updates"><code>--delay-updates</code></a> was specified (see below).</li>
+</ol>
+<p>When a modern rsync resumes the transfer of a file in the partial-dir, that
+partial file is now updated in-place instead of creating yet another
+tmp-file copy (so it maxes out at dest + tmp instead of dest + partial +
+tmp). This requires both ends of the transfer to be at least version
+3.2.0.</p>
+<p>For the purposes of the daemon-config's &quot;<code>refuse options</code>&quot; setting,
+<code>--partial-dir</code> does <u>not</u> imply <a href="#opt--partial"><code>--partial</code></a>. This is so that a
+refusal of the <a href="#opt--partial"><code>--partial</code></a> option can be used to disallow the
+overwriting of destination files with a partial transfer, while still
+allowing the safer idiom provided by <code>--partial-dir</code>.</p>
+</dd>
+
+<dt id="opt--delay-updates"><code>--delay-updates</code><a href="#opt--delay-updates" class="tgt"></a></dt><dd>
+<p>This option puts the temporary file from each updated file into a holding
+directory until the end of the transfer, at which time all the files are
+renamed into place in rapid succession. This attempts to make the updating
+of the files a little more atomic. By default the files are placed into a
+directory named <code>.~tmp~</code> in each file's destination directory, but if
+you've specified the <a href="#opt--partial-dir"><code>--partial-dir</code></a> option, that directory will be
+used instead. See the comments in the <a href="#opt--partial-dir"><code>--partial-dir</code></a> section for
+a discussion of how this <code>.~tmp~</code> dir will be excluded from the transfer,
+and what you can do if you want rsync to cleanup old <code>.~tmp~</code> dirs that
+might be lying around. Conflicts with <a href="#opt--inplace"><code>--inplace</code></a> and
+<a href="#opt--append"><code>--append</code></a>.</p>
+<p>This option implies <a href="#opt--no-inc-recursive"><code>--no-inc-recursive</code></a> since it needs the full
+file list in memory in order to be able to iterate over it at the end.</p>
+<p>This option uses more memory on the receiving side (one bit per file
+transferred) and also requires enough free disk space on the receiving side
+to hold an additional copy of all the updated files. Note also that you
+should not use an absolute path to <a href="#opt--partial-dir"><code>--partial-dir</code></a> unless:</p>
+<ol>
+<li>there is no chance of any of the files in the transfer having the same
+name (since all the updated files will be put into a single directory if
+the path is absolute), and</li>
+<li>there are no mount points in the hierarchy (since the delayed updates
+will fail if they can't be renamed into place).</li>
+</ol>
+<p>See also the &quot;atomic-rsync&quot; python script in the &quot;support&quot; subdir for an
+update algorithm that is even more atomic (it uses <a href="#opt--link-dest"><code>--link-dest</code></a>
+and a parallel hierarchy of files).</p>
+</dd>
+
+<span id="opt-m"></span><dt id="opt--prune-empty-dirs"><code>--prune-empty-dirs</code>, <code>-m</code><a href="#opt--prune-empty-dirs" class="tgt"></a></dt><dd>
+<p>This option tells the receiving rsync to get rid of empty directories from
+the file-list, including nested directories that have no non-directory
+children. This is useful for avoiding the creation of a bunch of useless
+directories when the sending rsync is recursively scanning a hierarchy of
+files using include/exclude/filter rules.</p>
+<p>This option can still leave empty directories on the receiving side if you
+make use of <a href="#TRANSFER_RULES">TRANSFER_RULES</a>.</p>
+<p>Because the file-list is actually being pruned, this option also affects
+what directories get deleted when a delete is active. However, keep in
+mind that excluded files and directories can prevent existing items from
+being deleted due to an exclude both hiding source files and protecting
+destination files. See the perishable filter-rule option for how to avoid
+this.</p>
+<p>You can prevent the pruning of certain empty directories from the file-list
+by using a global &quot;protect&quot; filter. For instance, this option would ensure
+that the directory &quot;emptydir&quot; was kept in the file-list:</p>
+<blockquote>
+<pre><code>--filter 'protect emptydir/'
+</code></pre>
+</blockquote>
+<p>Here's an example that copies all .pdf files in a hierarchy, only creating
+the necessary destination directories to hold the .pdf files, and ensures
+that any superfluous files and directories in the destination are removed
+(note the hide filter of non-directories being used instead of an exclude):</p>
+<blockquote>
+<pre><code>rsync -avm --del --include='*.pdf' -f 'hide,! */' src/ dest
+</code></pre>
+</blockquote>
+<p>If you didn't want to remove superfluous destination files, the more
+time-honored options of <code>--include='*/' --exclude='*'</code> would work
+fine in place of the hide-filter (if that is more natural to you).</p>
+</dd>
+
+<dt id="opt--progress"><code>--progress</code><a href="#opt--progress" class="tgt"></a></dt><dd>
+<p>This option tells rsync to print information showing the progress of the
+transfer. This gives a bored user something to watch. With a modern rsync
+this is the same as specifying <a href="#opt--info"><code>--info=flist2,name,progress</code></a>, but
+any user-supplied settings for those info flags takes precedence (e.g.
+<a href="#opt--info"><code>--info=flist0 --progress</code></a>).</p>
+<p>While rsync is transferring a regular file, it updates a progress line that
+looks like this:</p>
+<blockquote>
+<pre><code>782448 63% 110.64kB/s 0:00:04
+</code></pre>
+</blockquote>
+<p>In this example, the receiver has reconstructed 782448 bytes or 63% of the
+sender's file, which is being reconstructed at a rate of 110.64 kilobytes
+per second, and the transfer will finish in 4 seconds if the current rate
+is maintained until the end.</p>
+<p>These statistics can be misleading if rsync's delta-transfer algorithm is
+in use. For example, if the sender's file consists of the basis file
+followed by additional data, the reported rate will probably drop
+dramatically when the receiver gets to the literal data, and the transfer
+will probably take much longer to finish than the receiver estimated as it
+was finishing the matched part of the file.</p>
+<p>When the file transfer finishes, rsync replaces the progress line with a
+summary line that looks like this:</p>
+<blockquote>
+<pre><code>1,238,099 100% 146.38kB/s 0:00:08 (xfr#5, to-chk=169/396)
+</code></pre>
+</blockquote>
+<p>In this example, the file was 1,238,099 bytes long in total, the average
+rate of transfer for the whole file was 146.38 kilobytes per second over
+the 8 seconds that it took to complete, it was the 5th transfer of a
+regular file during the current rsync session, and there are 169 more files
+for the receiver to check (to see if they are up-to-date or not) remaining
+out of the 396 total files in the file-list.</p>
+<p>In an incremental recursion scan, rsync won't know the total number of
+files in the file-list until it reaches the ends of the scan, but since it
+starts to transfer files during the scan, it will display a line with the
+text &quot;ir-chk&quot; (for incremental recursion check) instead of &quot;to-chk&quot; until
+the point that it knows the full size of the list, at which point it will
+switch to using &quot;to-chk&quot;. Thus, seeing &quot;ir-chk&quot; lets you know that the
+total count of files in the file list is still going to increase (and each
+time it does, the count of files left to check will increase by the number
+of the files added to the list).</p>
+</dd>
+
+<dt id="opt-P"><code>-P</code><a href="#opt-P" class="tgt"></a></dt><dd>
+<p>The <code>-P</code> option is equivalent to &quot;<a href="#opt--partial"><code>--partial</code></a>
+<a href="#opt--progress"><code>--progress</code></a>&quot;. Its purpose is to make it much easier to specify
+these two options for a long transfer that may be interrupted.</p>
+<p>There is also a <a href="#opt--info"><code>--info=progress2</code></a> option that outputs statistics
+based on the whole transfer, rather than individual files. Use this flag
+without outputting a filename (e.g. avoid <code>-v</code> or specify
+<a href="#opt--info"><code>--info=name0</code></a>) if you want to see how the transfer is doing
+without scrolling the screen with a lot of names. (You don't need to
+specify the <a href="#opt--progress"><code>--progress</code></a> option in order to use
+<a href="#opt--info"><code>--info=progress2</code></a>.)</p>
+<p>Finally, you can get an instant progress report by sending rsync a signal
+of either SIGINFO or SIGVTALRM. On BSD systems, a SIGINFO is generated by
+typing a Ctrl+T (Linux doesn't currently support a SIGINFO signal). When
+the client-side process receives one of those signals, it sets a flag to
+output a single progress report which is output when the current file
+transfer finishes (so it may take a little time if a big file is being
+handled when the signal arrives). A filename is output (if needed)
+followed by the <a href="#opt--info"><code>--info=progress2</code></a> format of progress info. If you
+don't know which of the 3 rsync processes is the client process, it's OK to
+signal all of them (since the non-client processes ignore the signal).</p>
+<p>CAUTION: sending SIGVTALRM to an older rsync (pre-3.2.0) will kill it.</p>
+</dd>
+
+<dt id="opt--password-file"><code>--password-file=FILE</code><a href="#opt--password-file" class="tgt"></a></dt><dd>
+<p>This option allows you to provide a password for accessing an rsync daemon
+via a file or via standard input if <strong>FILE</strong> is <code>-</code>. The file should
+contain just the password on the first line (all other lines are ignored).
+Rsync will exit with an error if <strong>FILE</strong> is world readable or if a
+root-run rsync command finds a non-root-owned file.</p>
+<p>This option does not supply a password to a remote shell transport such as
+ssh; to learn how to do that, consult the remote shell's documentation.
+When accessing an rsync daemon using a remote shell as the transport, this
+option only comes into effect after the remote shell finishes its
+authentication (i.e. if you have also specified a password in the daemon's
+config file).</p>
+</dd>
+
+<dt id="opt--early-input"><code>--early-input=FILE</code><a href="#opt--early-input" class="tgt"></a></dt><dd>
+<p>This option allows rsync to send up to 5K of data to the &quot;early exec&quot;
+script on its stdin. One possible use of this data is to give the script a
+secret that can be used to mount an encrypted filesystem (which you should
+unmount in the the &quot;post-xfer exec&quot; script).</p>
+<p>The daemon must be at least version 3.2.1.</p>
+</dd>
+
+<dt id="opt--list-only"><code>--list-only</code><a href="#opt--list-only" class="tgt"></a></dt><dd>
+<p>This option will cause the source files to be listed instead of
+transferred. This option is inferred if there is a single source arg and
+no destination specified, so its main uses are:</p>
+<ol>
+<li>to turn a copy command that includes a destination arg into a
+file-listing command, or</li>
+<li>to be able to specify more than one source arg. Note: be sure to
+include the destination.</li>
+</ol>
+<p>CAUTION: keep in mind that a source arg with a wild-card is expanded by the
+shell into multiple args, so it is never safe to try to specify a single
+wild-card arg to try to infer this option. A safe example is:</p>
+<blockquote>
+<pre><code>rsync -av --list-only foo* dest/
+</code></pre>
+</blockquote>
+<p>This option always uses an output format that looks similar to this:</p>
+<blockquote>
+<pre><code>drwxrwxr-x 4,096 2022/09/30 12:53:11 support
+-rw-rw-r-- 80 2005/01/11 10:37:37 support/Makefile
+</code></pre>
+</blockquote>
+<p>The only option that affects this output style is (as of 3.1.0) the
+<a href="#opt--human-readable"><code>--human-readable</code></a> (<code>-h</code>) option. The default is to output sizes
+as byte counts with digit separators (in a 14-character-width column).
+Specifying at least one <code>-h</code> option makes the sizes output with unit
+suffixes. If you want old-style bytecount sizes without digit separators
+(and an 11-character-width column) use <code>--no-h</code>.</p>
+<p>Compatibility note: when requesting a remote listing of files from an rsync
+that is version 2.6.3 or older, you may encounter an error if you ask for a
+non-recursive listing. This is because a file listing implies the
+<a href="#opt--dirs"><code>--dirs</code></a> option w/o <a href="#opt--recursive"><code>--recursive</code></a>, and older rsyncs don't
+have that option. To avoid this problem, either specify the <code>--no-dirs</code>
+option (if you don't need to expand a directory's content), or turn on
+recursion and exclude the content of subdirectories: <code>-r --exclude='/*/*'</code>.</p>
+</dd>
+
+<dt id="opt--bwlimit"><code>--bwlimit=RATE</code><a href="#opt--bwlimit" class="tgt"></a></dt><dd>
+<p>This option allows you to specify the maximum transfer rate for the data
+sent over the socket, specified in units per second. The RATE value can be
+suffixed with a string to indicate a size multiplier, and may be a
+fractional value (e.g. <code>--bwlimit=1.5m</code>). If no suffix is specified, the
+value will be assumed to be in units of 1024 bytes (as if &quot;K&quot; or &quot;KiB&quot; had
+been appended). See the <a href="#opt--max-size"><code>--max-size</code></a> option for a description of
+all the available suffixes. A value of 0 specifies no limit.</p>
+<p>For backward-compatibility reasons, the rate limit will be rounded to the
+nearest KiB unit, so no rate smaller than 1024 bytes per second is
+possible.</p>
+<p>Rsync writes data over the socket in blocks, and this option both limits
+the size of the blocks that rsync writes, and tries to keep the average
+transfer rate at the requested limit. Some burstiness may be seen where
+rsync writes out a block of data and then sleeps to bring the average rate
+into compliance.</p>
+<p>Due to the internal buffering of data, the <a href="#opt--progress"><code>--progress</code></a> option may
+not be an accurate reflection on how fast the data is being sent. This is
+because some files can show up as being rapidly sent when the data is
+quickly buffered, while other can show up as very slow when the flushing of
+the output buffer occurs. This may be fixed in a future version.</p>
+<p>See also <a href="#dopt--bwlimit">the daemon version of the <code>--bwlimit</code> option</a>.</p>
+</dd>
+
+<span id="opt--time-limit"></span><dt id="opt--stop-after"><code>--stop-after=MINS</code>, (<code>--time-limit=MINS</code>)<a href="#opt--stop-after" class="tgt"></a></dt><dd>
+<p>This option tells rsync to stop copying when the specified number of
+minutes has elapsed.</p>
+<p>For maximal flexibility, rsync does not communicate this option to the
+remote rsync since it is usually enough that one side of the connection
+quits as specified. This allows the option's use even when only one side
+of the connection supports it. You can tell the remote side about the time
+limit using <a href="#opt--remote-option"><code>--remote-option</code></a> (<code>-M</code>), should the need arise.</p>
+<p>The <code>--time-limit</code> version of this option is deprecated.</p>
+</dd>
+
+<dt id="opt--stop-at"><code>--stop-at=y-m-dTh:m</code><a href="#opt--stop-at" class="tgt"></a></dt><dd>
+<p>This option tells rsync to stop copying when the specified point in time
+has been reached. The date &amp; time can be fully specified in a numeric
+format of year-month-dayThour:minute (e.g. 2000-12-31T23:59) in the local
+timezone. You may choose to separate the date numbers using slashes
+instead of dashes.</p>
+<p>The value can also be abbreviated in a variety of ways, such as specifying
+a 2-digit year and/or leaving off various values. In all cases, the value
+will be taken to be the next possible point in time where the supplied
+information matches. If the value specifies the current time or a past
+time, rsync exits with an error.</p>
+<p>For example, &quot;1-30&quot; specifies the next January 30th (at midnight local
+time), &quot;14:00&quot; specifies the next 2 P.M., &quot;1&quot; specifies the next 1st of the
+month at midnight, &quot;31&quot; specifies the next month where we can stop on its
+31st day, and &quot;:59&quot; specifies the next 59th minute after the hour.</p>
+<p>For maximal flexibility, rsync does not communicate this option to the
+remote rsync since it is usually enough that one side of the connection
+quits as specified. This allows the option's use even when only one side
+of the connection supports it. You can tell the remote side about the time
+limit using <a href="#opt--remote-option"><code>--remote-option</code></a> (<code>-M</code>), should the need arise. Do
+keep in mind that the remote host may have a different default timezone
+than your local host.</p>
+</dd>
+
+<dt id="opt--fsync"><code>--fsync</code><a href="#opt--fsync" class="tgt"></a></dt><dd>
+<p>Cause the receiving side to fsync each finished file. This may slow down
+the transfer, but can help to provide peace of mind when updating critical
+files.</p>
+</dd>
+
+<dt id="opt--write-batch"><code>--write-batch=FILE</code><a href="#opt--write-batch" class="tgt"></a></dt><dd>
+<p>Record a file that can later be applied to another identical destination
+with <a href="#opt--read-batch"><code>--read-batch</code></a>. See the &quot;BATCH MODE&quot; section for details, and
+also the <a href="#opt--only-write-batch"><code>--only-write-batch</code></a> option.</p>
+<p>This option overrides the negotiated checksum &amp; compress lists and always
+negotiates a choice based on old-school md5/md4/zlib choices. If you want
+a more modern choice, use the <a href="#opt--checksum-choice"><code>--checksum-choice</code></a> (<code>--cc</code>) and/or
+<a href="#opt--compress-choice"><code>--compress-choice</code></a> (<code>--zc</code>) options.</p>
+</dd>
+
+<dt id="opt--only-write-batch"><code>--only-write-batch=FILE</code><a href="#opt--only-write-batch" class="tgt"></a></dt><dd>
+<p>Works like <a href="#opt--write-batch"><code>--write-batch</code></a>, except that no updates are made on the
+destination system when creating the batch. This lets you transport the
+changes to the destination system via some other means and then apply the
+changes via <a href="#opt--read-batch"><code>--read-batch</code></a>.</p>
+<p>Note that you can feel free to write the batch directly to some portable
+media: if this media fills to capacity before the end of the transfer, you
+can just apply that partial transfer to the destination and repeat the
+whole process to get the rest of the changes (as long as you don't mind a
+partially updated destination system while the multi-update cycle is
+happening).</p>
+<p>Also note that you only save bandwidth when pushing changes to a remote
+system because this allows the batched data to be diverted from the sender
+into the batch file without having to flow over the wire to the receiver
+(when pulling, the sender is remote, and thus can't write the batch).</p>
+</dd>
+
+<dt id="opt--read-batch"><code>--read-batch=FILE</code><a href="#opt--read-batch" class="tgt"></a></dt><dd>
+<p>Apply all of the changes stored in FILE, a file previously generated by
+<a href="#opt--write-batch"><code>--write-batch</code></a>. If <u>FILE</u> is <code>-</code>, the batch data will be read
+from standard input. See the &quot;BATCH MODE&quot; section for details.</p>
+</dd>
+
+<dt id="opt--protocol"><code>--protocol=NUM</code><a href="#opt--protocol" class="tgt"></a></dt><dd>
+<p>Force an older protocol version to be used. This is useful for creating a
+batch file that is compatible with an older version of rsync. For
+instance, if rsync 2.6.4 is being used with the <a href="#opt--write-batch"><code>--write-batch</code></a>
+option, but rsync 2.6.3 is what will be used to run the
+<a href="#opt--read-batch"><code>--read-batch</code></a> option, you should use &quot;-&#8288;-&#8288;protocol=28&quot; when creating
+the batch file to force the older protocol version to be used in the batch
+file (assuming you can't upgrade the rsync on the reading system).</p>
+</dd>
+
+<dt id="opt--iconv"><code>--iconv=CONVERT_SPEC</code><a href="#opt--iconv" class="tgt"></a></dt><dd>
+<p>Rsync can convert filenames between character sets using this option.
+Using a CONVERT_SPEC of &quot;.&quot; tells rsync to look up the default
+character-set via the locale setting. Alternately, you can fully specify
+what conversion to do by giving a local and a remote charset separated by a
+comma in the order <code>--iconv=LOCAL,REMOTE</code>, e.g. <code>--iconv=utf8,iso88591</code>.
+This order ensures that the option will stay the same whether you're
+pushing or pulling files. Finally, you can specify either <code>--no-iconv</code> or
+a CONVERT_SPEC of &quot;-&#8288;&quot; to turn off any conversion. The default setting of
+this option is site-specific, and can also be affected via the
+<a href="#RSYNC_ICONV"><code>RSYNC_ICONV</code></a> environment variable.</p>
+<p>For a list of what charset names your local iconv library supports, you can
+run &quot;<code>iconv --list</code>&quot;.</p>
+<p>If you specify the <a href="#opt--secluded-args"><code>--secluded-args</code></a> (<code>-s</code>) option, rsync will
+translate the filenames you specify on the command-line that are being sent
+to the remote host. See also the <a href="#opt--files-from"><code>--files-from</code></a> option.</p>
+<p>Note that rsync does not do any conversion of names in filter files
+(including include/exclude files). It is up to you to ensure that you're
+specifying matching rules that can match on both sides of the transfer.
+For instance, you can specify extra include/exclude rules if there are
+filename differences on the two sides that need to be accounted for.</p>
+<p>When you pass an <code>--iconv</code> option to an rsync daemon that allows it, the
+daemon uses the charset specified in its &quot;charset&quot; configuration parameter
+regardless of the remote charset you actually pass. Thus, you may feel
+free to specify just the local charset for a daemon transfer (e.g.
+<code>--iconv=utf8</code>).</p>
+</dd>
+
+<span id="opt-6"></span><span id="opt--ipv6"></span><span id="opt-4"></span><dt id="opt--ipv4"><code>--ipv4</code>, <code>-4</code> or <code>--ipv6</code>, <code>-6</code><a href="#opt--ipv4" class="tgt"></a></dt><dd>
+<p>Tells rsync to prefer IPv4/IPv6 when creating sockets or running ssh. This
+affects sockets that rsync has direct control over, such as the outgoing
+socket when directly contacting an rsync daemon, as well as the forwarding
+of the <code>-4</code> or <code>-6</code> option to ssh when rsync can deduce that ssh is being
+used as the remote shell. For other remote shells you'll need to specify
+the &quot;<code>--rsh SHELL -4</code>&quot; option directly (or whatever IPv4/IPv6 hint options
+it uses).</p>
+<p>See also <a href="#dopt--ipv4">the daemon version of these options</a>.</p>
+<p>If rsync was compiled without support for IPv6, the <code>--ipv6</code> option will
+have no effect. The <code>rsync --version</code> output will contain &quot;<code>no IPv6</code>&quot; if
+is the case.</p>
+</dd>
+
+<dt id="opt--checksum-seed"><code>--checksum-seed=NUM</code><a href="#opt--checksum-seed" class="tgt"></a></dt><dd>
+<p>Set the checksum seed to the integer NUM. This 4 byte checksum seed is
+included in each block and MD4 file checksum calculation (the more modern
+MD5 file checksums don't use a seed). By default the checksum seed is
+generated by the server and defaults to the current <strong>time</strong>(). This
+option is used to set a specific checksum seed, which is useful for
+applications that want repeatable block checksums, or in the case where the
+user wants a more random checksum seed. Setting NUM to 0 causes rsync to
+use the default of <strong>time</strong>() for checksum seed.</p>
+</dd>
+</dl>
+<h2 id="DAEMON_OPTIONS">DAEMON OPTIONS<a href="#DAEMON_OPTIONS" class="tgt"></a></h2>
+<p>The options allowed when starting an rsync daemon are as follows:</p>
+<dl>
+
+<dt id="dopt--daemon"><code>--daemon</code><a href="#dopt--daemon" class="tgt"></a></dt><dd>
+<p>This tells rsync that it is to run as a daemon. The daemon you start
+running may be accessed using an rsync client using the <code>host::module</code> or
+<code>rsync://host/module/</code> syntax.</p>
+<p>If standard input is a socket then rsync will assume that it is being run
+via inetd, otherwise it will detach from the current terminal and become a
+background daemon. The daemon will read the config file (rsyncd.conf) on
+each connect made by a client and respond to requests accordingly.</p>
+<p>See the <a href="rsyncd.conf.5"><strong>rsyncd.conf</strong>(5)</a> manpage for more details.</p>
+</dd>
+
+<dt id="dopt--address"><code>--address=ADDRESS</code><a href="#dopt--address" class="tgt"></a></dt><dd>
+<p>By default rsync will bind to the wildcard address when run as a daemon
+with the <code>--daemon</code> option. The <code>--address</code> option allows you to specify a
+specific IP address (or hostname) to bind to. This makes virtual hosting
+possible in conjunction with the <code>--config</code> option.</p>
+<p>See also the <a href="rsyncd.conf.5#address">address</a> global option in the
+rsyncd.conf manpage and the <a href="#opt--address">client version of the <code>--address</code>
+option</a>.</p>
+</dd>
+
+<dt id="dopt--bwlimit"><code>--bwlimit=RATE</code><a href="#dopt--bwlimit" class="tgt"></a></dt><dd>
+<p>This option allows you to specify the maximum transfer rate for the data
+the daemon sends over the socket. The client can still specify a smaller
+<code>--bwlimit</code> value, but no larger value will be allowed.</p>
+<p>See the <a href="#opt--bwlimit">client version of the <code>--bwlimit</code> option</a> for some
+extra details.</p>
+</dd>
+
+<dt id="dopt--config"><code>--config=FILE</code><a href="#dopt--config" class="tgt"></a></dt><dd>
+<p>This specifies an alternate config file than the default. This is only
+relevant when <a href="#dopt--daemon"><code>--daemon</code></a> is specified. The default is
+/etc/rsyncd.conf unless the daemon is running over a remote shell program
+and the remote user is not the super-user; in that case the default is
+rsyncd.conf in the current directory (typically $HOME).</p>
+</dd>
+
+<span id="dopt-M"></span><dt id="dopt--dparam"><code>--dparam=OVERRIDE</code>, <code>-M</code><a href="#dopt--dparam" class="tgt"></a></dt><dd>
+<p>This option can be used to set a daemon-config parameter when starting up
+rsync in daemon mode. It is equivalent to adding the parameter at the end
+of the global settings prior to the first module's definition. The
+parameter names can be specified without spaces, if you so desire. For
+instance:</p>
+<blockquote>
+<pre><code>rsync --daemon -M pidfile=/path/rsync.pid
+</code></pre>
+</blockquote>
+</dd>
+
+<dt id="dopt--no-detach"><code>--no-detach</code><a href="#dopt--no-detach" class="tgt"></a></dt><dd>
+<p>When running as a daemon, this option instructs rsync to not detach itself
+and become a background process. This option is required when running as a
+service on Cygwin, and may also be useful when rsync is supervised by a
+program such as <code>daemontools</code> or AIX's <code>System Resource Controller</code>.
+<code>--no-detach</code> is also recommended when rsync is run under a debugger. This
+option has no effect if rsync is run from inetd or sshd.</p>
+</dd>
+
+<dt id="dopt--port"><code>--port=PORT</code><a href="#dopt--port" class="tgt"></a></dt><dd>
+<p>This specifies an alternate TCP port number for the daemon to listen on
+rather than the default of 873.</p>
+<p>See also <a href="#opt--port">the client version of the <code>--port</code> option</a> and the
+<a href="rsyncd.conf.5#port">port</a> global setting in the rsyncd.conf manpage.</p>
+</dd>
+
+<dt id="dopt--log-file"><code>--log-file=FILE</code><a href="#dopt--log-file" class="tgt"></a></dt><dd>
+<p>This option tells the rsync daemon to use the given log-file name instead
+of using the &quot;<code>log file</code>&quot; setting in the config file.</p>
+<p>See also <a href="#opt--log-file">the client version of the <code>--log-file</code> option</a>.</p>
+</dd>
+
+<dt id="dopt--log-file-format"><code>--log-file-format=FORMAT</code><a href="#dopt--log-file-format" class="tgt"></a></dt><dd>
+<p>This option tells the rsync daemon to use the given FORMAT string instead
+of using the &quot;<code>log format</code>&quot; setting in the config file. It also enables
+&quot;<code>transfer logging</code>&quot; unless the string is empty, in which case transfer
+logging is turned off.</p>
+<p>See also <a href="#opt--log-file-format">the client version of the <code>--log-file-format</code>
+option</a>.</p>
+</dd>
+
+<dt id="dopt--sockopts"><code>--sockopts</code><a href="#dopt--sockopts" class="tgt"></a></dt><dd>
+<p>This overrides the <a href="rsyncd.conf.5#socket_options"><code>socket options</code></a>
+setting in the rsyncd.conf file and has the same syntax.</p>
+<p>See also <a href="#opt--sockopts">the client version of the <code>--sockopts</code> option</a>.</p>
+</dd>
+
+<span id="dopt-v"></span><dt id="dopt--verbose"><code>--verbose</code>, <code>-v</code><a href="#dopt--verbose" class="tgt"></a></dt><dd>
+<p>This option increases the amount of information the daemon logs during its
+startup phase. After the client connects, the daemon's verbosity level
+will be controlled by the options that the client used and the
+&quot;<code>max verbosity</code>&quot; setting in the module's config section.</p>
+<p>See also <a href="#opt--verbose">the client version of the <code>--verbose</code> option</a>.</p>
+</dd>
+
+<span id="dopt-6"></span><span id="dopt--ipv6"></span><span id="dopt-4"></span><dt id="dopt--ipv4"><code>--ipv4</code>, <code>-4</code> or <code>--ipv6</code>, <code>-6</code><a href="#dopt--ipv4" class="tgt"></a></dt><dd>
+<p>Tells rsync to prefer IPv4/IPv6 when creating the incoming sockets that the
+rsync daemon will use to listen for connections. One of these options may
+be required in older versions of Linux to work around an IPv6 bug in the
+kernel (if you see an &quot;address already in use&quot; error when nothing else is
+using the port, try specifying <code>--ipv6</code> or <code>--ipv4</code> when starting the
+daemon).</p>
+<p>See also <a href="#opt--ipv4">the client version of these options</a>.</p>
+<p>If rsync was compiled without support for IPv6, the <code>--ipv6</code> option will
+have no effect. The <code>rsync --version</code> output will contain &quot;<code>no IPv6</code>&quot; if
+is the case.</p>
+</dd>
+
+<span id="dopt-h"></span><dt id="dopt--help"><code>--help</code>, <code>-h</code><a href="#dopt--help" class="tgt"></a></dt><dd>
+<p>When specified after <code>--daemon</code>, print a short help page describing the
+options available for starting an rsync daemon.</p>
+</dd>
+</dl>
+<h2 id="FILTER_RULES">FILTER RULES<a href="#FILTER_RULES" class="tgt"></a></h2>
+<p>The filter rules allow for custom control of several aspects of how files are
+handled:</p>
+<ul>
+<li>Control which files the sending side puts into the file list that describes
+the transfer hierarchy</li>
+<li>Control which files the receiving side protects from deletion when the file
+is not in the sender's file list</li>
+<li>Control which extended attribute names are skipped when copying xattrs</li>
+</ul>
+<p>The rules are either directly specified via option arguments or they can be
+read in from one or more files. The filter-rule files can even be a part of
+the hierarchy of files being copied, affecting different parts of the tree in
+different ways.</p>
+<h3 id="SIMPLE_INCLUDE_EXCLUDE_RULES">SIMPLE INCLUDE/EXCLUDE RULES<a href="#SIMPLE_INCLUDE_EXCLUDE_RULES" class="tgt"></a></h3>
+<p>We will first cover the basics of how include &amp; exclude rules affect what files
+are transferred, ignoring any deletion side-effects. Filter rules mainly
+affect the contents of directories that rsync is &quot;recursing&quot; into, but they can
+also affect a top-level item in the transfer that was specified as a argument.</p>
+<p>The default for any unmatched file/dir is for it to be included in the
+transfer, which puts the file/dir into the sender's file list. The use of an
+exclude rule causes one or more matching files/dirs to be left out of the
+sender's file list. An include rule can be used to limit the effect of an
+exclude rule that is matching too many files.</p>
+<p>The order of the rules is important because the first rule that matches is the
+one that takes effect. Thus, if an early rule excludes a file, no include rule
+that comes after it can have any effect. This means that you must place any
+include overrides somewhere prior to the exclude that it is intended to limit.</p>
+<p>When a directory is excluded, all its contents and sub-contents are also
+excluded. The sender doesn't scan through any of it at all, which can save a
+lot of time when skipping large unneeded sub-trees.</p>
+<p>It is also important to understand that the include/exclude rules are applied
+to every file and directory that the sender is recursing into. Thus, if you
+want a particular deep file to be included, you have to make sure that none of
+the directories that must be traversed on the way down to that file are
+excluded or else the file will never be discovered to be included. As an
+example, if the directory &quot;<code>a/path</code>&quot; was given as a transfer argument and you
+want to ensure that the file &quot;<code>a/path/down/deep/wanted.txt</code>&quot; is a part of the
+transfer, then the sender must not exclude the directories &quot;<code>a/path</code>&quot;,
+&quot;<code>a/path/down</code>&quot;, or &quot;<code>a/path/down/deep</code>&quot; as it makes it way scanning through
+the file tree.</p>
+<p>When you are working on the rules, it can be helpful to ask rsync to tell you
+what is being excluded/included and why. Specifying <code>--debug=FILTER</code> or (when
+pulling files) <code>-M--debug=FILTER</code> turns on level 1 of the FILTER debug
+information that will output a message any time that a file or directory is
+included or excluded and which rule it matched. Beginning in 3.2.4 it will
+also warn if a filter rule has trailing whitespace, since an exclude of &quot;foo&nbsp;&quot;
+(with a trailing space) will not exclude a file named &quot;foo&quot;.</p>
+<p>Exclude and include rules can specify wildcard <a href="#PATTERN_MATCHING_RULES">PATTERN MATCHING RULES</a>
+(similar to shell wildcards) that allow you to match things like a file suffix
+or a portion of a filename.</p>
+<p>A rule can be limited to only affecting a directory by putting a trailing slash
+onto the filename.</p>
+<h3 id="SIMPLE_INCLUDE_EXCLUDE_EXAMPLE">SIMPLE INCLUDE/EXCLUDE EXAMPLE<a href="#SIMPLE_INCLUDE_EXCLUDE_EXAMPLE" class="tgt"></a></h3>
+<p>With the following file tree created on the sending side:</p>
+<blockquote>
+<pre><code>mkdir x/
+touch x/file.txt
+mkdir x/y/
+touch x/y/file.txt
+touch x/y/zzz.txt
+mkdir x/z/
+touch x/z/file.txt
+</code></pre>
+</blockquote>
+<p>Then the following rsync command will transfer the file &quot;<code>x/y/file.txt</code>&quot; and
+the directories needed to hold it, resulting in the path &quot;<code>/tmp/x/y/file.txt</code>&quot;
+existing on the remote host:</p>
+<blockquote>
+<pre><code>rsync -ai -f'+ x/' -f'+ x/y/' -f'+ x/y/file.txt' -f'- *' x host:/tmp/
+</code></pre>
+</blockquote>
+<p>Aside: this copy could also have been accomplished using the <a href="#opt-R"><code>-R</code></a>
+option (though the 2 commands behave differently if deletions are enabled):</p>
+<blockquote>
+<pre><code>rsync -aiR x/y/file.txt host:/tmp/
+</code></pre>
+</blockquote>
+<p>The following command does not need an include of the &quot;x&quot; directory because it
+is not a part of the transfer (note the traililng slash). Running this command
+would copy just &quot;<code>/tmp/x/file.txt</code>&quot; because the &quot;y&quot; and &quot;z&quot; dirs get excluded:</p>
+<blockquote>
+<pre><code>rsync -ai -f'+ file.txt' -f'- *' x/ host:/tmp/x/
+</code></pre>
+</blockquote>
+<p>This command would omit the zzz.txt file while copying &quot;x&quot; and everything else
+it contains:</p>
+<blockquote>
+<pre><code>rsync -ai -f'- zzz.txt' x host:/tmp/
+</code></pre>
+</blockquote>
+<h3 id="FILTER_RULES_WHEN_DELETING">FILTER RULES WHEN DELETING<a href="#FILTER_RULES_WHEN_DELETING" class="tgt"></a></h3>
+<p>By default the include &amp; exclude filter rules affect both the sender
+(as it creates its file list)
+and the receiver (as it creates its file lists for calculating deletions). If
+no delete option is in effect, the receiver skips creating the delete-related
+file lists. This two-sided default can be manually overridden so that you are
+only specifying sender rules or receiver rules, as described in the <a href="#FILTER_RULES_IN_DEPTH">FILTER
+RULES IN DEPTH</a> section.</p>
+<p>When deleting, an exclude protects a file from being removed on the receiving
+side while an include overrides that protection (putting the file at risk of
+deletion). The default is for a file to be at risk&nbsp;-&#8288;-&#8288; its safety depends on it
+matching a corresponding file from the sender.</p>
+<p>An example of the two-sided exclude effect can be illustrated by the copying of
+a C development directory between 2 systems. When doing a touch-up copy, you
+might want to skip copying the built executable and the <code>.o</code> files (sender
+hide) so that the receiving side can build their own and not lose any object
+files that are already correct (receiver protect). For instance:</p>
+<blockquote>
+<pre><code>rsync -ai --del -f'- *.o' -f'- cmd' src host:/dest/
+</code></pre>
+</blockquote>
+<p>Note that using <code>-f'-p *.o'</code> is even better than <code>-f'- *.o'</code> if there is a
+chance that the directory structure may have changed. The &quot;p&quot; modifier is
+discussed in <a href="#FILTER_RULE_MODIFIERS">FILTER RULE MODIFIERS</a>.</p>
+<p>One final note, if your shell doesn't mind unexpanded wildcards, you could
+simplify the typing of the filter options by using an underscore in place of
+the space and leaving off the quotes. For instance, <code>-f -_*.o -f -_cmd</code> (and
+similar) could be used instead of the filter options above.</p>
+<h3 id="FILTER_RULES_IN_DEPTH">FILTER RULES IN DEPTH<a href="#FILTER_RULES_IN_DEPTH" class="tgt"></a></h3>
+<p>Rsync supports old-style include/exclude rules and new-style filter rules. The
+older rules are specified using <a href="#opt--include"><code>--include</code></a> and <a href="#opt--exclude"><code>--exclude</code></a> as
+well as the <a href="#opt--include-from"><code>--include-from</code></a> and <a href="#opt--exclude-from"><code>--exclude-from</code></a>. These are
+limited in behavior but they don't require a &quot;-&#8288;&quot; or &quot;+&quot; prefix. An old-style
+exclude rule is turned into a &quot;<code>- name</code>&quot; filter rule (with no modifiers) and an
+old-style include rule is turned into a &quot;<code>+ name</code>&quot; filter rule (with no
+modifiers).</p>
+<p>Rsync builds an ordered list of filter rules as specified on the command-line
+and/or read-in from files. New style filter rules have the following syntax:</p>
+<blockquote>
+<pre><code>RULE [PATTERN_OR_FILENAME]
+RULE,MODIFIERS [PATTERN_OR_FILENAME]
+</code></pre>
+</blockquote>
+<p>You have your choice of using either short or long RULE names, as described
+below. If you use a short-named rule, the ',' separating the RULE from the
+MODIFIERS is optional. The PATTERN or FILENAME that follows (when present)
+must come after either a single space or an underscore (_). Any additional
+spaces and/or underscores are considered to be a part of the pattern name.
+Here are the available rule prefixes:</p>
+<dl>
+<dt><code>exclude, '-'</code></dt><dd> specifies an exclude pattern that (by default) is both a
+<code>hide</code> and a <code>protect</code>.</dd>
+<dt><code>include, '+'</code></dt><dd> specifies an include pattern that (by default) is both a
+<code>show</code> and a <code>risk</code>.</dd>
+<dt><code>merge, '.'</code></dt><dd> specifies a merge-file on the client side to read for more
+rules.</dd>
+<dt><code>dir-merge, ':'</code></dt><dd> specifies a per-directory merge-file. Using this kind of
+filter rule requires that you trust the sending side's filter checking, so
+it has the side-effect mentioned under the <a href="#opt--trust-sender"><code>--trust-sender</code></a> option.</dd>
+<dt><code>hide, 'H'</code></dt><dd> specifies a pattern for hiding files from the transfer.
+Equivalent to a sender-only exclude, so <code>-f'H foo'</code> could also be specified
+as <code>-f'-s foo'</code>.</dd>
+<dt><code>show, 'S'</code></dt><dd> files that match the pattern are not hidden. Equivalent to a
+sender-only include, so <code>-f'S foo'</code> could also be specified as <code>-f'+s foo'</code>.</dd>
+<dt><code>protect, 'P'</code></dt><dd> specifies a pattern for protecting files from deletion.
+Equivalent to a receiver-only exclude, so <code>-f'P foo'</code> could also be
+specified as <code>-f'-r foo'</code>.</dd>
+<dt><code>risk, 'R'</code></dt><dd> files that match the pattern are not protected. Equivalent to a
+receiver-only include, so <code>-f'R foo'</code> could also be specified as <code>-f'+r foo'</code>.</dd>
+<dt><code>clear, '!'</code></dt><dd> clears the current include/exclude list (takes no arg)</dd>
+</dl>
+<p>When rules are being read from a file (using merge or dir-merge), empty lines
+are ignored, as are whole-line comments that start with a '<code>#</code>' (filename rules
+that contain a hash character are unaffected).</p>
+<p>Note also that the <a href="#opt--filter"><code>--filter</code></a>, <a href="#opt--include"><code>--include</code></a>, and
+<a href="#opt--exclude"><code>--exclude</code></a> options take one rule/pattern each. To add multiple ones,
+you can repeat the options on the command-line, use the merge-file syntax of
+the <a href="#opt--filter"><code>--filter</code></a> option, or the <a href="#opt--include-from"><code>--include-from</code></a> /
+<a href="#opt--exclude-from"><code>--exclude-from</code></a> options.</p>
+<h3 id="PATTERN_MATCHING_RULES">PATTERN MATCHING RULES<a href="#PATTERN_MATCHING_RULES" class="tgt"></a></h3>
+<p>Most of the rules mentioned above take an argument that specifies what the rule
+should match. If rsync is recursing through a directory hierarchy, keep in
+mind that each pattern is matched against the name of every directory in the
+descent path as rsync finds the filenames to send.</p>
+<p>The matching rules for the pattern argument take several forms:</p>
+<ul>
+<li>If a pattern contains a <code>/</code> (not counting a trailing slash) or a &quot;<code>**</code>&quot;
+(which can match a slash), then the pattern is matched against the full
+pathname, including any leading directories within the transfer. If the
+pattern doesn't contain a (non-trailing) <code>/</code> or a &quot;<code>**</code>&quot;, then it is matched
+only against the final component of the filename or pathname. For example,
+<code>foo</code> means that the final path component must be &quot;foo&quot; while <code>foo/bar</code> would
+match the last 2 elements of the path (as long as both elements are within
+the transfer).</li>
+<li>A pattern that ends with a <code>/</code> only matches a directory, not a regular file,
+symlink, or device.</li>
+<li>A pattern that starts with a <code>/</code> is anchored to the start of the transfer
+path instead of the end. For example, <code>/foo/**</code> or <code>/foo/bar/**</code> match only
+leading elements in the path. If the rule is read from a per-directory
+filter file, the transfer path being matched will begin at the level of the
+filter file instead of the top of the transfer. See the section on
+<a href="#ANCHORING_INCLUDE_EXCLUDE_PATTERNS">ANCHORING INCLUDE/EXCLUDE PATTERNS</a> for a full discussion of how to
+specify a pattern that matches at the root of the transfer.</li>
+</ul>
+<p>Rsync chooses between doing a simple string match and wildcard matching by
+checking if the pattern contains one of these three wildcard characters: '<code>*</code>',
+'<code>?</code>', and '<code>[</code>' :</p>
+<ul>
+<li>a '<code>?</code>' matches any single character except a slash (<code>/</code>).</li>
+<li>a '<code>*</code>' matches zero or more non-slash characters.</li>
+<li>a '<code>**</code>' matches zero or more characters, including slashes.</li>
+<li>a '<code>[</code>' introduces a character class, such as <code>[a-z]</code> or <code>[[:alpha:]]</code>, that
+must match one character.</li>
+<li>a trailing <code>***</code> in the pattern is a shorthand that allows you to match a
+directory and all its contents using a single rule. For example, specifying
+&quot;<code>dir_name/***</code>&quot; will match both the &quot;dir_name&quot; directory (as if &quot;<code>dir_name/</code>&quot;
+had been specified) and everything in the directory (as if &quot;<code>dir_name/**</code>&quot;
+had been specified).</li>
+<li>a backslash can be used to escape a wildcard character, but it is only
+interpreted as an escape character if at least one wildcard character is
+present in the match pattern. For instance, the pattern &quot;<code>foo\bar</code>&quot; matches
+that single backslash literally, while the pattern &quot;<code>foo\bar*</code>&quot; would need to
+be changed to &quot;<code>foo\\bar*</code>&quot; to avoid the &quot;<code>\b</code>&quot; becoming just &quot;b&quot;.</li>
+</ul>
+<p>Here are some examples of exclude/include matching:</p>
+<ul>
+<li>Option <code>-f'- *.o'</code> would exclude all filenames ending with <code>.o</code></li>
+<li>Option <code>-f'- /foo'</code> would exclude a file (or directory) named foo in the
+transfer-root directory</li>
+<li>Option <code>-f'- foo/'</code> would exclude any directory named foo</li>
+<li>Option <code>-f'- foo/*/bar'</code> would exclude any file/dir named bar which is at two
+levels below a directory named foo (if foo is in the transfer)</li>
+<li>Option <code>-f'- /foo/**/bar'</code> would exclude any file/dir named bar that was two
+or more levels below a top-level directory named foo (note that /foo/bar is
+<strong>not</strong> excluded by this)</li>
+<li>Options <code>-f'+ */' -f'+ *.c' -f'- *'</code> would include all directories and .c
+source files but nothing else</li>
+<li>Options <code>-f'+ foo/' -f'+ foo/bar.c' -f'- *'</code> would include only the foo
+directory and foo/bar.c (the foo directory must be explicitly included or it
+would be excluded by the &quot;<code>- *</code>&quot;)</li>
+</ul>
+<h3 id="FILTER_RULE_MODIFIERS">FILTER RULE MODIFIERS<a href="#FILTER_RULE_MODIFIERS" class="tgt"></a></h3>
+<p>The following modifiers are accepted after an include (+) or exclude (-&#8288;) rule:</p>
+<ul>
+<li>A <code>/</code> specifies that the include/exclude rule should be matched against the
+absolute pathname of the current item. For example, <code>-f'-/ /etc/passwd'</code>
+would exclude the passwd file any time the transfer was sending files from
+the &quot;/etc&quot; directory, and &quot;-&#8288;/ subdir/foo&quot; would always exclude &quot;foo&quot; when it
+is in a dir named &quot;subdir&quot;, even if &quot;foo&quot; is at the root of the current
+transfer.</li>
+<li>A <code>!</code> specifies that the include/exclude should take effect if the pattern
+fails to match. For instance, <code>-f'-! */'</code> would exclude all non-directories.</li>
+<li>A <code>C</code> is used to indicate that all the global CVS-exclude rules should be
+inserted as excludes in place of the &quot;-&#8288;C&quot;. No arg should follow.</li>
+<li>An <code>s</code> is used to indicate that the rule applies to the sending side. When a
+rule affects the sending side, it affects what files are put into the
+sender's file list. The default is for a rule to affect both sides unless
+<a href="#opt--delete-excluded"><code>--delete-excluded</code></a> was specified, in which case default rules become
+sender-side only. See also the hide (H) and show (S) rules, which are an
+alternate way to specify sending-side includes/excludes.</li>
+<li>An <code>r</code> is used to indicate that the rule applies to the receiving side. When
+a rule affects the receiving side, it prevents files from being deleted. See
+the <code>s</code> modifier for more info. See also the protect (P) and risk (R) rules,
+which are an alternate way to specify receiver-side includes/excludes.</li>
+<li>A <code>p</code> indicates that a rule is perishable, meaning that it is ignored in
+directories that are being deleted. For instance, the
+<a href="#opt--cvs-exclude"><code>--cvs-exclude</code></a> (<code>-C</code>) option's default rules that exclude things
+like &quot;CVS&quot; and &quot;<code>*.o</code>&quot; are marked as perishable, and will not prevent a
+directory that was removed on the source from being deleted on the
+destination.</li>
+<li>An <code>x</code> indicates that a rule affects xattr names in xattr copy/delete
+operations (and is thus ignored when matching file/dir names). If no
+xattr-matching rules are specified, a default xattr filtering rule is used
+(see the <a href="#opt--xattrs"><code>--xattrs</code></a> option).</li>
+</ul>
+<h3 id="MERGE-FILE_FILTER_RULES">MERGE-FILE FILTER RULES<a href="#MERGE-FILE_FILTER_RULES" class="tgt"></a></h3>
+<p>You can merge whole files into your filter rules by specifying either a merge
+(.) or a dir-merge (:) filter rule (as introduced in the <a href="#FILTER_RULES">FILTER RULES</a>
+section above).</p>
+<p>There are two kinds of merged files&nbsp;-&#8288;-&#8288; single-instance ('.') and per-directory
+(':'). A single-instance merge file is read one time, and its rules are
+incorporated into the filter list in the place of the &quot;.&quot; rule. For
+per-directory merge files, rsync will scan every directory that it traverses
+for the named file, merging its contents when the file exists into the current
+list of inherited rules. These per-directory rule files must be created on the
+sending side because it is the sending side that is being scanned for the
+available files to transfer. These rule files may also need to be transferred
+to the receiving side if you want them to affect what files don't get deleted
+(see <a href="#PER-DIRECTORY_RULES_AND_DELETE">PER-DIRECTORY RULES AND DELETE</a> below).</p>
+<p>Some examples:</p>
+<blockquote>
+<pre><code>merge /etc/rsync/default.rules
+. /etc/rsync/default.rules
+dir-merge .per-dir-filter
+dir-merge,n- .non-inherited-per-dir-excludes
+:n- .non-inherited-per-dir-excludes
+</code></pre>
+</blockquote>
+<p>The following modifiers are accepted after a merge or dir-merge rule:</p>
+<ul>
+<li>A <code>-</code> specifies that the file should consist of only exclude patterns, with
+no other rule-parsing except for in-file comments.</li>
+<li>A <code>+</code> specifies that the file should consist of only include patterns, with
+no other rule-parsing except for in-file comments.</li>
+<li>A <code>C</code> is a way to specify that the file should be read in a CVS-compatible
+manner. This turns on 'n', 'w', and '-&#8288;', but also allows the list-clearing
+token (!) to be specified. If no filename is provided, &quot;.cvsignore&quot; is
+assumed.</li>
+<li>A <code>e</code> will exclude the merge-file name from the transfer; e.g. &quot;dir-merge,e
+.rules&quot; is like &quot;dir-merge .rules&quot; and &quot;-&#8288; .rules&quot;.</li>
+<li>An <code>n</code> specifies that the rules are not inherited by subdirectories.</li>
+<li>A <code>w</code> specifies that the rules are word-split on whitespace instead of the
+normal line-splitting. This also turns off comments. Note: the space that
+separates the prefix from the rule is treated specially, so &quot;-&#8288; foo + bar&quot; is
+parsed as two rules (assuming that prefix-parsing wasn't also disabled).</li>
+<li>You may also specify any of the modifiers for the &quot;+&quot; or &quot;-&#8288;&quot; rules (above) in
+order to have the rules that are read in from the file default to having that
+modifier set (except for the <code>!</code> modifier, which would not be useful). For
+instance, &quot;merge,-&#8288;/ .excl&quot; would treat the contents of .excl as absolute-path
+excludes, while &quot;dir-merge,s .filt&quot; and &quot;:sC&quot; would each make all their
+per-directory rules apply only on the sending side. If the merge rule
+specifies sides to affect (via the <code>s</code> or <code>r</code> modifier or both), then the
+rules in the file must not specify sides (via a modifier or a rule prefix
+such as <code>hide</code>).</li>
+</ul>
+<p>Per-directory rules are inherited in all subdirectories of the directory where
+the merge-file was found unless the 'n' modifier was used. Each subdirectory's
+rules are prefixed to the inherited per-directory rules from its parents, which
+gives the newest rules a higher priority than the inherited rules. The entire
+set of dir-merge rules are grouped together in the spot where the merge-file
+was specified, so it is possible to override dir-merge rules via a rule that
+got specified earlier in the list of global rules. When the list-clearing rule
+(&quot;!&quot;) is read from a per-directory file, it only clears the inherited rules for
+the current merge file.</p>
+<p>Another way to prevent a single rule from a dir-merge file from being inherited
+is to anchor it with a leading slash. Anchored rules in a per-directory
+merge-file are relative to the merge-file's directory, so a pattern &quot;/foo&quot;
+would only match the file &quot;foo&quot; in the directory where the dir-merge filter
+file was found.</p>
+<p>Here's an example filter file which you'd specify via <code>--filter=&quot;. file&quot;:</code></p>
+<blockquote>
+<pre><code>merge /home/user/.global-filter
+- *.gz
+dir-merge .rules
++ *.[ch]
+- *.o
+- foo*
+</code></pre>
+</blockquote>
+<p>This will merge the contents of the /home/user/.global-filter file at the start
+of the list and also turns the &quot;.rules&quot; filename into a per-directory filter
+file. All rules read in prior to the start of the directory scan follow the
+global anchoring rules (i.e. a leading slash matches at the root of the
+transfer).</p>
+<p>If a per-directory merge-file is specified with a path that is a parent
+directory of the first transfer directory, rsync will scan all the parent dirs
+from that starting point to the transfer directory for the indicated
+per-directory file. For instance, here is a common filter (see <a href="#opt-F"><code>-F</code></a>):</p>
+<blockquote>
+<pre><code>--filter=': /.rsync-filter'
+</code></pre>
+</blockquote>
+<p>That rule tells rsync to scan for the file .rsync-filter in all directories
+from the root down through the parent directory of the transfer prior to the
+start of the normal directory scan of the file in the directories that are sent
+as a part of the transfer. (Note: for an rsync daemon, the root is always the
+same as the module's &quot;path&quot;.)</p>
+<p>Some examples of this pre-scanning for per-directory files:</p>
+<blockquote>
+<pre><code>rsync -avF /src/path/ /dest/dir
+rsync -av --filter=': ../../.rsync-filter' /src/path/ /dest/dir
+rsync -av --filter=': .rsync-filter' /src/path/ /dest/dir
+</code></pre>
+</blockquote>
+<p>The first two commands above will look for &quot;.rsync-filter&quot; in &quot;/&quot; and &quot;/src&quot;
+before the normal scan begins looking for the file in &quot;/src/path&quot; and its
+subdirectories. The last command avoids the parent-dir scan and only looks for
+the &quot;.rsync-filter&quot; files in each directory that is a part of the transfer.</p>
+<p>If you want to include the contents of a &quot;.cvsignore&quot; in your patterns, you
+should use the rule &quot;:C&quot;, which creates a dir-merge of the .cvsignore file, but
+parsed in a CVS-compatible manner. You can use this to affect where the
+<a href="#opt--cvs-exclude"><code>--cvs-exclude</code></a> (<code>-C</code>) option's inclusion of the per-directory
+.cvsignore file gets placed into your rules by putting the &quot;:C&quot; wherever you
+like in your filter rules. Without this, rsync would add the dir-merge rule
+for the .cvsignore file at the end of all your other rules (giving it a lower
+priority than your command-line rules). For example:</p>
+<blockquote>
+<pre><code>cat &lt;&lt;EOT | rsync -avC --filter='. -' a/ b
++ foo.o
+:C
+- *.old
+EOT
+rsync -avC --include=foo.o -f :C --exclude='*.old' a/ b
+</code></pre>
+</blockquote>
+<p>Both of the above rsync commands are identical. Each one will merge all the
+per-directory .cvsignore rules in the middle of the list rather than at the
+end. This allows their dir-specific rules to supersede the rules that follow
+the :C instead of being subservient to all your rules. To affect the other CVS
+exclude rules (i.e. the default list of exclusions, the contents of
+$HOME/.cvsignore, and the value of $CVSIGNORE) you should omit the <code>-C</code>
+command-line option and instead insert a &quot;-&#8288;C&quot; rule into your filter rules; e.g.
+&quot;<code>--filter=-C</code>&quot;.</p>
+<h3 id="LIST-CLEARING_FILTER_RULE">LIST-CLEARING FILTER RULE<a href="#LIST-CLEARING_FILTER_RULE" class="tgt"></a></h3>
+<p>You can clear the current include/exclude list by using the &quot;!&quot; filter rule (as
+introduced in the <a href="#FILTER_RULES">FILTER RULES</a> section above). The &quot;current&quot; list is either
+the global list of rules (if the rule is encountered while parsing the filter
+options) or a set of per-directory rules (which are inherited in their own
+sub-list, so a subdirectory can use this to clear out the parent's rules).</p>
+<h3 id="ANCHORING_INCLUDE_EXCLUDE_PATTERNS">ANCHORING INCLUDE/EXCLUDE PATTERNS<a href="#ANCHORING_INCLUDE_EXCLUDE_PATTERNS" class="tgt"></a></h3>
+<p>As mentioned earlier, global include/exclude patterns are anchored at the &quot;root
+of the transfer&quot; (as opposed to per-directory patterns, which are anchored at
+the merge-file's directory). If you think of the transfer as a subtree of
+names that are being sent from sender to receiver, the transfer-root is where
+the tree starts to be duplicated in the destination directory. This root
+governs where patterns that start with a / match.</p>
+<p>Because the matching is relative to the transfer-root, changing the trailing
+slash on a source path or changing your use of the <a href="#opt--relative"><code>--relative</code></a> option
+affects the path you need to use in your matching (in addition to changing how
+much of the file tree is duplicated on the destination host). The following
+examples demonstrate this.</p>
+<p>Let's say that we want to match two source files, one with an absolute
+path of &quot;/home/me/foo/bar&quot;, and one with a path of &quot;/home/you/bar/baz&quot;.
+Here is how the various command choices differ for a 2-source transfer:</p>
+<blockquote>
+<pre><code>Example cmd: rsync -a /home/me /home/you /dest
++/- pattern: /me/foo/bar
++/- pattern: /you/bar/baz
+Target file: /dest/me/foo/bar
+Target file: /dest/you/bar/baz
+</code></pre>
+</blockquote>
+<blockquote>
+<pre><code>Example cmd: rsync -a /home/me/ /home/you/ /dest
++/- pattern: /foo/bar (note missing &quot;me&quot;)
++/- pattern: /bar/baz (note missing &quot;you&quot;)
+Target file: /dest/foo/bar
+Target file: /dest/bar/baz
+</code></pre>
+</blockquote>
+<blockquote>
+<pre><code>Example cmd: rsync -a --relative /home/me/ /home/you /dest
++/- pattern: /home/me/foo/bar (note full path)
++/- pattern: /home/you/bar/baz (ditto)
+Target file: /dest/home/me/foo/bar
+Target file: /dest/home/you/bar/baz
+</code></pre>
+</blockquote>
+<blockquote>
+<pre><code>Example cmd: cd /home; rsync -a --relative me/foo you/ /dest
++/- pattern: /me/foo/bar (starts at specified path)
++/- pattern: /you/bar/baz (ditto)
+Target file: /dest/me/foo/bar
+Target file: /dest/you/bar/baz
+</code></pre>
+</blockquote>
+<p>The easiest way to see what name you should filter is to just look at the
+output when using <a href="#opt--verbose"><code>--verbose</code></a> and put a / in front of the name (use the
+<code>--dry-run</code> option if you're not yet ready to copy any files).</p>
+<h3 id="PER-DIRECTORY_RULES_AND_DELETE">PER-DIRECTORY RULES AND DELETE<a href="#PER-DIRECTORY_RULES_AND_DELETE" class="tgt"></a></h3>
+<p>Without a delete option, per-directory rules are only relevant on the sending
+side, so you can feel free to exclude the merge files themselves without
+affecting the transfer. To make this easy, the 'e' modifier adds this exclude
+for you, as seen in these two equivalent commands:</p>
+<blockquote>
+<pre><code>rsync -av --filter=': .excl' --exclude=.excl host:src/dir /dest
+rsync -av --filter=':e .excl' host:src/dir /dest
+</code></pre>
+</blockquote>
+<p>However, if you want to do a delete on the receiving side AND you want some
+files to be excluded from being deleted, you'll need to be sure that the
+receiving side knows what files to exclude. The easiest way is to include the
+per-directory merge files in the transfer and use <a href="#opt--delete-after"><code>--delete-after</code></a>,
+because this ensures that the receiving side gets all the same exclude rules as
+the sending side before it tries to delete anything:</p>
+<blockquote>
+<pre><code>rsync -avF --delete-after host:src/dir /dest
+</code></pre>
+</blockquote>
+<p>However, if the merge files are not a part of the transfer, you'll need to
+either specify some global exclude rules (i.e. specified on the command line),
+or you'll need to maintain your own per-directory merge files on the receiving
+side. An example of the first is this (assume that the remote .rules files
+exclude themselves):</p>
+<blockquote>
+<pre><code>rsync -av --filter=': .rules' --filter='. /my/extra.rules'
+ --delete host:src/dir /dest
+</code></pre>
+</blockquote>
+<p>In the above example the extra.rules file can affect both sides of the
+transfer, but (on the sending side) the rules are subservient to the rules
+merged from the .rules files because they were specified after the
+per-directory merge rule.</p>
+<p>In one final example, the remote side is excluding the .rsync-filter files from
+the transfer, but we want to use our own .rsync-filter files to control what
+gets deleted on the receiving side. To do this we must specifically exclude
+the per-directory merge files (so that they don't get deleted) and then put
+rules into the local files to control what else should not get deleted. Like
+one of these commands:</p>
+<blockquote>
+<pre><code>rsync -av --filter=':e /.rsync-filter' --delete \
+ host:src/dir /dest
+rsync -avFF --delete host:src/dir /dest
+</code></pre>
+</blockquote>
+<h2 id="TRANSFER_RULES">TRANSFER RULES<a href="#TRANSFER_RULES" class="tgt"></a></h2>
+<p>In addition to the <a href="#FILTER_RULES">FILTER RULES</a> that affect the recursive file scans that
+generate the file list on the sending and (when deleting) receiving sides,
+there are transfer rules. These rules affect which files the generator decides
+need to be transferred without the side effects of an exclude filter rule.
+Transfer rules affect only files and never directories.</p>
+<p>Because a transfer rule does not affect what goes into the sender's (and
+receiver's) file list, it cannot have any effect on which files get deleted on
+the receiving side. For example, if the file &quot;foo&quot; is present in the sender's
+list but its size is such that it is omitted due to a transfer rule, the
+receiving side does not request the file. However, its presence in the file
+list means that a delete pass will not remove a matching file named &quot;foo&quot; on
+the receiving side. On the other hand, a server-side exclude (hide) of the
+file &quot;foo&quot; leaves the file out of the server's file list, and absent a
+receiver-side exclude (protect) the receiver will remove a matching file named
+&quot;foo&quot; if deletions are requested.</p>
+<p>Given that the files are still in the sender's file list, the
+<a href="#opt--prune-empty-dirs"><code>--prune-empty-dirs</code></a> option will not judge a directory as being empty
+even if it contains only files that the transfer rules omitted.</p>
+<p>Similarly, a transfer rule does not have any extra effect on which files are
+deleted on the receiving side, so setting a maximum file size for the transfer
+does not prevent big files from being deleted.</p>
+<p>Examples of transfer rules include the default &quot;quick check&quot; algorithm (which
+compares size &amp; modify time), the <a href="#opt--update"><code>--update</code></a> option, the
+<a href="#opt--max-size"><code>--max-size</code></a> option, the <a href="#opt--ignore-non-existing"><code>--ignore-non-existing</code></a> option, and a
+few others.</p>
+<h2 id="BATCH_MODE">BATCH MODE<a href="#BATCH_MODE" class="tgt"></a></h2>
+<p>Batch mode can be used to apply the same set of updates to many identical
+systems. Suppose one has a tree which is replicated on a number of hosts. Now
+suppose some changes have been made to this source tree and those changes need
+to be propagated to the other hosts. In order to do this using batch mode,
+rsync is run with the write-batch option to apply the changes made to the
+source tree to one of the destination trees. The write-batch option causes the
+rsync client to store in a &quot;batch file&quot; all the information needed to repeat
+this operation against other, identical destination trees.</p>
+<p>Generating the batch file once saves having to perform the file status,
+checksum, and data block generation more than once when updating multiple
+destination trees. Multicast transport protocols can be used to transfer the
+batch update files in parallel to many hosts at once, instead of sending the
+same data to every host individually.</p>
+<p>To apply the recorded changes to another destination tree, run rsync with the
+read-batch option, specifying the name of the same batch file, and the
+destination tree. Rsync updates the destination tree using the information
+stored in the batch file.</p>
+<p>For your convenience, a script file is also created when the write-batch option
+is used: it will be named the same as the batch file with &quot;.sh&quot; appended. This
+script file contains a command-line suitable for updating a destination tree
+using the associated batch file. It can be executed using a Bourne (or
+Bourne-like) shell, optionally passing in an alternate destination tree
+pathname which is then used instead of the original destination path. This is
+useful when the destination tree path on the current host differs from the one
+used to create the batch file.</p>
+<p>Examples:</p>
+<blockquote>
+<pre><code>$ rsync --write-batch=foo -a host:/source/dir/ /adest/dir/
+$ scp foo* remote:
+$ ssh remote ./foo.sh /bdest/dir/
+</code></pre>
+</blockquote>
+<blockquote>
+<pre><code>$ rsync --write-batch=foo -a /source/dir/ /adest/dir/
+$ ssh remote rsync --read-batch=- -a /bdest/dir/ &lt;foo
+</code></pre>
+</blockquote>
+<p>In these examples, rsync is used to update /adest/dir/ from /source/dir/ and
+the information to repeat this operation is stored in &quot;foo&quot; and &quot;foo.sh&quot;. The
+host &quot;remote&quot; is then updated with the batched data going into the directory
+/bdest/dir. The differences between the two examples reveals some of the
+flexibility you have in how you deal with batches:</p>
+<ul>
+<li>The first example shows that the initial copy doesn't have to be local&nbsp;-&#8288;-&#8288; you
+can push or pull data to/from a remote host using either the remote-shell
+syntax or rsync daemon syntax, as desired.</li>
+<li>The first example uses the created &quot;foo.sh&quot; file to get the right rsync
+options when running the read-batch command on the remote host.</li>
+<li>The second example reads the batch data via standard input so that the batch
+file doesn't need to be copied to the remote machine first. This example
+avoids the foo.sh script because it needed to use a modified
+<a href="#opt--read-batch"><code>--read-batch</code></a> option, but you could edit the script file if you
+wished to make use of it (just be sure that no other option is trying to use
+standard input, such as the <a href="#opt--exclude-from"><code>--exclude-from=-</code></a> option).</li>
+</ul>
+<p>Caveats:</p>
+<p>The read-batch option expects the destination tree that it is updating to be
+identical to the destination tree that was used to create the batch update
+fileset. When a difference between the destination trees is encountered the
+update might be discarded with a warning (if the file appears to be up-to-date
+already) or the file-update may be attempted and then, if the file fails to
+verify, the update discarded with an error. This means that it should be safe
+to re-run a read-batch operation if the command got interrupted. If you wish
+to force the batched-update to always be attempted regardless of the file's
+size and date, use the <a href="#opt-I"><code>-I</code></a> option (when reading the batch). If an
+error occurs, the destination tree will probably be in a partially updated
+state. In that case, rsync can be used in its regular (non-batch) mode of
+operation to fix up the destination tree.</p>
+<p>The rsync version used on all destinations must be at least as new as the one
+used to generate the batch file. Rsync will die with an error if the protocol
+version in the batch file is too new for the batch-reading rsync to handle.
+See also the <a href="#opt--protocol"><code>--protocol</code></a> option for a way to have the creating rsync
+generate a batch file that an older rsync can understand. (Note that batch
+files changed format in version 2.6.3, so mixing versions older than that with
+newer versions will not work.)</p>
+<p>When reading a batch file, rsync will force the value of certain options to
+match the data in the batch file if you didn't set them to the same as the
+batch-writing command. Other options can (and should) be changed. For
+instance <a href="#opt--write-batch"><code>--write-batch</code></a> changes to <a href="#opt--read-batch"><code>--read-batch</code></a>,
+<a href="#opt--files-from"><code>--files-from</code></a> is dropped, and the <a href="#opt--filter"><code>--filter</code></a> /
+<a href="#opt--include"><code>--include</code></a> / <a href="#opt--exclude"><code>--exclude</code></a> options are not needed unless one of
+the <a href="#opt--delete"><code>--delete</code></a> options is specified.</p>
+<p>The code that creates the BATCH.sh file transforms any filter/include/exclude
+options into a single list that is appended as a &quot;here&quot; document to the shell
+script file. An advanced user can use this to modify the exclude list if a
+change in what gets deleted by <a href="#opt--delete"><code>--delete</code></a> is desired. A normal user
+can ignore this detail and just use the shell script as an easy way to run the
+appropriate <a href="#opt--read-batch"><code>--read-batch</code></a> command for the batched data.</p>
+<p>The original batch mode in rsync was based on &quot;rsync+&quot;, but the latest
+version uses a new implementation.</p>
+<h2 id="SYMBOLIC_LINKS">SYMBOLIC LINKS<a href="#SYMBOLIC_LINKS" class="tgt"></a></h2>
+<p>Three basic behaviors are possible when rsync encounters a symbolic
+link in the source directory.</p>
+<p>By default, symbolic links are not transferred at all. A message &quot;skipping
+non-regular&quot; file is emitted for any symlinks that exist.</p>
+<p>If <a href="#opt--links"><code>--links</code></a> is specified, then symlinks are added to the transfer
+(instead of being noisily ignored), and the default handling is to recreate
+them with the same target on the destination. Note that <a href="#opt--archive"><code>--archive</code></a>
+implies <a href="#opt--links"><code>--links</code></a>.</p>
+<p>If <a href="#opt--copy-links"><code>--copy-links</code></a> is specified, then symlinks are &quot;collapsed&quot; by
+copying their referent, rather than the symlink.</p>
+<p>Rsync can also distinguish &quot;safe&quot; and &quot;unsafe&quot; symbolic links. An example
+where this might be used is a web site mirror that wishes to ensure that the
+rsync module that is copied does not include symbolic links to <code>/etc/passwd</code> in
+the public section of the site. Using <a href="#opt--copy-unsafe-links"><code>--copy-unsafe-links</code></a> will cause
+any links to be copied as the file they point to on the destination. Using
+<a href="#opt--safe-links"><code>--safe-links</code></a> will cause unsafe links to be omitted by the receiver.
+(Note that you must specify or imply <a href="#opt--links"><code>--links</code></a> for
+<a href="#opt--safe-links"><code>--safe-links</code></a> to have any effect.)</p>
+<p>Symbolic links are considered unsafe if they are absolute symlinks (start with
+<code>/</code>), empty, or if they contain enough &quot;..&quot; components to ascend from the top
+of the transfer.</p>
+<p>Here's a summary of how the symlink options are interpreted. The list is in
+order of precedence, so if your combination of options isn't mentioned, use the
+first line that is a complete subset of your options:</p>
+<dl>
+<dt><code>--copy-links</code></dt><dd> Turn all symlinks into normal files and directories
+(leaving no symlinks in the transfer for any other options to affect).</dd>
+<dt><code>--copy-dirlinks</code></dt><dd> Turn just symlinks to directories into real
+directories, leaving all other symlinks to be handled as described below.</dd>
+<dt><code>--links --copy-unsafe-links</code></dt><dd> Turn all unsafe symlinks
+into files and create all safe symlinks.</dd>
+<dt><code>--copy-unsafe-links</code></dt><dd> Turn all unsafe symlinks into files, noisily
+skip all safe symlinks.</dd>
+<dt><code>--links --safe-links</code></dt><dd> The receiver skips creating
+unsafe symlinks found in the transfer and creates the safe ones.</dd>
+<dt><code>--links</code></dt><dd> Create all symlinks.</dd>
+</dl>
+<p>For the effect of <a href="#opt--munge-links"><code>--munge-links</code></a>, see the discussion in that option's
+section.</p>
+<p>Note that the <a href="#opt--keep-dirlinks"><code>--keep-dirlinks</code></a> option does not effect symlinks in the
+transfer but instead affects how rsync treats a symlink to a directory that
+already exists on the receiving side. See that option's section for a warning.</p>
+<h2 id="DIAGNOSTICS">DIAGNOSTICS<a href="#DIAGNOSTICS" class="tgt"></a></h2>
+<p>Rsync occasionally produces error messages that may seem a little cryptic. The
+one that seems to cause the most confusion is &quot;protocol version mismatch&nbsp;-&#8288;-&#8288; is
+your shell clean?&quot;.</p>
+<p>This message is usually caused by your startup scripts or remote shell facility
+producing unwanted garbage on the stream that rsync is using for its transport.
+The way to diagnose this problem is to run your remote shell like this:</p>
+<blockquote>
+<pre><code>ssh remotehost /bin/true &gt; out.dat
+</code></pre>
+</blockquote>
+<p>then look at out.dat. If everything is working correctly then out.dat should
+be a zero length file. If you are getting the above error from rsync then you
+will probably find that out.dat contains some text or data. Look at the
+contents and try to work out what is producing it. The most common cause is
+incorrectly configured shell startup scripts (such as .cshrc or .profile) that
+contain output statements for non-interactive logins.</p>
+<p>If you are having trouble debugging filter patterns, then try specifying the
+<code>-vv</code> option. At this level of verbosity rsync will show why each individual
+file is included or excluded.</p>
+<h2 id="EXIT_VALUES">EXIT VALUES<a href="#EXIT_VALUES" class="tgt"></a></h2>
+<ul>
+<li><strong>0</strong> -&#8288; Success</li>
+<li><strong>1</strong> -&#8288; Syntax or usage error</li>
+<li><strong>2</strong> -&#8288; Protocol incompatibility</li>
+<li><strong>3</strong> -&#8288; Errors selecting input/output files, dirs</li>
+<li><strong>4</strong> -&#8288; Requested action not supported. Either:
+<ul>
+<li>an attempt was made to manipulate 64-bit files on a platform that cannot support them</li>
+<li>an option was specified that is supported by the client and not by the server</li>
+</ul>
+</li>
+<li><strong>5</strong> -&#8288; Error starting client-server protocol</li>
+<li><strong>6</strong> -&#8288; Daemon unable to append to log-file</li>
+<li><strong>10</strong> -&#8288; Error in socket I/O</li>
+<li><strong>11</strong> -&#8288; Error in file I/O</li>
+<li><strong>12</strong> -&#8288; Error in rsync protocol data stream</li>
+<li><strong>13</strong> -&#8288; Errors with program diagnostics</li>
+<li><strong>14</strong> -&#8288; Error in IPC code</li>
+<li><strong>20</strong> -&#8288; Received SIGUSR1 or SIGINT</li>
+<li><strong>21</strong> -&#8288; Some error returned by <strong>waitpid()</strong></li>
+<li><strong>22</strong> -&#8288; Error allocating core memory buffers</li>
+<li><strong>23</strong> -&#8288; Partial transfer due to error</li>
+<li><strong>24</strong> -&#8288; Partial transfer due to vanished source files</li>
+<li><strong>25</strong> -&#8288; The -&#8288;-&#8288;max-delete limit stopped deletions</li>
+<li><strong>30</strong> -&#8288; Timeout in data send/receive</li>
+<li><strong>35</strong> -&#8288; Timeout waiting for daemon connection</li>
+</ul>
+<h2 id="ENVIRONMENT_VARIABLES">ENVIRONMENT VARIABLES<a href="#ENVIRONMENT_VARIABLES" class="tgt"></a></h2>
+<dl>
+
+<dt id="CVSIGNORE"><code>CVSIGNORE</code><a href="#CVSIGNORE" class="tgt"></a></dt><dd>
+<p>The CVSIGNORE environment variable supplements any ignore patterns in
+.cvsignore files. See the <a href="#opt--cvs-exclude"><code>--cvs-exclude</code></a> option for more details.</p>
+</dd>
+
+<dt id="RSYNC_ICONV"><code>RSYNC_ICONV</code><a href="#RSYNC_ICONV" class="tgt"></a></dt><dd>
+<p>Specify a default <a href="#opt--iconv"><code>--iconv</code></a> setting using this environment
+variable. First supported in 3.0.0.</p>
+</dd>
+
+<dt id="RSYNC_OLD_ARGS"><code>RSYNC_OLD_ARGS</code><a href="#RSYNC_OLD_ARGS" class="tgt"></a></dt><dd>
+<p>Specify a &quot;1&quot; if you want the <a href="#opt--old-args"><code>--old-args</code></a> option to be enabled by
+default, a &quot;2&quot; (or more) if you want it to be enabled in the
+repeated-option state, or a &quot;0&quot; to make sure that it is disabled by
+default. When this environment variable is set to a non-zero value, it
+supersedes the <a href="#RSYNC_PROTECT_ARGS"><code>RSYNC_PROTECT_ARGS</code></a> variable.</p>
+<p>This variable is ignored if <a href="#opt--old-args"><code>--old-args</code></a>, <code>--no-old-args</code>, or
+<a href="#opt--secluded-args"><code>--secluded-args</code></a> is specified on the command line.</p>
+<p>First supported in 3.2.4.</p>
+</dd>
+
+<dt id="RSYNC_PROTECT_ARGS"><code>RSYNC_PROTECT_ARGS</code><a href="#RSYNC_PROTECT_ARGS" class="tgt"></a></dt><dd>
+<p>Specify a non-zero numeric value if you want the <a href="#opt--secluded-args"><code>--secluded-args</code></a>
+option to be enabled by default, or a zero value to make sure that it is
+disabled by default.</p>
+<p>This variable is ignored if <a href="#opt--secluded-args"><code>--secluded-args</code></a>, <code>--no-secluded-args</code>,
+or <a href="#opt--old-args"><code>--old-args</code></a> is specified on the command line.</p>
+<p>First supported in 3.1.0. Starting in 3.2.4, this variable is ignored if
+<a href="#RSYNC_OLD_ARGS"><code>RSYNC_OLD_ARGS</code></a> is set to a non-zero value.</p>
+</dd>
+
+<dt id="RSYNC_RSH"><code>RSYNC_RSH</code><a href="#RSYNC_RSH" class="tgt"></a></dt><dd>
+<p>This environment variable allows you to override the default shell used as
+the transport for rsync. Command line options are permitted after the
+command name, just as in the <a href="#opt--rsh"><code>--rsh</code></a> (<code>-e</code>) option.</p>
+</dd>
+
+<dt id="RSYNC_PROXY"><code>RSYNC_PROXY</code><a href="#RSYNC_PROXY" class="tgt"></a></dt><dd>
+<p>This environment variable allows you to redirect your rsync
+client to use a web proxy when connecting to an rsync daemon. You should
+set <code>RSYNC_PROXY</code> to a hostname:port pair.</p>
+</dd>
+
+<dt id="RSYNC_PASSWORD"><code>RSYNC_PASSWORD</code><a href="#RSYNC_PASSWORD" class="tgt"></a></dt><dd>
+<p>This environment variable allows you to set the password for an rsync
+<strong>daemon</strong> connection, which avoids the password prompt. Note that this
+does <strong>not</strong> supply a password to a remote shell transport such as ssh
+(consult its documentation for how to do that).</p>
+</dd>
+
+<span id="LOGNAME"></span><dt id="USER"><code>USER</code> or <code>LOGNAME</code><a href="#USER" class="tgt"></a></dt><dd>
+<p>The USER or LOGNAME environment variables are used to determine the default
+username sent to an rsync daemon. If neither is set, the username defaults
+to &quot;nobody&quot;. If both are set, <code>USER</code> takes precedence.</p>
+</dd>
+
+<dt id="RSYNC_PARTIAL_DIR"><code>RSYNC_PARTIAL_DIR</code><a href="#RSYNC_PARTIAL_DIR" class="tgt"></a></dt><dd>
+<p>This environment variable specifies the directory to use for a
+<a href="#opt--partial"><code>--partial</code></a> transfer without implying that partial transfers be
+enabled. See the <a href="#opt--partial-dir"><code>--partial-dir</code></a> option for full details.</p>
+</dd>
+
+<dt id="RSYNC_COMPRESS_LIST"><code>RSYNC_COMPRESS_LIST</code><a href="#RSYNC_COMPRESS_LIST" class="tgt"></a></dt><dd>
+<p>This environment variable allows you to customize the negotiation of the
+compression algorithm by specifying an alternate order or a reduced list of
+names. Use the command <code>rsync --version</code> to see the available compression
+names. See the <a href="#opt--compress"><code>--compress</code></a> option for full details.</p>
+</dd>
+
+<dt id="RSYNC_CHECKSUM_LIST"><code>RSYNC_CHECKSUM_LIST</code><a href="#RSYNC_CHECKSUM_LIST" class="tgt"></a></dt><dd>
+<p>This environment variable allows you to customize the negotiation of the
+checksum algorithm by specifying an alternate order or a reduced list of
+names. Use the command <code>rsync --version</code> to see the available checksum
+names. See the <a href="#opt--checksum-choice"><code>--checksum-choice</code></a> option for full details.</p>
+</dd>
+
+<dt id="RSYNC_MAX_ALLOC"><code>RSYNC_MAX_ALLOC</code><a href="#RSYNC_MAX_ALLOC" class="tgt"></a></dt><dd>
+<p>This environment variable sets an allocation maximum as if you had used the
+<a href="#opt--max-alloc"><code>--max-alloc</code></a> option.</p>
+</dd>
+
+<dt id="RSYNC_PORT"><code>RSYNC_PORT</code><a href="#RSYNC_PORT" class="tgt"></a></dt><dd>
+<p>This environment variable is not read by rsync, but is instead set in
+its sub-environment when rsync is running the remote shell in combination
+with a daemon connection. This allows a script such as
+<a href="rsync-ssl.1"><code>rsync-ssl</code></a> to be able to know the port number that the user
+specified on the command line.</p>
+</dd>
+
+<dt id="HOME"><code>HOME</code><a href="#HOME" class="tgt"></a></dt><dd>
+<p>This environment variable is used to find the user's default .cvsignore
+file.</p>
+</dd>
+
+<dt id="RSYNC_CONNECT_PROG"><code>RSYNC_CONNECT_PROG</code><a href="#RSYNC_CONNECT_PROG" class="tgt"></a></dt><dd>
+<p>This environment variable is mainly used in debug setups to set the program
+to use when making a daemon connection. See <a href="#CONNECTING_TO_AN_RSYNC_DAEMON">CONNECTING TO AN RSYNC
+DAEMON</a> for full details.</p>
+</dd>
+
+<dt id="RSYNC_SHELL"><code>RSYNC_SHELL</code><a href="#RSYNC_SHELL" class="tgt"></a></dt><dd>
+<p>This environment variable is mainly used in debug setups to set the program
+to use to run the program specified by <a href="#RSYNC_CONNECT_PROG"><code>RSYNC_CONNECT_PROG</code></a>. See
+<a href="#CONNECTING_TO_AN_RSYNC_DAEMON">CONNECTING TO AN RSYNC DAEMON</a> for full details.</p>
+</dd>
+</dl>
+<h2 id="FILES">FILES<a href="#FILES" class="tgt"></a></h2>
+<p>/etc/rsyncd.conf or rsyncd.conf</p>
+<h2 id="SEE_ALSO">SEE ALSO<a href="#SEE_ALSO" class="tgt"></a></h2>
+<p><a href="rsync-ssl.1"><strong>rsync-ssl</strong>(1)</a>, <a href="rsyncd.conf.5"><strong>rsyncd.conf</strong>(5)</a>, <a href="rrsync.1"><strong>rrsync</strong>(1)</a></p>
+<h2 id="BUGS">BUGS<a href="#BUGS" class="tgt"></a></h2>
+<ul>
+<li>Times are transferred as *nix time_t values.</li>
+<li>When transferring to FAT filesystems rsync may re-sync unmodified files. See
+the comments on the <a href="#opt--modify-window"><code>--modify-window</code></a> option.</li>
+<li>File permissions, devices, etc. are transferred as native numerical values.</li>
+<li>See also the comments on the <a href="#opt--delete"><code>--delete</code></a> option.</li>
+</ul>
+<p>Please report bugs! See the web site at <a href="https://rsync.samba.org/">https://rsync.samba.org/</a>.</p>
+<h2 id="VERSION">VERSION<a href="#VERSION" class="tgt"></a></h2>
+<p>This manpage is current for version 3.2.7 of rsync.</p>
+<h2 id="INTERNAL_OPTIONS">INTERNAL OPTIONS<a href="#INTERNAL_OPTIONS" class="tgt"></a></h2>
+<p>The options <code>--server</code> and <code>--sender</code> are used internally by rsync, and should
+never be typed by a user under normal circumstances. Some awareness of these
+options may be needed in certain scenarios, such as when setting up a login
+that can only run an rsync command. For instance, the support directory of the
+rsync distribution has an example script named rrsync (for restricted rsync)
+that can be used with a restricted ssh login.</p>
+<h2 id="CREDITS">CREDITS<a href="#CREDITS" class="tgt"></a></h2>
+<p>Rsync is distributed under the GNU General Public License. See the file
+<a href="COPYING">COPYING</a> for details.</p>
+<p>An rsync web site is available at <a href="https://rsync.samba.org/">https://rsync.samba.org/</a>. The site
+includes an FAQ-O-Matic which may cover questions unanswered by this manual
+page.</p>
+<p>The rsync github project is <a href="https://github.com/WayneD/rsync">https://github.com/WayneD/rsync</a>.</p>
+<p>We would be delighted to hear from you if you like this program. Please
+contact the mailing-list at <a href="mailto:rsync@lists.samba.org">rsync@lists.samba.org</a>.</p>
+<p>This program uses the excellent zlib compression library written by Jean-loup
+Gailly and Mark Adler.</p>
+<h2 id="THANKS">THANKS<a href="#THANKS" class="tgt"></a></h2>
+<p>Special thanks go out to: John Van Essen, Matt McCutchen, Wesley W. Terpstra,
+David Dykstra, Jos Backus, Sebastian Krahmer, Martin Pool, and our
+gone-but-not-forgotten compadre, J.W. Schultz.</p>
+<p>Thanks also to Richard Brent, Brendan Mackay, Bill Waite, Stephen Rothwell and
+David Bell. I've probably missed some people, my apologies if I have.</p>
+<h2 id="AUTHOR">AUTHOR<a href="#AUTHOR" class="tgt"></a></h2>
+<p>Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people have later contributed to it. It is currently maintained by Wayne
+Davison.</p>
+<p>Mailing lists for support and development are available at
+<a href="https://lists.samba.org/">https://lists.samba.org/</a>.</p>
+<div style="float: right"><p><i>20 Oct 2022</i></p></div>
+</body></html>
diff --git a/rsync.1.md b/rsync.1.md
new file mode 100644
index 0000000..ee0a4f3
--- /dev/null
+++ b/rsync.1.md
@@ -0,0 +1,4842 @@
+## NAME
+
+rsync - a fast, versatile, remote (and local) file-copying tool
+
+## SYNOPSIS
+
+```
+Local:
+ rsync [OPTION...] SRC... [DEST]
+
+Access via remote shell:
+ Pull:
+ rsync [OPTION...] [USER@]HOST:SRC... [DEST]
+ Push:
+ rsync [OPTION...] SRC... [USER@]HOST:DEST
+
+Access via rsync daemon:
+ Pull:
+ rsync [OPTION...] [USER@]HOST::SRC... [DEST]
+ rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
+ Push:
+ rsync [OPTION...] SRC... [USER@]HOST::DEST
+ rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST)
+```
+
+Usages with just one SRC arg and no DEST arg will list the source files instead
+of copying.
+
+The online version of this manpage (that includes cross-linking of topics)
+is available at <https://download.samba.org/pub/rsync/rsync.1>.
+
+## DESCRIPTION
+
+Rsync is a fast and extraordinarily versatile file copying tool. It can copy
+locally, to/from another host over any remote shell, or to/from a remote rsync
+daemon. It offers a large number of options that control every aspect of its
+behavior and permit very flexible specification of the set of files to be
+copied. It is famous for its delta-transfer algorithm, which reduces the
+amount of data sent over the network by sending only the differences between
+the source files and the existing files in the destination. Rsync is widely
+used for backups and mirroring and as an improved copy command for everyday
+use.
+
+Rsync finds files that need to be transferred using a "quick check" algorithm
+(by default) that looks for files that have changed in size or in last-modified
+time. Any changes in the other preserved attributes (as requested by options)
+are made on the destination file directly when the quick check indicates that
+the file's data does not need to be updated.
+
+Some of the additional features of rsync are:
+
+- support for copying links, devices, owners, groups, and permissions
+- exclude and exclude-from options similar to GNU tar
+- a CVS exclude mode for ignoring the same files that CVS would ignore
+- can use any transparent remote shell, including ssh or rsh
+- does not require super-user privileges
+- pipelining of file transfers to minimize latency costs
+- support for anonymous or authenticated rsync daemons (ideal for mirroring)
+
+## GENERAL
+
+Rsync copies files either to or from a remote host, or locally on the current
+host (it does not support copying files between two remote hosts).
+
+There are two different ways for rsync to contact a remote system: using a
+remote-shell program as the transport (such as ssh or rsh) or contacting an
+rsync daemon directly via TCP. The remote-shell transport is used whenever the
+source or destination path contains a single colon (:) separator after a host
+specification. Contacting an rsync daemon directly happens when the source or
+destination path contains a double colon (::) separator after a host
+specification, OR when an rsync:// URL is specified (see also the [USING
+RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION](#) section for an
+exception to this latter rule).
+
+As a special case, if a single source arg is specified without a destination,
+the files are listed in an output format similar to "`ls -l`".
+
+As expected, if neither the source or destination path specify a remote host,
+the copy occurs locally (see also the [`--list-only`](#opt) option).
+
+Rsync refers to the local side as the client and the remote side as the server.
+Don't confuse server with an rsync daemon. A daemon is always a server, but a
+server can be either a daemon or a remote-shell spawned process.
+
+## SETUP
+
+See the file README.md for installation instructions.
+
+Once installed, you can use rsync to any machine that you can access via a
+remote shell (as well as some that you can access using the rsync daemon-mode
+protocol). For remote transfers, a modern rsync uses ssh for its
+communications, but it may have been configured to use a different remote shell
+by default, such as rsh or remsh.
+
+You can also specify any remote shell you like, either by using the [`-e`](#opt)
+command line option, or by setting the [`RSYNC_RSH`](#) environment variable.
+
+Note that rsync must be installed on both the source and destination machines.
+
+## USAGE
+
+You use rsync in the same way you use rcp. You must specify a source and a
+destination, one of which may be remote.
+
+Perhaps the best way to explain the syntax is with some examples:
+
+> rsync -t *.c foo:src/
+
+This would transfer all files matching the pattern `*.c` from the current
+directory to the directory src on the machine foo. If any of the files already
+exist on the remote system then the rsync remote-update protocol is used to
+update the file by sending only the differences in the data. Note that the
+expansion of wildcards on the command-line (`*.c`) into a list of files is
+handled by the shell before it runs rsync and not by rsync itself (exactly the
+same as all other Posix-style programs).
+
+> rsync -avz foo:src/bar /data/tmp
+
+This would recursively transfer all files from the directory src/bar on the
+machine foo into the /data/tmp/bar directory on the local machine. The files
+are transferred in archive mode, which ensures that symbolic links, devices,
+attributes, permissions, ownerships, etc. are preserved in the transfer.
+Additionally, compression will be used to reduce the size of data portions of
+the transfer.
+
+> rsync -avz foo:src/bar/ /data/tmp
+
+A trailing slash on the source changes this behavior to avoid creating an
+additional directory level at the destination. You can think of a trailing /
+on a source as meaning "copy the contents of this directory" as opposed to
+"copy the directory by name", but in both cases the attributes of the
+containing directory are transferred to the containing directory on the
+destination. In other words, each of the following commands copies the files
+in the same way, including their setting of the attributes of /dest/foo:
+
+> rsync -av /src/foo /dest
+> rsync -av /src/foo/ /dest/foo
+
+Note also that host and module references don't require a trailing slash to
+copy the contents of the default directory. For example, both of these copy
+the remote directory's contents into "/dest":
+
+> rsync -av host: /dest
+> rsync -av host::module /dest
+
+You can also use rsync in local-only mode, where both the source and
+destination don't have a ':' in the name. In this case it behaves like an
+improved copy command.
+
+Finally, you can list all the (listable) modules available from a particular
+rsync daemon by leaving off the module name:
+
+> rsync somehost.mydomain.com::
+
+## COPYING TO A DIFFERENT NAME
+
+When you want to copy a directory to a different name, use a trailing slash on
+the source directory to put the contents of the directory into any destination
+directory you like:
+
+> rsync -ai foo/ bar/
+
+Rsync also has the ability to customize a destination file's name when copying
+a single item. The rules for this are:
+
+- The transfer list must consist of a single item (either a file or an empty
+ directory)
+- The final element of the destination path must not exist as a directory
+- The destination path must not have been specified with a trailing slash
+
+Under those circumstances, rsync will set the name of the destination's single
+item to the last element of the destination path. Keep in mind that it is best
+to only use this idiom when copying a file and use the above trailing-slash
+idiom when copying a directory.
+
+The following example copies the `foo.c` file as `bar.c` in the `save` dir
+(assuming that `bar.c` isn't a directory):
+
+> rsync -ai src/foo.c save/bar.c
+
+The single-item copy rule might accidentally bite you if you unknowingly copy a
+single item and specify a destination dir that doesn't exist (without using a
+trailing slash). For example, if `src/*.c` matches one file and `save/dir`
+doesn't exist, this will confuse you by naming the destination file `save/dir`:
+
+> rsync -ai src/*.c save/dir
+
+To prevent such an accident, either make sure the destination dir exists or
+specify the destination path with a trailing slash:
+
+> rsync -ai src/*.c save/dir/
+
+## SORTED TRANSFER ORDER
+
+Rsync always sorts the specified filenames into its internal transfer list.
+This handles the merging together of the contents of identically named
+directories, makes it easy to remove duplicate filenames. It can, however,
+confuse someone when the files are transferred in a different order than what
+was given on the command-line.
+
+If you need a particular file to be transferred prior to another, either
+separate the files into different rsync calls, or consider using
+[`--delay-updates`](#opt) (which doesn't affect the sorted transfer order, but
+does make the final file-updating phase happen much more rapidly).
+
+## MULTI-HOST SECURITY
+
+Rsync takes steps to ensure that the file requests that are shared in a
+transfer are protected against various security issues. Most of the potential
+problems arise on the receiving side where rsync takes steps to ensure that the
+list of files being transferred remains within the bounds of what was
+requested.
+
+Toward this end, rsync 3.1.2 and later have aborted when a file list contains
+an absolute or relative path that tries to escape out of the top of the
+transfer. Also, beginning with version 3.2.5, rsync does two more safety
+checks of the file list to (1) ensure that no extra source arguments were added
+into the transfer other than those that the client requested and (2) ensure
+that the file list obeys the exclude rules that were sent to the sender.
+
+For those that don't yet have a 3.2.5 client rsync (or those that want to be
+extra careful), it is safest to do a copy into a dedicated destination
+directory for the remote files when you don't trust the remote host. For
+example, instead of doing an rsync copy into your home directory:
+
+> rsync -aiv host1:dir1 ~
+
+Dedicate a "host1-files" dir to the remote content:
+
+> rsync -aiv host1:dir1 ~/host1-files
+
+See the [`--trust-sender`](#opt) option for additional details.
+
+CAUTION: it is not particularly safe to use rsync to copy files from a
+case-preserving filesystem to a case-ignoring filesystem. If you must perform
+such a copy, you should either disable symlinks via `--no-links` or enable the
+munging of symlinks via [`--munge-links`](#opt) (and make sure you use the
+right local or remote option). This will prevent rsync from doing potentially
+dangerous things if a symlink name overlaps with a file or directory. It does
+not, however, ensure that you get a full copy of all the files (since that may
+not be possible when the names overlap). A potentially better solution is to
+list all the source files and create a safe list of filenames that you pass to
+the [`--files-from`](#opt) option. Any files that conflict in name would need
+to be copied to different destination directories using more than one copy.
+
+While a copy of a case-ignoring filesystem to a case-ignoring filesystem can
+work out fairly well, if no `--delete-during` or `--delete-before` option is
+active, rsync can potentially update an existing file on the receiveing side
+without noticing that the upper-/lower-case of the filename should be changed
+to match the sender.
+
+## ADVANCED USAGE
+
+The syntax for requesting multiple files from a remote host is done by
+specifying additional remote-host args in the same style as the first, or with
+the hostname omitted. For instance, all these work:
+
+> rsync -aiv host:file1 :file2 host:file{3,4} /dest/
+> rsync -aiv host::modname/file{1,2} host::modname/extra /dest/
+> rsync -aiv host::modname/first ::extra-file{1,2} /dest/
+
+Note that a daemon connection only supports accessing one module per copy
+command, so if the start of a follow-up path doesn't begin with the
+modname of the first path, it is assumed to be a path in the module (such as
+the extra-file1 & extra-file2 that are grabbed above).
+
+Really old versions of rsync (2.6.9 and before) only allowed specifying one
+remote-source arg, so some people have instead relied on the remote-shell
+performing space splitting to break up an arg into multiple paths. Such
+unintuitive behavior is no longer supported by default (though you can request
+it, as described below).
+
+Starting in 3.2.4, filenames are passed to a remote shell in such a way as to
+preserve the characters you give it. Thus, if you ask for a file with spaces
+in the name, that's what the remote rsync looks for:
+
+> rsync -aiv host:'a simple file.pdf' /dest/
+
+If you use scripts that have been written to manually apply extra quoting to
+the remote rsync args (or to require remote arg splitting), you can ask rsync
+to let your script handle the extra escaping. This is done by either adding
+the [`--old-args`](#opt) option to the rsync runs in the script (which requires
+a new rsync) or exporting [RSYNC_OLD_ARGS](#)=1 and [RSYNC_PROTECT_ARGS](#)=0
+(which works with old or new rsync versions).
+
+## CONNECTING TO AN RSYNC DAEMON
+
+It is also possible to use rsync without a remote shell as the transport. In
+this case you will directly connect to a remote rsync daemon, typically using
+TCP port 873. (This obviously requires the daemon to be running on the remote
+system, so refer to the [STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS](#)
+section below for information on that.)
+
+Using rsync in this way is the same as using it with a remote shell except
+that:
+
+- Use either double-colon syntax or rsync:// URL syntax instead of the
+ single-colon (remote shell) syntax.
+- The first element of the "path" is actually a module name.
+- Additional remote source args can use an abbreviated syntax that omits the
+ hostname and/or the module name, as discussed in [ADVANCED USAGE](#).
+- The remote daemon may print a "message of the day" when you connect.
+- If you specify only the host (with no module or path) then a list of
+ accessible modules on the daemon is output.
+- If you specify a remote source path but no destination, a listing of the
+ matching files on the remote daemon is output.
+- The [`--rsh`](#opt) (`-e`) option must be omitted to avoid changing the
+ connection style from using a socket connection to [USING RSYNC-DAEMON
+ FEATURES VIA A REMOTE-SHELL CONNECTION](#).
+
+An example that copies all the files in a remote module named "src":
+
+> rsync -av host::src /dest
+
+Some modules on the remote daemon may require authentication. If so, you will
+receive a password prompt when you connect. You can avoid the password prompt
+by setting the environment variable [`RSYNC_PASSWORD`](#) to the password you
+want to use or using the [`--password-file`](#opt) option. This may be useful
+when scripting rsync.
+
+WARNING: On some systems environment variables are visible to all users. On
+those systems using [`--password-file`](#opt) is recommended.
+
+You may establish the connection via a web proxy by setting the environment
+variable [`RSYNC_PROXY`](#) to a hostname:port pair pointing to your web proxy.
+Note that your web proxy's configuration must support proxy connections to port
+873.
+
+You may also establish a daemon connection using a program as a proxy by
+setting the environment variable [`RSYNC_CONNECT_PROG`](#) to the commands you
+wish to run in place of making a direct socket connection. The string may
+contain the escape "%H" to represent the hostname specified in the rsync
+command (so use "%%" if you need a single "%" in your string). For example:
+
+> export RSYNC_CONNECT_PROG='ssh proxyhost nc %H 873'
+> rsync -av targethost1::module/src/ /dest/
+> rsync -av rsync://targethost2/module/src/ /dest/
+
+The command specified above uses ssh to run nc (netcat) on a proxyhost, which
+forwards all data to port 873 (the rsync daemon) on the targethost (%H).
+
+Note also that if the [`RSYNC_SHELL`](#) environment variable is set, that
+program will be used to run the `RSYNC_CONNECT_PROG` command instead of using
+the default shell of the **system()** call.
+
+## USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION
+
+It is sometimes useful to use various features of an rsync daemon (such as
+named modules) without actually allowing any new socket connections into a
+system (other than what is already required to allow remote-shell access).
+Rsync supports connecting to a host using a remote shell and then spawning a
+single-use "daemon" server that expects to read its config file in the home dir
+of the remote user. This can be useful if you want to encrypt a daemon-style
+transfer's data, but since the daemon is started up fresh by the remote user,
+you may not be able to use features such as chroot or change the uid used by
+the daemon. (For another way to encrypt a daemon transfer, consider using ssh
+to tunnel a local port to a remote machine and configure a normal rsync daemon
+on that remote host to only allow connections from "localhost".)
+
+From the user's perspective, a daemon transfer via a remote-shell connection
+uses nearly the same command-line syntax as a normal rsync-daemon transfer,
+with the only exception being that you must explicitly set the remote shell
+program on the command-line with the [`--rsh=COMMAND`](#opt) option. (Setting the
+RSYNC_RSH in the environment will not turn on this functionality.) For example:
+
+> rsync -av --rsh=ssh host::module /dest
+
+If you need to specify a different remote-shell user, keep in mind that the
+user@ prefix in front of the host is specifying the rsync-user value (for a
+module that requires user-based authentication). This means that you must give
+the '-l user' option to ssh when specifying the remote-shell, as in this
+example that uses the short version of the [`--rsh`](#opt) option:
+
+> rsync -av -e "ssh -l ssh-user" rsync-user@host::module /dest
+
+The "ssh-user" will be used at the ssh level; the "rsync-user" will be used to
+log-in to the "module".
+
+In this setup, the daemon is started by the ssh command that is accessing the
+system (which can be forced via the `~/.ssh/authorized_keys` file, if desired).
+However, when accessing a daemon directly, it needs to be started beforehand.
+
+## STARTING AN RSYNC DAEMON TO ACCEPT CONNECTIONS
+
+In order to connect to an rsync daemon, the remote system needs to have a
+daemon already running (or it needs to have configured something like inetd to
+spawn an rsync daemon for incoming connections on a particular port). For full
+information on how to start a daemon that will handling incoming socket
+connections, see the [**rsyncd.conf**(5)](rsyncd.conf.5) manpage -- that is
+the config file for the daemon, and it contains the full details for how to run
+the daemon (including stand-alone and inetd configurations).
+
+If you're using one of the remote-shell transports for the transfer, there is
+no need to manually start an rsync daemon.
+
+## EXAMPLES
+
+Here are some examples of how rsync can be used.
+
+To backup a home directory, which consists of large MS Word files and mail
+folders, a per-user cron job can be used that runs this each day:
+
+> rsync -aiz . bkhost:backup/joe/
+
+To move some files from a remote host to the local host, you could run:
+
+> rsync -aiv --remove-source-files rhost:/tmp/{file1,file2}.c ~/src/
+
+## OPTION SUMMARY
+
+Here is a short summary of the options available in rsync. Each option also
+has its own detailed description later in this manpage.
+
+[comment]: # (help-rsync.h)
+[comment]: # (Keep these short enough that they'll be under 80 chars when indented by 7 chars.)
+
+```
+--verbose, -v increase verbosity
+--info=FLAGS fine-grained informational verbosity
+--debug=FLAGS fine-grained debug verbosity
+--stderr=e|a|c change stderr output mode (default: errors)
+--quiet, -q suppress non-error messages
+--no-motd suppress daemon-mode MOTD
+--checksum, -c skip based on checksum, not mod-time & size
+--archive, -a archive mode is -rlptgoD (no -A,-X,-U,-N,-H)
+--no-OPTION turn off an implied OPTION (e.g. --no-D)
+--recursive, -r recurse into directories
+--relative, -R use relative path names
+--no-implied-dirs don't send implied dirs with --relative
+--backup, -b make backups (see --suffix & --backup-dir)
+--backup-dir=DIR make backups into hierarchy based in DIR
+--suffix=SUFFIX backup suffix (default ~ w/o --backup-dir)
+--update, -u skip files that are newer on the receiver
+--inplace update destination files in-place
+--append append data onto shorter files
+--append-verify --append w/old data in file checksum
+--dirs, -d transfer directories without recursing
+--old-dirs, --old-d works like --dirs when talking to old rsync
+--mkpath create destination's missing path components
+--links, -l copy symlinks as symlinks
+--copy-links, -L transform symlink into referent file/dir
+--copy-unsafe-links only "unsafe" symlinks are transformed
+--safe-links ignore symlinks that point outside the tree
+--munge-links munge symlinks to make them safe & unusable
+--copy-dirlinks, -k transform symlink to dir into referent dir
+--keep-dirlinks, -K treat symlinked dir on receiver as dir
+--hard-links, -H preserve hard links
+--perms, -p preserve permissions
+--executability, -E preserve executability
+--chmod=CHMOD affect file and/or directory permissions
+--acls, -A preserve ACLs (implies --perms)
+--xattrs, -X preserve extended attributes
+--owner, -o preserve owner (super-user only)
+--group, -g preserve group
+--devices preserve device files (super-user only)
+--copy-devices copy device contents as a regular file
+--write-devices write to devices as files (implies --inplace)
+--specials preserve special files
+-D same as --devices --specials
+--times, -t preserve modification times
+--atimes, -U preserve access (use) times
+--open-noatime avoid changing the atime on opened files
+--crtimes, -N preserve create times (newness)
+--omit-dir-times, -O omit directories from --times
+--omit-link-times, -J omit symlinks from --times
+--super receiver attempts super-user activities
+--fake-super store/recover privileged attrs using xattrs
+--sparse, -S turn sequences of nulls into sparse blocks
+--preallocate allocate dest files before writing them
+--dry-run, -n perform a trial run with no changes made
+--whole-file, -W copy files whole (w/o delta-xfer algorithm)
+--checksum-choice=STR choose the checksum algorithm (aka --cc)
+--one-file-system, -x don't cross filesystem boundaries
+--block-size=SIZE, -B force a fixed checksum block-size
+--rsh=COMMAND, -e specify the remote shell to use
+--rsync-path=PROGRAM specify the rsync to run on remote machine
+--existing skip creating new files on receiver
+--ignore-existing skip updating files that exist on receiver
+--remove-source-files sender removes synchronized files (non-dir)
+--del an alias for --delete-during
+--delete delete extraneous files from dest dirs
+--delete-before receiver deletes before xfer, not during
+--delete-during receiver deletes during the transfer
+--delete-delay find deletions during, delete after
+--delete-after receiver deletes after transfer, not during
+--delete-excluded also delete excluded files from dest dirs
+--ignore-missing-args ignore missing source args without error
+--delete-missing-args delete missing source args from destination
+--ignore-errors delete even if there are I/O errors
+--force force deletion of dirs even if not empty
+--max-delete=NUM don't delete more than NUM files
+--max-size=SIZE don't transfer any file larger than SIZE
+--min-size=SIZE don't transfer any file smaller than SIZE
+--max-alloc=SIZE change a limit relating to memory alloc
+--partial keep partially transferred files
+--partial-dir=DIR put a partially transferred file into DIR
+--delay-updates put all updated files into place at end
+--prune-empty-dirs, -m prune empty directory chains from file-list
+--numeric-ids don't map uid/gid values by user/group name
+--usermap=STRING custom username mapping
+--groupmap=STRING custom groupname mapping
+--chown=USER:GROUP simple username/groupname mapping
+--timeout=SECONDS set I/O timeout in seconds
+--contimeout=SECONDS set daemon connection timeout in seconds
+--ignore-times, -I don't skip files that match size and time
+--size-only skip files that match in size
+--modify-window=NUM, -@ set the accuracy for mod-time comparisons
+--temp-dir=DIR, -T create temporary files in directory DIR
+--fuzzy, -y find similar file for basis if no dest file
+--compare-dest=DIR also compare destination files relative to DIR
+--copy-dest=DIR ... and include copies of unchanged files
+--link-dest=DIR hardlink to files in DIR when unchanged
+--compress, -z compress file data during the transfer
+--compress-choice=STR choose the compression algorithm (aka --zc)
+--compress-level=NUM explicitly set compression level (aka --zl)
+--skip-compress=LIST skip compressing files with suffix in LIST
+--cvs-exclude, -C auto-ignore files in the same way CVS does
+--filter=RULE, -f add a file-filtering RULE
+-F same as --filter='dir-merge /.rsync-filter'
+ repeated: --filter='- .rsync-filter'
+--exclude=PATTERN exclude files matching PATTERN
+--exclude-from=FILE read exclude patterns from FILE
+--include=PATTERN don't exclude files matching PATTERN
+--include-from=FILE read include patterns from FILE
+--files-from=FILE read list of source-file names from FILE
+--from0, -0 all *-from/filter files are delimited by 0s
+--old-args disable the modern arg-protection idiom
+--secluded-args, -s use the protocol to safely send the args
+--trust-sender trust the remote sender's file list
+--copy-as=USER[:GROUP] specify user & optional group for the copy
+--address=ADDRESS bind address for outgoing socket to daemon
+--port=PORT specify double-colon alternate port number
+--sockopts=OPTIONS specify custom TCP options
+--blocking-io use blocking I/O for the remote shell
+--outbuf=N|L|B set out buffering to None, Line, or Block
+--stats give some file-transfer stats
+--8-bit-output, -8 leave high-bit chars unescaped in output
+--human-readable, -h output numbers in a human-readable format
+--progress show progress during transfer
+-P same as --partial --progress
+--itemize-changes, -i output a change-summary for all updates
+--remote-option=OPT, -M send OPTION to the remote side only
+--out-format=FORMAT output updates using the specified FORMAT
+--log-file=FILE log what we're doing to the specified FILE
+--log-file-format=FMT log updates using the specified FMT
+--password-file=FILE read daemon-access password from FILE
+--early-input=FILE use FILE for daemon's early exec input
+--list-only list the files instead of copying them
+--bwlimit=RATE limit socket I/O bandwidth
+--stop-after=MINS Stop rsync after MINS minutes have elapsed
+--stop-at=y-m-dTh:m Stop rsync at the specified point in time
+--fsync fsync every written file
+--write-batch=FILE write a batched update to FILE
+--only-write-batch=FILE like --write-batch but w/o updating dest
+--read-batch=FILE read a batched update from FILE
+--protocol=NUM force an older protocol version to be used
+--iconv=CONVERT_SPEC request charset conversion of filenames
+--checksum-seed=NUM set block/file checksum seed (advanced)
+--ipv4, -4 prefer IPv4
+--ipv6, -6 prefer IPv6
+--version, -V print the version + other info and exit
+--help, -h (*) show this help (* -h is help only on its own)
+```
+
+Rsync can also be run as a daemon, in which case the following options are
+accepted:
+
+[comment]: # (help-rsyncd.h)
+
+```
+--daemon run as an rsync daemon
+--address=ADDRESS bind to the specified address
+--bwlimit=RATE limit socket I/O bandwidth
+--config=FILE specify alternate rsyncd.conf file
+--dparam=OVERRIDE, -M override global daemon config parameter
+--no-detach do not detach from the parent
+--port=PORT listen on alternate port number
+--log-file=FILE override the "log file" setting
+--log-file-format=FMT override the "log format" setting
+--sockopts=OPTIONS specify custom TCP options
+--verbose, -v increase verbosity
+--ipv4, -4 prefer IPv4
+--ipv6, -6 prefer IPv6
+--help, -h show this help (when used with --daemon)
+```
+
+## OPTIONS
+
+Rsync accepts both long (double-dash + word) and short (single-dash + letter)
+options. The full list of the available options are described below. If an
+option can be specified in more than one way, the choices are comma-separated.
+Some options only have a long variant, not a short.
+
+If the option takes a parameter, the parameter is only listed after the long
+variant, even though it must also be specified for the short. When specifying
+a parameter, you can either use the form `--option=param`, `--option param`,
+`-o=param`, `-o param`, or `-oparam` (the latter choices assume that your
+option has a short variant).
+
+The parameter may need to be quoted in some manner for it to survive the
+shell's command-line parsing. Also keep in mind that a leading tilde (`~`) in
+a pathname is substituted by your shell, so make sure that you separate the
+option name from the pathname using a space if you want the local shell to
+expand it.
+
+[comment]: # (Some markup below uses a literal non-breakable space when a backtick string)
+[comment]: # (needs to contain a space since markdown strips spaces from the start/end)
+
+[comment]: # (An OL starting at 0 is converted into a DL by the parser.)
+
+0. `--help`
+
+ Print a short help page describing the options available in rsync and exit.
+ You can also use `-h` for `--help` when it is used without any other
+ options (since it normally means [`--human-readable`](#opt)).
+
+0. `--version`, `-V`
+
+ Print the rsync version plus other info and exit. When repeated, the
+ information is output is a JSON format that is still fairly readable
+ (client side only).
+
+ The output includes a list of compiled-in capabilities, a list of
+ optimizations, the default list of checksum algorithms, the default list of
+ compression algorithms, the default list of daemon auth digests, a link to
+ the rsync web site, and a few other items.
+
+0. `--verbose`, `-v`
+
+ This option increases the amount of information you are given during the
+ transfer. By default, rsync works silently. A single `-v` will give you
+ information about what files are being transferred and a brief summary at
+ the end. Two `-v` options will give you information on what files are
+ being skipped and slightly more information at the end. More than two `-v`
+ options should only be used if you are debugging rsync.
+
+ The end-of-run summary tells you the number of bytes sent to the remote
+ rsync (which is the receiving side on a local copy), the number of bytes
+ received from the remote host, and the average bytes per second of the
+ transferred data computed over the entire length of the rsync run. The
+ second line shows the total size (in bytes), which is the sum of all the
+ file sizes that rsync considered transferring. It also shows a "speedup"
+ value, which is a ratio of the total file size divided by the sum of the
+ sent and received bytes (which is really just a feel-good bigger-is-better
+ number). Note that these byte values can be made more (or less)
+ human-readable by using the [`--human-readable`](#opt) (or
+ `--no-human-readable`) options.
+
+ In a modern rsync, the `-v` option is equivalent to the setting of groups
+ of [`--info`](#opt) and [`--debug`](#opt) options. You can choose to use
+ these newer options in addition to, or in place of using `--verbose`, as
+ any fine-grained settings override the implied settings of `-v`. Both
+ [`--info`](#opt) and [`--debug`](#opt) have a way to ask for help that
+ tells you exactly what flags are set for each increase in verbosity.
+
+ However, do keep in mind that a daemon's "`max verbosity`" setting will limit
+ how high of a level the various individual flags can be set on the daemon
+ side. For instance, if the max is 2, then any info and/or debug flag that
+ is set to a higher value than what would be set by `-vv` will be downgraded
+ to the `-vv` level in the daemon's logging.
+
+0. `--info=FLAGS`
+
+ This option lets you have fine-grained control over the information output
+ you want to see. An individual flag name may be followed by a level
+ number, with 0 meaning to silence that output, 1 being the default output
+ level, and higher numbers increasing the output of that flag (for those
+ that support higher levels). Use `--info=help` to see all the available
+ flag names, what they output, and what flag names are added for each
+ increase in the verbose level. Some examples:
+
+ > rsync -a --info=progress2 src/ dest/
+ > rsync -avv --info=stats2,misc1,flist0 src/ dest/
+
+ Note that `--info=name`'s output is affected by the [`--out-format`](#opt)
+ and [`--itemize-changes`](#opt) (`-i`) options. See those options for more
+ information on what is output and when.
+
+ This option was added to 3.1.0, so an older rsync on the server side might
+ reject your attempts at fine-grained control (if one or more flags needed
+ to be send to the server and the server was too old to understand them).
+ See also the "`max verbosity`" caveat above when dealing with a daemon.
+
+0. `--debug=FLAGS`
+
+ This option lets you have fine-grained control over the debug output you
+ want to see. An individual flag name may be followed by a level number,
+ with 0 meaning to silence that output, 1 being the default output level,
+ and higher numbers increasing the output of that flag (for those that
+ support higher levels). Use `--debug=help` to see all the available flag
+ names, what they output, and what flag names are added for each increase in
+ the verbose level. Some examples:
+
+ > rsync -avvv --debug=none src/ dest/
+ > rsync -avA --del --debug=del2,acl src/ dest/
+
+ Note that some debug messages will only be output when the [`--stderr=all`](#opt)
+ option is specified, especially those pertaining to I/O and buffer debugging.
+
+ Beginning in 3.2.0, this option is no longer auto-forwarded to the server
+ side in order to allow you to specify different debug values for each side
+ of the transfer, as well as to specify a new debug option that is only
+ present in one of the rsync versions. If you want to duplicate the same
+ option on both sides, using brace expansion is an easy way to save you some
+ typing. This works in zsh and bash:
+
+ > rsync -aiv {-M,}--debug=del2 src/ dest/
+
+0. `--stderr=errors|all|client`
+
+ This option controls which processes output to stderr and if info messages
+ are also changed to stderr. The mode strings can be abbreviated, so feel
+ free to use a single letter value. The 3 possible choices are:
+
+ - `errors` - (the default) causes all the rsync processes to send an
+ error directly to stderr, even if the process is on the remote side of
+ the transfer. Info messages are sent to the client side via the protocol
+ stream. If stderr is not available (i.e. when directly connecting with a
+ daemon via a socket) errors fall back to being sent via the protocol
+ stream.
+
+ - `all` - causes all rsync messages (info and error) to get written
+ directly to stderr from all (possible) processes. This causes stderr to
+ become line-buffered (instead of raw) and eliminates the ability to
+ divide up the info and error messages by file handle. For those doing
+ debugging or using several levels of verbosity, this option can help to
+ avoid clogging up the transfer stream (which should prevent any chance of
+ a deadlock bug hanging things up). It also allows [`--debug`](#opt) to
+ enable some extra I/O related messages.
+
+ - `client` - causes all rsync messages to be sent to the client side
+ via the protocol stream. One client process outputs all messages, with
+ errors on stderr and info messages on stdout. This **was** the default
+ in older rsync versions, but can cause error delays when a lot of
+ transfer data is ahead of the messages. If you're pushing files to an
+ older rsync, you may want to use `--stderr=all` since that idiom has
+ been around for several releases.
+
+ This option was added in rsync 3.2.3. This version also began the
+ forwarding of a non-default setting to the remote side, though rsync uses
+ the backward-compatible options `--msgs2stderr` and `--no-msgs2stderr` to
+ represent the `all` and `client` settings, respectively. A newer rsync
+ will continue to accept these older option names to maintain compatibility.
+
+0. `--quiet`, `-q`
+
+ This option decreases the amount of information you are given during the
+ transfer, notably suppressing information messages from the remote server.
+ This option is useful when invoking rsync from cron.
+
+0. `--no-motd`
+
+ This option affects the information that is output by the client at the
+ start of a daemon transfer. This suppresses the message-of-the-day (MOTD)
+ text, but it also affects the list of modules that the daemon sends in
+ response to the "rsync host::" request (due to a limitation in the rsync
+ protocol), so omit this option if you want to request the list of modules
+ from the daemon.
+
+0. `--ignore-times`, `-I`
+
+ Normally rsync will skip any files that are already the same size and have
+ the same modification timestamp. This option turns off this "quick check"
+ behavior, causing all files to be updated.
+
+ This option can be confusing compared to [`--ignore-existing`](#opt) and
+ [`--ignore-non-existing`](#opt) in that that they cause rsync to transfer
+ fewer files, while this option causes rsync to transfer more files.
+
+0. `--size-only`
+
+ This modifies rsync's "quick check" algorithm for finding files that need
+ to be transferred, changing it from the default of transferring files with
+ either a changed size or a changed last-modified time to just looking for
+ files that have changed in size. This is useful when starting to use rsync
+ after using another mirroring system which may not preserve timestamps
+ exactly.
+
+0. `--modify-window=NUM`, `-@`
+
+ When comparing two timestamps, rsync treats the timestamps as being equal
+ if they differ by no more than the modify-window value. The default is 0,
+ which matches just integer seconds. If you specify a negative value (and
+ the receiver is at least version 3.1.3) then nanoseconds will also be taken
+ into account. Specifying 1 is useful for copies to/from MS Windows FAT
+ filesystems, because FAT represents times with a 2-second resolution
+ (allowing times to differ from the original by up to 1 second).
+
+ If you want all your transfers to default to comparing nanoseconds, you can
+ create a `~/.popt` file and put these lines in it:
+
+ > rsync alias -a -a@-1
+ > rsync alias -t -t@-1
+
+ With that as the default, you'd need to specify `--modify-window=0` (aka
+ `-@0`) to override it and ignore nanoseconds, e.g. if you're copying
+ between ext3 and ext4, or if the receiving rsync is older than 3.1.3.
+
+0. `--checksum`, `-c`
+
+ This changes the way rsync checks if the files have been changed and are in
+ need of a transfer. Without this option, rsync uses a "quick check" that
+ (by default) checks if each file's size and time of last modification match
+ between the sender and receiver. This option changes this to compare a
+ 128-bit checksum for each file that has a matching size. Generating the
+ checksums means that both sides will expend a lot of disk I/O reading all
+ the data in the files in the transfer, so this can slow things down
+ significantly (and this is prior to any reading that will be done to
+ transfer changed files)
+
+ The sending side generates its checksums while it is doing the file-system
+ scan that builds the list of the available files. The receiver generates
+ its checksums when it is scanning for changed files, and will checksum any
+ file that has the same size as the corresponding sender's file: files with
+ either a changed size or a changed checksum are selected for transfer.
+
+ Note that rsync always verifies that each _transferred_ file was correctly
+ reconstructed on the receiving side by checking a whole-file checksum that
+ is generated as the file is transferred, but that automatic
+ after-the-transfer verification has nothing to do with this option's
+ before-the-transfer "Does this file need to be updated?" check.
+
+ The checksum used is auto-negotiated between the client and the server, but
+ can be overridden using either the [`--checksum-choice`](#opt) (`--cc`)
+ option or an environment variable that is discussed in that option's
+ section.
+
+0. `--archive`, `-a`
+
+ This is equivalent to `-rlptgoD`. It is a quick way of saying you want
+ recursion and want to preserve almost everything. Be aware that it does
+ **not** include preserving ACLs (`-A`), xattrs (`-X`), atimes (`-U`),
+ crtimes (`-N`), nor the finding and preserving of hardlinks (`-H`).
+
+ The only exception to the above equivalence is when [`--files-from`](#opt)
+ is specified, in which case [`-r`](#opt) is not implied.
+
+0. `--no-OPTION`
+
+ You may turn off one or more implied options by prefixing the option name
+ with "no-". Not all positive options have a negated opposite, but a lot
+ do, including those that can be used to disable an implied option (e.g.
+ `--no-D`, `--no-perms`) or have different defaults in various circumstances
+ (e.g. [`--no-whole-file`](#opt), `--no-blocking-io`, `--no-dirs`). Every
+ valid negated option accepts both the short and the long option name after
+ the "no-" prefix (e.g. `--no-R` is the same as `--no-relative`).
+
+ As an example, if you want to use [`--archive`](#opt) (`-a`) but don't want
+ [`--owner`](#opt) (`-o`), instead of converting `-a` into `-rlptgD`, you
+ can specify `-a --no-o` (aka `--archive --no-owner`).
+
+ The order of the options is important: if you specify `--no-r -a`, the `-r`
+ option would end up being turned on, the opposite of `-a --no-r`. Note
+ also that the side-effects of the [`--files-from`](#opt) option are NOT
+ positional, as it affects the default state of several options and slightly
+ changes the meaning of [`-a`](#opt) (see the [`--files-from`](#opt) option
+ for more details).
+
+0. `--recursive`, `-r`
+
+ This tells rsync to copy directories recursively. See also
+ [`--dirs`](#opt) (`-d`) for an option that allows the scanning of a single
+ directory.
+
+ See the [`--inc-recursive`](#opt) option for a discussion of the
+ incremental recursion for creating the list of files to transfer.
+
+0. `--inc-recursive`, `--i-r`
+
+ This option explicitly enables on incremental recursion when scanning for
+ files, which is enabled by default when using the [`--recursive`](#opt)
+ option and both sides of the transfer are running rsync 3.0.0 or newer.
+
+ Incremental recursion uses much less memory than non-incremental, while
+ also beginning the transfer more quickly (since it doesn't need to scan the
+ entire transfer hierarchy before it starts transferring files). If no
+ recursion is enabled in the source files, this option has no effect.
+
+ Some options require rsync to know the full file list, so these options
+ disable the incremental recursion mode. These include:
+ - [`--delete-before`](#opt) (the old default of [`--delete`](#opt))
+ - [`--delete-after`](#opt)
+ - [`--prune-empty-dirs`](#opt)
+ - [`--delay-updates`](#opt)
+
+ In order to make [`--delete`](#opt) compatible with incremental recursion,
+ rsync 3.0.0 made [`--delete-during`](#opt) the default delete mode (which
+ was first added in 2.6.4).
+
+ One side-effect of incremental recursion is that any missing
+ sub-directories inside a recursively-scanned directory are (by default)
+ created prior to recursing into the sub-dirs. This earlier creation point
+ (compared to a non-incremental recursion) allows rsync to then set the
+ modify time of the finished directory right away (without having to delay
+ that until a bunch of recursive copying has finished). However, these
+ early directories don't yet have their completed mode, mtime, or ownership
+ set -- they have more restrictive rights until the subdirectory's copying
+ actually begins. This early-creation idiom can be avoided by using the
+ [`--omit-dir-times`](#opt) option.
+
+ Incremental recursion can be disabled using the
+ [`--no-inc-recursive`](#opt) (`--no-i-r`) option.
+
+0. `--no-inc-recursive`, `--no-i-r`
+
+ Disables the new incremental recursion algorithm of the
+ [`--recursive`](#opt) option. This makes rsync scan the full file list
+ before it begins to transfer files. See [`--inc-recursive`](#opt) for more
+ info.
+
+0. `--relative`, `-R`
+
+ Use relative paths. This means that the full path names specified on the
+ command line are sent to the server rather than just the last parts of the
+ filenames. This is particularly useful when you want to send several
+ different directories at the same time. For example, if you used this
+ command:
+
+ > rsync -av /foo/bar/baz.c remote:/tmp/
+
+ would create a file named baz.c in /tmp/ on the remote machine. If instead
+ you used
+
+ > rsync -avR /foo/bar/baz.c remote:/tmp/
+
+ then a file named /tmp/foo/bar/baz.c would be created on the remote
+ machine, preserving its full path. These extra path elements are called
+ "implied directories" (i.e. the "foo" and the "foo/bar" directories in the
+ above example).
+
+ Beginning with rsync 3.0.0, rsync always sends these implied directories as
+ real directories in the file list, even if a path element is really a
+ symlink on the sending side. This prevents some really unexpected behaviors
+ when copying the full path of a file that you didn't realize had a symlink
+ in its path. If you want to duplicate a server-side symlink, include both
+ the symlink via its path, and referent directory via its real path. If
+ you're dealing with an older rsync on the sending side, you may need to use
+ the [`--no-implied-dirs`](#opt) option.
+
+ It is also possible to limit the amount of path information that is sent as
+ implied directories for each path you specify. With a modern rsync on the
+ sending side (beginning with 2.6.7), you can insert a dot and a slash into
+ the source path, like this:
+
+ > rsync -avR /foo/./bar/baz.c remote:/tmp/
+
+ That would create /tmp/bar/baz.c on the remote machine. (Note that the dot
+ must be followed by a slash, so "/foo/." would not be abbreviated.) For
+ older rsync versions, you would need to use a chdir to limit the source
+ path. For example, when pushing files:
+
+ > (cd /foo; rsync -avR bar/baz.c remote:/tmp/)
+
+ (Note that the parens put the two commands into a sub-shell, so that the
+ "cd" command doesn't remain in effect for future commands.) If you're
+ pulling files from an older rsync, use this idiom (but only for a
+ non-daemon transfer):
+
+ > rsync -avR --rsync-path="cd /foo; rsync" \
+ > remote:bar/baz.c /tmp/
+
+0. `--no-implied-dirs`
+
+ This option affects the default behavior of the [`--relative`](#opt) option. When
+ it is specified, the attributes of the implied directories from the source
+ names are not included in the transfer. This means that the corresponding
+ path elements on the destination system are left unchanged if they exist,
+ and any missing implied directories are created with default attributes.
+ This even allows these implied path elements to have big differences, such
+ as being a symlink to a directory on the receiving side.
+
+ For instance, if a command-line arg or a files-from entry told rsync to
+ transfer the file "path/foo/file", the directories "path" and "path/foo"
+ are implied when [`--relative`](#opt) is used. If "path/foo" is a symlink to "bar"
+ on the destination system, the receiving rsync would ordinarily delete
+ "path/foo", recreate it as a directory, and receive the file into the new
+ directory. With `--no-implied-dirs`, the receiving rsync updates
+ "path/foo/file" using the existing path elements, which means that the file
+ ends up being created in "path/bar". Another way to accomplish this link
+ preservation is to use the [`--keep-dirlinks`](#opt) option (which will also affect
+ symlinks to directories in the rest of the transfer).
+
+ When pulling files from an rsync older than 3.0.0, you may need to use this
+ option if the sending side has a symlink in the path you request and you
+ wish the implied directories to be transferred as normal directories.
+
+0. `--backup`, `-b`
+
+ With this option, preexisting destination files are renamed as each file is
+ transferred or deleted. You can control where the backup file goes and
+ what (if any) suffix gets appended using the [`--backup-dir`](#opt) and
+ [`--suffix`](#opt) options.
+
+ If you don't specify [`--backup-dir`](#opt):
+
+ 1. the [`--omit-dir-times`](#opt) option will be forced on
+ 2. the use of [`--delete`](#opt) (without [`--delete-excluded`](#opt)),
+ causes rsync to add a "protect" [filter-rule](#FILTER_RULES) for the
+ backup suffix to the end of all your existing filters that looks like
+ this: `-f "P *~"`. This rule prevents previously backed-up files from
+ being deleted.
+
+ Note that if you are supplying your own filter rules, you may need to
+ manually insert your own exclude/protect rule somewhere higher up in the
+ list so that it has a high enough priority to be effective (e.g. if your
+ rules specify a trailing inclusion/exclusion of `*`, the auto-added rule
+ would never be reached).
+
+0. `--backup-dir=DIR`
+
+ This implies the [`--backup`](#opt) option, and tells rsync to store all
+ backups in the specified directory on the receiving side. This can be used
+ for incremental backups. You can additionally specify a backup suffix
+ using the [`--suffix`](#opt) option (otherwise the files backed up in the
+ specified directory will keep their original filenames).
+
+ Note that if you specify a relative path, the backup directory will be
+ relative to the destination directory, so you probably want to specify
+ either an absolute path or a path that starts with "../". If an rsync
+ daemon is the receiver, the backup dir cannot go outside the module's path
+ hierarchy, so take extra care not to delete it or copy into it.
+
+0. `--suffix=SUFFIX`
+
+ This option allows you to override the default backup suffix used with the
+ [`--backup`](#opt) (`-b`) option. The default suffix is a `~` if no
+ [`--backup-dir`](#opt) was specified, otherwise it is an empty string.
+
+0. `--update`, `-u`
+
+ This forces rsync to skip any files which exist on the destination and have
+ a modified time that is newer than the source file. (If an existing
+ destination file has a modification time equal to the source file's, it
+ will be updated if the sizes are different.)
+
+ Note that this does not affect the copying of dirs, symlinks, or other
+ special files. Also, a difference of file format between the sender and
+ receiver is always considered to be important enough for an update, no
+ matter what date is on the objects. In other words, if the source has a
+ directory where the destination has a file, the transfer would occur
+ regardless of the timestamps.
+
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
+
+ A caution for those that choose to combine [`--inplace`](#opt) with
+ `--update`: an interrupted transfer will leave behind a partial file on the
+ receiving side that has a very recent modified time, so re-running the
+ transfer will probably **not** continue the interrupted file. As such, it
+ is usually best to avoid combining this with[ `--inplace`](#opt) unless you
+ have implemented manual steps to handle any interrupted in-progress files.
+
+0. `--inplace`
+
+ This option changes how rsync transfers a file when its data needs to be
+ updated: instead of the default method of creating a new copy of the file
+ and moving it into place when it is complete, rsync instead writes the
+ updated data directly to the destination file.
+
+ This has several effects:
+
+ - Hard links are not broken. This means the new data will be visible
+ through other hard links to the destination file. Moreover, attempts to
+ copy differing source files onto a multiply-linked destination file will
+ result in a "tug of war" with the destination data changing back and
+ forth.
+ - In-use binaries cannot be updated (either the OS will prevent this from
+ happening, or binaries that attempt to swap-in their data will misbehave
+ or crash).
+ - The file's data will be in an inconsistent state during the transfer and
+ will be left that way if the transfer is interrupted or if an update
+ fails.
+ - A file that rsync cannot write to cannot be updated. While a super user
+ can update any file, a normal user needs to be granted write permission
+ for the open of the file for writing to be successful.
+ - The efficiency of rsync's delta-transfer algorithm may be reduced if some
+ data in the destination file is overwritten before it can be copied to a
+ position later in the file. This does not apply if you use [`--backup`](#opt),
+ since rsync is smart enough to use the backup file as the basis file for
+ the transfer.
+
+ WARNING: you should not use this option to update files that are being
+ accessed by others, so be careful when choosing to use this for a copy.
+
+ This option is useful for transferring large files with block-based changes
+ or appended data, and also on systems that are disk bound, not network
+ bound. It can also help keep a copy-on-write filesystem snapshot from
+ diverging the entire contents of a file that only has minor changes.
+
+ The option implies [`--partial`](#opt) (since an interrupted transfer does
+ not delete the file), but conflicts with [`--partial-dir`](#opt) and
+ [`--delay-updates`](#opt). Prior to rsync 2.6.4 `--inplace` was also
+ incompatible with [`--compare-dest`](#opt) and [`--link-dest`](#opt).
+
+0. `--append`
+
+ This special copy mode only works to efficiently update files that are
+ known to be growing larger where any existing content on the receiving side
+ is also known to be the same as the content on the sender. The use of
+ `--append` **can be dangerous** if you aren't 100% sure that all the files
+ in the transfer are shared, growing files. You should thus use filter
+ rules to ensure that you weed out any files that do not fit this criteria.
+
+ Rsync updates these growing file in-place without verifying any of the
+ existing content in the file (it only verifies the content that it is
+ appending). Rsync skips any files that exist on the receiving side that
+ are not shorter than the associated file on the sending side (which means
+ that new files are transferred). It also skips any files whose size on the
+ sending side gets shorter during the send negotiations (rsync warns about a
+ "diminished" file when this happens).
+
+ This does not interfere with the updating of a file's non-content
+ attributes (e.g. permissions, ownership, etc.) when the file does not need
+ to be transferred, nor does it affect the updating of any directories or
+ non-regular files.
+
+0. `--append-verify`
+
+ This special copy mode works like [`--append`](#opt) except that all the
+ data in the file is included in the checksum verification (making it less
+ efficient but also potentially safer). This option **can be dangerous** if
+ you aren't 100% sure that all the files in the transfer are shared, growing
+ files. See the [`--append`](#opt) option for more details.
+
+ Note: prior to rsync 3.0.0, the [`--append`](#opt) option worked like
+ `--append-verify`, so if you are interacting with an older rsync (or the
+ transfer is using a protocol prior to 30), specifying either append option
+ will initiate an `--append-verify` transfer.
+
+0. `--dirs`, `-d`
+
+ Tell the sending side to include any directories that are encountered.
+ Unlike [`--recursive`](#opt), a directory's contents are not copied unless
+ the directory name specified is "." or ends with a trailing slash (e.g.
+ ".", "dir/.", "dir/", etc.). Without this option or the
+ [`--recursive`](#opt) option, rsync will skip all directories it encounters
+ (and output a message to that effect for each one). If you specify both
+ `--dirs` and [`--recursive`](#opt), `--recursive` takes precedence.
+
+ The `--dirs` option is implied by the [`--files-from`](#opt) option or the
+ [`--list-only`](#opt) option (including an implied [`--list-only`](#opt)
+ usage) if [`--recursive`](#opt) wasn't specified (so that directories are
+ seen in the listing). Specify `--no-dirs` (or `--no-d`) if you want to
+ turn this off.
+
+ There is also a backward-compatibility helper option, `--old-dirs`
+ (`--old-d`) that tells rsync to use a hack of `-r --exclude='/*/*'` to get
+ an older rsync to list a single directory without recursing.
+
+0. `--mkpath`
+
+ Create all missing path components of the destination path.
+
+ By default, rsync allows only the final component of the destination path
+ to not exist, which is an attempt to help you to validate your destination
+ path. With this option, rsync creates all the missing destination-path
+ components, just as if `mkdir -p $DEST_PATH` had been run on the receiving
+ side.
+
+ When specifying a destination path, including a trailing slash ensures that
+ the whole path is treated as directory names to be created, even when the
+ file list has a single item. See the [COPYING TO A DIFFERENT NAME](#)
+ section for full details on how rsync decides if a final destination-path
+ component should be created as a directory or not.
+
+ If you would like the newly-created destination dirs to match the dirs on
+ the sending side, you should be using [`--relative`](#opt) (`-R`) instead
+ of `--mkpath`. For instance, the following two commands result in the same
+ destination tree, but only the second command ensures that the
+ "some/extra/path" components match the dirs on the sending side:
+
+ > rsync -ai --mkpath host:some/extra/path/*.c some/extra/path/
+ > rsync -aiR host:some/extra/path/*.c ./
+
+0. `--links`, `-l`
+
+ Add symlinks to the transferred files instead of noisily ignoring them with
+ a "non-regular file" warning for each symlink encountered. You can
+ alternately silence the warning by specifying [`--info=nonreg0`](#opt).
+
+ The default handling of symlinks is to recreate each symlink's unchanged
+ value on the receiving side.
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
+0. `--copy-links`, `-L`
+
+ The sender transforms each symlink encountered in the transfer into the
+ referent item, following the symlink chain to the file or directory that it
+ references. If a symlink chain is broken, an error is output and the file
+ is dropped from the transfer.
+
+ This option supersedes any other options that affect symlinks in the
+ transfer, since there are no symlinks left in the transfer.
+
+ This option does not change the handling of existing symlinks on the
+ receiving side, unlike versions of rsync prior to 2.6.3 which had the
+ side-effect of telling the receiving side to also follow symlinks. A
+ modern rsync won't forward this option to a remote receiver (since only the
+ sender needs to know about it), so this caveat should only affect someone
+ using an rsync client older than 2.6.7 (which is when `-L` stopped being
+ forwarded to the receiver).
+
+ See the [`--keep-dirlinks`](#opt) (`-K`) if you need a symlink to a
+ directory to be treated as a real directory on the receiving side.
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
+0. `--copy-unsafe-links`
+
+ This tells rsync to copy the referent of symbolic links that point outside
+ the copied tree. Absolute symlinks are also treated like ordinary files,
+ and so are any symlinks in the source path itself when [`--relative`](#opt)
+ is used.
+
+ Note that the cut-off point is the top of the transfer, which is the part
+ of the path that rsync isn't mentioning in the verbose output. If you copy
+ "/src/subdir" to "/dest/" then the "subdir" directory is a name inside the
+ transfer tree, not the top of the transfer (which is /src) so it is legal
+ for created relative symlinks to refer to other names inside the /src and
+ /dest directories. If you instead copy "/src/subdir/" (with a trailing
+ slash) to "/dest/subdir" that would not allow symlinks to any files outside
+ of "subdir".
+
+ Note that safe symlinks are only copied if [`--links`](#opt) was also
+ specified or implied. The `--copy-unsafe-links` option has no extra effect
+ when combined with [`--copy-links`](#opt).
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
+0. `--safe-links`
+
+ This tells the receiving rsync to ignore any symbolic links in the transfer
+ which point outside the copied tree. All absolute symlinks are also
+ ignored.
+
+ Since this ignoring is happening on the receiving side, it will still be
+ effective even when the sending side has munged symlinks (when it is using
+ [`--munge-links`](#opt)). It also affects deletions, since the file being
+ present in the transfer prevents any matching file on the receiver from
+ being deleted when the symlink is deemed to be unsafe and is skipped.
+
+ This option must be combined with [`--links`](#opt) (or
+ [`--archive`](#opt)) to have any symlinks in the transfer to conditionally
+ ignore. Its effect is superseded by [`--copy-unsafe-links`](#opt).
+
+ Using this option in conjunction with [`--relative`](#opt) may give
+ unexpected results.
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
+0. `--munge-links`
+
+ This option affects just one side of the transfer and tells rsync to munge
+ symlink values when it is receiving files or unmunge symlink values when it
+ is sending files. The munged values make the symlinks unusable on disk but
+ allows the original contents of the symlinks to be recovered.
+
+ The server-side rsync often enables this option without the client's
+ knowledge, such as in an rsync daemon's configuration file or by an option
+ given to the rrsync (restricted rsync) script. When specified on the
+ client side, specify the option normally if it is the client side that
+ has/needs the munged symlinks, or use `-M--munge-links` to give the option
+ to the server when it has/needs the munged symlinks. Note that on a local
+ transfer, the client is the sender, so specifying the option directly
+ unmunges symlinks while specifying it as a remote option munges symlinks.
+
+ This option has no effect when sent to a daemon via [`--remote-option`](#opt)
+ because the daemon configures whether it wants munged symlinks via its
+ "`munge symlinks`" parameter.
+
+ The symlink value is munged/unmunged once it is in the transfer, so any
+ option that transforms symlinks into non-symlinks occurs prior to the
+ munging/unmunging **except** for [`--safe-links`](#opt), which is a choice
+ that the receiver makes, so it bases its decision on the munged/unmunged
+ value. This does mean that if a receiver has munging enabled, that using
+ [`--safe-links`](#opt) will cause all symlinks to be ignored (since they
+ are all absolute).
+
+ The method that rsync uses to munge the symlinks is to prefix each one's
+ value with the string "/rsyncd-munged/". This prevents the links from
+ being used as long as the directory does not exist. When this option is
+ enabled, rsync will refuse to run if that path is a directory or a symlink
+ to a directory (though it only checks at startup). See also the
+ "munge-symlinks" python script in the support directory of the source code
+ for a way to munge/unmunge one or more symlinks in-place.
+
+0. `--copy-dirlinks`, `-k`
+
+ This option causes the sending side to treat a symlink to a directory as
+ though it were a real directory. This is useful if you don't want symlinks
+ to non-directories to be affected, as they would be using
+ [`--copy-links`](#opt).
+
+ Without this option, if the sending side has replaced a directory with a
+ symlink to a directory, the receiving side will delete anything that is in
+ the way of the new symlink, including a directory hierarchy (as long as
+ [`--force`](#opt) or [`--delete`](#opt) is in effect).
+
+ See also [`--keep-dirlinks`](#opt) for an analogous option for the
+ receiving side.
+
+ `--copy-dirlinks` applies to all symlinks to directories in the source. If
+ you want to follow only a few specified symlinks, a trick you can use is to
+ pass them as additional source args with a trailing slash, using
+ [`--relative`](#opt) to make the paths match up right. For example:
+
+ > rsync -r --relative src/./ src/./follow-me/ dest/
+
+ This works because rsync calls **lstat**(2) on the source arg as given, and
+ the trailing slash makes **lstat**(2) follow the symlink, giving rise to a
+ directory in the file-list which overrides the symlink found during the
+ scan of "src/./".
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
+0. `--keep-dirlinks`, `-K`
+
+ This option causes the receiving side to treat a symlink to a directory as
+ though it were a real directory, but only if it matches a real directory
+ from the sender. Without this option, the receiver's symlink would be
+ deleted and replaced with a real directory.
+
+ For example, suppose you transfer a directory "foo" that contains a file
+ "file", but "foo" is a symlink to directory "bar" on the receiver. Without
+ `--keep-dirlinks`, the receiver deletes symlink "foo", recreates it as a
+ directory, and receives the file into the new directory. With
+ `--keep-dirlinks`, the receiver keeps the symlink and "file" ends up in
+ "bar".
+
+ One note of caution: if you use `--keep-dirlinks`, you must trust all the
+ symlinks in the copy or enable the [`--munge-links`](#opt) option on the
+ receiving side! If it is possible for an untrusted user to create their
+ own symlink to any real directory, the user could then (on a subsequent
+ copy) replace the symlink with a real directory and affect the content of
+ whatever directory the symlink references. For backup copies, you are
+ better off using something like a bind mount instead of a symlink to modify
+ your receiving hierarchy.
+
+ See also [`--copy-dirlinks`](#opt) for an analogous option for the sending
+ side.
+
+ See the [SYMBOLIC LINKS](#) section for multi-option info.
+
+0. `--hard-links`, `-H`
+
+ This tells rsync to look for hard-linked files in the source and link
+ together the corresponding files on the destination. Without this option,
+ hard-linked files in the source are treated as though they were separate
+ files.
+
+ This option does NOT necessarily ensure that the pattern of hard links on
+ the destination exactly matches that on the source. Cases in which the
+ destination may end up with extra hard links include the following:
+
+ - If the destination contains extraneous hard-links (more linking than what
+ is present in the source file list), the copying algorithm will not break
+ them explicitly. However, if one or more of the paths have content
+ differences, the normal file-update process will break those extra links
+ (unless you are using the [`--inplace`](#opt) option).
+ - If you specify a [`--link-dest`](#opt) directory that contains hard
+ links, the linking of the destination files against the
+ [`--link-dest`](#opt) files can cause some paths in the destination to
+ become linked together due to the [`--link-dest`](#opt) associations.
+
+ Note that rsync can only detect hard links between files that are inside
+ the transfer set. If rsync updates a file that has extra hard-link
+ connections to files outside the transfer, that linkage will be broken. If
+ you are tempted to use the [`--inplace`](#opt) option to avoid this breakage, be
+ very careful that you know how your files are being updated so that you are
+ certain that no unintended changes happen due to lingering hard links (and
+ see the [`--inplace`](#opt) option for more caveats).
+
+ If incremental recursion is active (see [`--inc-recursive`](#opt)), rsync
+ may transfer a missing hard-linked file before it finds that another link
+ for that contents exists elsewhere in the hierarchy. This does not affect
+ the accuracy of the transfer (i.e. which files are hard-linked together),
+ just its efficiency (i.e. copying the data for a new, early copy of a
+ hard-linked file that could have been found later in the transfer in
+ another member of the hard-linked set of files). One way to avoid this
+ inefficiency is to disable incremental recursion using the
+ [`--no-inc-recursive`](#opt) option.
+
+0. `--perms`, `-p`
+
+ This option causes the receiving rsync to set the destination permissions
+ to be the same as the source permissions. (See also the [`--chmod`](#opt)
+ option for a way to modify what rsync considers to be the source
+ permissions.)
+
+ When this option is _off_, permissions are set as follows:
+
+ - Existing files (including updated files) retain their existing
+ permissions, though the [`--executability`](#opt) option might change
+ just the execute permission for the file.
+ - New files get their "normal" permission bits set to the source file's
+ permissions masked with the receiving directory's default permissions
+ (either the receiving process's umask, or the permissions specified via
+ the destination directory's default ACL), and their special permission
+ bits disabled except in the case where a new directory inherits a setgid
+ bit from its parent directory.
+
+ Thus, when `--perms` and [`--executability`](#opt) are both disabled, rsync's
+ behavior is the same as that of other file-copy utilities, such as **cp**(1)
+ and **tar**(1).
+
+ In summary: to give destination files (both old and new) the source
+ permissions, use `--perms`. To give new files the destination-default
+ permissions (while leaving existing files unchanged), make sure that the
+ `--perms` option is off and use [`--chmod=ugo=rwX`](#opt) (which ensures
+ that all non-masked bits get enabled). If you'd care to make this latter
+ behavior easier to type, you could define a popt alias for it, such as
+ putting this line in the file `~/.popt` (the following defines the `-Z`
+ option, and includes `--no-g` to use the default group of the destination
+ dir):
+
+ > rsync alias -Z --no-p --no-g --chmod=ugo=rwX
+
+ You could then use this new option in a command such as this one:
+
+ > rsync -avZ src/ dest/
+
+ (Caveat: make sure that `-a` does not follow `-Z`, or it will re-enable the
+ two `--no-*` options mentioned above.)
+
+ The preservation of the destination's setgid bit on newly-created
+ directories when `--perms` is off was added in rsync 2.6.7. Older rsync
+ versions erroneously preserved the three special permission bits for
+ newly-created files when `--perms` was off, while overriding the
+ destination's setgid bit setting on a newly-created directory. Default ACL
+ observance was added to the ACL patch for rsync 2.6.7, so older (or
+ non-ACL-enabled) rsyncs use the umask even if default ACLs are present.
+ (Keep in mind that it is the version of the receiving rsync that affects
+ these behaviors.)
+
+0. `--executability`, `-E`
+
+ This option causes rsync to preserve the executability (or
+ non-executability) of regular files when [`--perms`](#opt) is not enabled.
+ A regular file is considered to be executable if at least one 'x' is turned
+ on in its permissions. When an existing destination file's executability
+ differs from that of the corresponding source file, rsync modifies the
+ destination file's permissions as follows:
+
+ - To make a file non-executable, rsync turns off all its 'x' permissions.
+ - To make a file executable, rsync turns on each 'x' permission that has a
+ corresponding 'r' permission enabled.
+
+ If [`--perms`](#opt) is enabled, this option is ignored.
+
+0. `--acls`, `-A`
+
+ This option causes rsync to update the destination ACLs to be the same as
+ the source ACLs. The option also implies [`--perms`](#opt).
+
+ The source and destination systems must have compatible ACL entries for
+ this option to work properly. See the [`--fake-super`](#opt) option for a
+ way to backup and restore ACLs that are not compatible.
+
+0. `--xattrs`, `-X`
+
+ This option causes rsync to update the destination extended attributes to
+ be the same as the source ones.
+
+ For systems that support extended-attribute namespaces, a copy being done
+ by a super-user copies all namespaces except system.\*. A normal user only
+ copies the user.\* namespace. To be able to backup and restore non-user
+ namespaces as a normal user, see the [`--fake-super`](#opt) option.
+
+ The above name filtering can be overridden by using one or more filter
+ options with the **x** modifier. When you specify an xattr-affecting
+ filter rule, rsync requires that you do your own system/user filtering, as
+ well as any additional filtering for what xattr names are copied and what
+ names are allowed to be deleted. For example, to skip the system
+ namespace, you could specify:
+
+ > --filter='-x system.*'
+
+ To skip all namespaces except the user namespace, you could specify a
+ negated-user match:
+
+ > --filter='-x! user.*'
+
+ To prevent any attributes from being deleted, you could specify a
+ receiver-only rule that excludes all names:
+
+ > --filter='-xr *'
+
+ Note that the `-X` option does not copy rsync's special xattr values (e.g.
+ those used by [`--fake-super`](#opt)) unless you repeat the option (e.g. `-XX`).
+ This "copy all xattrs" mode cannot be used with [`--fake-super`](#opt).
+
+0. `--chmod=CHMOD`
+
+ This option tells rsync to apply one or more comma-separated "chmod" modes
+ to the permission of the files in the transfer. The resulting value is
+ treated as though it were the permissions that the sending side supplied
+ for the file, which means that this option can seem to have no effect on
+ existing files if [`--perms`](#opt) is not enabled.
+
+ In addition to the normal parsing rules specified in the **chmod**(1)
+ manpage, you can specify an item that should only apply to a directory by
+ prefixing it with a 'D', or specify an item that should only apply to a
+ file by prefixing it with a 'F'. For example, the following will ensure
+ that all directories get marked set-gid, that no files are other-writable,
+ that both are user-writable and group-writable, and that both have
+ consistent executability across all bits:
+
+ > --chmod=Dg+s,ug+w,Fo-w,+X
+
+ Using octal mode numbers is also allowed:
+
+ > --chmod=D2775,F664
+
+ It is also legal to specify multiple `--chmod` options, as each additional
+ option is just appended to the list of changes to make.
+
+ See the [`--perms`](#opt) and [`--executability`](#opt) options for how the
+ resulting permission value can be applied to the files in the transfer.
+
+0. `--owner`, `-o`
+
+ This option causes rsync to set the owner of the destination file to be the
+ same as the source file, but only if the receiving rsync is being run as
+ the super-user (see also the [`--super`](#opt) and [`--fake-super`](#opt)
+ options). Without this option, the owner of new and/or transferred files
+ are set to the invoking user on the receiving side.
+
+ The preservation of ownership will associate matching names by default, but
+ may fall back to using the ID number in some circumstances (see also the
+ [`--numeric-ids`](#opt) option for a full discussion).
+
+0. `--group`, `-g`
+
+ This option causes rsync to set the group of the destination file to be the
+ same as the source file. If the receiving program is not running as the
+ super-user (or if `--no-super` was specified), only groups that the
+ invoking user on the receiving side is a member of will be preserved.
+ Without this option, the group is set to the default group of the invoking
+ user on the receiving side.
+
+ The preservation of group information will associate matching names by
+ default, but may fall back to using the ID number in some circumstances
+ (see also the [`--numeric-ids`](#opt) option for a full discussion).
+
+0. `--devices`
+
+ This option causes rsync to transfer character and block device files to
+ the remote system to recreate these devices. If the receiving rsync is not
+ being run as the super-user, rsync silently skips creating the device files
+ (see also the [`--super`](#opt) and [`--fake-super`](#opt) options).
+
+ By default, rsync generates a "non-regular file" warning for each device
+ file encountered when this option is not set. You can silence the warning
+ by specifying [`--info=nonreg0`](#opt).
+
+0. `--specials`
+
+ This option causes rsync to transfer special files, such as named sockets
+ and fifos. If the receiving rsync is not being run as the super-user,
+ rsync silently skips creating the special files (see also the
+ [`--super`](#opt) and [`--fake-super`](#opt) options).
+
+ By default, rsync generates a "non-regular file" warning for each special
+ file encountered when this option is not set. You can silence the warning
+ by specifying [`--info=nonreg0`](#opt).
+
+0. `-D`
+
+ The `-D` option is equivalent to "[`--devices`](#opt)
+ [`--specials`](#opt)".
+
+0. `--copy-devices`
+
+ This tells rsync to treat a device on the sending side as a regular file,
+ allowing it to be copied to a normal destination file (or another device
+ if `--write-devices` was also specified).
+
+ This option is refused by default by an rsync daemon.
+
+0. `--write-devices`
+
+ This tells rsync to treat a device on the receiving side as a regular file,
+ allowing the writing of file data into a device.
+
+ This option implies the [`--inplace`](#opt) option.
+
+ Be careful using this, as you should know what devices are present on the
+ receiving side of the transfer, especially when running rsync as root.
+
+ This option is refused by default by an rsync daemon.
+
+0. `--times`, `-t`
+
+ This tells rsync to transfer modification times along with the files and
+ update them on the remote system. Note that if this option is not used,
+ the optimization that excludes files that have not been modified cannot be
+ effective; in other words, a missing `-t` (or [`-a`](#opt)) will cause the
+ next transfer to behave as if it used [`--ignore-times`](#opt) (`-I`),
+ causing all files to be updated (though rsync's delta-transfer algorithm
+ will make the update fairly efficient if the files haven't actually
+ changed, you're much better off using `-t`).
+
+ A modern rsync that is using transfer protocol 30 or 31 conveys a modify
+ time using up to 8-bytes. If rsync is forced to speak an older protocol
+ (perhaps due to the remote rsync being older than 3.0.0) a modify time is
+ conveyed using 4-bytes. Prior to 3.2.7, these shorter values could convey
+ a date range of 13-Dec-1901 to 19-Jan-2038. Beginning with 3.2.7, these
+ 4-byte values now convey a date range of 1-Jan-1970 to 7-Feb-2106. If you
+ have files dated older than 1970, make sure your rsync executables are
+ upgraded so that the full range of dates can be conveyed.
+
+0. `--atimes`, `-U`
+
+ This tells rsync to set the access (use) times of the destination files to
+ the same value as the source files.
+
+ If repeated, it also sets the [`--open-noatime`](#opt) option, which can help you
+ to make the sending and receiving systems have the same access times on the
+ transferred files without needing to run rsync an extra time after a file
+ is transferred.
+
+ Note that some older rsync versions (prior to 3.2.0) may have been built
+ with a pre-release `--atimes` patch that does not imply
+ [`--open-noatime`](#opt) when this option is repeated.
+
+0. `--open-noatime`
+
+ This tells rsync to open files with the O_NOATIME flag (on systems that
+ support it) to avoid changing the access time of the files that are being
+ transferred. If your OS does not support the O_NOATIME flag then rsync
+ will silently ignore this option. Note also that some filesystems are
+ mounted to avoid updating the atime on read access even without the
+ O_NOATIME flag being set.
+
+0. `--crtimes`, `-N,`
+
+ This tells rsync to set the create times (newness) of the destination
+ files to the same value as the source files.
+
+0. `--omit-dir-times`, `-O`
+
+ This tells rsync to omit directories when it is preserving modification,
+ access, and create times. If NFS is sharing the directories on the receiving
+ side, it is a good idea to use `-O`. This option is inferred if you use
+ [`--backup`](#opt) without [`--backup-dir`](#opt).
+
+ This option also has the side-effect of avoiding early creation of missing
+ sub-directories when incremental recursion is enabled, as discussed in the
+ [`--inc-recursive`](#opt) section.
+
+0. `--omit-link-times`, `-J`
+
+ This tells rsync to omit symlinks when it is preserving modification,
+ access, and create times.
+
+0. `--super`
+
+ This tells the receiving side to attempt super-user activities even if the
+ receiving rsync wasn't run by the super-user. These activities include:
+ preserving users via the [`--owner`](#opt) option, preserving all groups
+ (not just the current user's groups) via the [`--group`](#opt) option, and
+ copying devices via the [`--devices`](#opt) option. This is useful for
+ systems that allow such activities without being the super-user, and also
+ for ensuring that you will get errors if the receiving side isn't being run
+ as the super-user. To turn off super-user activities, the super-user can
+ use `--no-super`.
+
+0. `--fake-super`
+
+ When this option is enabled, rsync simulates super-user activities by
+ saving/restoring the privileged attributes via special extended attributes
+ that are attached to each file (as needed). This includes the file's owner
+ and group (if it is not the default), the file's device info (device &
+ special files are created as empty text files), and any permission bits
+ that we won't allow to be set on the real file (e.g. the real file gets
+ u-s,g-s,o-t for safety) or that would limit the owner's access (since the
+ real super-user can always access/change a file, the files we create can
+ always be accessed/changed by the creating user). This option also handles
+ ACLs (if [`--acls`](#opt) was specified) and non-user extended attributes
+ (if [`--xattrs`](#opt) was specified).
+
+ This is a good way to backup data without using a super-user, and to store
+ ACLs from incompatible systems.
+
+ The `--fake-super` option only affects the side where the option is used.
+ To affect the remote side of a remote-shell connection, use the
+ [`--remote-option`](#opt) (`-M`) option:
+
+ > rsync -av -M--fake-super /src/ host:/dest/
+
+ For a local copy, this option affects both the source and the destination.
+ If you wish a local copy to enable this option just for the destination
+ files, specify `-M--fake-super`. If you wish a local copy to enable this
+ option just for the source files, combine `--fake-super` with `-M--super`.
+
+ This option is overridden by both [`--super`](#opt) and `--no-super`.
+
+ See also the [`fake super`](rsyncd.conf.5#fake_super) setting in the
+ daemon's rsyncd.conf file.
+
+0. `--sparse`, `-S`
+
+ Try to handle sparse files efficiently so they take up less space on the
+ destination. If combined with [`--inplace`](#opt) the file created might
+ not end up with sparse blocks with some combinations of kernel version
+ and/or filesystem type. If [`--whole-file`](#opt) is in effect (e.g. for a
+ local copy) then it will always work because rsync truncates the file prior
+ to writing out the updated version.
+
+ Note that versions of rsync older than 3.1.3 will reject the combination of
+ `--sparse` and [`--inplace`](#opt).
+
+0. `--preallocate`
+
+ This tells the receiver to allocate each destination file to its eventual
+ size before writing data to the file. Rsync will only use the real
+ filesystem-level preallocation support provided by Linux's **fallocate**(2)
+ system call or Cygwin's **posix_fallocate**(3), not the slow glibc
+ implementation that writes a null byte into each block.
+
+ Without this option, larger files may not be entirely contiguous on the
+ filesystem, but with this option rsync will probably copy more slowly. If
+ the destination is not an extent-supporting filesystem (such as ext4, xfs,
+ NTFS, etc.), this option may have no positive effect at all.
+
+ If combined with [`--sparse`](#opt), the file will only have sparse blocks
+ (as opposed to allocated sequences of null bytes) if the kernel version and
+ filesystem type support creating holes in the allocated data.
+
+0. `--dry-run`, `-n`
+
+ This makes rsync perform a trial run that doesn't make any changes (and
+ produces mostly the same output as a real run). It is most commonly used
+ in combination with the [`--verbose`](#opt) (`-v`) and/or
+ [`--itemize-changes`](#opt) (`-i`) options to see what an rsync command is
+ going to do before one actually runs it.
+
+ The output of [`--itemize-changes`](#opt) is supposed to be exactly the
+ same on a dry run and a subsequent real run (barring intentional trickery
+ and system call failures); if it isn't, that's a bug. Other output should
+ be mostly unchanged, but may differ in some areas. Notably, a dry run does
+ not send the actual data for file transfers, so [`--progress`](#opt) has no
+ effect, the "bytes sent", "bytes received", "literal data", and "matched
+ data" statistics are too small, and the "speedup" value is equivalent to a
+ run where no file transfers were needed.
+
+0. `--whole-file`, `-W`
+
+ This option disables rsync's delta-transfer algorithm, which causes all
+ transferred files to be sent whole. The transfer may be faster if this
+ option is used when the bandwidth between the source and destination
+ machines is higher than the bandwidth to disk (especially when the "disk"
+ is actually a networked filesystem). This is the default when both the
+ source and destination are specified as local paths, but only if no
+ batch-writing option is in effect.
+
+0. `--no-whole-file`, `--no-W`
+
+ Disable whole-file updating when it is enabled by default for a local
+ transfer. This usually slows rsync down, but it can be useful if you are
+ trying to minimize the writes to the destination file (if combined with
+ [`--inplace`](#opt)) or for testing the checksum-based update algorithm.
+
+ See also the [`--whole-file`](#opt) option.
+
+0. `--checksum-choice=STR`, `--cc=STR`
+
+ This option overrides the checksum algorithms. If one algorithm name is
+ specified, it is used for both the transfer checksums and (assuming
+ [`--checksum`](#opt) is specified) the pre-transfer checksums. If two
+ comma-separated names are supplied, the first name affects the transfer
+ checksums, and the second name affects the pre-transfer checksums (`-c`).
+
+ The checksum options that you may be able to use are:
+
+ - `auto` (the default automatic choice)
+ - `xxh128`
+ - `xxh3`
+ - `xxh64` (aka `xxhash`)
+ - `md5`
+ - `md4`
+ - `sha1`
+ - `none`
+
+ Run `rsync --version` to see the default checksum list compiled into your
+ version (which may differ from the list above).
+
+ If "none" is specified for the first (or only) name, the [`--whole-file`](#opt)
+ option is forced on and no checksum verification is performed on the
+ transferred data. If "none" is specified for the second (or only) name,
+ the [`--checksum`](#opt) option cannot be used.
+
+ The "auto" option is the default, where rsync bases its algorithm choice on
+ a negotiation between the client and the server as follows:
+
+ When both sides of the transfer are at least 3.2.0, rsync chooses the first
+ algorithm in the client's list of choices that is also in the server's list
+ of choices. If no common checksum choice is found, rsync exits with
+ an error. If the remote rsync is too old to support checksum negotiation,
+ a value is chosen based on the protocol version (which chooses between MD5
+ and various flavors of MD4 based on protocol age).
+
+ The default order can be customized by setting the environment variable
+ [`RSYNC_CHECKSUM_LIST`](#) to a space-separated list of acceptable checksum
+ names. If the string contains a "`&`" character, it is separated into the
+ "client string & server string", otherwise the same string applies to both.
+ If the string (or string portion) contains no non-whitespace characters,
+ the default checksum list is used. This method does not allow you to
+ specify the transfer checksum separately from the pre-transfer checksum,
+ and it discards "auto" and all unknown checksum names. A list with only
+ invalid names results in a failed negotiation.
+
+ The use of the `--checksum-choice` option overrides this environment list.
+
+0. `--one-file-system`, `-x`
+
+ This tells rsync to avoid crossing a filesystem boundary when recursing.
+ This does not limit the user's ability to specify items to copy from
+ multiple filesystems, just rsync's recursion through the hierarchy of each
+ directory that the user specified, and also the analogous recursion on the
+ receiving side during deletion. Also keep in mind that rsync treats a
+ "bind" mount to the same device as being on the same filesystem.
+
+ If this option is repeated, rsync omits all mount-point directories from
+ the copy. Otherwise, it includes an empty directory at each mount-point it
+ encounters (using the attributes of the mounted directory because those of
+ the underlying mount-point directory are inaccessible).
+
+ If rsync has been told to collapse symlinks (via [`--copy-links`](#opt) or
+ [`--copy-unsafe-links`](#opt)), a symlink to a directory on another device
+ is treated like a mount-point. Symlinks to non-directories are unaffected
+ by this option.
+
+0. `--ignore-non-existing`, `--existing`
+
+ This tells rsync to skip creating files (including directories) that do not
+ exist yet on the destination. If this option is combined with the
+ [`--ignore-existing`](#opt) option, no files will be updated (which can be
+ useful if all you want to do is delete extraneous files).
+
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
+
+0. `--ignore-existing`
+
+ This tells rsync to skip updating files that already exist on the
+ destination (this does _not_ ignore existing directories, or nothing would
+ get done). See also [`--ignore-non-existing`](#opt).
+
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
+
+ This option can be useful for those doing backups using the
+ [`--link-dest`](#opt) option when they need to continue a backup run that
+ got interrupted. Since a [`--link-dest`](#opt) run is copied into a new
+ directory hierarchy (when it is used properly), using [`--ignore-existing`
+ will ensure that the already-handled files don't get tweaked (which avoids
+ a change in permissions on the hard-linked files). This does mean that
+ this option is only looking at the existing files in the destination
+ hierarchy itself.
+
+ When [`--info=skip2`](#opt) is used rsync will output "FILENAME exists
+ (INFO)" messages where the INFO indicates one of "type change", "sum
+ change" (requires [`-c`](#opt)), "file change" (based on the quick check),
+ "attr change", or "uptodate". Using [`--info=skip1`](#opt) (which is also
+ implied by 2 [`-v`](#opt) options) outputs the exists message without the
+ INFO suffix.
+
+0. `--remove-source-files`
+
+ This tells rsync to remove from the sending side the files (meaning
+ non-directories) that are a part of the transfer and have been successfully
+ duplicated on the receiving side.
+
+ Note that you should only use this option on source files that are
+ quiescent. If you are using this to move files that show up in a
+ particular directory over to another host, make sure that the finished
+ files get renamed into the source directory, not directly written into it,
+ so that rsync can't possibly transfer a file that is not yet fully written.
+ If you can't first write the files into a different directory, you should
+ use a naming idiom that lets rsync avoid transferring files that are not
+ yet finished (e.g. name the file "foo.new" when it is written, rename it to
+ "foo" when it is done, and then use the option [`--exclude='*.new'`](#opt)
+ for the rsync transfer).
+
+ Starting with 3.1.0, rsync will skip the sender-side removal (and output an
+ error) if the file's size or modify time has not stayed unchanged.
+
+ Starting with 3.2.6, a local rsync copy will ensure that the sender does
+ not remove a file the receiver just verified, such as when the user
+ accidentally makes the source and destination directory the same path.
+
+0. `--delete`
+
+ This tells rsync to delete extraneous files from the receiving side (ones
+ that aren't on the sending side), but only for the directories that are
+ being synchronized. You must have asked rsync to send the whole directory
+ (e.g. "`dir`" or "`dir/`") without using a wildcard for the directory's
+ contents (e.g. "`dir/*`") since the wildcard is expanded by the shell and
+ rsync thus gets a request to transfer individual files, not the files'
+ parent directory. Files that are excluded from the transfer are also
+ excluded from being deleted unless you use the [`--delete-excluded`](#opt)
+ option or mark the rules as only matching on the sending side (see the
+ include/exclude modifiers in the [FILTER RULES](#) section).
+
+ Prior to rsync 2.6.7, this option would have no effect unless
+ [`--recursive`](#opt) was enabled. Beginning with 2.6.7, deletions will
+ also occur when [`--dirs`](#opt) (`-d`) is enabled, but only for
+ directories whose contents are being copied.
+
+ This option can be dangerous if used incorrectly! It is a very good idea to
+ first try a run using the [`--dry-run`](#opt) (`-n`) option to see what
+ files are going to be deleted.
+
+ If the sending side detects any I/O errors, then the deletion of any files
+ at the destination will be automatically disabled. This is to prevent
+ temporary filesystem failures (such as NFS errors) on the sending side from
+ causing a massive deletion of files on the destination. You can override
+ this with the [`--ignore-errors`](#opt) option.
+
+ The `--delete` option may be combined with one of the --delete-WHEN options
+ without conflict, as well as [`--delete-excluded`](#opt). However, if none
+ of the `--delete-WHEN` options are specified, rsync will choose the
+ [`--delete-during`](#opt) algorithm when talking to rsync 3.0.0 or newer,
+ or the [`--delete-before`](#opt) algorithm when talking to an older rsync.
+ See also [`--delete-delay`](#opt) and [`--delete-after`](#opt).
+
+0. `--delete-before`
+
+ Request that the file-deletions on the receiving side be done before the
+ transfer starts. See [`--delete`](#opt) (which is implied) for more
+ details on file-deletion.
+
+ Deleting before the transfer is helpful if the filesystem is tight for
+ space and removing extraneous files would help to make the transfer
+ possible. However, it does introduce a delay before the start of the
+ transfer, and this delay might cause the transfer to timeout (if
+ [`--timeout`](#opt) was specified). It also forces rsync to use the old,
+ non-incremental recursion algorithm that requires rsync to scan all the
+ files in the transfer into memory at once (see [`--recursive`](#opt)).
+
+0. `--delete-during`, `--del`
+
+ Request that the file-deletions on the receiving side be done incrementally
+ as the transfer happens. The per-directory delete scan is done right
+ before each directory is checked for updates, so it behaves like a more
+ efficient [`--delete-before`](#opt), including doing the deletions prior to
+ any per-directory filter files being updated. This option was first added
+ in rsync version 2.6.4. See [`--delete`](#opt) (which is implied) for more
+ details on file-deletion.
+
+0. `--delete-delay`
+
+ Request that the file-deletions on the receiving side be computed during
+ the transfer (like [`--delete-during`](#opt)), and then removed after the
+ transfer completes. This is useful when combined with
+ [`--delay-updates`](#opt) and/or [`--fuzzy`](#opt), and is more efficient
+ than using [`--delete-after`](#opt) (but can behave differently, since
+ [`--delete-after`](#opt) computes the deletions in a separate pass after
+ all updates are done). If the number of removed files overflows an
+ internal buffer, a temporary file will be created on the receiving side to
+ hold the names (it is removed while open, so you shouldn't see it during
+ the transfer). If the creation of the temporary file fails, rsync will try
+ to fall back to using [`--delete-after`](#opt) (which it cannot do if
+ [`--recursive`](#opt) is doing an incremental scan). See
+ [`--delete`](#opt) (which is implied) for more details on file-deletion.
+
+0. `--delete-after`
+
+ Request that the file-deletions on the receiving side be done after the
+ transfer has completed. This is useful if you are sending new
+ per-directory merge files as a part of the transfer and you want their
+ exclusions to take effect for the delete phase of the current transfer. It
+ also forces rsync to use the old, non-incremental recursion algorithm that
+ requires rsync to scan all the files in the transfer into memory at once
+ (see [`--recursive`](#opt)). See [`--delete`](#opt) (which is implied) for
+ more details on file-deletion.
+
+ See also the [`--delete-delay`](#opt) option that might be a faster choice
+ for those that just want the deletions to occur at the end of the transfer.
+
+0. `--delete-excluded`
+
+ This option turns any unqualified exclude/include rules into server-side
+ rules that do not affect the receiver's deletions.
+
+ By default, an exclude or include has both a server-side effect (to "hide"
+ and "show" files when building the server's file list) and a receiver-side
+ effect (to "protect" and "risk" files when deletions are occurring). Any
+ rule that has no modifier to specify what sides it is executed on will be
+ instead treated as if it were a server-side rule only, avoiding any
+ "protect" effects of the rules.
+
+ A rule can still apply to both sides even with this option specified if the
+ rule is given both the sender & receiver modifier letters (e.g., `-f'-sr
+ foo'`). Receiver-side protect/risk rules can also be explicitly specified
+ to limit the deletions. This saves you from having to edit a bunch of
+ `-f'- foo'` rules into `-f'-s foo'` (aka `-f'H foo'`) rules (not to mention
+ the corresponding includes).
+
+ See the [FILTER RULES](#) section for more information. See
+ [`--delete`](#opt) (which is implied) for more details on deletion.
+
+0. `--ignore-missing-args`
+
+ When rsync is first processing the explicitly requested source files (e.g.
+ command-line arguments or [`--files-from`](#opt) entries), it is normally
+ an error if the file cannot be found. This option suppresses that error,
+ and does not try to transfer the file. This does not affect subsequent
+ vanished-file errors if a file was initially found to be present and later
+ is no longer there.
+
+0. `--delete-missing-args`
+
+ This option takes the behavior of the (implied)
+ [`--ignore-missing-args`](#opt) option a step farther: each missing arg
+ will become a deletion request of the corresponding destination file on the
+ receiving side (should it exist). If the destination file is a non-empty
+ directory, it will only be successfully deleted if [`--force`](#opt) or
+ [`--delete`](#opt) are in effect. Other than that, this option is
+ independent of any other type of delete processing.
+
+ The missing source files are represented by special file-list entries which
+ display as a "`*missing`" entry in the [`--list-only`](#opt) output.
+
+0. `--ignore-errors`
+
+ Tells [`--delete`](#opt) to go ahead and delete files even when there are
+ I/O errors.
+
+0. `--force`
+
+ This option tells rsync to delete a non-empty directory when it is to be
+ replaced by a non-directory. This is only relevant if deletions are not
+ active (see [`--delete`](#opt) for details).
+
+ Note for older rsync versions: `--force` used to still be required when
+ using [`--delete-after`](#opt), and it used to be non-functional unless the
+ [`--recursive`](#opt) option was also enabled.
+
+0. `--max-delete=NUM`
+
+ This tells rsync not to delete more than NUM files or directories. If that
+ limit is exceeded, all further deletions are skipped through the end of the
+ transfer. At the end, rsync outputs a warning (including a count of the
+ skipped deletions) and exits with an error code of 25 (unless some more
+ important error condition also occurred).
+
+ Beginning with version 3.0.0, you may specify `--max-delete=0` to be warned
+ about any extraneous files in the destination without removing any of them.
+ Older clients interpreted this as "unlimited", so if you don't know what
+ version the client is, you can use the less obvious `--max-delete=-1` as a
+ backward-compatible way to specify that no deletions be allowed (though
+ really old versions didn't warn when the limit was exceeded).
+
+0. `--max-size=SIZE`
+
+ This tells rsync to avoid transferring any file that is larger than the
+ specified SIZE. A numeric value can be suffixed with a string to indicate
+ the numeric units or left unqualified to specify bytes. Feel free to use a
+ fractional value along with the units, such as `--max-size=1.5m`.
+
+ This option is a [TRANSFER RULE](#TRANSFER_RULES), so don't expect any
+ exclude side effects.
+
+ The first letter of a units string can be `B` (bytes), `K` (kilo), `M`
+ (mega), `G` (giga), `T` (tera), or `P` (peta). If the string is a single
+ char or has "ib" added to it (e.g. "G" or "GiB") then the units are
+ multiples of 1024. If you use a two-letter suffix that ends with a "B"
+ (e.g. "kb") then you get units that are multiples of 1000. The string's
+ letters can be any mix of upper and lower-case that you want to use.
+
+ Finally, if the string ends with either "+1" or "-1", it is offset by one
+ byte in the indicated direction. The largest possible value is usually
+ `8192P-1`.
+
+ Examples: `--max-size=1.5mb-1` is 1499999 bytes, and `--max-size=2g+1` is
+ 2147483649 bytes.
+
+ Note that rsync versions prior to 3.1.0 did not allow `--max-size=0`.
+
+0. `--min-size=SIZE`
+
+ This tells rsync to avoid transferring any file that is smaller than the
+ specified SIZE, which can help in not transferring small, junk files. See
+ the [`--max-size`](#opt) option for a description of SIZE and other info.
+
+ Note that rsync versions prior to 3.1.0 did not allow `--min-size=0`.
+
+0. `--max-alloc=SIZE`
+
+ By default rsync limits an individual malloc/realloc to about 1GB in size.
+ For most people this limit works just fine and prevents a protocol error
+ causing rsync to request massive amounts of memory. However, if you have
+ many millions of files in a transfer, a large amount of server memory, and
+ you don't want to split up your transfer into multiple parts, you can
+ increase the per-allocation limit to something larger and rsync will
+ consume more memory.
+
+ Keep in mind that this is not a limit on the total size of allocated
+ memory. It is a sanity-check value for each individual allocation.
+
+ See the [`--max-size`](#opt) option for a description of how SIZE can be
+ specified. The default suffix if none is given is bytes.
+
+ Beginning in 3.2.3, a value of 0 specifies no limit.
+
+ You can set a default value using the environment variable
+ [`RSYNC_MAX_ALLOC`](#) using the same SIZE values as supported by this
+ option. If the remote rsync doesn't understand the `--max-alloc` option,
+ you can override an environmental value by specifying `--max-alloc=1g`,
+ which will make rsync avoid sending the option to the remote side (because
+ "1G" is the default).
+
+0. `--block-size=SIZE`, `-B`
+
+ This forces the block size used in rsync's delta-transfer algorithm to a
+ fixed value. It is normally selected based on the size of each file being
+ updated. See the technical report for details.
+
+ Beginning in 3.2.3 the SIZE can be specified with a suffix as detailed in
+ the [`--max-size`](#opt) option. Older versions only accepted a byte count.
+
+0. `--rsh=COMMAND`, `-e`
+
+ This option allows you to choose an alternative remote shell program to use
+ for communication between the local and remote copies of rsync. Typically,
+ rsync is configured to use ssh by default, but you may prefer to use rsh on
+ a local network.
+
+ If this option is used with `[user@]host::module/path`, then the remote
+ shell _COMMAND_ will be used to run an rsync daemon on the remote host, and
+ all data will be transmitted through that remote shell connection, rather
+ than through a direct socket connection to a running rsync daemon on the
+ remote host. See the [USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL
+ CONNECTION](#) section above.
+
+ Beginning with rsync 3.2.0, the [`RSYNC_PORT`](#) environment variable will
+ be set when a daemon connection is being made via a remote-shell
+ connection. It is set to 0 if the default daemon port is being assumed, or
+ it is set to the value of the rsync port that was specified via either the
+ [`--port`](#opt) option or a non-empty port value in an `rsync://` URL.
+ This allows the script to discern if a non-default port is being requested,
+ allowing for things such as an SSL or stunnel helper script to connect to a
+ default or alternate port.
+
+ Command-line arguments are permitted in COMMAND provided that COMMAND is
+ presented to rsync as a single argument. You must use spaces (not tabs or
+ other whitespace) to separate the command and args from each other, and you
+ can use single- and/or double-quotes to preserve spaces in an argument (but
+ not backslashes). Note that doubling a single-quote inside a single-quoted
+ string gives you a single-quote; likewise for double-quotes (though you
+ need to pay attention to which quotes your shell is parsing and which
+ quotes rsync is parsing). Some examples:
+
+ > -e 'ssh -p 2234'
+ > -e 'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"'
+
+ (Note that ssh users can alternately customize site-specific connect
+ options in their .ssh/config file.)
+
+ You can also choose the remote shell program using the [`RSYNC_RSH`](#)
+ environment variable, which accepts the same range of values as `-e`.
+
+ See also the [`--blocking-io`](#opt) option which is affected by this
+ option.
+
+0. `--rsync-path=PROGRAM`
+
+ Use this to specify what program is to be run on the remote machine to
+ start-up rsync. Often used when rsync is not in the default remote-shell's
+ path (e.g. `--rsync-path=/usr/local/bin/rsync`). Note that PROGRAM is run
+ with the help of a shell, so it can be any program, script, or command
+ sequence you'd care to run, so long as it does not corrupt the standard-in
+ & standard-out that rsync is using to communicate.
+
+ One tricky example is to set a different default directory on the remote
+ machine for use with the [`--relative`](#opt) option. For instance:
+
+ > rsync -avR --rsync-path="cd /a/b && rsync" host:c/d /e/
+
+0. `--remote-option=OPTION`, `-M`
+
+ This option is used for more advanced situations where you want certain
+ effects to be limited to one side of the transfer only. For instance, if
+ you want to pass [`--log-file=FILE`](#opt) and [`--fake-super`](#opt) to
+ the remote system, specify it like this:
+
+ > rsync -av -M --log-file=foo -M--fake-super src/ dest/
+
+ If you want to have an option affect only the local side of a transfer when
+ it normally affects both sides, send its negation to the remote side. Like
+ this:
+
+ > rsync -av -x -M--no-x src/ dest/
+
+ Be cautious using this, as it is possible to toggle an option that will
+ cause rsync to have a different idea about what data to expect next over
+ the socket, and that will make it fail in a cryptic fashion.
+
+ Note that you should use a separate `-M` option for each remote option you
+ want to pass. On older rsync versions, the presence of any spaces in the
+ remote-option arg could cause it to be split into separate remote args, but
+ this requires the use of [`--old-args`](#opt) in a modern rsync.
+
+ When performing a local transfer, the "local" side is the sender and the
+ "remote" side is the receiver.
+
+ Note some versions of the popt option-parsing library have a bug in them
+ that prevents you from using an adjacent arg with an equal in it next to a
+ short option letter (e.g. `-M--log-file=/tmp/foo`). If this bug affects
+ your version of popt, you can use the version of popt that is included with
+ rsync.
+
+0. `--cvs-exclude`, `-C`
+
+ This is a useful shorthand for excluding a broad range of files that you
+ often don't want to transfer between systems. It uses a similar algorithm
+ to CVS to determine if a file should be ignored.
+
+ The exclude list is initialized to exclude the following items (these
+ initial items are marked as perishable -- see the [FILTER RULES](#)
+ section):
+
+ [comment]: # (This list gets used for the default-cvsignore.h file.)
+
+ > `RCS`
+ > `SCCS`
+ > `CVS`
+ > `CVS.adm`
+ > `RCSLOG`
+ > `cvslog.*`
+ > `tags`
+ > `TAGS`
+ > `.make.state`
+ > `.nse_depinfo`
+ > `*~`
+ > `#*`
+ > `.#*`
+ > `,*`
+ > `_$*`
+ > `*$`
+ > `*.old`
+ > `*.bak`
+ > `*.BAK`
+ > `*.orig`
+ > `*.rej`
+ > `.del-*`
+ > `*.a`
+ > `*.olb`
+ > `*.o`
+ > `*.obj`
+ > `*.so`
+ > `*.exe`
+ > `*.Z`
+ > `*.elc`
+ > `*.ln`
+ > `core`
+ > `.svn/`
+ > `.git/`
+ > `.hg/`
+ > `.bzr/`
+
+ then, files listed in a $HOME/.cvsignore are added to the list and any
+ files listed in the CVSIGNORE environment variable (all cvsignore names are
+ delimited by whitespace).
+
+ Finally, any file is ignored if it is in the same directory as a .cvsignore
+ file and matches one of the patterns listed therein. Unlike rsync's
+ filter/exclude files, these patterns are split on whitespace. See the
+ **cvs**(1) manual for more information.
+
+ If you're combining `-C` with your own [`--filter`](#opt) rules, you should
+ note that these CVS excludes are appended at the end of your own rules,
+ regardless of where the `-C` was placed on the command-line. This makes
+ them a lower priority than any rules you specified explicitly. If you want
+ to control where these CVS excludes get inserted into your filter rules,
+ you should omit the `-C` as a command-line option and use a combination of
+ [`--filter=:C`](#opt) and [`--filter=-C`](#opt) (either on your
+ command-line or by putting the ":C" and "-C" rules into a filter file with
+ your other rules). The first option turns on the per-directory scanning
+ for the .cvsignore file. The second option does a one-time import of the
+ CVS excludes mentioned above.
+
+0. `--filter=RULE`, `-f`
+
+ This option allows you to add rules to selectively exclude certain files
+ from the list of files to be transferred. This is most useful in
+ combination with a recursive transfer.
+
+ You may use as many `--filter` options on the command line as you like to
+ build up the list of files to exclude. If the filter contains whitespace,
+ be sure to quote it so that the shell gives the rule to rsync as a single
+ argument. The text below also mentions that you can use an underscore to
+ replace the space that separates a rule from its arg.
+
+ See the [FILTER RULES](#) section for detailed information on this option.
+
+0. `-F`
+
+ The `-F` option is a shorthand for adding two [`--filter`](#opt) rules to
+ your command. The first time it is used is a shorthand for this rule:
+
+ > --filter='dir-merge /.rsync-filter'
+
+ This tells rsync to look for per-directory .rsync-filter files that have
+ been sprinkled through the hierarchy and use their rules to filter the
+ files in the transfer. If `-F` is repeated, it is a shorthand for this
+ rule:
+
+ > --filter='exclude .rsync-filter'
+
+ This filters out the .rsync-filter files themselves from the transfer.
+
+ See the [FILTER RULES](#) section for detailed information on how these
+ options work.
+
+0. `--exclude=PATTERN`
+
+ This option is a simplified form of the [`--filter`](#opt) option that
+ specifies an exclude rule and does not allow the full rule-parsing syntax
+ of normal filter rules. This is equivalent to specifying `-f'- PATTERN'`.
+
+ See the [FILTER RULES](#) section for detailed information on this option.
+
+0. `--exclude-from=FILE`
+
+ This option is related to the [`--exclude`](#opt) option, but it specifies
+ a FILE that contains exclude patterns (one per line). Blank lines in the
+ file are ignored, as are whole-line comments that start with '`;`' or '`#`'
+ (filename rules that contain those characters are unaffected).
+
+ If a line begins with "`- `" (dash, space) or "`+ `" (plus, space), then
+ the type of rule is being explicitly specified as an exclude or an include
+ (respectively). Any rules without such a prefix are taken to be an exclude.
+
+ If a line consists of just "`!`", then the current filter rules are cleared
+ before adding any further rules.
+
+ If _FILE_ is '`-`', the list will be read from standard input.
+
+0. `--include=PATTERN`
+
+ This option is a simplified form of the [`--filter`](#opt) option that
+ specifies an include rule and does not allow the full rule-parsing syntax
+ of normal filter rules. This is equivalent to specifying `-f'+ PATTERN'`.
+
+ See the [FILTER RULES](#) section for detailed information on this option.
+
+0. `--include-from=FILE`
+
+ This option is related to the [`--include`](#opt) option, but it specifies
+ a FILE that contains include patterns (one per line). Blank lines in the
+ file are ignored, as are whole-line comments that start with '`;`' or '`#`'
+ (filename rules that contain those characters are unaffected).
+
+ If a line begins with "`- `" (dash, space) or "`+ `" (plus, space), then
+ the type of rule is being explicitly specified as an exclude or an include
+ (respectively). Any rules without such a prefix are taken to be an include.
+
+ If a line consists of just "`!`", then the current filter rules are cleared
+ before adding any further rules.
+
+ If _FILE_ is '`-`', the list will be read from standard input.
+
+0. `--files-from=FILE`
+
+ Using this option allows you to specify the exact list of files to transfer
+ (as read from the specified FILE or '`-`' for standard input). It also
+ tweaks the default behavior of rsync to make transferring just the
+ specified files and directories easier:
+
+ - The [`--relative`](#opt) (`-R`) option is implied, which preserves the
+ path information that is specified for each item in the file (use
+ `--no-relative` or `--no-R` if you want to turn that off).
+ - The [`--dirs`](#opt) (`-d`) option is implied, which will create
+ directories specified in the list on the destination rather than noisily
+ skipping them (use `--no-dirs` or `--no-d` if you want to turn that off).
+ - The [`--archive`](#opt) (`-a`) option's behavior does not imply
+ [`--recursive`](#opt) (`-r`), so specify it explicitly, if you want it.
+ - These side-effects change the default state of rsync, so the position of
+ the `--files-from` option on the command-line has no bearing on how other
+ options are parsed (e.g. [`-a`](#opt) works the same before or after
+ `--files-from`, as does `--no-R` and all other options).
+
+ The filenames that are read from the FILE are all relative to the source
+ dir -- any leading slashes are removed and no ".." references are allowed
+ to go higher than the source dir. For example, take this command:
+
+ > rsync -a --files-from=/tmp/foo /usr remote:/backup
+
+ If /tmp/foo contains the string "bin" (or even "/bin"), the /usr/bin
+ directory will be created as /backup/bin on the remote host. If it
+ contains "bin/" (note the trailing slash), the immediate contents of the
+ directory would also be sent (without needing to be explicitly mentioned in
+ the file -- this began in version 2.6.4). In both cases, if the
+ [`-r`](#opt) option was enabled, that dir's entire hierarchy would also be
+ transferred (keep in mind that [`-r`](#opt) needs to be specified
+ explicitly with `--files-from`, since it is not implied by [`-a`](#opt).
+ Also note that the effect of the (enabled by default) [`-r`](#opt) option
+ is to duplicate only the path info that is read from the file -- it does
+ not force the duplication of the source-spec path (/usr in this case).
+
+ In addition, the `--files-from` file can be read from the remote host
+ instead of the local host if you specify a "host:" in front of the file
+ (the host must match one end of the transfer). As a short-cut, you can
+ specify just a prefix of ":" to mean "use the remote end of the transfer".
+ For example:
+
+ > rsync -a --files-from=:/path/file-list src:/ /tmp/copy
+
+ This would copy all the files specified in the /path/file-list file that
+ was located on the remote "src" host.
+
+ If the [`--iconv`](#opt) and [`--secluded-args`](#opt) options are specified
+ and the `--files-from` filenames are being sent from one host to another,
+ the filenames will be translated from the sending host's charset to the
+ receiving host's charset.
+
+ NOTE: sorting the list of files in the `--files-from` input helps rsync to
+ be more efficient, as it will avoid re-visiting the path elements that are
+ shared between adjacent entries. If the input is not sorted, some path
+ elements (implied directories) may end up being scanned multiple times, and
+ rsync will eventually unduplicate them after they get turned into file-list
+ elements.
+
+0. `--from0`, `-0`
+
+ This tells rsync that the rules/filenames it reads from a file are
+ terminated by a null ('\\0') character, not a NL, CR, or CR+LF. This
+ affects [`--exclude-from`](#opt), [`--include-from`](#opt),
+ [`--files-from`](#opt), and any merged files specified in a
+ [`--filter`](#opt) rule. It does not affect [`--cvs-exclude`](#opt) (since
+ all names read from a .cvsignore file are split on whitespace).
+
+0. `--old-args`
+
+ This option tells rsync to stop trying to protect the arg values on the
+ remote side from unintended word-splitting or other misinterpretation.
+ It also allows the client to treat an empty arg as a "." instead of
+ generating an error.
+
+ The default in a modern rsync is for "shell-active" characters (including
+ spaces) to be backslash-escaped in the args that are sent to the remote
+ shell. The wildcard characters `*`, `?`, `[`, & `]` are not escaped in
+ filename args (allowing them to expand into multiple filenames) while being
+ protected in option args, such as [`--usermap`](#opt).
+
+ If you have a script that wants to use old-style arg splitting in its
+ filenames, specify this option once. If the remote shell has a problem
+ with any backslash escapes at all, specify this option twice.
+
+ You may also control this setting via the [`RSYNC_OLD_ARGS`](#) environment
+ variable. If it has the value "1", rsync will default to a single-option
+ setting. If it has the value "2" (or more), rsync will default to a
+ repeated-option setting. If it is "0", you'll get the default escaping
+ behavior. The environment is always overridden by manually specified
+ positive or negative options (the negative is `--no-old-args`).
+
+ Note that this option also disables the extra safety check added in 3.2.5
+ that ensures that a remote sender isn't including extra top-level items in
+ the file-list that you didn't request. This side-effect is necessary
+ because we can't know for sure what names to expect when the remote shell
+ is interpreting the args.
+
+ This option conflicts with the [`--secluded-args`](#opt) option.
+
+0. `--secluded-args`, `-s`
+
+ This option sends all filenames and most options to the remote rsync via
+ the protocol (not the remote shell command line) which avoids letting the
+ remote shell modify them. Wildcards are expanded on the remote host by
+ rsync instead of a shell.
+
+ This is similar to the default backslash-escaping of args that was added
+ in 3.2.4 (see [`--old-args`](#opt)) in that it prevents things like space
+ splitting and unwanted special-character side-effects. However, it has the
+ drawbacks of being incompatible with older rsync versions (prior to 3.0.0)
+ and of being refused by restricted shells that want to be able to inspect
+ all the option values for safety.
+
+ This option is useful for those times that you need the argument's
+ character set to be converted for the remote host, if the remote shell is
+ incompatible with the default backslash-escpaing method, or there is some
+ other reason that you want the majority of the options and arguments to
+ bypass the command-line of the remote shell.
+
+ If you combine this option with [`--iconv`](#opt), the args related to the
+ remote side will be translated from the local to the remote character-set.
+ The translation happens before wild-cards are expanded. See also the
+ [`--files-from`](#opt) option.
+
+ You may also control this setting via the [`RSYNC_PROTECT_ARGS`](#)
+ environment variable. If it has a non-zero value, this setting will be
+ enabled by default, otherwise it will be disabled by default. Either state
+ is overridden by a manually specified positive or negative version of this
+ option (note that `--no-s` and `--no-secluded-args` are the negative
+ versions). This environment variable is also superseded by a non-zero
+ [`RSYNC_OLD_ARGS`](#) export.
+
+ This option conflicts with the [`--old-args`](#opt) option.
+
+ This option used to be called `--protect-args` (before 3.2.6) and that
+ older name can still be used (though specifying it as `-s` is always the
+ easiest and most compatible choice).
+
+0. `--trust-sender`
+
+ This option disables two extra validation checks that a local client
+ performs on the file list generated by a remote sender. This option should
+ only be used if you trust the sender to not put something malicious in the
+ file list (something that could possibly be done via a modified rsync, a
+ modified shell, or some other similar manipulation).
+
+ Normally, the rsync client (as of version 3.2.5) runs two extra validation
+ checks when pulling files from a remote rsync:
+
+ - It verifies that additional arg items didn't get added at the top of the
+ transfer.
+ - It verifies that none of the items in the file list are names that should
+ have been excluded (if filter rules were specified).
+
+ Note that various options can turn off one or both of these checks if the
+ option interferes with the validation. For instance:
+
+ - Using a per-directory filter file reads filter rules that only the server
+ knows about, so the filter checking is disabled.
+ - Using the [`--old-args`](#opt) option allows the sender to manipulate the
+ requested args, so the arg checking is disabled.
+ - Reading the files-from list from the server side means that the client
+ doesn't know the arg list, so the arg checking is disabled.
+ - Using [`--read-batch`](#opt) disables both checks since the batch file's
+ contents will have been verified when it was created.
+
+ This option may help an under-powered client server if the extra pattern
+ matching is slowing things down on a huge transfer. It can also be used to
+ work around a currently-unknown bug in the verification logic for a transfer
+ from a trusted sender.
+
+ When using this option it is a good idea to specify a dedicated destination
+ directory, as discussed in the [MULTI-HOST SECURITY](#) section.
+
+0. `--copy-as=USER[:GROUP]`
+
+ This option instructs rsync to use the USER and (if specified after a
+ colon) the GROUP for the copy operations. This only works if the user that
+ is running rsync has the ability to change users. If the group is not
+ specified then the user's default groups are used.
+
+ This option can help to reduce the risk of an rsync being run as root into
+ or out of a directory that might have live changes happening to it and you
+ want to make sure that root-level read or write actions of system files are
+ not possible. While you could alternatively run all of rsync as the
+ specified user, sometimes you need the root-level host-access credentials
+ to be used, so this allows rsync to drop root for the copying part of the
+ operation after the remote-shell or daemon connection is established.
+
+ The option only affects one side of the transfer unless the transfer is
+ local, in which case it affects both sides. Use the
+ [`--remote-option`](#opt) to affect the remote side, such as
+ `-M--copy-as=joe`. For a local transfer, the lsh (or lsh.sh) support file
+ provides a local-shell helper script that can be used to allow a
+ "localhost:" or "lh:" host-spec to be specified without needing to setup
+ any remote shells, allowing you to specify remote options that affect the
+ side of the transfer that is using the host-spec (and using hostname "lh"
+ avoids the overriding of the remote directory to the user's home dir).
+
+ For example, the following rsync writes the local files as user "joe":
+
+ > sudo rsync -aiv --copy-as=joe host1:backups/joe/ /home/joe/
+
+ This makes all files owned by user "joe", limits the groups to those that
+ are available to that user, and makes it impossible for the joe user to do
+ a timed exploit of the path to induce a change to a file that the joe user
+ has no permissions to change.
+
+ The following command does a local copy into the "dest/" dir as user "joe"
+ (assuming you've installed support/lsh into a dir on your $PATH):
+
+ > sudo rsync -aive lsh -M--copy-as=joe src/ lh:dest/
+
+0. `--temp-dir=DIR`, `-T`
+
+ This option instructs rsync to use DIR as a scratch directory when creating
+ temporary copies of the files transferred on the receiving side. The
+ default behavior is to create each temporary file in the same directory as
+ the associated destination file. Beginning with rsync 3.1.1, the temp-file
+ names inside the specified DIR will not be prefixed with an extra dot
+ (though they will still have a random suffix added).
+
+ This option is most often used when the receiving disk partition does not
+ have enough free space to hold a copy of the largest file in the transfer.
+ In this case (i.e. when the scratch directory is on a different disk
+ partition), rsync will not be able to rename each received temporary file
+ over the top of the associated destination file, but instead must copy it
+ into place. Rsync does this by copying the file over the top of the
+ destination file, which means that the destination file will contain
+ truncated data during this copy. If this were not done this way (even if
+ the destination file were first removed, the data locally copied to a
+ temporary file in the destination directory, and then renamed into place)
+ it would be possible for the old file to continue taking up disk space (if
+ someone had it open), and thus there might not be enough room to fit the
+ new version on the disk at the same time.
+
+ If you are using this option for reasons other than a shortage of disk
+ space, you may wish to combine it with the [`--delay-updates`](#opt)
+ option, which will ensure that all copied files get put into subdirectories
+ in the destination hierarchy, awaiting the end of the transfer. If you
+ don't have enough room to duplicate all the arriving files on the
+ destination partition, another way to tell rsync that you aren't overly
+ concerned about disk space is to use the [`--partial-dir`](#opt) option
+ with a relative path; because this tells rsync that it is OK to stash off a
+ copy of a single file in a subdir in the destination hierarchy, rsync will
+ use the partial-dir as a staging area to bring over the copied file, and
+ then rename it into place from there. (Specifying a [`--partial-dir`](#opt)
+ with an absolute path does not have this side-effect.)
+
+0. `--fuzzy`, `-y`
+
+ This option tells rsync that it should look for a basis file for any
+ destination file that is missing. The current algorithm looks in the same
+ directory as the destination file for either a file that has an identical
+ size and modified-time, or a similarly-named file. If found, rsync uses
+ the fuzzy basis file to try to speed up the transfer.
+
+ If the option is repeated, the fuzzy scan will also be done in any matching
+ alternate destination directories that are specified via
+ [`--compare-dest`](#opt), [`--copy-dest`](#opt), or [`--link-dest`](#opt).
+
+ Note that the use of the [`--delete`](#opt) option might get rid of any
+ potential fuzzy-match files, so either use [`--delete-after`](#opt) or
+ specify some filename exclusions if you need to prevent this.
+
+0. `--compare-dest=DIR`
+
+ This option instructs rsync to use _DIR_ on the destination machine as an
+ additional hierarchy to compare destination files against doing transfers
+ (if the files are missing in the destination directory). If a file is
+ found in _DIR_ that is identical to the sender's file, the file will NOT be
+ transferred to the destination directory. This is useful for creating a
+ sparse backup of just files that have changed from an earlier backup. This
+ option is typically used to copy into an empty (or newly created)
+ directory.
+
+ Beginning in version 2.6.4, multiple `--compare-dest` directories may be
+ provided, which will cause rsync to search the list in the order specified
+ for an exact match. If a match is found that differs only in attributes, a
+ local copy is made and the attributes updated. If a match is not found, a
+ basis file from one of the _DIRs_ will be selected to try to speed up the
+ transfer.
+
+ If _DIR_ is a relative path, it is relative to the destination directory.
+ See also [`--copy-dest`](#opt) and [`--link-dest`](#opt).
+
+ NOTE: beginning with version 3.1.0, rsync will remove a file from a
+ non-empty destination hierarchy if an exact match is found in one of the
+ compare-dest hierarchies (making the end result more closely match a fresh
+ copy).
+
+0. `--copy-dest=DIR`
+
+ This option behaves like [`--compare-dest`](#opt), but rsync will also copy
+ unchanged files found in _DIR_ to the destination directory using a local
+ copy. This is useful for doing transfers to a new destination while
+ leaving existing files intact, and then doing a flash-cutover when all
+ files have been successfully transferred.
+
+ Multiple `--copy-dest` directories may be provided, which will cause rsync
+ to search the list in the order specified for an unchanged file. If a
+ match is not found, a basis file from one of the _DIRs_ will be selected to
+ try to speed up the transfer.
+
+ If _DIR_ is a relative path, it is relative to the destination directory.
+ See also [`--compare-dest`](#opt) and [`--link-dest`](#opt).
+
+0. `--link-dest=DIR`
+
+ This option behaves like [`--copy-dest`](#opt), but unchanged files are
+ hard linked from _DIR_ to the destination directory. The files must be
+ identical in all preserved attributes (e.g. permissions, possibly
+ ownership) in order for the files to be linked together. An example:
+
+ > rsync -av --link-dest=$PWD/prior_dir host:src_dir/ new_dir/
+
+ If files aren't linking, double-check their attributes. Also check if
+ some attributes are getting forced outside of rsync's control, such a mount
+ option that squishes root to a single user, or mounts a removable drive
+ with generic ownership (such as OS X's "Ignore ownership on this volume"
+ option).
+
+ Beginning in version 2.6.4, multiple `--link-dest` directories may be
+ provided, which will cause rsync to search the list in the order specified
+ for an exact match (there is a limit of 20 such directories). If a match
+ is found that differs only in attributes, a local copy is made and the
+ attributes updated. If a match is not found, a basis file from one of the
+ _DIRs_ will be selected to try to speed up the transfer.
+
+ This option works best when copying into an empty destination hierarchy, as
+ existing files may get their attributes tweaked, and that can affect
+ alternate destination files via hard-links. Also, itemizing of changes can
+ get a bit muddled. Note that prior to version 3.1.0, an
+ alternate-directory exact match would never be found (nor linked into the
+ destination) when a destination file already exists.
+
+ Note that if you combine this option with [`--ignore-times`](#opt), rsync will not
+ link any files together because it only links identical files together as a
+ substitute for transferring the file, never as an additional check after
+ the file is updated.
+
+ If _DIR_ is a relative path, it is relative to the destination directory.
+ See also [`--compare-dest`](#opt) and [`--copy-dest`](#opt).
+
+ Note that rsync versions prior to 2.6.1 had a bug that could prevent
+ `--link-dest` from working properly for a non-super-user when
+ [`--owner`](#opt) (`-o`) was specified (or implied). You can work-around
+ this bug by avoiding the `-o` option (or using `--no-o`) when sending to an
+ old rsync.
+
+0. `--compress`, `-z`
+
+ With this option, rsync compresses the file data as it is sent to the
+ destination machine, which reduces the amount of data being transmitted --
+ something that is useful over a slow connection.
+
+ Rsync supports multiple compression methods and will choose one for you
+ unless you force the choice using the [`--compress-choice`](#opt) (`--zc`)
+ option.
+
+ Run `rsync --version` to see the default compress list compiled into your
+ version.
+
+ When both sides of the transfer are at least 3.2.0, rsync chooses the first
+ algorithm in the client's list of choices that is also in the server's list
+ of choices. If no common compress choice is found, rsync exits with
+ an error. If the remote rsync is too old to support checksum negotiation,
+ its list is assumed to be "zlib".
+
+ The default order can be customized by setting the environment variable
+ [`RSYNC_COMPRESS_LIST`](#) to a space-separated list of acceptable
+ compression names. If the string contains a "`&`" character, it is
+ separated into the "client string & server string", otherwise the same
+ string applies to both. If the string (or string portion) contains no
+ non-whitespace characters, the default compress list is used. Any unknown
+ compression names are discarded from the list, but a list with only invalid
+ names results in a failed negotiation.
+
+ There are some older rsync versions that were configured to reject a `-z`
+ option and require the use of `-zz` because their compression library was
+ not compatible with the default zlib compression method. You can usually
+ ignore this weirdness unless the rsync server complains and tells you to
+ specify `-zz`.
+
+0. `--compress-choice=STR`, `--zc=STR`
+
+ This option can be used to override the automatic negotiation of the
+ compression algorithm that occurs when [`--compress`](#opt) is used. The
+ option implies [`--compress`](#opt) unless "none" was specified, which
+ instead implies `--no-compress`.
+
+ The compression options that you may be able to use are:
+
+ - `zstd`
+ - `lz4`
+ - `zlibx`
+ - `zlib`
+ - `none`
+
+ Run `rsync --version` to see the default compress list compiled into your
+ version (which may differ from the list above).
+
+ Note that if you see an error about an option named `--old-compress` or
+ `--new-compress`, this is rsync trying to send the `--compress-choice=zlib`
+ or `--compress-choice=zlibx` option in a backward-compatible manner that
+ more rsync versions understand. This error indicates that the older rsync
+ version on the server will not allow you to force the compression type.
+
+ Note that the "zlibx" compression algorithm is just the "zlib" algorithm
+ with matched data excluded from the compression stream (to try to make it
+ more compatible with an external zlib implementation).
+
+0. `--compress-level=NUM`, `--zl=NUM`
+
+ Explicitly set the compression level to use (see [`--compress`](#opt),
+ `-z`) instead of letting it default. The [`--compress`](#opt) option is
+ implied as long as the level chosen is not a "don't compress" level for the
+ compression algorithm that is in effect (e.g. zlib compression treats level
+ 0 as "off").
+
+ The level values vary depending on the checksum in effect. Because rsync
+ will negotiate a checksum choice by default (when the remote rsync is new
+ enough), it can be good to combine this option with a
+ [`--compress-choice`](#opt) (`--zc`) option unless you're sure of the
+ choice in effect. For example:
+
+ > rsync -aiv --zc=zstd --zl=22 host:src/ dest/
+
+ For zlib & zlibx compression the valid values are from 1 to 9 with 6 being
+ the default. Specifying `--zl=0` turns compression off, and specifying
+ `--zl=-1` chooses the default level of 6.
+
+ For zstd compression the valid values are from -131072 to 22 with 3 being
+ the default. Specifying 0 chooses the default of 3.
+
+ For lz4 compression there are no levels, so the value is always 0.
+
+ If you specify a too-large or too-small value, the number is silently
+ limited to a valid value. This allows you to specify something like
+ `--zl=999999999` and be assured that you'll end up with the maximum
+ compression level no matter what algorithm was chosen.
+
+ If you want to know the compression level that is in effect, specify
+ [`--debug=nstr`](#opt) to see the "negotiated string" results. This will
+ report something like "`Client compress: zstd (level 3)`" (along with the
+ checksum choice in effect).
+
+0. `--skip-compress=LIST`
+
+ **NOTE:** no compression method currently supports per-file compression
+ changes, so this option has no effect.
+
+ Override the list of file suffixes that will be compressed as little as
+ possible. Rsync sets the compression level on a per-file basis based on
+ the file's suffix. If the compression algorithm has an "off" level, then
+ no compression occurs for those files. Other algorithms that support
+ changing the streaming level on-the-fly will have the level minimized to
+ reduces the CPU usage as much as possible for a matching file.
+
+ The **LIST** should be one or more file suffixes (without the dot) separated
+ by slashes (`/`). You may specify an empty string to indicate that no files
+ should be skipped.
+
+ Simple character-class matching is supported: each must consist of a list
+ of letters inside the square brackets (e.g. no special classes, such as
+ "[:alpha:]", are supported, and '-' has no special meaning).
+
+ The characters asterisk (`*`) and question-mark (`?`) have no special meaning.
+
+ Here's an example that specifies 6 suffixes to skip (since 1 of the 5 rules
+ matches 2 suffixes):
+
+ > --skip-compress=gz/jpg/mp[34]/7z/bz2
+
+ The default file suffixes in the skip-compress list in this version of
+ rsync are:
+
+ [comment]: # (This list gets used for the default-dont-compress.h file.)
+
+ > 3g2
+ > 3gp
+ > 7z
+ > aac
+ > ace
+ > apk
+ > avi
+ > bz2
+ > deb
+ > dmg
+ > ear
+ > f4v
+ > flac
+ > flv
+ > gpg
+ > gz
+ > iso
+ > jar
+ > jpeg
+ > jpg
+ > lrz
+ > lz
+ > lz4
+ > lzma
+ > lzo
+ > m1a
+ > m1v
+ > m2a
+ > m2ts
+ > m2v
+ > m4a
+ > m4b
+ > m4p
+ > m4r
+ > m4v
+ > mka
+ > mkv
+ > mov
+ > mp1
+ > mp2
+ > mp3
+ > mp4
+ > mpa
+ > mpeg
+ > mpg
+ > mpv
+ > mts
+ > odb
+ > odf
+ > odg
+ > odi
+ > odm
+ > odp
+ > ods
+ > odt
+ > oga
+ > ogg
+ > ogm
+ > ogv
+ > ogx
+ > opus
+ > otg
+ > oth
+ > otp
+ > ots
+ > ott
+ > oxt
+ > png
+ > qt
+ > rar
+ > rpm
+ > rz
+ > rzip
+ > spx
+ > squashfs
+ > sxc
+ > sxd
+ > sxg
+ > sxm
+ > sxw
+ > sz
+ > tbz
+ > tbz2
+ > tgz
+ > tlz
+ > ts
+ > txz
+ > tzo
+ > vob
+ > war
+ > webm
+ > webp
+ > xz
+ > z
+ > zip
+ > zst
+
+ This list will be replaced by your `--skip-compress` list in all but one
+ situation: a copy from a daemon rsync will add your skipped suffixes to its
+ list of non-compressing files (and its list may be configured to a
+ different default).
+
+0. `--numeric-ids`
+
+ With this option rsync will transfer numeric group and user IDs rather than
+ using user and group names and mapping them at both ends.
+
+ By default rsync will use the username and groupname to determine what
+ ownership to give files. The special uid 0 and the special group 0 are
+ never mapped via user/group names even if the `--numeric-ids` option is not
+ specified.
+
+ If a user or group has no name on the source system or it has no match on
+ the destination system, then the numeric ID from the source system is used
+ instead. See also the [`use chroot`](rsyncd.conf.5#use_chroot) setting
+ in the rsyncd.conf manpage for some comments on how the chroot setting
+ affects rsync's ability to look up the names of the users and groups and
+ what you can do about it.
+
+0. `--usermap=STRING`, `--groupmap=STRING`
+
+ These options allow you to specify users and groups that should be mapped
+ to other values by the receiving side. The **STRING** is one or more
+ **FROM**:**TO** pairs of values separated by commas. Any matching **FROM**
+ value from the sender is replaced with a **TO** value from the receiver.
+ You may specify usernames or user IDs for the **FROM** and **TO** values,
+ and the **FROM** value may also be a wild-card string, which will be
+ matched against the sender's names (wild-cards do NOT match against ID
+ numbers, though see below for why a '`*`' matches everything). You may
+ instead specify a range of ID numbers via an inclusive range: LOW-HIGH.
+ For example:
+
+ > --usermap=0-99:nobody,wayne:admin,*:normal --groupmap=usr:1,1:usr
+
+ The first match in the list is the one that is used. You should specify
+ all your user mappings using a single `--usermap` option, and/or all your
+ group mappings using a single `--groupmap` option.
+
+ Note that the sender's name for the 0 user and group are not transmitted to
+ the receiver, so you should either match these values using a 0, or use the
+ names in effect on the receiving side (typically "root"). All other
+ **FROM** names match those in use on the sending side. All **TO** names
+ match those in use on the receiving side.
+
+ Any IDs that do not have a name on the sending side are treated as having
+ an empty name for the purpose of matching. This allows them to be matched
+ via a "`*`" or using an empty name. For instance:
+
+ > --usermap=:nobody --groupmap=*:nobody
+
+ When the [`--numeric-ids`](#opt) option is used, the sender does not send any
+ names, so all the IDs are treated as having an empty name. This means that
+ you will need to specify numeric **FROM** values if you want to map these
+ nameless IDs to different values.
+
+ For the `--usermap` option to work, the receiver will need to be running as
+ a super-user (see also the [`--super`](#opt) and [`--fake-super`](#opt)
+ options). For the `--groupmap` option to work, the receiver will need to
+ have permissions to set that group.
+
+ Starting with rsync 3.2.4, the `--usermap` option implies the
+ [`--owner`](#opt) (`-o`) option while the `--groupmap` option implies the
+ [`--group`](#opt) (`-g`) option (since rsync needs to have those options
+ enabled for the mapping options to work).
+
+ An older rsync client may need to use [`-s`](#opt) to avoid a complaint
+ about wildcard characters, but a modern rsync handles this automatically.
+
+0. `--chown=USER:GROUP`
+
+ This option forces all files to be owned by USER with group GROUP. This is
+ a simpler interface than using [`--usermap`](#opt) & [`--groupmap`](#opt)
+ directly, but it is implemented using those options internally so they
+ cannot be mixed. If either the USER or GROUP is empty, no mapping for the
+ omitted user/group will occur. If GROUP is empty, the trailing colon may
+ be omitted, but if USER is empty, a leading colon must be supplied.
+
+ If you specify "`--chown=foo:bar`", this is exactly the same as specifying
+ "`--usermap=*:foo --groupmap=*:bar`", only easier (and with the same
+ implied [`--owner`](#opt) and/or [`--group`](#opt) options).
+
+ An older rsync client may need to use [`-s`](#opt) to avoid a complaint
+ about wildcard characters, but a modern rsync handles this automatically.
+
+0. `--timeout=SECONDS`
+
+ This option allows you to set a maximum I/O timeout in seconds. If no data
+ is transferred for the specified time then rsync will exit. The default is
+ 0, which means no timeout.
+
+0. `--contimeout=SECONDS`
+
+ This option allows you to set the amount of time that rsync will wait for
+ its connection to an rsync daemon to succeed. If the timeout is reached,
+ rsync exits with an error.
+
+0. `--address=ADDRESS`
+
+ By default rsync will bind to the wildcard address when connecting to an
+ rsync daemon. The `--address` option allows you to specify a specific IP
+ address (or hostname) to bind to.
+
+ See also [the daemon version of the `--address` option](#dopt--address).
+
+0. `--port=PORT`
+
+ This specifies an alternate TCP port number to use rather than the default
+ of 873. This is only needed if you are using the double-colon (::) syntax
+ to connect with an rsync daemon (since the URL syntax has a way to specify
+ the port as a part of the URL).
+
+ See also [the daemon version of the `--port` option](#dopt--port).
+
+0. `--sockopts=OPTIONS`
+
+ This option can provide endless fun for people who like to tune their
+ systems to the utmost degree. You can set all sorts of socket options
+ which may make transfers faster (or slower!). Read the manpage for the
+ `setsockopt()` system call for details on some of the options you may be
+ able to set. By default no special socket options are set. This only
+ affects direct socket connections to a remote rsync daemon.
+
+ See also [the daemon version of the `--sockopts` option](#dopt--sockopts).
+
+0. `--blocking-io`
+
+ This tells rsync to use blocking I/O when launching a remote shell
+ transport. If the remote shell is either rsh or remsh, rsync defaults to
+ using blocking I/O, otherwise it defaults to using non-blocking I/O. (Note
+ that ssh prefers non-blocking I/O.)
+
+0. `--outbuf=MODE`
+
+ This sets the output buffering mode. The mode can be None (aka
+ Unbuffered), Line, or Block (aka Full). You may specify as little as a
+ single letter for the mode, and use upper or lower case.
+
+ The main use of this option is to change Full buffering to Line buffering
+ when rsync's output is going to a file or pipe.
+
+0. `--itemize-changes`, `-i`
+
+ Requests a simple itemized list of the changes that are being made to each
+ file, including attribute changes. This is exactly the same as specifying
+ [`--out-format='%i %n%L'`](#opt). If you repeat the option, unchanged
+ files will also be output, but only if the receiving rsync is at least
+ version 2.6.7 (you can use `-vv` with older versions of rsync, but that
+ also turns on the output of other verbose messages).
+
+ The "%i" escape has a cryptic output that is 11 letters long. The general
+ format is like the string `YXcstpoguax`, where **Y** is replaced by the type
+ of update being done, **X** is replaced by the file-type, and the other
+ letters represent attributes that may be output if they are being modified.
+
+ The update types that replace the **Y** are as follows:
+
+ - A `<` means that a file is being transferred to the remote host (sent).
+ - A `>` means that a file is being transferred to the local host
+ (received).
+ - A `c` means that a local change/creation is occurring for the item (such
+ as the creation of a directory or the changing of a symlink, etc.).
+ - A `h` means that the item is a hard link to another item (requires
+ [`--hard-links`](#opt)).
+ - A `.` means that the item is not being updated (though it might have
+ attributes that are being modified).
+ - A `*` means that the rest of the itemized-output area contains a message
+ (e.g. "deleting").
+
+ The file-types that replace the **X** are: `f` for a file, a `d` for a
+ directory, an `L` for a symlink, a `D` for a device, and a `S` for a
+ special file (e.g. named sockets and fifos).
+
+ The other letters in the string indicate if some attributes of the file
+ have changed, as follows:
+
+ - "`.`" - the attribute is unchanged.
+ - "`+`" - the file is newly created.
+ - "` `" - all the attributes are unchanged (all dots turn to spaces).
+ - "`?`" - the change is unknown (when the remote rsync is old).
+ - A letter indicates an attribute is being updated.
+
+ The attribute that is associated with each letter is as follows:
+
+ - A `c` means either that a regular file has a different checksum (requires
+ [`--checksum`](#opt)) or that a symlink, device, or special file has a
+ changed value. Note that if you are sending files to an rsync prior to
+ 3.0.1, this change flag will be present only for checksum-differing
+ regular files.
+ - A `s` means the size of a regular file is different and will be updated
+ by the file transfer.
+ - A `t` means the modification time is different and is being updated to
+ the sender's value (requires [`--times`](#opt)). An alternate value of
+ `T` means that the modification time will be set to the transfer time,
+ which happens when a file/symlink/device is updated without
+ [`--times`](#opt) and when a symlink is changed and the receiver can't
+ set its time. (Note: when using an rsync 3.0.0 client, you might see the
+ `s` flag combined with `t` instead of the proper `T` flag for this
+ time-setting failure.)
+ - A `p` means the permissions are different and are being updated to the
+ sender's value (requires [`--perms`](#opt)).
+ - An `o` means the owner is different and is being updated to the sender's
+ value (requires [`--owner`](#opt) and super-user privileges).
+ - A `g` means the group is different and is being updated to the sender's
+ value (requires [`--group`](#opt) and the authority to set the group).
+ - A `u`|`n`|`b` indicates the following information:
+ - `u` means the access (use) time is different and is being updated to
+ the sender's value (requires [`--atimes`](#opt))
+ - `n` means the create time (newness) is different and is being updated
+ to the sender's value (requires [`--crtimes`](#opt))
+ - `b` means that both the access and create times are being updated
+ - The `a` means that the ACL information is being changed.
+ - The `x` means that the extended attribute information is being changed.
+
+ One other output is possible: when deleting files, the "%i" will output the
+ string "`*deleting`" for each item that is being removed (assuming that you
+ are talking to a recent enough rsync that it logs deletions instead of
+ outputting them as a verbose message).
+
+0. `--out-format=FORMAT`
+
+ This allows you to specify exactly what the rsync client outputs to the
+ user on a per-update basis. The format is a text string containing
+ embedded single-character escape sequences prefixed with a percent (%)
+ character. A default format of "%n%L" is assumed if either
+ [`--info=name`](#opt) or [`-v`](#opt) is specified (this tells you just the
+ name of the file and, if the item is a link, where it points). For a full
+ list of the possible escape characters, see the [`log
+ format`](rsyncd.conf.5#log_format) setting in the rsyncd.conf manpage.
+
+ Specifying the `--out-format` option implies the [`--info=name`](#opt)
+ option, which will mention each file, dir, etc. that gets updated in a
+ significant way (a transferred file, a recreated symlink/device, or a
+ touched directory). In addition, if the itemize-changes escape (%i) is
+ included in the string (e.g. if the [`--itemize-changes`](#opt) option was
+ used), the logging of names increases to mention any item that is changed
+ in any way (as long as the receiving side is at least 2.6.4). See the
+ [`--itemize-changes`](#opt) option for a description of the output of "%i".
+
+ Rsync will output the out-format string prior to a file's transfer unless
+ one of the transfer-statistic escapes is requested, in which case the
+ logging is done at the end of the file's transfer. When this late logging
+ is in effect and [`--progress`](#opt) is also specified, rsync will also
+ output the name of the file being transferred prior to its progress
+ information (followed, of course, by the out-format output).
+
+0. `--log-file=FILE`
+
+ This option causes rsync to log what it is doing to a file. This is
+ similar to the logging that a daemon does, but can be requested for the
+ client side and/or the server side of a non-daemon transfer. If specified
+ as a client option, transfer logging will be enabled with a default format
+ of "%i %n%L". See the [`--log-file-format`](#opt) option if you wish to
+ override this.
+
+ Here's an example command that requests the remote side to log what is
+ happening:
+
+ > rsync -av --remote-option=--log-file=/tmp/rlog src/ dest/
+
+ This is very useful if you need to debug why a connection is closing
+ unexpectedly.
+
+ See also [the daemon version of the `--log-file` option](#dopt--log-file).
+
+0. `--log-file-format=FORMAT`
+
+ This allows you to specify exactly what per-update logging is put into the
+ file specified by the [`--log-file`](#opt) option (which must also be
+ specified for this option to have any effect). If you specify an empty
+ string, updated files will not be mentioned in the log file. For a list of
+ the possible escape characters, see the [`log format`](rsyncd.conf.5#log_format)
+ setting in the rsyncd.conf manpage.
+
+ The default FORMAT used if [`--log-file`](#opt) is specified and this
+ option is not is '%i %n%L'.
+
+ See also [the daemon version of the `--log-file-format`
+ option](#dopt--log-file-format).
+
+0. `--stats`
+
+ This tells rsync to print a verbose set of statistics on the file transfer,
+ allowing you to tell how effective rsync's delta-transfer algorithm is for
+ your data. This option is equivalent to [`--info=stats2`](#opt) if
+ combined with 0 or 1 [`-v`](#opt) options, or [`--info=stats3`](#opt) if
+ combined with 2 or more [`-v`](#opt) options.
+
+ The current statistics are as follows:
+
+ - `Number of files` is the count of all "files" (in the generic sense),
+ which includes directories, symlinks, etc. The total count will be
+ followed by a list of counts by filetype (if the total is non-zero). For
+ example: "(reg: 5, dir: 3, link: 2, dev: 1, special: 1)" lists the totals
+ for regular files, directories, symlinks, devices, and special files. If
+ any of value is 0, it is completely omitted from the list.
+ - `Number of created files` is the count of how many "files" (generic
+ sense) were created (as opposed to updated). The total count will be
+ followed by a list of counts by filetype (if the total is non-zero).
+ - `Number of deleted files` is the count of how many "files" (generic
+ sense) were deleted. The total count will be
+ followed by a list of counts by filetype (if the total is non-zero).
+ Note that this line is only output if deletions are in effect, and only
+ if protocol 31 is being used (the default for rsync 3.1.x).
+ - `Number of regular files transferred` is the count of normal files that
+ were updated via rsync's delta-transfer algorithm, which does not include
+ dirs, symlinks, etc. Note that rsync 3.1.0 added the word "regular" into
+ this heading.
+ - `Total file size` is the total sum of all file sizes in the transfer.
+ This does not count any size for directories or special files, but does
+ include the size of symlinks.
+ - `Total transferred file size` is the total sum of all files sizes for
+ just the transferred files.
+ - `Literal data` is how much unmatched file-update data we had to send to
+ the receiver for it to recreate the updated files.
+ - `Matched data` is how much data the receiver got locally when recreating
+ the updated files.
+ - `File list size` is how big the file-list data was when the sender sent
+ it to the receiver. This is smaller than the in-memory size for the file
+ list due to some compressing of duplicated data when rsync sends the
+ list.
+ - `File list generation time` is the number of seconds that the sender
+ spent creating the file list. This requires a modern rsync on the
+ sending side for this to be present.
+ - `File list transfer time` is the number of seconds that the sender spent
+ sending the file list to the receiver.
+ - `Total bytes sent` is the count of all the bytes that rsync sent from the
+ client side to the server side.
+ - `Total bytes received` is the count of all non-message bytes that rsync
+ received by the client side from the server side. "Non-message" bytes
+ means that we don't count the bytes for a verbose message that the server
+ sent to us, which makes the stats more consistent.
+
+0. `--8-bit-output`, `-8`
+
+ This tells rsync to leave all high-bit characters unescaped in the output
+ instead of trying to test them to see if they're valid in the current
+ locale and escaping the invalid ones. All control characters (but never
+ tabs) are always escaped, regardless of this option's setting.
+
+ The escape idiom that started in 2.6.7 is to output a literal backslash
+ (`\`) and a hash (`#`), followed by exactly 3 octal digits. For example, a
+ newline would output as "`\#012`". A literal backslash that is in a
+ filename is not escaped unless it is followed by a hash and 3 digits (0-9).
+
+0. `--human-readable`, `-h`
+
+ Output numbers in a more human-readable format. There are 3 possible levels:
+
+ 1. output numbers with a separator between each set of 3 digits (either a
+ comma or a period, depending on if the decimal point is represented by a
+ period or a comma).
+ 2. output numbers in units of 1000 (with a character suffix for larger
+ units -- see below).
+ 3. output numbers in units of 1024.
+
+ The default is human-readable level 1. Each `-h` option increases the
+ level by one. You can take the level down to 0 (to output numbers as pure
+ digits) by specifying the `--no-human-readable` (`--no-h`) option.
+
+ The unit letters that are appended in levels 2 and 3 are: `K` (kilo), `M`
+ (mega), `G` (giga), `T` (tera), or `P` (peta). For example, a 1234567-byte
+ file would output as 1.23M in level-2 (assuming that a period is your local
+ decimal point).
+
+ Backward compatibility note: versions of rsync prior to 3.1.0 do not
+ support human-readable level 1, and they default to level 0. Thus,
+ specifying one or two `-h` options will behave in a comparable manner in
+ old and new versions as long as you didn't specify a `--no-h` option prior
+ to one or more `-h` options. See the [`--list-only`](#opt) option for one
+ difference.
+
+0. `--partial`
+
+ By default, rsync will delete any partially transferred file if the
+ transfer is interrupted. In some circumstances it is more desirable to
+ keep partially transferred files. Using the `--partial` option tells rsync
+ to keep the partial file which should make a subsequent transfer of the
+ rest of the file much faster.
+
+0. `--partial-dir=DIR`
+
+ This option modifies the behavior of the [`--partial`](#opt) option while
+ also implying that it be enabled. This enhanced partial-file method puts
+ any partially transferred files into the specified _DIR_ instead of writing
+ the partial file out to the destination file. On the next transfer, rsync
+ will use a file found in this dir as data to speed up the resumption of the
+ transfer and then delete it after it has served its purpose.
+
+ Note that if [`--whole-file`](#opt) is specified (or implied), any
+ partial-dir files that are found for a file that is being updated will
+ simply be removed (since rsync is sending files without using rsync's
+ delta-transfer algorithm).
+
+ Rsync will create the _DIR_ if it is missing, but just the last dir -- not
+ the whole path. This makes it easy to use a relative path (such as
+ "`--partial-dir=.rsync-partial`") to have rsync create the
+ partial-directory in the destination file's directory when it is needed,
+ and then remove it again when the partial file is deleted. Note that this
+ directory removal is only done for a relative pathname, as it is expected
+ that an absolute path is to a directory that is reserved for partial-dir
+ work.
+
+ If the partial-dir value is not an absolute path, rsync will add an exclude
+ rule at the end of all your existing excludes. This will prevent the
+ sending of any partial-dir files that may exist on the sending side, and
+ will also prevent the untimely deletion of partial-dir items on the
+ receiving side. An example: the above `--partial-dir` option would add the
+ equivalent of this "perishable" exclude at the end of any other filter
+ rules: `-f '-p .rsync-partial/'`
+
+ If you are supplying your own exclude rules, you may need to add your own
+ exclude/hide/protect rule for the partial-dir because:
+
+ 1. the auto-added rule may be ineffective at the end of your other rules, or
+ 2. you may wish to override rsync's exclude choice.
+
+ For instance, if you want to make rsync clean-up any left-over partial-dirs
+ that may be lying around, you should specify [`--delete-after`](#opt) and
+ add a "risk" filter rule, e.g. `-f 'R .rsync-partial/'`. Avoid using
+ [`--delete-before`](#opt) or [`--delete-during`](#opt) unless you don't
+ need rsync to use any of the left-over partial-dir data during the current
+ run.
+
+ IMPORTANT: the `--partial-dir` should not be writable by other users or it
+ is a security risk! E.g. AVOID "/tmp"!
+
+ You can also set the partial-dir value the [`RSYNC_PARTIAL_DIR`](#)
+ environment variable. Setting this in the environment does not force
+ [`--partial`](#opt) to be enabled, but rather it affects where partial
+ files go when [`--partial`](#opt) is specified. For instance, instead of
+ using `--partial-dir=.rsync-tmp` along with [`--progress`](#opt), you could
+ set [`RSYNC_PARTIAL_DIR=.rsync-tmp`](#) in your environment and then use
+ the [`-P`](#opt) option to turn on the use of the .rsync-tmp dir for
+ partial transfers. The only times that the [`--partial`](#opt) option does
+ not look for this environment value are:
+
+ 1. when [`--inplace`](#opt) was specified (since [`--inplace`](#opt)
+ conflicts with `--partial-dir`), and
+ 2. when [`--delay-updates`](#opt) was specified (see below).
+
+ When a modern rsync resumes the transfer of a file in the partial-dir, that
+ partial file is now updated in-place instead of creating yet another
+ tmp-file copy (so it maxes out at dest + tmp instead of dest + partial +
+ tmp). This requires both ends of the transfer to be at least version
+ 3.2.0.
+
+ For the purposes of the daemon-config's "`refuse options`" setting,
+ `--partial-dir` does _not_ imply [`--partial`](#opt). This is so that a
+ refusal of the [`--partial`](#opt) option can be used to disallow the
+ overwriting of destination files with a partial transfer, while still
+ allowing the safer idiom provided by `--partial-dir`.
+
+0. `--delay-updates`
+
+ This option puts the temporary file from each updated file into a holding
+ directory until the end of the transfer, at which time all the files are
+ renamed into place in rapid succession. This attempts to make the updating
+ of the files a little more atomic. By default the files are placed into a
+ directory named `.~tmp~` in each file's destination directory, but if
+ you've specified the [`--partial-dir`](#opt) option, that directory will be
+ used instead. See the comments in the [`--partial-dir`](#opt) section for
+ a discussion of how this `.~tmp~` dir will be excluded from the transfer,
+ and what you can do if you want rsync to cleanup old `.~tmp~` dirs that
+ might be lying around. Conflicts with [`--inplace`](#opt) and
+ [`--append`](#opt).
+
+ This option implies [`--no-inc-recursive`](#opt) since it needs the full
+ file list in memory in order to be able to iterate over it at the end.
+
+ This option uses more memory on the receiving side (one bit per file
+ transferred) and also requires enough free disk space on the receiving side
+ to hold an additional copy of all the updated files. Note also that you
+ should not use an absolute path to [`--partial-dir`](#opt) unless:
+
+ 1. there is no chance of any of the files in the transfer having the same
+ name (since all the updated files will be put into a single directory if
+ the path is absolute), and
+ 2. there are no mount points in the hierarchy (since the delayed updates
+ will fail if they can't be renamed into place).
+
+ See also the "atomic-rsync" python script in the "support" subdir for an
+ update algorithm that is even more atomic (it uses [`--link-dest`](#opt)
+ and a parallel hierarchy of files).
+
+0. `--prune-empty-dirs`, `-m`
+
+ This option tells the receiving rsync to get rid of empty directories from
+ the file-list, including nested directories that have no non-directory
+ children. This is useful for avoiding the creation of a bunch of useless
+ directories when the sending rsync is recursively scanning a hierarchy of
+ files using include/exclude/filter rules.
+
+ This option can still leave empty directories on the receiving side if you
+ make use of [TRANSFER_RULES](#).
+
+ Because the file-list is actually being pruned, this option also affects
+ what directories get deleted when a delete is active. However, keep in
+ mind that excluded files and directories can prevent existing items from
+ being deleted due to an exclude both hiding source files and protecting
+ destination files. See the perishable filter-rule option for how to avoid
+ this.
+
+ You can prevent the pruning of certain empty directories from the file-list
+ by using a global "protect" filter. For instance, this option would ensure
+ that the directory "emptydir" was kept in the file-list:
+
+ > --filter 'protect emptydir/'
+
+ Here's an example that copies all .pdf files in a hierarchy, only creating
+ the necessary destination directories to hold the .pdf files, and ensures
+ that any superfluous files and directories in the destination are removed
+ (note the hide filter of non-directories being used instead of an exclude):
+
+ > rsync -avm --del --include='*.pdf' -f 'hide,! */' src/ dest
+
+ If you didn't want to remove superfluous destination files, the more
+ time-honored options of `--include='*/' --exclude='*'` would work
+ fine in place of the hide-filter (if that is more natural to you).
+
+0. `--progress`
+
+ This option tells rsync to print information showing the progress of the
+ transfer. This gives a bored user something to watch. With a modern rsync
+ this is the same as specifying [`--info=flist2,name,progress`](#opt), but
+ any user-supplied settings for those info flags takes precedence (e.g.
+ [`--info=flist0 --progress`](#opt)).
+
+ While rsync is transferring a regular file, it updates a progress line that
+ looks like this:
+
+ > 782448 63% 110.64kB/s 0:00:04
+
+ In this example, the receiver has reconstructed 782448 bytes or 63% of the
+ sender's file, which is being reconstructed at a rate of 110.64 kilobytes
+ per second, and the transfer will finish in 4 seconds if the current rate
+ is maintained until the end.
+
+ These statistics can be misleading if rsync's delta-transfer algorithm is
+ in use. For example, if the sender's file consists of the basis file
+ followed by additional data, the reported rate will probably drop
+ dramatically when the receiver gets to the literal data, and the transfer
+ will probably take much longer to finish than the receiver estimated as it
+ was finishing the matched part of the file.
+
+ When the file transfer finishes, rsync replaces the progress line with a
+ summary line that looks like this:
+
+ > 1,238,099 100% 146.38kB/s 0:00:08 (xfr#5, to-chk=169/396)
+
+ In this example, the file was 1,238,099 bytes long in total, the average
+ rate of transfer for the whole file was 146.38 kilobytes per second over
+ the 8 seconds that it took to complete, it was the 5th transfer of a
+ regular file during the current rsync session, and there are 169 more files
+ for the receiver to check (to see if they are up-to-date or not) remaining
+ out of the 396 total files in the file-list.
+
+ In an incremental recursion scan, rsync won't know the total number of
+ files in the file-list until it reaches the ends of the scan, but since it
+ starts to transfer files during the scan, it will display a line with the
+ text "ir-chk" (for incremental recursion check) instead of "to-chk" until
+ the point that it knows the full size of the list, at which point it will
+ switch to using "to-chk". Thus, seeing "ir-chk" lets you know that the
+ total count of files in the file list is still going to increase (and each
+ time it does, the count of files left to check will increase by the number
+ of the files added to the list).
+
+0. `-P`
+
+ The `-P` option is equivalent to "[`--partial`](#opt)
+ [`--progress`](#opt)". Its purpose is to make it much easier to specify
+ these two options for a long transfer that may be interrupted.
+
+ There is also a [`--info=progress2`](#opt) option that outputs statistics
+ based on the whole transfer, rather than individual files. Use this flag
+ without outputting a filename (e.g. avoid `-v` or specify
+ [`--info=name0`](#opt)) if you want to see how the transfer is doing
+ without scrolling the screen with a lot of names. (You don't need to
+ specify the [`--progress`](#opt) option in order to use
+ [`--info=progress2`](#opt).)
+
+ Finally, you can get an instant progress report by sending rsync a signal
+ of either SIGINFO or SIGVTALRM. On BSD systems, a SIGINFO is generated by
+ typing a Ctrl+T (Linux doesn't currently support a SIGINFO signal). When
+ the client-side process receives one of those signals, it sets a flag to
+ output a single progress report which is output when the current file
+ transfer finishes (so it may take a little time if a big file is being
+ handled when the signal arrives). A filename is output (if needed)
+ followed by the [`--info=progress2`](#opt) format of progress info. If you
+ don't know which of the 3 rsync processes is the client process, it's OK to
+ signal all of them (since the non-client processes ignore the signal).
+
+ CAUTION: sending SIGVTALRM to an older rsync (pre-3.2.0) will kill it.
+
+0. `--password-file=FILE`
+
+ This option allows you to provide a password for accessing an rsync daemon
+ via a file or via standard input if **FILE** is `-`. The file should
+ contain just the password on the first line (all other lines are ignored).
+ Rsync will exit with an error if **FILE** is world readable or if a
+ root-run rsync command finds a non-root-owned file.
+
+ This option does not supply a password to a remote shell transport such as
+ ssh; to learn how to do that, consult the remote shell's documentation.
+ When accessing an rsync daemon using a remote shell as the transport, this
+ option only comes into effect after the remote shell finishes its
+ authentication (i.e. if you have also specified a password in the daemon's
+ config file).
+
+0. `--early-input=FILE`
+
+ This option allows rsync to send up to 5K of data to the "early exec"
+ script on its stdin. One possible use of this data is to give the script a
+ secret that can be used to mount an encrypted filesystem (which you should
+ unmount in the the "post-xfer exec" script).
+
+ The daemon must be at least version 3.2.1.
+
+0. `--list-only`
+
+ This option will cause the source files to be listed instead of
+ transferred. This option is inferred if there is a single source arg and
+ no destination specified, so its main uses are:
+
+ 1. to turn a copy command that includes a destination arg into a
+ file-listing command, or
+ 2. to be able to specify more than one source arg. Note: be sure to
+ include the destination.
+
+ CAUTION: keep in mind that a source arg with a wild-card is expanded by the
+ shell into multiple args, so it is never safe to try to specify a single
+ wild-card arg to try to infer this option. A safe example is:
+
+ > rsync -av --list-only foo* dest/
+
+ This option always uses an output format that looks similar to this:
+
+ > drwxrwxr-x 4,096 2022/09/30 12:53:11 support
+ > -rw-rw-r-- 80 2005/01/11 10:37:37 support/Makefile
+
+ The only option that affects this output style is (as of 3.1.0) the
+ [`--human-readable`](#opt) (`-h`) option. The default is to output sizes
+ as byte counts with digit separators (in a 14-character-width column).
+ Specifying at least one `-h` option makes the sizes output with unit
+ suffixes. If you want old-style bytecount sizes without digit separators
+ (and an 11-character-width column) use `--no-h`.
+
+ Compatibility note: when requesting a remote listing of files from an rsync
+ that is version 2.6.3 or older, you may encounter an error if you ask for a
+ non-recursive listing. This is because a file listing implies the
+ [`--dirs`](#opt) option w/o [`--recursive`](#opt), and older rsyncs don't
+ have that option. To avoid this problem, either specify the `--no-dirs`
+ option (if you don't need to expand a directory's content), or turn on
+ recursion and exclude the content of subdirectories: `-r --exclude='/*/*'`.
+
+0. `--bwlimit=RATE`
+
+ This option allows you to specify the maximum transfer rate for the data
+ sent over the socket, specified in units per second. The RATE value can be
+ suffixed with a string to indicate a size multiplier, and may be a
+ fractional value (e.g. `--bwlimit=1.5m`). If no suffix is specified, the
+ value will be assumed to be in units of 1024 bytes (as if "K" or "KiB" had
+ been appended). See the [`--max-size`](#opt) option for a description of
+ all the available suffixes. A value of 0 specifies no limit.
+
+ For backward-compatibility reasons, the rate limit will be rounded to the
+ nearest KiB unit, so no rate smaller than 1024 bytes per second is
+ possible.
+
+ Rsync writes data over the socket in blocks, and this option both limits
+ the size of the blocks that rsync writes, and tries to keep the average
+ transfer rate at the requested limit. Some burstiness may be seen where
+ rsync writes out a block of data and then sleeps to bring the average rate
+ into compliance.
+
+ Due to the internal buffering of data, the [`--progress`](#opt) option may
+ not be an accurate reflection on how fast the data is being sent. This is
+ because some files can show up as being rapidly sent when the data is
+ quickly buffered, while other can show up as very slow when the flushing of
+ the output buffer occurs. This may be fixed in a future version.
+
+ See also [the daemon version of the `--bwlimit` option](#dopt--bwlimit).
+
+0. `--stop-after=MINS`, (`--time-limit=MINS`)
+
+ This option tells rsync to stop copying when the specified number of
+ minutes has elapsed.
+
+ For maximal flexibility, rsync does not communicate this option to the
+ remote rsync since it is usually enough that one side of the connection
+ quits as specified. This allows the option's use even when only one side
+ of the connection supports it. You can tell the remote side about the time
+ limit using [`--remote-option`](#opt) (`-M`), should the need arise.
+
+ The `--time-limit` version of this option is deprecated.
+
+0. `--stop-at=y-m-dTh:m`
+
+ This option tells rsync to stop copying when the specified point in time
+ has been reached. The date & time can be fully specified in a numeric
+ format of year-month-dayThour:minute (e.g. 2000-12-31T23:59) in the local
+ timezone. You may choose to separate the date numbers using slashes
+ instead of dashes.
+
+ The value can also be abbreviated in a variety of ways, such as specifying
+ a 2-digit year and/or leaving off various values. In all cases, the value
+ will be taken to be the next possible point in time where the supplied
+ information matches. If the value specifies the current time or a past
+ time, rsync exits with an error.
+
+ For example, "1-30" specifies the next January 30th (at midnight local
+ time), "14:00" specifies the next 2 P.M., "1" specifies the next 1st of the
+ month at midnight, "31" specifies the next month where we can stop on its
+ 31st day, and ":59" specifies the next 59th minute after the hour.
+
+ For maximal flexibility, rsync does not communicate this option to the
+ remote rsync since it is usually enough that one side of the connection
+ quits as specified. This allows the option's use even when only one side
+ of the connection supports it. You can tell the remote side about the time
+ limit using [`--remote-option`](#opt) (`-M`), should the need arise. Do
+ keep in mind that the remote host may have a different default timezone
+ than your local host.
+
+0. `--fsync`
+
+ Cause the receiving side to fsync each finished file. This may slow down
+ the transfer, but can help to provide peace of mind when updating critical
+ files.
+
+0. `--write-batch=FILE`
+
+ Record a file that can later be applied to another identical destination
+ with [`--read-batch`](#opt). See the "BATCH MODE" section for details, and
+ also the [`--only-write-batch`](#opt) option.
+
+ This option overrides the negotiated checksum & compress lists and always
+ negotiates a choice based on old-school md5/md4/zlib choices. If you want
+ a more modern choice, use the [`--checksum-choice`](#opt) (`--cc`) and/or
+ [`--compress-choice`](#opt) (`--zc`) options.
+
+0. `--only-write-batch=FILE`
+
+ Works like [`--write-batch`](#opt), except that no updates are made on the
+ destination system when creating the batch. This lets you transport the
+ changes to the destination system via some other means and then apply the
+ changes via [`--read-batch`](#opt).
+
+ Note that you can feel free to write the batch directly to some portable
+ media: if this media fills to capacity before the end of the transfer, you
+ can just apply that partial transfer to the destination and repeat the
+ whole process to get the rest of the changes (as long as you don't mind a
+ partially updated destination system while the multi-update cycle is
+ happening).
+
+ Also note that you only save bandwidth when pushing changes to a remote
+ system because this allows the batched data to be diverted from the sender
+ into the batch file without having to flow over the wire to the receiver
+ (when pulling, the sender is remote, and thus can't write the batch).
+
+0. `--read-batch=FILE`
+
+ Apply all of the changes stored in FILE, a file previously generated by
+ [`--write-batch`](#opt). If _FILE_ is `-`, the batch data will be read
+ from standard input. See the "BATCH MODE" section for details.
+
+0. `--protocol=NUM`
+
+ Force an older protocol version to be used. This is useful for creating a
+ batch file that is compatible with an older version of rsync. For
+ instance, if rsync 2.6.4 is being used with the [`--write-batch`](#opt)
+ option, but rsync 2.6.3 is what will be used to run the
+ [`--read-batch`](#opt) option, you should use "--protocol=28" when creating
+ the batch file to force the older protocol version to be used in the batch
+ file (assuming you can't upgrade the rsync on the reading system).
+
+0. `--iconv=CONVERT_SPEC`
+
+ Rsync can convert filenames between character sets using this option.
+ Using a CONVERT_SPEC of "." tells rsync to look up the default
+ character-set via the locale setting. Alternately, you can fully specify
+ what conversion to do by giving a local and a remote charset separated by a
+ comma in the order `--iconv=LOCAL,REMOTE`, e.g. `--iconv=utf8,iso88591`.
+ This order ensures that the option will stay the same whether you're
+ pushing or pulling files. Finally, you can specify either `--no-iconv` or
+ a CONVERT_SPEC of "-" to turn off any conversion. The default setting of
+ this option is site-specific, and can also be affected via the
+ [`RSYNC_ICONV`](#) environment variable.
+
+ For a list of what charset names your local iconv library supports, you can
+ run "`iconv --list`".
+
+ If you specify the [`--secluded-args`](#opt) (`-s`) option, rsync will
+ translate the filenames you specify on the command-line that are being sent
+ to the remote host. See also the [`--files-from`](#opt) option.
+
+ Note that rsync does not do any conversion of names in filter files
+ (including include/exclude files). It is up to you to ensure that you're
+ specifying matching rules that can match on both sides of the transfer.
+ For instance, you can specify extra include/exclude rules if there are
+ filename differences on the two sides that need to be accounted for.
+
+ When you pass an `--iconv` option to an rsync daemon that allows it, the
+ daemon uses the charset specified in its "charset" configuration parameter
+ regardless of the remote charset you actually pass. Thus, you may feel
+ free to specify just the local charset for a daemon transfer (e.g.
+ `--iconv=utf8`).
+
+0. `--ipv4`, `-4` or `--ipv6`, `-6`
+
+ Tells rsync to prefer IPv4/IPv6 when creating sockets or running ssh. This
+ affects sockets that rsync has direct control over, such as the outgoing
+ socket when directly contacting an rsync daemon, as well as the forwarding
+ of the `-4` or `-6` option to ssh when rsync can deduce that ssh is being
+ used as the remote shell. For other remote shells you'll need to specify
+ the "`--rsh SHELL -4`" option directly (or whatever IPv4/IPv6 hint options
+ it uses).
+
+ See also [the daemon version of these options](#dopt--ipv4).
+
+ If rsync was compiled without support for IPv6, the `--ipv6` option will
+ have no effect. The `rsync --version` output will contain "`no IPv6`" if
+ is the case.
+
+0. `--checksum-seed=NUM`
+
+ Set the checksum seed to the integer NUM. This 4 byte checksum seed is
+ included in each block and MD4 file checksum calculation (the more modern
+ MD5 file checksums don't use a seed). By default the checksum seed is
+ generated by the server and defaults to the current **time**(). This
+ option is used to set a specific checksum seed, which is useful for
+ applications that want repeatable block checksums, or in the case where the
+ user wants a more random checksum seed. Setting NUM to 0 causes rsync to
+ use the default of **time**() for checksum seed.
+
+## DAEMON OPTIONS
+
+The options allowed when starting an rsync daemon are as follows:
+
+0. `--daemon`
+
+ This tells rsync that it is to run as a daemon. The daemon you start
+ running may be accessed using an rsync client using the `host::module` or
+ `rsync://host/module/` syntax.
+
+ If standard input is a socket then rsync will assume that it is being run
+ via inetd, otherwise it will detach from the current terminal and become a
+ background daemon. The daemon will read the config file (rsyncd.conf) on
+ each connect made by a client and respond to requests accordingly.
+
+ See the [**rsyncd.conf**(5)](rsyncd.conf.5) manpage for more details.
+
+0. `--address=ADDRESS`
+
+ By default rsync will bind to the wildcard address when run as a daemon
+ with the `--daemon` option. The `--address` option allows you to specify a
+ specific IP address (or hostname) to bind to. This makes virtual hosting
+ possible in conjunction with the `--config` option.
+
+ See also the [address](rsyncd.conf.5#address) global option in the
+ rsyncd.conf manpage and the [client version of the `--address`
+ option](#opt--address).
+
+0. `--bwlimit=RATE`
+
+ This option allows you to specify the maximum transfer rate for the data
+ the daemon sends over the socket. The client can still specify a smaller
+ `--bwlimit` value, but no larger value will be allowed.
+
+ See the [client version of the `--bwlimit` option](#opt--bwlimit) for some
+ extra details.
+
+0. `--config=FILE`
+
+ This specifies an alternate config file than the default. This is only
+ relevant when [`--daemon`](#dopt) is specified. The default is
+ /etc/rsyncd.conf unless the daemon is running over a remote shell program
+ and the remote user is not the super-user; in that case the default is
+ rsyncd.conf in the current directory (typically $HOME).
+
+0. `--dparam=OVERRIDE`, `-M`
+
+ This option can be used to set a daemon-config parameter when starting up
+ rsync in daemon mode. It is equivalent to adding the parameter at the end
+ of the global settings prior to the first module's definition. The
+ parameter names can be specified without spaces, if you so desire. For
+ instance:
+
+ > rsync --daemon -M pidfile=/path/rsync.pid
+
+0. `--no-detach`
+
+ When running as a daemon, this option instructs rsync to not detach itself
+ and become a background process. This option is required when running as a
+ service on Cygwin, and may also be useful when rsync is supervised by a
+ program such as `daemontools` or AIX's `System Resource Controller`.
+ `--no-detach` is also recommended when rsync is run under a debugger. This
+ option has no effect if rsync is run from inetd or sshd.
+
+0. `--port=PORT`
+
+ This specifies an alternate TCP port number for the daemon to listen on
+ rather than the default of 873.
+
+ See also [the client version of the `--port` option](#opt--port) and the
+ [port](rsyncd.conf.5#port) global setting in the rsyncd.conf manpage.
+
+0. `--log-file=FILE`
+
+ This option tells the rsync daemon to use the given log-file name instead
+ of using the "`log file`" setting in the config file.
+
+ See also [the client version of the `--log-file` option](#opt--log-file).
+
+0. `--log-file-format=FORMAT`
+
+ This option tells the rsync daemon to use the given FORMAT string instead
+ of using the "`log format`" setting in the config file. It also enables
+ "`transfer logging`" unless the string is empty, in which case transfer
+ logging is turned off.
+
+ See also [the client version of the `--log-file-format`
+ option](#opt--log-file-format).
+
+0. `--sockopts`
+
+ This overrides the [`socket options`](rsyncd.conf.5#socket_options)
+ setting in the rsyncd.conf file and has the same syntax.
+
+ See also [the client version of the `--sockopts` option](#opt--sockopts).
+
+0. `--verbose`, `-v`
+
+ This option increases the amount of information the daemon logs during its
+ startup phase. After the client connects, the daemon's verbosity level
+ will be controlled by the options that the client used and the
+ "`max verbosity`" setting in the module's config section.
+
+ See also [the client version of the `--verbose` option](#opt--verbose).
+
+0. `--ipv4`, `-4` or `--ipv6`, `-6`
+
+ Tells rsync to prefer IPv4/IPv6 when creating the incoming sockets that the
+ rsync daemon will use to listen for connections. One of these options may
+ be required in older versions of Linux to work around an IPv6 bug in the
+ kernel (if you see an "address already in use" error when nothing else is
+ using the port, try specifying `--ipv6` or `--ipv4` when starting the
+ daemon).
+
+ See also [the client version of these options](#opt--ipv4).
+
+ If rsync was compiled without support for IPv6, the `--ipv6` option will
+ have no effect. The `rsync --version` output will contain "`no IPv6`" if
+ is the case.
+
+0. `--help`, `-h`
+
+ When specified after `--daemon`, print a short help page describing the
+ options available for starting an rsync daemon.
+
+## FILTER RULES
+
+The filter rules allow for custom control of several aspects of how files are
+handled:
+
+- Control which files the sending side puts into the file list that describes
+ the transfer hierarchy
+- Control which files the receiving side protects from deletion when the file
+ is not in the sender's file list
+- Control which extended attribute names are skipped when copying xattrs
+
+The rules are either directly specified via option arguments or they can be
+read in from one or more files. The filter-rule files can even be a part of
+the hierarchy of files being copied, affecting different parts of the tree in
+different ways.
+
+### SIMPLE INCLUDE/EXCLUDE RULES
+
+We will first cover the basics of how include & exclude rules affect what files
+are transferred, ignoring any deletion side-effects. Filter rules mainly
+affect the contents of directories that rsync is "recursing" into, but they can
+also affect a top-level item in the transfer that was specified as a argument.
+
+The default for any unmatched file/dir is for it to be included in the
+transfer, which puts the file/dir into the sender's file list. The use of an
+exclude rule causes one or more matching files/dirs to be left out of the
+sender's file list. An include rule can be used to limit the effect of an
+exclude rule that is matching too many files.
+
+The order of the rules is important because the first rule that matches is the
+one that takes effect. Thus, if an early rule excludes a file, no include rule
+that comes after it can have any effect. This means that you must place any
+include overrides somewhere prior to the exclude that it is intended to limit.
+
+When a directory is excluded, all its contents and sub-contents are also
+excluded. The sender doesn't scan through any of it at all, which can save a
+lot of time when skipping large unneeded sub-trees.
+
+It is also important to understand that the include/exclude rules are applied
+to every file and directory that the sender is recursing into. Thus, if you
+want a particular deep file to be included, you have to make sure that none of
+the directories that must be traversed on the way down to that file are
+excluded or else the file will never be discovered to be included. As an
+example, if the directory "`a/path`" was given as a transfer argument and you
+want to ensure that the file "`a/path/down/deep/wanted.txt`" is a part of the
+transfer, then the sender must not exclude the directories "`a/path`",
+"`a/path/down`", or "`a/path/down/deep`" as it makes it way scanning through
+the file tree.
+
+When you are working on the rules, it can be helpful to ask rsync to tell you
+what is being excluded/included and why. Specifying `--debug=FILTER` or (when
+pulling files) `-M--debug=FILTER` turns on level 1 of the FILTER debug
+information that will output a message any time that a file or directory is
+included or excluded and which rule it matched. Beginning in 3.2.4 it will
+also warn if a filter rule has trailing whitespace, since an exclude of "foo "
+(with a trailing space) will not exclude a file named "foo".
+
+Exclude and include rules can specify wildcard [PATTERN MATCHING RULES](#)
+(similar to shell wildcards) that allow you to match things like a file suffix
+or a portion of a filename.
+
+A rule can be limited to only affecting a directory by putting a trailing slash
+onto the filename.
+
+### SIMPLE INCLUDE/EXCLUDE EXAMPLE
+
+With the following file tree created on the sending side:
+
+> mkdir x/
+> touch x/file.txt
+> mkdir x/y/
+> touch x/y/file.txt
+> touch x/y/zzz.txt
+> mkdir x/z/
+> touch x/z/file.txt
+
+Then the following rsync command will transfer the file "`x/y/file.txt`" and
+the directories needed to hold it, resulting in the path "`/tmp/x/y/file.txt`"
+existing on the remote host:
+
+> rsync -ai -f'+ x/' -f'+ x/y/' -f'+ x/y/file.txt' -f'- *' x host:/tmp/
+
+Aside: this copy could also have been accomplished using the [`-R`](#opt)
+option (though the 2 commands behave differently if deletions are enabled):
+
+> rsync -aiR x/y/file.txt host:/tmp/
+
+The following command does not need an include of the "x" directory because it
+is not a part of the transfer (note the traililng slash). Running this command
+would copy just "`/tmp/x/file.txt`" because the "y" and "z" dirs get excluded:
+
+> rsync -ai -f'+ file.txt' -f'- *' x/ host:/tmp/x/
+
+This command would omit the zzz.txt file while copying "x" and everything else
+it contains:
+
+> rsync -ai -f'- zzz.txt' x host:/tmp/
+
+### FILTER RULES WHEN DELETING
+
+By default the include & exclude filter rules affect both the sender
+(as it creates its file list)
+and the receiver (as it creates its file lists for calculating deletions). If
+no delete option is in effect, the receiver skips creating the delete-related
+file lists. This two-sided default can be manually overridden so that you are
+only specifying sender rules or receiver rules, as described in the [FILTER
+RULES IN DEPTH](#) section.
+
+When deleting, an exclude protects a file from being removed on the receiving
+side while an include overrides that protection (putting the file at risk of
+deletion). The default is for a file to be at risk -- its safety depends on it
+matching a corresponding file from the sender.
+
+An example of the two-sided exclude effect can be illustrated by the copying of
+a C development directory between 2 systems. When doing a touch-up copy, you
+might want to skip copying the built executable and the `.o` files (sender
+hide) so that the receiving side can build their own and not lose any object
+files that are already correct (receiver protect). For instance:
+
+> rsync -ai --del -f'- *.o' -f'- cmd' src host:/dest/
+
+Note that using `-f'-p *.o'` is even better than `-f'- *.o'` if there is a
+chance that the directory structure may have changed. The "p" modifier is
+discussed in [FILTER RULE MODIFIERS](#).
+
+One final note, if your shell doesn't mind unexpanded wildcards, you could
+simplify the typing of the filter options by using an underscore in place of
+the space and leaving off the quotes. For instance, `-f -_*.o -f -_cmd` (and
+similar) could be used instead of the filter options above.
+
+### FILTER RULES IN DEPTH
+
+Rsync supports old-style include/exclude rules and new-style filter rules. The
+older rules are specified using [`--include`](#opt) and [`--exclude`](#opt) as
+well as the [`--include-from`](#opt) and [`--exclude-from`](#opt). These are
+limited in behavior but they don't require a "-" or "+" prefix. An old-style
+exclude rule is turned into a "`- name`" filter rule (with no modifiers) and an
+old-style include rule is turned into a "`+ name`" filter rule (with no
+modifiers).
+
+Rsync builds an ordered list of filter rules as specified on the command-line
+and/or read-in from files. New style filter rules have the following syntax:
+
+> RULE [PATTERN_OR_FILENAME]
+> RULE,MODIFIERS [PATTERN_OR_FILENAME]
+
+You have your choice of using either short or long RULE names, as described
+below. If you use a short-named rule, the ',' separating the RULE from the
+MODIFIERS is optional. The PATTERN or FILENAME that follows (when present)
+must come after either a single space or an underscore (\_). Any additional
+spaces and/or underscores are considered to be a part of the pattern name.
+Here are the available rule prefixes:
+
+0. `exclude, '-'` specifies an exclude pattern that (by default) is both a
+ `hide` and a `protect`.
+0. `include, '+'` specifies an include pattern that (by default) is both a
+ `show` and a `risk`.
+0. `merge, '.'` specifies a merge-file on the client side to read for more
+ rules.
+0. `dir-merge, ':'` specifies a per-directory merge-file. Using this kind of
+ filter rule requires that you trust the sending side's filter checking, so
+ it has the side-effect mentioned under the [`--trust-sender`](#opt) option.
+0. `hide, 'H'` specifies a pattern for hiding files from the transfer.
+ Equivalent to a sender-only exclude, so `-f'H foo'` could also be specified
+ as `-f'-s foo'`.
+0. `show, 'S'` files that match the pattern are not hidden. Equivalent to a
+ sender-only include, so `-f'S foo'` could also be specified as `-f'+s
+ foo'`.
+0. `protect, 'P'` specifies a pattern for protecting files from deletion.
+ Equivalent to a receiver-only exclude, so `-f'P foo'` could also be
+ specified as `-f'-r foo'`.
+0. `risk, 'R'` files that match the pattern are not protected. Equivalent to a
+ receiver-only include, so `-f'R foo'` could also be specified as `-f'+r
+ foo'`.
+0. `clear, '!'` clears the current include/exclude list (takes no arg)
+
+When rules are being read from a file (using merge or dir-merge), empty lines
+are ignored, as are whole-line comments that start with a '`#`' (filename rules
+that contain a hash character are unaffected).
+
+Note also that the [`--filter`](#opt), [`--include`](#opt), and
+[`--exclude`](#opt) options take one rule/pattern each. To add multiple ones,
+you can repeat the options on the command-line, use the merge-file syntax of
+the [`--filter`](#opt) option, or the [`--include-from`](#opt) /
+[`--exclude-from`](#opt) options.
+
+### PATTERN MATCHING RULES
+
+Most of the rules mentioned above take an argument that specifies what the rule
+should match. If rsync is recursing through a directory hierarchy, keep in
+mind that each pattern is matched against the name of every directory in the
+descent path as rsync finds the filenames to send.
+
+The matching rules for the pattern argument take several forms:
+
+- If a pattern contains a `/` (not counting a trailing slash) or a "`**`"
+ (which can match a slash), then the pattern is matched against the full
+ pathname, including any leading directories within the transfer. If the
+ pattern doesn't contain a (non-trailing) `/` or a "`**`", then it is matched
+ only against the final component of the filename or pathname. For example,
+ `foo` means that the final path component must be "foo" while `foo/bar` would
+ match the last 2 elements of the path (as long as both elements are within
+ the transfer).
+- A pattern that ends with a `/` only matches a directory, not a regular file,
+ symlink, or device.
+- A pattern that starts with a `/` is anchored to the start of the transfer
+ path instead of the end. For example, `/foo/**` or `/foo/bar/**` match only
+ leading elements in the path. If the rule is read from a per-directory
+ filter file, the transfer path being matched will begin at the level of the
+ filter file instead of the top of the transfer. See the section on
+ [ANCHORING INCLUDE/EXCLUDE PATTERNS](#) for a full discussion of how to
+ specify a pattern that matches at the root of the transfer.
+
+Rsync chooses between doing a simple string match and wildcard matching by
+checking if the pattern contains one of these three wildcard characters: '`*`',
+'`?`', and '`[`' :
+
+- a '`?`' matches any single character except a slash (`/`).
+- a '`*`' matches zero or more non-slash characters.
+- a '`**`' matches zero or more characters, including slashes.
+- a '`[`' introduces a character class, such as `[a-z]` or `[[:alpha:]]`, that
+ must match one character.
+- a trailing `***` in the pattern is a shorthand that allows you to match a
+ directory and all its contents using a single rule. For example, specifying
+ "`dir_name/***`" will match both the "dir_name" directory (as if "`dir_name/`"
+ had been specified) and everything in the directory (as if "`dir_name/**`"
+ had been specified).
+- a backslash can be used to escape a wildcard character, but it is only
+ interpreted as an escape character if at least one wildcard character is
+ present in the match pattern. For instance, the pattern "`foo\bar`" matches
+ that single backslash literally, while the pattern "`foo\bar*`" would need to
+ be changed to "`foo\\bar*`" to avoid the "`\b`" becoming just "b".
+
+Here are some examples of exclude/include matching:
+
+- Option `-f'- *.o'` would exclude all filenames ending with `.o`
+- Option `-f'- /foo'` would exclude a file (or directory) named foo in the
+ transfer-root directory
+- Option `-f'- foo/'` would exclude any directory named foo
+- Option `-f'- foo/*/bar'` would exclude any file/dir named bar which is at two
+ levels below a directory named foo (if foo is in the transfer)
+- Option `-f'- /foo/**/bar'` would exclude any file/dir named bar that was two
+ or more levels below a top-level directory named foo (note that /foo/bar is
+ **not** excluded by this)
+- Options `-f'+ */' -f'+ *.c' -f'- *'` would include all directories and .c
+ source files but nothing else
+- Options `-f'+ foo/' -f'+ foo/bar.c' -f'- *'` would include only the foo
+ directory and foo/bar.c (the foo directory must be explicitly included or it
+ would be excluded by the "`- *`")
+
+### FILTER RULE MODIFIERS
+
+The following modifiers are accepted after an include (+) or exclude (-) rule:
+
+- A `/` specifies that the include/exclude rule should be matched against the
+ absolute pathname of the current item. For example, `-f'-/ /etc/passwd'`
+ would exclude the passwd file any time the transfer was sending files from
+ the "/etc" directory, and "-/ subdir/foo" would always exclude "foo" when it
+ is in a dir named "subdir", even if "foo" is at the root of the current
+ transfer.
+- A `!` specifies that the include/exclude should take effect if the pattern
+ fails to match. For instance, `-f'-! */'` would exclude all non-directories.
+- A `C` is used to indicate that all the global CVS-exclude rules should be
+ inserted as excludes in place of the "-C". No arg should follow.
+- An `s` is used to indicate that the rule applies to the sending side. When a
+ rule affects the sending side, it affects what files are put into the
+ sender's file list. The default is for a rule to affect both sides unless
+ [`--delete-excluded`](#opt) was specified, in which case default rules become
+ sender-side only. See also the hide (H) and show (S) rules, which are an
+ alternate way to specify sending-side includes/excludes.
+- An `r` is used to indicate that the rule applies to the receiving side. When
+ a rule affects the receiving side, it prevents files from being deleted. See
+ the `s` modifier for more info. See also the protect (P) and risk (R) rules,
+ which are an alternate way to specify receiver-side includes/excludes.
+- A `p` indicates that a rule is perishable, meaning that it is ignored in
+ directories that are being deleted. For instance, the
+ [`--cvs-exclude`](#opt) (`-C`) option's default rules that exclude things
+ like "CVS" and "`*.o`" are marked as perishable, and will not prevent a
+ directory that was removed on the source from being deleted on the
+ destination.
+- An `x` indicates that a rule affects xattr names in xattr copy/delete
+ operations (and is thus ignored when matching file/dir names). If no
+ xattr-matching rules are specified, a default xattr filtering rule is used
+ (see the [`--xattrs`](#opt) option).
+
+### MERGE-FILE FILTER RULES
+
+You can merge whole files into your filter rules by specifying either a merge
+(.) or a dir-merge (:) filter rule (as introduced in the [FILTER RULES](#)
+section above).
+
+There are two kinds of merged files -- single-instance ('.') and per-directory
+(':'). A single-instance merge file is read one time, and its rules are
+incorporated into the filter list in the place of the "." rule. For
+per-directory merge files, rsync will scan every directory that it traverses
+for the named file, merging its contents when the file exists into the current
+list of inherited rules. These per-directory rule files must be created on the
+sending side because it is the sending side that is being scanned for the
+available files to transfer. These rule files may also need to be transferred
+to the receiving side if you want them to affect what files don't get deleted
+(see [PER-DIRECTORY RULES AND DELETE](#) below).
+
+Some examples:
+
+> merge /etc/rsync/default.rules
+> . /etc/rsync/default.rules
+> dir-merge .per-dir-filter
+> dir-merge,n- .non-inherited-per-dir-excludes
+> :n- .non-inherited-per-dir-excludes
+
+The following modifiers are accepted after a merge or dir-merge rule:
+
+- A `-` specifies that the file should consist of only exclude patterns, with
+ no other rule-parsing except for in-file comments.
+- A `+` specifies that the file should consist of only include patterns, with
+ no other rule-parsing except for in-file comments.
+- A `C` is a way to specify that the file should be read in a CVS-compatible
+ manner. This turns on 'n', 'w', and '-', but also allows the list-clearing
+ token (!) to be specified. If no filename is provided, ".cvsignore" is
+ assumed.
+- A `e` will exclude the merge-file name from the transfer; e.g. "dir-merge,e
+ .rules" is like "dir-merge .rules" and "- .rules".
+- An `n` specifies that the rules are not inherited by subdirectories.
+- A `w` specifies that the rules are word-split on whitespace instead of the
+ normal line-splitting. This also turns off comments. Note: the space that
+ separates the prefix from the rule is treated specially, so "- foo + bar" is
+ parsed as two rules (assuming that prefix-parsing wasn't also disabled).
+- You may also specify any of the modifiers for the "+" or "-" rules (above) in
+ order to have the rules that are read in from the file default to having that
+ modifier set (except for the `!` modifier, which would not be useful). For
+ instance, "merge,-/ .excl" would treat the contents of .excl as absolute-path
+ excludes, while "dir-merge,s .filt" and ":sC" would each make all their
+ per-directory rules apply only on the sending side. If the merge rule
+ specifies sides to affect (via the `s` or `r` modifier or both), then the
+ rules in the file must not specify sides (via a modifier or a rule prefix
+ such as `hide`).
+
+Per-directory rules are inherited in all subdirectories of the directory where
+the merge-file was found unless the 'n' modifier was used. Each subdirectory's
+rules are prefixed to the inherited per-directory rules from its parents, which
+gives the newest rules a higher priority than the inherited rules. The entire
+set of dir-merge rules are grouped together in the spot where the merge-file
+was specified, so it is possible to override dir-merge rules via a rule that
+got specified earlier in the list of global rules. When the list-clearing rule
+("!") is read from a per-directory file, it only clears the inherited rules for
+the current merge file.
+
+Another way to prevent a single rule from a dir-merge file from being inherited
+is to anchor it with a leading slash. Anchored rules in a per-directory
+merge-file are relative to the merge-file's directory, so a pattern "/foo"
+would only match the file "foo" in the directory where the dir-merge filter
+file was found.
+
+Here's an example filter file which you'd specify via `--filter=". file":`
+
+> merge /home/user/.global-filter
+> - *.gz
+> dir-merge .rules
+> + *.[ch]
+> - *.o
+> - foo*
+
+This will merge the contents of the /home/user/.global-filter file at the start
+of the list and also turns the ".rules" filename into a per-directory filter
+file. All rules read in prior to the start of the directory scan follow the
+global anchoring rules (i.e. a leading slash matches at the root of the
+transfer).
+
+If a per-directory merge-file is specified with a path that is a parent
+directory of the first transfer directory, rsync will scan all the parent dirs
+from that starting point to the transfer directory for the indicated
+per-directory file. For instance, here is a common filter (see [`-F`](#opt)):
+
+> --filter=': /.rsync-filter'
+
+That rule tells rsync to scan for the file .rsync-filter in all directories
+from the root down through the parent directory of the transfer prior to the
+start of the normal directory scan of the file in the directories that are sent
+as a part of the transfer. (Note: for an rsync daemon, the root is always the
+same as the module's "path".)
+
+Some examples of this pre-scanning for per-directory files:
+
+> rsync -avF /src/path/ /dest/dir
+> rsync -av --filter=': ../../.rsync-filter' /src/path/ /dest/dir
+> rsync -av --filter=': .rsync-filter' /src/path/ /dest/dir
+
+The first two commands above will look for ".rsync-filter" in "/" and "/src"
+before the normal scan begins looking for the file in "/src/path" and its
+subdirectories. The last command avoids the parent-dir scan and only looks for
+the ".rsync-filter" files in each directory that is a part of the transfer.
+
+If you want to include the contents of a ".cvsignore" in your patterns, you
+should use the rule ":C", which creates a dir-merge of the .cvsignore file, but
+parsed in a CVS-compatible manner. You can use this to affect where the
+[`--cvs-exclude`](#opt) (`-C`) option's inclusion of the per-directory
+.cvsignore file gets placed into your rules by putting the ":C" wherever you
+like in your filter rules. Without this, rsync would add the dir-merge rule
+for the .cvsignore file at the end of all your other rules (giving it a lower
+priority than your command-line rules). For example:
+
+> ```
+> cat <<EOT | rsync -avC --filter='. -' a/ b
+> + foo.o
+> :C
+> - *.old
+> EOT
+> rsync -avC --include=foo.o -f :C --exclude='*.old' a/ b
+> ```
+
+Both of the above rsync commands are identical. Each one will merge all the
+per-directory .cvsignore rules in the middle of the list rather than at the
+end. This allows their dir-specific rules to supersede the rules that follow
+the :C instead of being subservient to all your rules. To affect the other CVS
+exclude rules (i.e. the default list of exclusions, the contents of
+$HOME/.cvsignore, and the value of $CVSIGNORE) you should omit the `-C`
+command-line option and instead insert a "-C" rule into your filter rules; e.g.
+"`--filter=-C`".
+
+### LIST-CLEARING FILTER RULE
+
+You can clear the current include/exclude list by using the "!" filter rule (as
+introduced in the [FILTER RULES](#) section above). The "current" list is either
+the global list of rules (if the rule is encountered while parsing the filter
+options) or a set of per-directory rules (which are inherited in their own
+sub-list, so a subdirectory can use this to clear out the parent's rules).
+
+### ANCHORING INCLUDE/EXCLUDE PATTERNS
+
+As mentioned earlier, global include/exclude patterns are anchored at the "root
+of the transfer" (as opposed to per-directory patterns, which are anchored at
+the merge-file's directory). If you think of the transfer as a subtree of
+names that are being sent from sender to receiver, the transfer-root is where
+the tree starts to be duplicated in the destination directory. This root
+governs where patterns that start with a / match.
+
+Because the matching is relative to the transfer-root, changing the trailing
+slash on a source path or changing your use of the [`--relative`](#opt) option
+affects the path you need to use in your matching (in addition to changing how
+much of the file tree is duplicated on the destination host). The following
+examples demonstrate this.
+
+Let's say that we want to match two source files, one with an absolute
+path of "/home/me/foo/bar", and one with a path of "/home/you/bar/baz".
+Here is how the various command choices differ for a 2-source transfer:
+
+> ```
+> Example cmd: rsync -a /home/me /home/you /dest
+> +/- pattern: /me/foo/bar
+> +/- pattern: /you/bar/baz
+> Target file: /dest/me/foo/bar
+> Target file: /dest/you/bar/baz
+> ```
+
+> ```
+> Example cmd: rsync -a /home/me/ /home/you/ /dest
+> +/- pattern: /foo/bar (note missing "me")
+> +/- pattern: /bar/baz (note missing "you")
+> Target file: /dest/foo/bar
+> Target file: /dest/bar/baz
+> ```
+
+> ```
+> Example cmd: rsync -a --relative /home/me/ /home/you /dest
+> +/- pattern: /home/me/foo/bar (note full path)
+> +/- pattern: /home/you/bar/baz (ditto)
+> Target file: /dest/home/me/foo/bar
+> Target file: /dest/home/you/bar/baz
+> ```
+
+> ```
+> Example cmd: cd /home; rsync -a --relative me/foo you/ /dest
+> +/- pattern: /me/foo/bar (starts at specified path)
+> +/- pattern: /you/bar/baz (ditto)
+> Target file: /dest/me/foo/bar
+> Target file: /dest/you/bar/baz
+> ```
+
+The easiest way to see what name you should filter is to just look at the
+output when using [`--verbose`](#opt) and put a / in front of the name (use the
+`--dry-run` option if you're not yet ready to copy any files).
+
+### PER-DIRECTORY RULES AND DELETE
+
+Without a delete option, per-directory rules are only relevant on the sending
+side, so you can feel free to exclude the merge files themselves without
+affecting the transfer. To make this easy, the 'e' modifier adds this exclude
+for you, as seen in these two equivalent commands:
+
+> rsync -av --filter=': .excl' --exclude=.excl host:src/dir /dest
+> rsync -av --filter=':e .excl' host:src/dir /dest
+
+However, if you want to do a delete on the receiving side AND you want some
+files to be excluded from being deleted, you'll need to be sure that the
+receiving side knows what files to exclude. The easiest way is to include the
+per-directory merge files in the transfer and use [`--delete-after`](#opt),
+because this ensures that the receiving side gets all the same exclude rules as
+the sending side before it tries to delete anything:
+
+> rsync -avF --delete-after host:src/dir /dest
+
+However, if the merge files are not a part of the transfer, you'll need to
+either specify some global exclude rules (i.e. specified on the command line),
+or you'll need to maintain your own per-directory merge files on the receiving
+side. An example of the first is this (assume that the remote .rules files
+exclude themselves):
+
+> rsync -av --filter=': .rules' --filter='. /my/extra.rules'
+> --delete host:src/dir /dest
+
+In the above example the extra.rules file can affect both sides of the
+transfer, but (on the sending side) the rules are subservient to the rules
+merged from the .rules files because they were specified after the
+per-directory merge rule.
+
+In one final example, the remote side is excluding the .rsync-filter files from
+the transfer, but we want to use our own .rsync-filter files to control what
+gets deleted on the receiving side. To do this we must specifically exclude
+the per-directory merge files (so that they don't get deleted) and then put
+rules into the local files to control what else should not get deleted. Like
+one of these commands:
+
+> ```
+> rsync -av --filter=':e /.rsync-filter' --delete \
+> host:src/dir /dest
+> rsync -avFF --delete host:src/dir /dest
+> ```
+
+## TRANSFER RULES
+
+In addition to the [FILTER RULES](#) that affect the recursive file scans that
+generate the file list on the sending and (when deleting) receiving sides,
+there are transfer rules. These rules affect which files the generator decides
+need to be transferred without the side effects of an exclude filter rule.
+Transfer rules affect only files and never directories.
+
+Because a transfer rule does not affect what goes into the sender's (and
+receiver's) file list, it cannot have any effect on which files get deleted on
+the receiving side. For example, if the file "foo" is present in the sender's
+list but its size is such that it is omitted due to a transfer rule, the
+receiving side does not request the file. However, its presence in the file
+list means that a delete pass will not remove a matching file named "foo" on
+the receiving side. On the other hand, a server-side exclude (hide) of the
+file "foo" leaves the file out of the server's file list, and absent a
+receiver-side exclude (protect) the receiver will remove a matching file named
+"foo" if deletions are requested.
+
+Given that the files are still in the sender's file list, the
+[`--prune-empty-dirs`](#opt) option will not judge a directory as being empty
+even if it contains only files that the transfer rules omitted.
+
+Similarly, a transfer rule does not have any extra effect on which files are
+deleted on the receiving side, so setting a maximum file size for the transfer
+does not prevent big files from being deleted.
+
+Examples of transfer rules include the default "quick check" algorithm (which
+compares size & modify time), the [`--update`](#opt) option, the
+[`--max-size`](#opt) option, the [`--ignore-non-existing`](#opt) option, and a
+few others.
+
+## BATCH MODE
+
+Batch mode can be used to apply the same set of updates to many identical
+systems. Suppose one has a tree which is replicated on a number of hosts. Now
+suppose some changes have been made to this source tree and those changes need
+to be propagated to the other hosts. In order to do this using batch mode,
+rsync is run with the write-batch option to apply the changes made to the
+source tree to one of the destination trees. The write-batch option causes the
+rsync client to store in a "batch file" all the information needed to repeat
+this operation against other, identical destination trees.
+
+Generating the batch file once saves having to perform the file status,
+checksum, and data block generation more than once when updating multiple
+destination trees. Multicast transport protocols can be used to transfer the
+batch update files in parallel to many hosts at once, instead of sending the
+same data to every host individually.
+
+To apply the recorded changes to another destination tree, run rsync with the
+read-batch option, specifying the name of the same batch file, and the
+destination tree. Rsync updates the destination tree using the information
+stored in the batch file.
+
+For your convenience, a script file is also created when the write-batch option
+is used: it will be named the same as the batch file with ".sh" appended. This
+script file contains a command-line suitable for updating a destination tree
+using the associated batch file. It can be executed using a Bourne (or
+Bourne-like) shell, optionally passing in an alternate destination tree
+pathname which is then used instead of the original destination path. This is
+useful when the destination tree path on the current host differs from the one
+used to create the batch file.
+
+Examples:
+
+> $ rsync --write-batch=foo -a host:/source/dir/ /adest/dir/
+> $ scp foo* remote:
+> $ ssh remote ./foo.sh /bdest/dir/
+
+> $ rsync --write-batch=foo -a /source/dir/ /adest/dir/
+> $ ssh remote rsync --read-batch=- -a /bdest/dir/ <foo
+
+In these examples, rsync is used to update /adest/dir/ from /source/dir/ and
+the information to repeat this operation is stored in "foo" and "foo.sh". The
+host "remote" is then updated with the batched data going into the directory
+/bdest/dir. The differences between the two examples reveals some of the
+flexibility you have in how you deal with batches:
+
+- The first example shows that the initial copy doesn't have to be local -- you
+ can push or pull data to/from a remote host using either the remote-shell
+ syntax or rsync daemon syntax, as desired.
+- The first example uses the created "foo.sh" file to get the right rsync
+ options when running the read-batch command on the remote host.
+- The second example reads the batch data via standard input so that the batch
+ file doesn't need to be copied to the remote machine first. This example
+ avoids the foo.sh script because it needed to use a modified
+ [`--read-batch`](#opt) option, but you could edit the script file if you
+ wished to make use of it (just be sure that no other option is trying to use
+ standard input, such as the [`--exclude-from=-`](#opt) option).
+
+Caveats:
+
+The read-batch option expects the destination tree that it is updating to be
+identical to the destination tree that was used to create the batch update
+fileset. When a difference between the destination trees is encountered the
+update might be discarded with a warning (if the file appears to be up-to-date
+already) or the file-update may be attempted and then, if the file fails to
+verify, the update discarded with an error. This means that it should be safe
+to re-run a read-batch operation if the command got interrupted. If you wish
+to force the batched-update to always be attempted regardless of the file's
+size and date, use the [`-I`](#opt) option (when reading the batch). If an
+error occurs, the destination tree will probably be in a partially updated
+state. In that case, rsync can be used in its regular (non-batch) mode of
+operation to fix up the destination tree.
+
+The rsync version used on all destinations must be at least as new as the one
+used to generate the batch file. Rsync will die with an error if the protocol
+version in the batch file is too new for the batch-reading rsync to handle.
+See also the [`--protocol`](#opt) option for a way to have the creating rsync
+generate a batch file that an older rsync can understand. (Note that batch
+files changed format in version 2.6.3, so mixing versions older than that with
+newer versions will not work.)
+
+When reading a batch file, rsync will force the value of certain options to
+match the data in the batch file if you didn't set them to the same as the
+batch-writing command. Other options can (and should) be changed. For
+instance [`--write-batch`](#opt) changes to [`--read-batch`](#opt),
+[`--files-from`](#opt) is dropped, and the [`--filter`](#opt) /
+[`--include`](#opt) / [`--exclude`](#opt) options are not needed unless one of
+the [`--delete`](#opt) options is specified.
+
+The code that creates the BATCH.sh file transforms any filter/include/exclude
+options into a single list that is appended as a "here" document to the shell
+script file. An advanced user can use this to modify the exclude list if a
+change in what gets deleted by [`--delete`](#opt) is desired. A normal user
+can ignore this detail and just use the shell script as an easy way to run the
+appropriate [`--read-batch`](#opt) command for the batched data.
+
+The original batch mode in rsync was based on "rsync+", but the latest
+version uses a new implementation.
+
+## SYMBOLIC LINKS
+
+Three basic behaviors are possible when rsync encounters a symbolic
+link in the source directory.
+
+By default, symbolic links are not transferred at all. A message "skipping
+non-regular" file is emitted for any symlinks that exist.
+
+If [`--links`](#opt) is specified, then symlinks are added to the transfer
+(instead of being noisily ignored), and the default handling is to recreate
+them with the same target on the destination. Note that [`--archive`](#opt)
+implies [`--links`](#opt).
+
+If [`--copy-links`](#opt) is specified, then symlinks are "collapsed" by
+copying their referent, rather than the symlink.
+
+Rsync can also distinguish "safe" and "unsafe" symbolic links. An example
+where this might be used is a web site mirror that wishes to ensure that the
+rsync module that is copied does not include symbolic links to `/etc/passwd` in
+the public section of the site. Using [`--copy-unsafe-links`](#opt) will cause
+any links to be copied as the file they point to on the destination. Using
+[`--safe-links`](#opt) will cause unsafe links to be omitted by the receiver.
+(Note that you must specify or imply [`--links`](#opt) for
+[`--safe-links`](#opt) to have any effect.)
+
+Symbolic links are considered unsafe if they are absolute symlinks (start with
+`/`), empty, or if they contain enough ".." components to ascend from the top
+of the transfer.
+
+Here's a summary of how the symlink options are interpreted. The list is in
+order of precedence, so if your combination of options isn't mentioned, use the
+first line that is a complete subset of your options:
+
+0. `--copy-links` Turn all symlinks into normal files and directories
+ (leaving no symlinks in the transfer for any other options to affect).
+0. `--copy-dirlinks` Turn just symlinks to directories into real
+ directories, leaving all other symlinks to be handled as described below.
+0. `--links --copy-unsafe-links` Turn all unsafe symlinks
+ into files and create all safe symlinks.
+0. `--copy-unsafe-links` Turn all unsafe symlinks into files, noisily
+ skip all safe symlinks.
+0. `--links --safe-links` The receiver skips creating
+ unsafe symlinks found in the transfer and creates the safe ones.
+0. `--links` Create all symlinks.
+
+For the effect of [`--munge-links`](#opt), see the discussion in that option's
+section.
+
+Note that the [`--keep-dirlinks`](#opt) option does not effect symlinks in the
+transfer but instead affects how rsync treats a symlink to a directory that
+already exists on the receiving side. See that option's section for a warning.
+
+## DIAGNOSTICS
+
+Rsync occasionally produces error messages that may seem a little cryptic. The
+one that seems to cause the most confusion is "protocol version mismatch -- is
+your shell clean?".
+
+This message is usually caused by your startup scripts or remote shell facility
+producing unwanted garbage on the stream that rsync is using for its transport.
+The way to diagnose this problem is to run your remote shell like this:
+
+> ssh remotehost /bin/true > out.dat
+
+then look at out.dat. If everything is working correctly then out.dat should
+be a zero length file. If you are getting the above error from rsync then you
+will probably find that out.dat contains some text or data. Look at the
+contents and try to work out what is producing it. The most common cause is
+incorrectly configured shell startup scripts (such as .cshrc or .profile) that
+contain output statements for non-interactive logins.
+
+If you are having trouble debugging filter patterns, then try specifying the
+`-vv` option. At this level of verbosity rsync will show why each individual
+file is included or excluded.
+
+## EXIT VALUES
+
+- **0** - Success
+- **1** - Syntax or usage error
+- **2** - Protocol incompatibility
+- **3** - Errors selecting input/output files, dirs
+- **4** - Requested action not supported. Either:
+ - an attempt was made to manipulate 64-bit files on a platform that cannot support them
+ - an option was specified that is supported by the client and not by the server
+- **5** - Error starting client-server protocol
+- **6** - Daemon unable to append to log-file
+- **10** - Error in socket I/O
+- **11** - Error in file I/O
+- **12** - Error in rsync protocol data stream
+- **13** - Errors with program diagnostics
+- **14** - Error in IPC code
+- **20** - Received SIGUSR1 or SIGINT
+- **21** - Some error returned by **waitpid()**
+- **22** - Error allocating core memory buffers
+- **23** - Partial transfer due to error
+- **24** - Partial transfer due to vanished source files
+- **25** - The --max-delete limit stopped deletions
+- **30** - Timeout in data send/receive
+- **35** - Timeout waiting for daemon connection
+
+## ENVIRONMENT VARIABLES
+
+0. `CVSIGNORE`
+
+ The CVSIGNORE environment variable supplements any ignore patterns in
+ .cvsignore files. See the [`--cvs-exclude`](#opt) option for more details.
+
+0. `RSYNC_ICONV`
+
+ Specify a default [`--iconv`](#opt) setting using this environment
+ variable. First supported in 3.0.0.
+
+0. `RSYNC_OLD_ARGS`
+
+ Specify a "1" if you want the [`--old-args`](#opt) option to be enabled by
+ default, a "2" (or more) if you want it to be enabled in the
+ repeated-option state, or a "0" to make sure that it is disabled by
+ default. When this environment variable is set to a non-zero value, it
+ supersedes the [`RSYNC_PROTECT_ARGS`](#) variable.
+
+ This variable is ignored if [`--old-args`](#opt), `--no-old-args`, or
+ [`--secluded-args`](#opt) is specified on the command line.
+
+ First supported in 3.2.4.
+
+0. `RSYNC_PROTECT_ARGS`
+
+ Specify a non-zero numeric value if you want the [`--secluded-args`](#opt)
+ option to be enabled by default, or a zero value to make sure that it is
+ disabled by default.
+
+ This variable is ignored if [`--secluded-args`](#opt), `--no-secluded-args`,
+ or [`--old-args`](#opt) is specified on the command line.
+
+ First supported in 3.1.0. Starting in 3.2.4, this variable is ignored if
+ [`RSYNC_OLD_ARGS`](#) is set to a non-zero value.
+
+0. `RSYNC_RSH`
+
+ This environment variable allows you to override the default shell used as
+ the transport for rsync. Command line options are permitted after the
+ command name, just as in the [`--rsh`](#opt) (`-e`) option.
+
+0. `RSYNC_PROXY`
+
+ This environment variable allows you to redirect your rsync
+ client to use a web proxy when connecting to an rsync daemon. You should
+ set `RSYNC_PROXY` to a hostname:port pair.
+
+0. `RSYNC_PASSWORD`
+
+ This environment variable allows you to set the password for an rsync
+ **daemon** connection, which avoids the password prompt. Note that this
+ does **not** supply a password to a remote shell transport such as ssh
+ (consult its documentation for how to do that).
+
+0. `USER` or `LOGNAME`
+
+ The USER or LOGNAME environment variables are used to determine the default
+ username sent to an rsync daemon. If neither is set, the username defaults
+ to "nobody". If both are set, `USER` takes precedence.
+
+0. `RSYNC_PARTIAL_DIR`
+
+ This environment variable specifies the directory to use for a
+ [`--partial`](#opt) transfer without implying that partial transfers be
+ enabled. See the [`--partial-dir`](#opt) option for full details.
+
+0. `RSYNC_COMPRESS_LIST`
+
+ This environment variable allows you to customize the negotiation of the
+ compression algorithm by specifying an alternate order or a reduced list of
+ names. Use the command `rsync --version` to see the available compression
+ names. See the [`--compress`](#opt) option for full details.
+
+0. `RSYNC_CHECKSUM_LIST`
+
+ This environment variable allows you to customize the negotiation of the
+ checksum algorithm by specifying an alternate order or a reduced list of
+ names. Use the command `rsync --version` to see the available checksum
+ names. See the [`--checksum-choice`](#opt) option for full details.
+
+0. `RSYNC_MAX_ALLOC`
+
+ This environment variable sets an allocation maximum as if you had used the
+ [`--max-alloc`](#opt) option.
+
+0. `RSYNC_PORT`
+
+ This environment variable is not read by rsync, but is instead set in
+ its sub-environment when rsync is running the remote shell in combination
+ with a daemon connection. This allows a script such as
+ [`rsync-ssl`](rsync-ssl.1) to be able to know the port number that the user
+ specified on the command line.
+
+0. `HOME`
+
+ This environment variable is used to find the user's default .cvsignore
+ file.
+
+0. `RSYNC_CONNECT_PROG`
+
+ This environment variable is mainly used in debug setups to set the program
+ to use when making a daemon connection. See [CONNECTING TO AN RSYNC
+ DAEMON](#) for full details.
+
+0. `RSYNC_SHELL`
+
+ This environment variable is mainly used in debug setups to set the program
+ to use to run the program specified by [`RSYNC_CONNECT_PROG`](#). See
+ [CONNECTING TO AN RSYNC DAEMON](#) for full details.
+
+## FILES
+
+/etc/rsyncd.conf or rsyncd.conf
+
+## SEE ALSO
+
+[**rsync-ssl**(1)](rsync-ssl.1), [**rsyncd.conf**(5)](rsyncd.conf.5), [**rrsync**(1)](rrsync.1)
+
+## BUGS
+
+- Times are transferred as \*nix time_t values.
+- When transferring to FAT filesystems rsync may re-sync unmodified files. See
+ the comments on the [`--modify-window`](#opt) option.
+- File permissions, devices, etc. are transferred as native numerical values.
+- See also the comments on the [`--delete`](#opt) option.
+
+Please report bugs! See the web site at <https://rsync.samba.org/>.
+
+## VERSION
+
+This manpage is current for version @VERSION@ of rsync.
+
+## INTERNAL OPTIONS
+
+The options `--server` and `--sender` are used internally by rsync, and should
+never be typed by a user under normal circumstances. Some awareness of these
+options may be needed in certain scenarios, such as when setting up a login
+that can only run an rsync command. For instance, the support directory of the
+rsync distribution has an example script named rrsync (for restricted rsync)
+that can be used with a restricted ssh login.
+
+## CREDITS
+
+Rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
+
+An rsync web site is available at <https://rsync.samba.org/>. The site
+includes an FAQ-O-Matic which may cover questions unanswered by this manual
+page.
+
+The rsync github project is <https://github.com/WayneD/rsync>.
+
+We would be delighted to hear from you if you like this program. Please
+contact the mailing-list at <rsync@lists.samba.org>.
+
+This program uses the excellent zlib compression library written by Jean-loup
+Gailly and Mark Adler.
+
+## THANKS
+
+Special thanks go out to: John Van Essen, Matt McCutchen, Wesley W. Terpstra,
+David Dykstra, Jos Backus, Sebastian Krahmer, Martin Pool, and our
+gone-but-not-forgotten compadre, J.W. Schultz.
+
+Thanks also to Richard Brent, Brendan Mackay, Bill Waite, Stephen Rothwell and
+David Bell. I've probably missed some people, my apologies if I have.
+
+## AUTHOR
+
+Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people have later contributed to it. It is currently maintained by Wayne
+Davison.
+
+Mailing lists for support and development are available at
+<https://lists.samba.org/>.
diff --git a/rsync.c b/rsync.c
new file mode 100644
index 0000000..cd288f5
--- /dev/null
+++ b/rsync.c
@@ -0,0 +1,828 @@
+/*
+ * Routines common to more than one of the rsync processes.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
+#include <libcharset.h>
+#elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO
+#include <langinfo.h>
+#endif
+
+extern int dry_run;
+extern int preserve_acls;
+extern int preserve_xattrs;
+extern int preserve_perms;
+extern int preserve_executability;
+extern int preserve_mtimes;
+extern int omit_dir_times;
+extern int omit_link_times;
+extern int am_root;
+extern int am_server;
+extern int am_daemon;
+extern int am_sender;
+extern int am_receiver;
+extern int am_generator;
+extern int am_starting_up;
+extern int allow_8bit_chars;
+extern int protocol_version;
+extern int got_kill_signal;
+extern int called_from_signal_handler;
+extern int inc_recurse;
+extern int inplace;
+extern int flist_eof;
+extern int file_old_total;
+extern int keep_dirlinks;
+extern int make_backups;
+extern int sanitize_paths;
+extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern struct chmod_mode_struct *daemon_chmod_modes;
+#ifdef ICONV_OPTION
+extern char *iconv_opt;
+#endif
+
+#define UPDATED_OWNER (1<<0)
+#define UPDATED_GROUP (1<<1)
+#define UPDATED_MTIME (1<<2)
+#define UPDATED_ATIME (1<<3)
+#define UPDATED_ACLS (1<<4)
+#define UPDATED_MODE (1<<5)
+#define UPDATED_CRTIME (1<<6)
+
+#ifdef ICONV_CONST
+iconv_t ic_chck = (iconv_t)-1;
+# ifdef ICONV_OPTION
+iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1;
+# endif
+
+static const char *default_charset(void)
+{
+# if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
+ return locale_charset();
+# elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO
+ return nl_langinfo(CODESET);
+# else
+ return ""; /* Works with (at the very least) gnu iconv... */
+# endif
+}
+
+void setup_iconv(void)
+{
+ const char *defset = default_charset();
+# ifdef ICONV_OPTION
+ const char *charset;
+ char *cp;
+# endif
+
+ if (!am_server && !allow_8bit_chars) {
+ /* It's OK if this fails... */
+ ic_chck = iconv_open(defset, defset);
+
+ if (DEBUG_GTE(ICONV, 2)) {
+ if (ic_chck == (iconv_t)-1) {
+ rprintf(FINFO,
+ "msg checking via isprint()"
+ " (iconv_open(\"%s\", \"%s\") errno: %d)\n",
+ defset, defset, errno);
+ } else {
+ rprintf(FINFO,
+ "msg checking charset: %s\n",
+ defset);
+ }
+ }
+ } else
+ ic_chck = (iconv_t)-1;
+
+# ifdef ICONV_OPTION
+ if (!iconv_opt)
+ return;
+
+ if ((cp = strchr(iconv_opt, ',')) != NULL) {
+ if (am_server) /* A local transfer needs this. */
+ iconv_opt = cp + 1;
+ else
+ *cp = '\0';
+ }
+
+ if (!*iconv_opt || (*iconv_opt == '.' && iconv_opt[1] == '\0'))
+ charset = defset;
+ else
+ charset = iconv_opt;
+
+ if ((ic_send = iconv_open(UTF8_CHARSET, charset)) == (iconv_t)-1) {
+ rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+ UTF8_CHARSET, charset);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if ((ic_recv = iconv_open(charset, UTF8_CHARSET)) == (iconv_t)-1) {
+ rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+ charset, UTF8_CHARSET);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (DEBUG_GTE(ICONV, 1)) {
+ rprintf(FINFO, "[%s] charset: %s\n",
+ who_am_i(), *charset ? charset : "[LOCALE]");
+ }
+# endif
+}
+
+/* This function converts the chars in the "in" xbuf into characters in the
+ * "out" xbuf. The ".len" chars of the "in" xbuf is used starting from its
+ * ".pos". The ".size" of the "out" xbuf restricts how many characters can
+ * be stored, starting at its ".pos+.len" position. Note that the last byte
+ * of the "out" xbuf is not used, which reserves space for a trailing '\0'
+ * (though it is up to the caller to store a trailing '\0', as needed).
+ *
+ * We return a 0 on success or a -1 on error. An error also sets errno to
+ * E2BIG, EILSEQ, or EINVAL (see below); otherwise errno will be set to 0.
+ * The "in" xbuf is altered to update ".pos" and ".len". The "out" xbuf has
+ * data appended, and its ".len" incremented (see below for a ".size" note).
+ *
+ * If ICB_CIRCULAR_OUT is set in "flags", the chars going into the "out" xbuf
+ * can wrap around to the start, and the xbuf may have its ".size" reduced
+ * (presumably by 1 byte) if the iconv code doesn't have space to store a
+ * multi-byte character at the physical end of the ".buf" (though no reducing
+ * happens if ".pos" is <= 1, since there is no room to wrap around).
+ *
+ * If ICB_EXPAND_OUT is set in "flags", the "out" xbuf will be allocated if
+ * empty, and (as long as ICB_CIRCULAR_OUT is not set) expanded if too small.
+ * This prevents the return of E2BIG (except for a circular xbuf).
+ *
+ * If ICB_INCLUDE_BAD is set in "flags", any badly-encoded chars are included
+ * verbatim in the "out" xbuf, so EILSEQ will not be returned.
+ *
+ * If ICB_INCLUDE_INCOMPLETE is set in "flags", any incomplete multi-byte
+ * chars are included, which ensures that EINVAL is not returned.
+ *
+ * If ICB_INIT is set, the iconv() conversion state is initialized prior to
+ * processing the characters. */
+int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags)
+{
+ ICONV_CONST char *ibuf;
+ size_t icnt, ocnt, opos;
+ char *obuf;
+
+ if (!out->size && flags & ICB_EXPAND_OUT) {
+ size_t siz = ROUND_UP_1024(in->len * 2);
+ alloc_xbuf(out, siz);
+ } else if (out->len+1 >= out->size) {
+ /* There is no room to even start storing data. */
+ if (!(flags & ICB_EXPAND_OUT) || flags & ICB_CIRCULAR_OUT) {
+ errno = E2BIG;
+ return -1;
+ }
+ realloc_xbuf(out, out->size + ROUND_UP_1024(in->len * 2));
+ }
+
+ if (flags & ICB_INIT)
+ iconv(ic, NULL, 0, NULL, 0);
+
+ ibuf = in->buf + in->pos;
+ icnt = in->len;
+
+ opos = out->pos + out->len;
+ if (flags & ICB_CIRCULAR_OUT) {
+ if (opos >= out->size) {
+ opos -= out->size;
+ /* We know that out->pos is not 0 due to the "no room" check
+ * above, so this can't go "negative". */
+ ocnt = out->pos - opos - 1;
+ } else {
+ /* Allow the use of all bytes to the physical end of the buffer
+ * unless pos is 0, in which case we reserve our trailing '\0'. */
+ ocnt = out->size - opos - (out->pos ? 0 : 1);
+ }
+ } else
+ ocnt = out->size - opos - 1;
+ obuf = out->buf + opos;
+
+ while (icnt) {
+ while (iconv(ic, &ibuf, &icnt, &obuf, &ocnt) == (size_t)-1) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EINVAL) {
+ if (!(flags & ICB_INCLUDE_INCOMPLETE))
+ goto finish;
+ if (!ocnt)
+ goto e2big;
+ } else if (errno == EILSEQ) {
+ if (!(flags & ICB_INCLUDE_BAD))
+ goto finish;
+ if (!ocnt)
+ goto e2big;
+ } else if (errno == E2BIG) {
+ size_t siz;
+ e2big:
+ opos = obuf - out->buf;
+ if (flags & ICB_CIRCULAR_OUT && out->pos > 1 && opos > out->pos) {
+ /* We are in a divided circular buffer at the physical
+ * end with room to wrap to the start. If iconv() refused
+ * to use one or more trailing bytes in the buffer, we
+ * set the size to ignore the unused bytes. */
+ if (opos < out->size)
+ reduce_iobuf_size(out, opos);
+ obuf = out->buf;
+ ocnt = out->pos - 1;
+ continue;
+ }
+ if (!(flags & ICB_EXPAND_OUT) || flags & ICB_CIRCULAR_OUT) {
+ errno = E2BIG;
+ goto finish;
+ }
+ siz = ROUND_UP_1024(in->len * 2);
+ realloc_xbuf(out, out->size + siz);
+ obuf = out->buf + opos;
+ ocnt += siz;
+ continue;
+ } else {
+ rsyserr(FERROR, errno, "unexpected error from iconv()");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ *obuf++ = *ibuf++;
+ ocnt--, icnt--;
+ if (!icnt)
+ break;
+ }
+ }
+
+ errno = 0;
+
+ finish:
+ opos = obuf - out->buf;
+ if (flags & ICB_CIRCULAR_OUT && opos < out->pos)
+ opos += out->size;
+ out->len = opos - out->pos;
+
+ in->len = icnt;
+ in->pos = ibuf - in->buf;
+
+ return errno ? -1 : 0;
+}
+#endif
+
+void send_protected_args(int fd, char *args[])
+{
+ int i;
+#ifdef ICONV_OPTION
+ int convert = ic_send != (iconv_t)-1;
+ xbuf outbuf, inbuf;
+
+ if (convert)
+ alloc_xbuf(&outbuf, 1024);
+#endif
+
+ for (i = 0; args[i]; i++) {} /* find first NULL */
+ args[i] = "rsync"; /* set a new arg0 */
+ if (DEBUG_GTE(CMD, 1))
+ print_child_argv("protected args:", args + i + 1);
+ do {
+ if (!args[i][0])
+ write_buf(fd, ".", 2);
+#ifdef ICONV_OPTION
+ else if (convert) {
+ INIT_XBUF_STRLEN(inbuf, args[i]);
+ iconvbufs(ic_send, &inbuf, &outbuf,
+ ICB_EXPAND_OUT | ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT);
+ outbuf.buf[outbuf.len] = '\0';
+ write_buf(fd, outbuf.buf, outbuf.len + 1);
+ outbuf.len = 0;
+ }
+#endif
+ else
+ write_buf(fd, args[i], strlen(args[i]) + 1);
+ } while (args[++i]);
+ write_byte(fd, 0);
+
+#ifdef ICONV_OPTION
+ if (convert)
+ free(outbuf.buf);
+#endif
+}
+
+int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr, char *buf, int *len_ptr)
+{
+ int len, iflags = 0;
+ struct file_list *flist;
+ uchar fnamecmp_type = FNAMECMP_FNAME;
+ int ndx;
+
+ read_loop:
+ while (1) {
+ ndx = read_ndx(f_in);
+
+ if (ndx >= 0)
+ break;
+ if (ndx == NDX_DONE)
+ return ndx;
+ if (ndx == NDX_DEL_STATS) {
+ read_del_stats(f_in);
+ if (am_sender && am_server)
+ write_del_stats(f_out);
+ continue;
+ }
+ if (!inc_recurse || am_sender) {
+ int last;
+ if (first_flist)
+ last = first_flist->prev->ndx_start + first_flist->prev->used - 1;
+ else
+ last = -1;
+ rprintf(FERROR,
+ "Invalid file index: %d (%d - %d) [%s]\n",
+ ndx, NDX_DONE, last, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (ndx == NDX_FLIST_EOF) {
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ write_int(f_out, NDX_FLIST_EOF);
+ continue;
+ }
+ ndx = NDX_FLIST_OFFSET - ndx;
+ if (ndx < 0 || ndx >= dir_flist->used) {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ rprintf(FERROR,
+ "Invalid dir index: %d (%d - %d) [%s]\n",
+ ndx, NDX_FLIST_OFFSET,
+ NDX_FLIST_OFFSET - dir_flist->used + 1,
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ /* Send all the data we read for this flist to the generator. */
+ start_flist_forward(ndx);
+ flist = recv_file_list(f_in, ndx);
+ flist->parent_ndx = ndx;
+ stop_flist_forward();
+ }
+
+ iflags = protocol_version >= 29 ? read_shortint(f_in)
+ : ITEM_TRANSFER | ITEM_MISSING_DATA;
+
+ /* Support the protocol-29 keep-alive style. */
+ if (protocol_version < 30 && ndx == cur_flist->used && iflags == ITEM_IS_NEW) {
+ if (am_sender)
+ maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
+ goto read_loop;
+ }
+
+ flist = flist_for_ndx(ndx, "read_ndx_and_attrs");
+ if (flist != cur_flist) {
+ cur_flist = flist;
+ if (am_sender) {
+ file_old_total = cur_flist->used;
+ for (flist = first_flist; flist != cur_flist; flist = flist->next)
+ file_old_total += flist->used;
+ }
+ }
+
+ if (iflags & ITEM_BASIS_TYPE_FOLLOWS)
+ fnamecmp_type = read_byte(f_in);
+ *type_ptr = fnamecmp_type;
+
+ if (iflags & ITEM_XNAME_FOLLOWS) {
+ if ((len = read_vstring(f_in, buf, MAXPATHLEN)) < 0)
+ exit_cleanup(RERR_PROTOCOL);
+
+ if (sanitize_paths) {
+ sanitize_path(buf, buf, "", 0, SP_DEFAULT);
+ len = strlen(buf);
+ }
+ } else {
+ *buf = '\0';
+ len = -1;
+ }
+ *len_ptr = len;
+
+ if (iflags & ITEM_TRANSFER) {
+ int i = ndx - cur_flist->ndx_start;
+ if (i < 0 || !S_ISREG(cur_flist->files[i]->mode)) {
+ rprintf(FERROR,
+ "received request to transfer non-regular file: %d [%s]\n",
+ ndx, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ }
+
+ *iflag_ptr = iflags;
+ return ndx;
+}
+
+/*
+ free a sums struct
+ */
+void free_sums(struct sum_struct *s)
+{
+ if (s->sums) free(s->sums);
+ free(s);
+}
+
+/* This is only called when we aren't preserving permissions. Figure out what
+ * the permissions should be and return them merged back into the mode. */
+mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
+ int exists)
+{
+ int new_mode;
+ /* If the file already exists, we'll return the local permissions,
+ * possibly tweaked by the --executability option. */
+ if (exists) {
+ new_mode = (flist_mode & ~CHMOD_BITS) | (stat_mode & CHMOD_BITS);
+ if (preserve_executability && S_ISREG(flist_mode)) {
+ /* If the source file is executable, grant execute
+ * rights to everyone who can read, but ONLY if the
+ * file isn't already executable. */
+ if (!(flist_mode & 0111))
+ new_mode &= ~0111;
+ else if (!(stat_mode & 0111))
+ new_mode |= (new_mode & 0444) >> 2;
+ }
+ } else {
+ /* Apply destination default permissions and turn
+ * off special permissions. */
+ new_mode = flist_mode & (~CHMOD_BITS | dflt_perms);
+ }
+ return new_mode;
+}
+
+static int same_mtime(struct file_struct *file, STRUCT_STAT *st, int extra_accuracy)
+{
+#ifdef ST_MTIME_NSEC
+ uint32 f1_nsec = F_MOD_NSEC_or_0(file);
+ uint32 f2_nsec = (uint32)st->ST_MTIME_NSEC;
+#else
+ uint32 f1_nsec = 0, f2_nsec = 0;
+#endif
+
+ if (extra_accuracy) /* ignore modify_window when setting the time after a transfer or checksum check */
+ return file->modtime == st->st_mtime && f1_nsec == f2_nsec;
+
+ return same_time(file->modtime, f1_nsec, st->st_mtime , f2_nsec);
+}
+
+int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ const char *fnamecmp, int flags)
+{
+ int updated = 0;
+ stat_x sx2;
+ int change_uid, change_gid;
+ mode_t new_mode = file->mode;
+ int inherit;
+
+ if (!sxp) {
+ if (dry_run)
+ return 1;
+ if (link_stat(fname, &sx2.st, 0) < 0) {
+ rsyserr(FERROR_XFER, errno, "stat %s failed",
+ full_fname(fname));
+ return 0;
+ }
+ init_stat_x(&sx2);
+ sxp = &sx2;
+ inherit = !preserve_perms;
+ } else
+ inherit = !preserve_perms && file->flags & FLAG_DIR_CREATED;
+
+ if (inherit && S_ISDIR(new_mode) && sxp->st.st_mode & S_ISGID) {
+ /* We just created this directory and its setgid
+ * bit is on, so make sure it stays on. */
+ new_mode |= S_ISGID;
+ }
+
+ if (daemon_chmod_modes && !S_ISLNK(new_mode))
+ new_mode = tweak_mode(new_mode, daemon_chmod_modes);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode) && !ACL_READY(*sxp))
+ get_acl(fname, sxp);
+#endif
+
+ change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
+ change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
+ && sxp->st.st_gid != (gid_t)F_GROUP(file);
+#ifndef CAN_CHOWN_SYMLINK
+ if (S_ISLNK(sxp->st.st_mode)) {
+ ;
+ } else
+#endif
+ if (change_uid || change_gid) {
+ if (DEBUG_GTE(OWN, 1)) {
+ if (change_uid) {
+ rprintf(FINFO,
+ "set uid of %s from %u to %u\n",
+ fname, (unsigned)sxp->st.st_uid, F_OWNER(file));
+ }
+ if (change_gid) {
+ rprintf(FINFO,
+ "set gid of %s from %u to %u\n",
+ fname, (unsigned)sxp->st.st_gid, F_GROUP(file));
+ }
+ }
+ if (am_root >= 0) {
+ uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
+ gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
+ if (do_lchown(fname, uid, gid) != 0) {
+ /* We shouldn't have attempted to change uid
+ * or gid unless have the privilege. */
+ rsyserr(FERROR_XFER, errno, "%s %s failed",
+ change_uid ? "chown" : "chgrp",
+ full_fname(fname));
+ goto cleanup;
+ }
+ if (uid == (uid_t)-1 && sxp->st.st_uid != (uid_t)-1)
+ rprintf(FERROR_XFER, "uid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
+ if (gid == (gid_t)-1 && sxp->st.st_gid != (gid_t)-1)
+ rprintf(FERROR_XFER, "gid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
+ /* A lchown had been done, so we need to re-stat if
+ * the destination had the setuid or setgid bits set
+ * (due to the side effect of the chown call). */
+ if (sxp->st.st_mode & (S_ISUID | S_ISGID)) {
+ link_stat(fname, &sxp->st,
+ keep_dirlinks && S_ISDIR(sxp->st.st_mode));
+ }
+ }
+ if (change_uid)
+ updated |= UPDATED_OWNER;
+ if (change_gid)
+ updated |= UPDATED_GROUP;
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (am_root < 0)
+ set_stat_xattr(fname, file, new_mode);
+ if (preserve_xattrs && fnamecmp)
+ set_xattr(fname, file, fnamecmp, sxp);
+#endif
+
+ if ((omit_dir_times && S_ISDIR(sxp->st.st_mode))
+ || (omit_link_times && S_ISLNK(sxp->st.st_mode)))
+ flags |= ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME;
+ else {
+ if (!preserve_mtimes)
+ flags |= ATTRS_SKIP_MTIME;
+ if (!atimes_ndx || S_ISDIR(sxp->st.st_mode))
+ flags |= ATTRS_SKIP_ATIME;
+ /* Don't set the creation date on the root folder of an HFS+ volume. */
+ if (sxp->st.st_ino == 2 && S_ISDIR(sxp->st.st_mode))
+ flags |= ATTRS_SKIP_CRTIME;
+ }
+ if (sxp != &sx2)
+ memcpy(&sx2.st, &sxp->st, sizeof sx2.st);
+ if (!(flags & ATTRS_SKIP_MTIME) && !same_mtime(file, &sxp->st, flags & ATTRS_ACCURATE_TIME)) {
+ sx2.st.st_mtime = file->modtime;
+#ifdef ST_MTIME_NSEC
+ sx2.st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file);
+#endif
+ updated |= UPDATED_MTIME;
+ }
+ if (!(flags & ATTRS_SKIP_ATIME)) {
+ time_t file_atime = F_ATIME(file);
+ if (flags & ATTRS_ACCURATE_TIME || !same_time(sxp->st.st_atime, 0, file_atime, 0)) {
+ sx2.st.st_atime = file_atime;
+#ifdef ST_ATIME_NSEC
+ sx2.st.ST_ATIME_NSEC = 0;
+#endif
+ updated |= UPDATED_ATIME;
+ }
+ }
+#ifdef SUPPORT_CRTIMES
+ if (crtimes_ndx && !(flags & ATTRS_SKIP_CRTIME)) {
+ time_t file_crtime = F_CRTIME(file);
+ if (sxp->crtime == 0)
+ sxp->crtime = get_create_time(fname, &sxp->st);
+ if (!same_time(sxp->crtime, 0L, file_crtime, 0L)) {
+ if (
+#ifdef HAVE_GETATTRLIST
+ do_setattrlist_crtime(fname, file_crtime) == 0
+#elif defined __CYGWIN__
+ do_SetFileTime(fname, file_crtime) == 0
+#else
+#error Unknown crtimes implementation
+#endif
+ )
+ updated |= UPDATED_CRTIME;
+ }
+ }
+#endif
+ if (updated & (UPDATED_MTIME|UPDATED_ATIME)) {
+ int ret = set_times(fname, &sx2.st);
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno, "failed to set times on %s", full_fname(fname));
+ goto cleanup;
+ }
+ if (ret > 0) { /* ret == 1 if symlink could not be set */
+ updated &= ~(UPDATED_MTIME|UPDATED_ATIME);
+ file->flags |= FLAG_TIME_FAILED;
+ }
+ }
+
+#ifdef SUPPORT_ACLS
+ /* It's OK to call set_acl() now, even for a dir, as the generator
+ * will enable owner-writability using chmod, if necessary.
+ *
+ * If set_acl() changes permission bits in the process of setting
+ * an access ACL, it changes sxp->st.st_mode so we know whether we
+ * need to chmod(). */
+ if (preserve_acls && !S_ISLNK(new_mode)) {
+ if (set_acl(fname, file, sxp, new_mode) > 0)
+ updated |= UPDATED_ACLS;
+ }
+#endif
+
+#ifdef HAVE_CHMOD
+ if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
+ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to set permissions on %s",
+ full_fname(fname));
+ goto cleanup;
+ }
+ if (ret == 0) /* ret == 1 if symlink could not be set */
+ updated |= UPDATED_MODE;
+ }
+#endif
+
+ if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) {
+ if (updated)
+ rprintf(FCLIENT, "%s\n", fname);
+ else
+ rprintf(FCLIENT, "%s is uptodate\n", fname);
+ }
+ cleanup:
+ if (sxp == &sx2)
+ free_stat_x(&sx2);
+ return updated;
+}
+
+/* This is only called for SIGINT, SIGHUP, and SIGTERM. */
+void sig_int(int sig_num)
+{
+ called_from_signal_handler = 1;
+
+ /* KLUGE: if the user hits Ctrl-C while ssh is prompting
+ * for a password, then our cleanup's sending of a SIGUSR1
+ * signal to all our children may kill ssh before it has a
+ * chance to restore the tty settings (i.e. turn echo back
+ * on). By sleeping for a short time, ssh gets a bigger
+ * chance to do the right thing. If child processes are
+ * not ssh waiting for a password, then this tiny delay
+ * shouldn't hurt anything. */
+ msleep(400);
+
+ /* If we're an rsync daemon listener (not a daemon server),
+ * we'll exit with status 0 if we received SIGTERM. */
+ if (am_daemon && !am_server && sig_num == SIGTERM)
+ exit_cleanup(0);
+
+ /* If the signal arrived on the server side (or for the receiver
+ * process on the client), we want to try to do a controlled shutdown
+ * that lets the client side (generator process) know what happened.
+ * To do this, we set a flag and let the normal process handle the
+ * shutdown. We only attempt this if multiplexed IO is in effect and
+ * we didn't already set the flag. */
+ if (!got_kill_signal && (am_server || am_receiver)) {
+ got_kill_signal = sig_num;
+ called_from_signal_handler = 0;
+ return;
+ }
+
+ exit_cleanup(RERR_SIGNAL);
+}
+
+/* Finish off a file transfer: renaming the file and setting the file's
+ * attributes (e.g. permissions, ownership, etc.). If the robust_rename()
+ * call is forced to copy the temp file and partialptr is both non-NULL and
+ * not an absolute path, we stage the file into the partial-dir and then
+ * rename it into place. This returns 1 on success or 0 on failure. */
+int finish_transfer(const char *fname, const char *fnametmp,
+ const char *fnamecmp, const char *partialptr,
+ struct file_struct *file, int ok_to_set_time,
+ int overwriting_basis)
+{
+ int ret;
+ const char *temp_copy_name = partialptr && *partialptr != '/' ? partialptr : NULL;
+
+ if (inplace) {
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "finishing %s\n", fname);
+ fnametmp = fname;
+ goto do_set_file_attrs;
+ }
+
+ if (make_backups > 0 && overwriting_basis) {
+ int ok = make_backup(fname, False);
+ if (!ok)
+ exit_cleanup(RERR_FILEIO);
+ if (ok == 1 && fnamecmp == fname)
+ fnamecmp = get_backup_name(fname);
+ }
+
+ /* Change permissions before putting the file into place. */
+ set_file_attrs(fnametmp, file, NULL, fnamecmp,
+ ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME);
+
+ /* move tmp file over real file */
+ if (DEBUG_GTE(RECV, 1))
+ rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
+ ret = robust_rename(fnametmp, fname, temp_copy_name, file->mode);
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno, "%s %s -> \"%s\"",
+ ret == -2 ? "copy" : "rename",
+ full_fname(fnametmp), fname);
+ if (!partialptr || (ret == -2 && temp_copy_name)
+ || robust_rename(fnametmp, partialptr, NULL, file->mode) < 0)
+ do_unlink(fnametmp);
+ return 0;
+ }
+ if (ret == 0) {
+ /* The file was moved into place (not copied), so it's done. */
+ return 1;
+ }
+ /* The file was copied, so tweak the perms of the copied file. If it
+ * was copied to partialptr, move it into its final destination. */
+ fnametmp = temp_copy_name ? temp_copy_name : fname;
+
+ do_set_file_attrs:
+ set_file_attrs(fnametmp, file, NULL, fnamecmp,
+ ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME);
+
+ if (temp_copy_name) {
+ if (do_rename(fnametmp, fname) < 0) {
+ rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\"",
+ full_fname(fnametmp), fname);
+ return 0;
+ }
+ handle_partial_dir(temp_copy_name, PDIR_DELETE);
+ }
+ return 1;
+}
+
+struct file_list *flist_for_ndx(int ndx, const char *fatal_error_loc)
+{
+ struct file_list *flist = cur_flist;
+
+ if (!flist && !(flist = first_flist))
+ goto not_found;
+
+ while (ndx < flist->ndx_start-1) {
+ if (flist == first_flist)
+ goto not_found;
+ flist = flist->prev;
+ }
+ while (ndx >= flist->ndx_start + flist->used) {
+ if (!(flist = flist->next))
+ goto not_found;
+ }
+ return flist;
+
+ not_found:
+ if (fatal_error_loc) {
+ int first, last;
+ if (first_flist) {
+ first = first_flist->ndx_start - 1;
+ last = first_flist->prev->ndx_start + first_flist->prev->used - 1;
+ } else {
+ first = 0;
+ last = -1;
+ }
+ rprintf(FERROR,
+ "File-list index %d not in %d - %d (%s) [%s]\n",
+ ndx, first, last, fatal_error_loc, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ return NULL;
+}
+
+const char *who_am_i(void)
+{
+ if (am_starting_up)
+ return am_server ? "server" : "client";
+ return am_sender ? "sender"
+ : am_generator ? "generator"
+ : am_receiver ? "receiver"
+ : "Receiver"; /* pre-forked receiver */
+}
diff --git a/rsync.h b/rsync.h
new file mode 100644
index 0000000..d3709fe
--- /dev/null
+++ b/rsync.h
@@ -0,0 +1,1486 @@
+/*
+ * Copyright (C) 1996, 2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#define False 0
+#define True 1
+#define Unset (-1) /* Our BOOL values are always an int. */
+
+#define BLOCK_SIZE 700
+#define RSYNC_RSH_ENV "RSYNC_RSH"
+#define RSYNC_RSH_IO_ENV "RSYNC_RSH_IO"
+
+#define RSYNC_NAME "rsync"
+/* RSYNCD_SYSCONF is now set in config.h */
+#define RSYNCD_USERCONF "rsyncd.conf"
+
+#define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock"
+#define URL_PREFIX "rsync://"
+
+#define SYMLINK_PREFIX "/rsyncd-munged/" /* This MUST have a trailing slash! */
+#define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1)
+
+#define BACKUP_SUFFIX "~"
+
+/* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
+ incompatible with older versions :-( */
+#define CHAR_OFFSET 0
+
+/* These flags are only used during the flist transfer. */
+
+#define XMIT_TOP_DIR (1<<0)
+#define XMIT_SAME_MODE (1<<1)
+#define XMIT_SAME_RDEV_pre28 (1<<2) /* protocols 20 - 27 */
+#define XMIT_EXTENDED_FLAGS (1<<2) /* protocols 28 - now */
+#define XMIT_SAME_UID (1<<3)
+#define XMIT_SAME_GID (1<<4)
+#define XMIT_SAME_NAME (1<<5)
+#define XMIT_LONG_NAME (1<<6)
+#define XMIT_SAME_TIME (1<<7)
+
+#define XMIT_SAME_RDEV_MAJOR (1<<8) /* protocols 28 - now (devices only) */
+#define XMIT_NO_CONTENT_DIR (1<<8) /* protocols 30 - now (dirs only) */
+#define XMIT_HLINKED (1<<9) /* protocols 28 - now (non-dirs) */
+#define XMIT_SAME_DEV_pre30 (1<<10) /* protocols 28 - 29 */
+#define XMIT_USER_NAME_FOLLOWS (1<<10) /* protocols 30 - now */
+#define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
+#define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
+#define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
+#define XMIT_IO_ERROR_ENDLIST (1<<12) /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
+#define XMIT_MOD_NSEC (1<<13) /* protocols 31 - now */
+#define XMIT_SAME_ATIME (1<<14) /* any protocol - restricted by command-line option */
+#define XMIT_UNUSED_15 (1<<15) /* unused flag bit */
+
+/* The following XMIT flags require an rsync that uses a varint for the flag values */
+
+#define XMIT_RESERVED_16 (1<<16) /* reserved for future fileflags use */
+#define XMIT_CRTIME_EQ_MTIME (1<<17) /* any protocol - restricted by command-line option */
+
+/* These flags are used in the live flist data. */
+
+#define FLAG_TOP_DIR (1<<0) /* sender/receiver/generator */
+#define FLAG_OWNED_BY_US (1<<0) /* generator: set by make_file() for aux flists only */
+#define FLAG_FILE_SENT (1<<1) /* sender/receiver/generator */
+#define FLAG_DIR_CREATED (1<<1) /* generator */
+#define FLAG_CONTENT_DIR (1<<2) /* sender/receiver/generator */
+#define FLAG_MOUNT_DIR (1<<3) /* sender/generator (dirs only) */
+#define FLAG_SKIP_HLINK (1<<3) /* receiver/generator (w/FLAG_HLINKED) */
+#define FLAG_DUPLICATE (1<<4) /* sender */
+#define FLAG_MISSING_DIR (1<<4) /* generator */
+#define FLAG_HLINKED (1<<5) /* receiver/generator (checked on all types) */
+#define FLAG_HLINK_FIRST (1<<6) /* receiver/generator (w/FLAG_HLINKED) */
+#define FLAG_IMPLIED_DIR (1<<6) /* sender/receiver/generator (dirs only) */
+#define FLAG_HLINK_LAST (1<<7) /* receiver/generator */
+#define FLAG_HLINK_DONE (1<<8) /* receiver/generator (checked on all types) */
+#define FLAG_LENGTH64 (1<<9) /* sender/receiver/generator */
+#define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */
+#define FLAG_TIME_FAILED (1<<11)/* generator */
+#define FLAG_MOD_NSEC (1<<12) /* sender/receiver/generator */
+
+/* These flags are passed to functions but not stored. */
+
+#define FLAG_DIVERT_DIRS (1<<16) /* sender, but must be unique */
+#define FLAG_PERHAPS_DIR (1<<17) /* generator */
+
+/* These flags are for get_dirlist(). */
+#define GDL_IGNORE_FILTER_RULES (1<<0)
+#define GDL_PERHAPS_DIR (1<<1)
+
+/* Some helper macros for matching bits. */
+#define BITS_SET(val,bits) (((val) & (bits)) == (bits))
+#define BITS_SETnUNSET(val,onbits,offbits) (((val) & ((onbits)|(offbits))) == (onbits))
+#define BITS_EQUAL(b1,b2,mask) (((unsigned)(b1) & (unsigned)(mask)) \
+ == ((unsigned)(b2) & (unsigned)(mask)))
+
+/* Update this if you make incompatible changes and ALSO update the
+ * SUBPROTOCOL_VERSION if it is not a final (official) release. */
+#define PROTOCOL_VERSION 31
+
+/* This is used when working on a new protocol version or for any unofficial
+ * protocol tweaks. It should be a non-zero value for each pre-release repo
+ * change that affects the protocol. The official pre-release versions should
+ * start with 1 (after incrementing the PROTOCOL_VERSION) and go up by 1 for
+ * each new protocol change. For unofficial changes, pick a fairly large
+ * random number that will hopefully not collide with anyone else's unofficial
+ * protocol. It must ALWAYS be 0 when the protocol goes final (and official)
+ * and NEVER before! When rsync negotiates a protocol match, it will only
+ * allow the newest protocol to be used if the SUBPROTOCOL_VERSION matches.
+ * All older protocol versions MUST be compatible with the final, official
+ * release of the protocol, so don't tweak the code to change the protocol
+ * behavior for an older protocol version. */
+#define SUBPROTOCOL_VERSION 0
+
+/* We refuse to interoperate with versions that are not in this range.
+ * Note that we assume we'll work with later versions: the onus is on
+ * people writing them to make sure that they don't send us anything
+ * we won't understand.
+ *
+ * Interoperation with old but supported protocol versions
+ * should cause a warning to be printed. At a future date
+ * the old protocol will become the minimum and
+ * compatibility code removed.
+ *
+ * There are two possible explanations for the limit at
+ * MAX_PROTOCOL_VERSION: either to allow new major-rev versions that
+ * do not interoperate with us, and (more likely) so that we can
+ * detect an attempt to connect rsync to a non-rsync server, which is
+ * unlikely to begin by sending a byte between MIN_PROTOCL_VERSION and
+ * MAX_PROTOCOL_VERSION. */
+
+#define MIN_PROTOCOL_VERSION 20
+#define OLD_PROTOCOL_VERSION 25
+#define MAX_PROTOCOL_VERSION 40
+
+#define MIN_FILECNT_LOOKAHEAD 1000
+#define MAX_FILECNT_LOOKAHEAD 10000
+
+#define RSYNC_PORT 873
+
+#define SPARSE_WRITE_SIZE (1024)
+#define WRITE_SIZE (32*1024)
+#define CHUNK_SIZE (32*1024)
+#define MAX_MAP_SIZE (256*1024)
+#define IO_BUFFER_SIZE (32*1024)
+#define MAX_BLOCK_SIZE ((int32)1 << 17)
+
+/* For compatibility with older rsyncs */
+#define OLD_MAX_BLOCK_SIZE ((int32)1 << 29)
+
+#define ROUND_UP_1024(siz) ((siz) & (1024-1) ? ((siz) | (1024-1)) + 1 : (siz))
+
+#define IOERR_GENERAL (1<<0) /* For backward compatibility, this must == 1 */
+#define IOERR_VANISHED (1<<1)
+#define IOERR_DEL_LIMIT (1<<2)
+
+#define MAX_ARGS 1000
+#define MAX_BASIS_DIRS 20
+#define MAX_SERVER_ARGS (MAX_BASIS_DIRS*2 + 100)
+
+#define COMPARE_DEST 1
+#define COPY_DEST 2
+#define LINK_DEST 3
+
+#define MPLEX_BASE 7
+
+#define NO_FILTERS 0
+#define SERVER_FILTERS 1
+#define ALL_FILTERS 2
+
+#define XFLG_FATAL_ERRORS (1<<0)
+#define XFLG_OLD_PREFIXES (1<<1)
+#define XFLG_ANCHORED2ABS (1<<2) /* leading slash indicates absolute */
+#define XFLG_ABS_IF_SLASH (1<<3) /* leading or interior slash is absolute */
+#define XFLG_DIR2WILD3 (1<<4) /* dir/ match gets trailing *** added */
+
+#define ATTRS_REPORT (1<<0)
+#define ATTRS_SKIP_MTIME (1<<1)
+#define ATTRS_ACCURATE_TIME (1<<2)
+#define ATTRS_SKIP_ATIME (1<<3)
+#define ATTRS_SKIP_CRTIME (1<<5)
+
+#define MSG_FLUSH 2
+#define FULL_FLUSH 1
+#define NORMAL_FLUSH 0
+
+#define PDIR_CREATE 1
+#define PDIR_DELETE 0
+
+/* Note: 0x00 - 0x7F are used for basis_dir[] indexes! */
+#define FNAMECMP_BASIS_DIR_LOW 0x00 /* Must remain 0! */
+#define FNAMECMP_BASIS_DIR_HIGH 0x7F
+#define FNAMECMP_FNAME 0x80
+#define FNAMECMP_PARTIAL_DIR 0x81
+#define FNAMECMP_BACKUP 0x82
+#define FNAMECMP_FUZZY 0x83
+
+/* For use by the itemize_changes code */
+#define ITEM_REPORT_ATIME (1<<0)
+#define ITEM_REPORT_CHANGE (1<<1)
+#define ITEM_REPORT_SIZE (1<<2) /* regular files only */
+#define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
+#define ITEM_REPORT_TIME (1<<3)
+#define ITEM_REPORT_PERMS (1<<4)
+#define ITEM_REPORT_OWNER (1<<5)
+#define ITEM_REPORT_GROUP (1<<6)
+#define ITEM_REPORT_ACL (1<<7)
+#define ITEM_REPORT_XATTR (1<<8)
+#define ITEM_REPORT_CRTIME (1<<10)
+#define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
+#define ITEM_XNAME_FOLLOWS (1<<12)
+#define ITEM_IS_NEW (1<<13)
+#define ITEM_LOCAL_CHANGE (1<<14)
+#define ITEM_TRANSFER (1<<15)
+/* These are outside the range of the transmitted flags. */
+#define ITEM_MISSING_DATA (1<<16) /* used by log_formatted() */
+#define ITEM_DELETED (1<<17) /* used by log_formatted() */
+#define ITEM_MATCHED (1<<18) /* used by itemize() */
+
+#define SIGNIFICANT_ITEM_FLAGS (~(\
+ ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE))
+
+#define CFN_KEEP_DOT_DIRS (1<<0)
+#define CFN_KEEP_TRAILING_SLASH (1<<1)
+#define CFN_DROP_TRAILING_DOT_DIR (1<<2)
+#define CFN_COLLAPSE_DOT_DOT_DIRS (1<<3)
+#define CFN_REFUSE_DOT_DOT_DIRS (1<<4)
+
+#define SP_DEFAULT 0
+#define SP_KEEP_DOT_DIRS (1<<0)
+
+#define CD_NORMAL 0
+#define CD_SKIP_CHDIR 1
+
+/* Log-message categories. FLOG only goes to the log file, not the client;
+ * FCLIENT is the opposite. */
+enum logcode {
+ FNONE=0, /* never sent */
+ FERROR_XFER=1, FINFO=2, /* sent over socket for any protocol */
+ FERROR=3, FWARNING=4, /* sent over socket for protocols >= 30 */
+ FERROR_SOCKET=5, FLOG=6, /* only sent via receiver -> generator pipe */
+ FERROR_UTF8=8, /* only sent via receiver -> generator pipe */
+ FCLIENT=7 /* never transmitted (e.g. server converts to FINFO) */
+};
+
+/* Messages types that are sent over the message channel. The logcode
+ * values must all be present here with identical numbers. */
+enum msgcode {
+ MSG_DATA=0, /* raw data on the multiplexed stream */
+ MSG_ERROR_XFER=FERROR_XFER, MSG_INFO=FINFO, /* remote logging */
+ MSG_ERROR=FERROR, MSG_WARNING=FWARNING, /* protocol-30 remote logging */
+ MSG_ERROR_SOCKET=FERROR_SOCKET, /* sibling logging */
+ MSG_ERROR_UTF8=FERROR_UTF8, /* sibling logging */
+ MSG_LOG=FLOG, MSG_CLIENT=FCLIENT, /* sibling logging */
+ MSG_REDO=9, /* reprocess indicated flist index */
+ MSG_STATS=10, /* message has stats data for generator */
+ MSG_IO_ERROR=22,/* the sending side had an I/O error */
+ MSG_IO_TIMEOUT=33,/* tell client about a daemon's timeout value */
+ MSG_NOOP=42, /* a do-nothing message (legacy protocol-30 only) */
+ MSG_ERROR_EXIT=86, /* synchronize an error exit (siblings and protocol >= 31) */
+ MSG_SUCCESS=100,/* successfully updated indicated flist index */
+ MSG_DELETED=101,/* successfully deleted a file on receiving side */
+ MSG_NO_SEND=102,/* sender failed to open a file we wanted */
+};
+
+enum filetype {
+ FT_UNSUPPORTED, FT_REG, FT_DIR, FT_SYMLINK, FT_SPECIAL, FT_DEVICE
+};
+
+#define NDX_DONE -1
+#define NDX_FLIST_EOF -2
+#define NDX_DEL_STATS -3
+#define NDX_FLIST_OFFSET -101
+
+/* For calling delete_item() and delete_dir_contents(). */
+#define DEL_NO_UID_WRITE (1<<0) /* file/dir has our uid w/o write perm */
+#define DEL_RECURSE (1<<1) /* if dir, delete all contents */
+#define DEL_DIR_IS_EMPTY (1<<2) /* internal delete_FUNCTIONS use only */
+#define DEL_FOR_FILE (1<<3) /* making room for a replacement file */
+#define DEL_FOR_DIR (1<<4) /* making room for a replacement dir */
+#define DEL_FOR_SYMLINK (1<<5) /* making room for a replacement symlink */
+#define DEL_FOR_DEVICE (1<<6) /* making room for a replacement device */
+#define DEL_FOR_SPECIAL (1<<7) /* making room for a replacement special */
+#define DEL_FOR_BACKUP (1<<8) /* the delete is for a backup operation */
+
+#define DEL_MAKE_ROOM (DEL_FOR_FILE|DEL_FOR_DIR|DEL_FOR_SYMLINK|DEL_FOR_DEVICE|DEL_FOR_SPECIAL)
+
+enum delret {
+ DR_SUCCESS = 0, DR_FAILURE, DR_AT_LIMIT, DR_NOT_EMPTY
+};
+
+/* Defines for make_path() */
+#define MKP_DROP_NAME (1<<0) /* drop trailing filename or trailing slash */
+#define MKP_SKIP_SLASH (1<<1) /* skip one or more leading slashes */
+
+/* Defines for maybe_send_keepalive() */
+#define MSK_ALLOW_FLUSH (1<<0)
+#define MSK_ACTIVE_RECEIVER (1<<1)
+
+#include "errcode.h"
+
+#include "config.h"
+
+/* The default RSYNC_RSH is always set in config.h. */
+
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_BSD_STRING_H
+# include <bsd/string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#if defined HAVE_MALLOC_H && (defined HAVE_MALLINFO || !defined HAVE_STDLIB_H)
+#include <malloc.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <signal.h>
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#include <errno.h>
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
+#if defined HAVE_UTIMENSAT || defined HAVE_LUTIMES || defined HAVE_SETATTRLIST
+#define CAN_SET_SYMLINK_TIMES 1
+#endif
+
+#if defined HAVE_LCHOWN || defined CHOWN_MODIFIES_SYMLINK
+#define CAN_CHOWN_SYMLINK 1
+#endif
+
+#if defined HAVE_LCHMOD || defined HAVE_SETATTRLIST
+#define CAN_CHMOD_SYMLINK 1
+#endif
+
+#if defined HAVE_UTIMENSAT || defined HAVE_SETATTRLIST
+#define CAN_SET_NSEC 1
+#endif
+
+#ifdef CAN_SET_NSEC
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#define ST_MTIME_NSEC st_mtim.tv_nsec
+#define ST_ATIME_NSEC st_atim.tv_nsec
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
+#define ST_MTIME_NSEC st_mtimensec
+#define ST_ATIME_NSEC st_atimensec
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
+#define ST_MTIME_NSEC st_mtimespec.tv_nsec
+#define ST_ATIME_NSEC st_atimespec.tv_nsec
+#endif
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_MODE_H
+/* apparently AIX needs this for S_ISLNK */
+#ifndef S_ISLNK
+#include <sys/mode.h>
+#endif
+#endif
+
+/* these are needed for the uid/gid mapping code */
+#include <pwd.h>
+#include <grp.h>
+
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <syslog.h>
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#else
+# define dirent direct
+# ifdef HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
+# define makedev mkdev
+# endif
+#elif defined MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif
+
+#ifdef MAKEDEV_TAKES_3_ARGS
+#define MAKEDEV(devmajor,devminor) makedev(0,devmajor,devminor)
+#else
+#ifndef __TANDEM
+#define MAKEDEV(devmajor,devminor) makedev(devmajor,devminor)
+#else
+# define major DEV_TO_MAJOR
+# define minor DEV_TO_MINOR
+# define MAKEDEV MAJORMINOR_TO_DEV
+#endif
+#endif
+
+#ifdef __TANDEM
+# include <floss.h(floss_read,floss_write,floss_fork,floss_execvp)>
+# include <floss.h(floss_getpwuid,floss_select,floss_seteuid)>
+# define S_IEXEC S_IXUSR
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+
+#ifdef HAVE_COMPAT_H
+#include <compat.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#if defined USE_ICONV_OPEN && defined HAVE_ICONV_H
+#include <iconv.h>
+#ifndef ICONV_CONST
+#define ICONV_CONST
+#endif
+#else
+#ifdef ICONV_CONST
+#undef ICONV_CONST
+#endif
+#ifdef ICONV_OPTION
+#undef ICONV_OPTION
+#endif
+#ifdef iconv_t
+#undef iconv_t
+#endif
+#define iconv_t int
+#endif
+
+#include <assert.h>
+
+#include "lib/pool_alloc.h"
+
+#ifndef HAVE_ID_T
+typedef unsigned int id_t;
+#endif
+#ifndef HAVE_PID_T
+typedef int pid_t;
+#endif
+#ifndef HAVE_MODE_T
+typedef unsigned int mode_t;
+#endif
+#ifndef HAVE_OFF_T
+typedef long off_t;
+#undef SIZEOF_OFF_T
+#define SIZEOF_OFF_T SIZEOF_LONG
+#endif
+#ifndef HAVE_SIZE_T
+typedef unsigned int size_t;
+#endif
+
+#define BOOL int
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#ifdef SIGNED_CHAR_OK
+#define schar signed char
+#else
+#define schar char
+#endif
+
+#ifndef int16
+#if SIZEOF_INT16_T == 2
+# define int16 int16_t
+#else
+# define int16 short
+#endif
+#endif
+
+#ifndef uint16
+#if SIZEOF_UINT16_T == 2
+# define uint16 uint16_t
+#else
+# define uint16 unsigned int16
+#endif
+#endif
+
+#if !defined __APPLE__ || defined HAVE_GETATTRLIST
+#define SUPPORT_ATIMES 1
+#endif
+
+#if defined HAVE_GETATTRLIST || defined __CYGWIN__
+#define SUPPORT_CRTIMES 1
+#endif
+
+/* Find a variable that is either exactly 32-bits or longer.
+ * If some code depends on 32-bit truncation, it will need to
+ * take special action in a "#if SIZEOF_INT32 > 4" section. */
+#ifndef int32
+#if SIZEOF_INT32_T == 4
+# define int32 int32_t
+# define SIZEOF_INT32 4
+#elif SIZEOF_INT == 4
+# define int32 int
+# define SIZEOF_INT32 4
+#elif SIZEOF_LONG == 4
+# define int32 long
+# define SIZEOF_INT32 4
+#elif SIZEOF_SHORT == 4
+# define int32 short
+# define SIZEOF_INT32 4
+#elif SIZEOF_INT > 4
+# define int32 int
+# define SIZEOF_INT32 SIZEOF_INT
+#elif SIZEOF_LONG > 4
+# define int32 long
+# define SIZEOF_INT32 SIZEOF_LONG
+#else
+# error Could not find a 32-bit integer variable
+#endif
+#else
+# define SIZEOF_INT32 4
+#endif
+
+#ifndef uint32
+#if SIZEOF_UINT32_T == 4
+# define uint32 uint32_t
+#else
+# define uint32 unsigned int32
+#endif
+#endif
+
+#if SIZEOF_OFF_T == 8 || !SIZEOF_OFF64_T || !defined HAVE_STRUCT_STAT64
+#define OFF_T off_t
+#define STRUCT_STAT struct stat
+#define SIZEOF_CAPITAL_OFF_T SIZEOF_OFF_T
+#else
+#define OFF_T off64_t
+#define STRUCT_STAT struct stat64
+#define USE_STAT64_FUNCS 1
+#define SIZEOF_CAPITAL_OFF_T SIZEOF_OFF64_T
+#endif
+
+/* CAVEAT: on some systems, int64 will really be a 32-bit integer IFF
+ * that's the maximum size the file system can handle and there is no
+ * 64-bit type available. The rsync source must therefore take steps
+ * to ensure that any code that really requires a 64-bit integer has
+ * it (e.g. the checksum code uses two 32-bit integers for its 64-bit
+ * counter). */
+#if SIZEOF_INT64_T == 8
+# define int64 int64_t
+# define SIZEOF_INT64 8
+#elif SIZEOF_LONG == 8
+# define int64 long
+# define SIZEOF_INT64 8
+#elif SIZEOF_INT == 8
+# define int64 int
+# define SIZEOF_INT64 8
+#elif SIZEOF_LONG_LONG == 8
+# define int64 long long
+# define SIZEOF_INT64 8
+#elif SIZEOF_OFF64_T == 8
+# define int64 off64_t
+# define SIZEOF_INT64 8
+#elif SIZEOF_OFF_T == 8
+# define int64 off_t
+# define SIZEOF_INT64 8
+#elif SIZEOF_INT > 8
+# define int64 int
+# define SIZEOF_INT64 SIZEOF_INT
+#elif SIZEOF_LONG > 8
+# define int64 long
+# define SIZEOF_INT64 SIZEOF_LONG
+#elif SIZEOF_LONG_LONG > 8
+# define int64 long long
+# define SIZEOF_INT64 SIZEOF_LONG_LONG
+#else
+/* As long as it gets... */
+# define int64 off_t
+# define SIZEOF_INT64 SIZEOF_OFF_T
+#endif
+
+#define HT_KEY32 0
+#define HT_KEY64 1
+
+struct hashtable {
+ void *nodes;
+ int32 size, entries;
+ uint32 node_size;
+ short key64;
+};
+
+struct ht_int32_node {
+ void *data;
+ int32 key;
+};
+
+struct ht_int64_node {
+ void *data;
+ int64 key;
+};
+
+#define HT_NODE(tbl, bkts, i) ((void*)((char*)(bkts) + (i)*(tbl)->node_size))
+#define HT_KEY(node, k64) ((k64)? ((struct ht_int64_node*)(node))->key \
+ : (int64)((struct ht_int32_node*)(node))->key)
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#define SUM_LENGTH 16
+#define SHORT_SUM_LENGTH 2
+#define BLOCKSUM_BIAS 10
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+/* We want a roomy line buffer that can hold more than MAXPATHLEN,
+ * and significantly more than an overly short MAXPATHLEN. */
+#if MAXPATHLEN < 4096
+#define BIGPATHBUFLEN (4096+1024)
+#else
+#define BIGPATHBUFLEN (MAXPATHLEN+1024)
+#endif
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET 127
+#endif
+
+#if HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|HAVE_HPUX_ACLS
+#define ACLS_NEED_MASK 1
+#endif
+
+#if defined HAVE_FALLOCATE || HAVE_SYS_FALLOCATE
+#ifdef HAVE_LINUX_FALLOC_H
+#include <linux/falloc.h>
+#endif
+#ifdef FALLOC_FL_KEEP_SIZE
+#define SUPPORT_PREALLOCATION 1
+#elif defined HAVE_FTRUNCATE
+#define SUPPORT_PREALLOCATION 1
+#define PREALLOCATE_NEEDS_TRUNCATE 1
+#endif
+#else /* !fallocate */
+#if defined HAVE_EFFICIENT_POSIX_FALLOCATE && defined HAVE_FTRUNCATE
+#define SUPPORT_PREALLOCATION 1
+#define PREALLOCATE_NEEDS_TRUNCATE 1
+#endif
+#endif
+
+#if SIZEOF_CHARP == 4
+# define PTRS_ARE_32 1
+# define PTR_EXTRA_CNT 1
+#elif SIZEOF_CHARP == 8
+# define PTRS_ARE_64 1
+# define PTR_EXTRA_CNT EXTRA64_CNT
+#else
+# error Character pointers are not 4 or 8 bytes.
+#endif
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#define USE_FLEXIBLE_ARRAY 1
+#define SIZE_T_FMT_MOD "z" /* printf supports %zd */
+#define SIZE_T_FMT_CAST size_t
+#else
+#define SIZE_T_FMT_MOD "l" /* printf supports %ld */
+#define SIZE_T_FMT_CAST long
+#endif
+
+union file_extras {
+ int32 num;
+ uint32 unum;
+#ifdef PTRS_ARE_32
+ const char* ptr;
+#endif
+};
+
+union file_extras64 {
+ int64 num;
+#ifdef PTRS_ARE_64
+ const char* ptr;
+#endif
+};
+
+struct file_struct {
+ const char *dirname; /* The dir info inside the transfer */
+ time_t modtime; /* When the item was last modified */
+ uint32 len32; /* Lowest 32 bits of the file's length */
+ uint16 mode; /* The item's type and permissions */
+ uint16 flags; /* The FLAG_* bits for this item */
+#ifdef USE_FLEXIBLE_ARRAY
+ const char basename[]; /* The basename (AKA filename) follows */
+#else
+ const char basename[1]; /* A kluge that should work like a flexible array */
+#endif
+};
+
+extern int file_extra_cnt;
+extern int inc_recurse;
+extern int atimes_ndx;
+extern int crtimes_ndx;
+extern int pathname_ndx;
+extern int depth_ndx;
+extern int uid_ndx;
+extern int gid_ndx;
+extern int acls_ndx;
+extern int xattrs_ndx;
+extern int file_sum_extra_cnt;
+
+#ifdef USE_FLEXIBLE_ARRAY
+#define FILE_STRUCT_LEN (sizeof (struct file_struct))
+#else
+#define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
+#endif
+#define EXTRA_LEN (sizeof (union file_extras))
+#define DEV_EXTRA_CNT 2
+#define DIRNODE_EXTRA_CNT 3
+#define EXTRA64_CNT ((sizeof (union file_extras64) + EXTRA_LEN - 1) / EXTRA_LEN)
+#define SUM_EXTRA_CNT file_sum_extra_cnt
+
+#define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx))
+#define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump))
+
+/* These are guaranteed to be allocated first in the array so that they
+ * are aligned for direct int64-pointer access. */
+#define REQ_EXTRA64(f,ndx) ((union file_extras64*)REQ_EXTRA(f,ndx))
+
+#define NSEC_BUMP(f) ((f)->flags & FLAG_MOD_NSEC ? 1 : 0)
+#define LEN64_BUMP(f) ((f)->flags & FLAG_LENGTH64 ? 1 : 0)
+#define START_BUMP(f) (NSEC_BUMP(f) + LEN64_BUMP(f))
+#define HLINK_BUMP(f) ((f)->flags & (FLAG_HLINKED|FLAG_HLINK_DONE) ? inc_recurse+1 : 0)
+#define ACL_BUMP(f) (acls_ndx ? 1 : 0)
+
+/* The length applies to all items. */
+#if SIZEOF_INT64 < 8
+#define F_LENGTH(f) ((int64)(f)->len32)
+#else
+#define F_HIGH_LEN(f) (OPT_EXTRA(f, NSEC_BUMP(f))->unum)
+#define F_LENGTH(f) ((int64)(f)->len32 + ((f)->flags & FLAG_LENGTH64 ? (int64)F_HIGH_LEN(f) << 32 : 0))
+#endif
+
+#define F_MOD_NSEC(f) OPT_EXTRA(f, 0)->unum
+#define F_MOD_NSEC_or_0(f) ((f)->flags & FLAG_MOD_NSEC ? F_MOD_NSEC(f) : 0)
+
+/* If there is a symlink string, it is always right after the basename */
+#define F_SYMLINK(f) ((f)->basename + strlen((f)->basename) + 1)
+
+/* The sending side always has this available: */
+#ifdef PTRS_ARE_32
+#define F_PATHNAME(f) REQ_EXTRA(f, pathname_ndx)->ptr
+#else
+#define F_PATHNAME(f) REQ_EXTRA64(f, pathname_ndx)->ptr
+#endif
+
+/* The receiving side always has this available: */
+#define F_DEPTH(f) REQ_EXTRA(f, depth_ndx)->num
+
+/* When the associated option is on, all entries will have these present: */
+#define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
+#define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
+#define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
+#define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
+#define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
+#define F_ATIME(f) REQ_EXTRA64(f, atimes_ndx)->num
+#define F_CRTIME(f) REQ_EXTRA64(f, crtimes_ndx)->num
+
+/* These items are per-entry optional: */
+#define F_HL_GNUM(f) OPT_EXTRA(f, START_BUMP(f))->num /* non-dirs */
+#define F_HL_PREV(f) OPT_EXTRA(f, START_BUMP(f)+inc_recurse)->num /* non-dirs */
+#define F_DIR_NODE_P(f) (&OPT_EXTRA(f, START_BUMP(f) \
+ + DIRNODE_EXTRA_CNT - 1)->num) /* sender dirs */
+#define F_DIR_RELNAMES_P(f) (&OPT_EXTRA(f, START_BUMP(f) + DIRNODE_EXTRA_CNT \
+ + PTR_EXTRA_CNT - 1)->num) /* sender dirs */
+#define F_DIR_DEFACL(f) OPT_EXTRA(f, START_BUMP(f))->unum /* receiver dirs */
+#define F_DIR_DEV_P(f) (&OPT_EXTRA(f, START_BUMP(f) + ACL_BUMP(f) \
+ + DEV_EXTRA_CNT - 1)->unum) /* receiver dirs */
+
+/* This optional item might follow an F_HL_*() item. */
+#define F_RDEV_P(f) (&OPT_EXTRA(f, START_BUMP(f) + HLINK_BUMP(f) + DEV_EXTRA_CNT - 1)->unum)
+
+/* The sum is only present on regular files. */
+#define F_SUM(f) ((char*)OPT_EXTRA(f, START_BUMP(f) + HLINK_BUMP(f) \
+ + SUM_EXTRA_CNT - 1))
+
+/* Some utility defines: */
+#define F_IS_ACTIVE(f) (f)->basename[0]
+#define F_IS_HLINKED(f) ((f)->flags & FLAG_HLINKED)
+
+#define F_HLINK_NOT_FIRST(f) BITS_SETnUNSET((f)->flags, FLAG_HLINKED, FLAG_HLINK_FIRST)
+#define F_HLINK_NOT_LAST(f) BITS_SETnUNSET((f)->flags, FLAG_HLINKED, FLAG_HLINK_LAST)
+
+/* These access the F_DIR_DEV_P() and F_RDEV_P() values: */
+#define DEV_MAJOR(a) (a)[0]
+#define DEV_MINOR(a) (a)[1]
+
+/* These access the F_DIRS_NODE_P() values: */
+#define DIR_PARENT(a) (a)[0]
+#define DIR_FIRST_CHILD(a) (a)[1]
+#define DIR_NEXT_SIBLING(a) (a)[2]
+
+#define IS_MISSING_FILE(statbuf) ((statbuf).st_mode == 0)
+
+/*
+ * Start the flist array at FLIST_START entries and grow it
+ * by doubling until FLIST_LINEAR then grow by FLIST_LINEAR
+ */
+#define FLIST_START (32)
+#define FLIST_START_LARGE (32 * 1024)
+#define FLIST_LINEAR (FLIST_START_LARGE * 512)
+
+/*
+ * Extent size for allocation pools: A minimum size of 128KB
+ * is needed to mmap them so that freeing will release the
+ * space to the OS.
+ *
+ * Larger sizes reduce leftover fragments and speed free calls
+ * (when they happen). Smaller sizes increase the chance of
+ * freed allocations freeing whole extents.
+ */
+#define NORMAL_EXTENT (256 * 1024)
+#define SMALL_EXTENT (128 * 1024)
+
+#define FLIST_TEMP (1<<1)
+
+struct file_list {
+ struct file_list *next, *prev;
+ struct file_struct **files, **sorted;
+ alloc_pool_t file_pool;
+ void *pool_boundary;
+ int used, malloced;
+ int low, high; /* 0-relative index values excluding empties */
+ int ndx_start; /* the start offset for inc_recurse mode */
+ int flist_num; /* 1-relative file_list number or 0 */
+ int parent_ndx; /* dir_flist index of parent directory */
+ int in_progress, to_redo;
+};
+
+#define SUMFLG_SAME_OFFSET (1<<0)
+
+struct sum_buf {
+ OFF_T offset; /**< offset in file of this chunk */
+ int32 len; /**< length of chunk of file */
+ uint32 sum1; /**< simple checksum */
+ int32 chain; /**< next hash-table collision */
+ short flags; /**< flag bits */
+ char sum2[SUM_LENGTH]; /**< checksum */
+};
+
+struct sum_struct {
+ OFF_T flength; /**< total file length */
+ struct sum_buf *sums; /**< points to info for each chunk */
+ int32 count; /**< how many chunks */
+ int32 blength; /**< block_length */
+ int32 remainder; /**< flength % block_length */
+ int s2length; /**< sum2_length */
+};
+
+struct map_struct {
+ OFF_T file_size; /* File size (from stat) */
+ OFF_T p_offset; /* Window start */
+ OFF_T p_fd_offset; /* offset of cursor in fd ala lseek */
+ char *p; /* Window pointer */
+ int32 p_size; /* Largest window size we allocated */
+ int32 p_len; /* Latest (rounded) window size */
+ int32 def_window_size; /* Default window size */
+ int fd; /* File Descriptor */
+ int status; /* first errno from read errors */
+};
+
+#define NAME_IS_FILE (0) /* filter name as a file */
+#define NAME_IS_DIR (1<<0) /* filter name as a dir */
+#define NAME_IS_XATTR (1<<2) /* filter name as an xattr */
+
+#define FILTRULE_WILD (1<<0) /* pattern has '*', '[', and/or '?' */
+#define FILTRULE_WILD2 (1<<1) /* pattern has '**' */
+#define FILTRULE_WILD2_PREFIX (1<<2) /* pattern starts with "**" */
+#define FILTRULE_WILD3_SUFFIX (1<<3) /* pattern ends with "***" */
+#define FILTRULE_ABS_PATH (1<<4) /* path-match on absolute path */
+#define FILTRULE_INCLUDE (1<<5) /* this is an include, not an exclude */
+#define FILTRULE_DIRECTORY (1<<6) /* this matches only directories */
+#define FILTRULE_WORD_SPLIT (1<<7) /* split rules on whitespace */
+#define FILTRULE_NO_INHERIT (1<<8) /* don't inherit these rules */
+#define FILTRULE_NO_PREFIXES (1<<9) /* parse no prefixes from patterns */
+#define FILTRULE_MERGE_FILE (1<<10)/* specifies a file to merge */
+#define FILTRULE_PERDIR_MERGE (1<<11)/* merge-file is searched per-dir */
+#define FILTRULE_EXCLUDE_SELF (1<<12)/* merge-file name should be excluded */
+#define FILTRULE_FINISH_SETUP (1<<13)/* per-dir merge file needs setup */
+#define FILTRULE_NEGATE (1<<14)/* rule matches when pattern does not */
+#define FILTRULE_CVS_IGNORE (1<<15)/* rule was -C or :C */
+#define FILTRULE_SENDER_SIDE (1<<16)/* rule applies to the sending side */
+#define FILTRULE_RECEIVER_SIDE (1<<17)/* rule applies to the receiving side */
+#define FILTRULE_CLEAR_LIST (1<<18)/* this item is the "!" token */
+#define FILTRULE_PERISHABLE (1<<19)/* perishable if parent dir goes away */
+#define FILTRULE_XATTR (1<<20)/* rule only applies to xattr names */
+
+#define FILTRULES_SIDES (FILTRULE_SENDER_SIDE | FILTRULE_RECEIVER_SIDE)
+
+typedef struct filter_struct {
+ struct filter_struct *next;
+ char *pattern;
+ uint32 rflags;
+ union {
+ int slash_cnt;
+ struct filter_list_struct *mergelist;
+ } u;
+ uchar elide;
+} filter_rule;
+
+typedef struct filter_list_struct {
+ filter_rule *head;
+ filter_rule *tail;
+ char *debug_type;
+} filter_rule_list;
+
+struct stats {
+ int64 total_size;
+ int64 total_transferred_size;
+ int64 total_written;
+ int64 total_read;
+ int64 literal_data;
+ int64 matched_data;
+ int64 flist_buildtime;
+ int64 flist_xfertime;
+ int64 flist_size;
+ int num_files, num_dirs, num_symlinks, num_devices, num_specials;
+ int created_files, created_dirs, created_symlinks, created_devices, created_specials;
+ int deleted_files, deleted_dirs, deleted_symlinks, deleted_devices, deleted_specials;
+ int xferred_files;
+};
+
+struct chmod_mode_struct;
+
+struct flist_ndx_item {
+ struct flist_ndx_item *next;
+ int ndx;
+};
+
+typedef struct {
+ struct flist_ndx_item *head, *tail;
+} flist_ndx_list;
+
+#define EMPTY_ITEM_LIST {NULL, 0, 0}
+
+typedef struct {
+ void *items;
+ size_t count;
+ size_t malloced;
+} item_list;
+
+#define EXPAND_ITEM_LIST(lp, type, incr) \
+ (type*)expand_item_list(lp, sizeof (type), #type, incr)
+
+#define EMPTY_XBUF {NULL, 0, 0, 0}
+
+typedef struct {
+ char *buf;
+ size_t pos; /* pos = read pos in the buf */
+ size_t len; /* len = chars following pos */
+ size_t size; /* size = total space in buf */
+} xbuf;
+
+#define INIT_XBUF(xb, str, ln, sz) (xb).buf = (str), (xb).len = (ln), (xb).size = (sz), (xb).pos = 0
+#define INIT_XBUF_STRLEN(xb, str) (xb).buf = (str), (xb).len = strlen((xb).buf), (xb).size = (size_t)-1, (xb).pos = 0
+/* This one is used to make an output xbuf based on a char[] buffer: */
+#define INIT_CONST_XBUF(xb, bf) (xb).buf = (bf), (xb).size = sizeof (bf), (xb).len = (xb).pos = 0
+
+#define ICB_EXPAND_OUT (1<<0)
+#define ICB_INCLUDE_BAD (1<<1)
+#define ICB_INCLUDE_INCOMPLETE (1<<2)
+#define ICB_CIRCULAR_OUT (1<<3)
+#define ICB_INIT (1<<4)
+
+#define IOBUF_KEEP_BUFS 0
+#define IOBUF_FREE_BUFS 1
+
+#define MPLX_SWITCHING IOBUF_KEEP_BUFS
+#define MPLX_ALL_DONE IOBUF_FREE_BUFS
+#define MPLX_TO_BUFFERED 2
+
+#define RL_EOL_NULLS (1<<0)
+#define RL_DUMP_COMMENTS (1<<1)
+#define RL_CONVERT (1<<2)
+
+typedef struct {
+ char name_type;
+#ifdef USE_FLEXIBLE_ARRAY
+ char fname[]; /* has variable size */
+#else
+ char fname[1]; /* A kluge that should work like a flexible array */
+#endif
+} relnamecache;
+
+#ifdef USE_FLEXIBLE_ARRAY
+#define RELNAMECACHE_LEN (sizeof (relnamecache))
+#else
+#define RELNAMECACHE_LEN (offsetof(relnamecache, fname))
+#endif
+
+#include "byteorder.h"
+#include "lib/mdigest.h"
+#include "lib/wildmatch.h"
+#include "lib/permstring.h"
+#include "lib/addrinfo.h"
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#else
+# if __GNUC__ <= 2
+# define NORETURN
+# endif
+#endif
+
+#define UNUSED(x) x __attribute__((__unused__))
+#ifndef NORETURN
+#define NORETURN __attribute__((__noreturn__))
+#endif
+
+typedef struct {
+ STRUCT_STAT st;
+ time_t crtime;
+#ifdef SUPPORT_ACLS
+ struct rsync_acl *acc_acl; /* access ACL */
+ struct rsync_acl *def_acl; /* default ACL */
+#endif
+#ifdef SUPPORT_XATTRS
+ item_list *xattr;
+#endif
+} stat_x;
+
+#define ACL_READY(sx) ((sx).acc_acl != NULL)
+#define XATTR_READY(sx) ((sx).xattr != NULL)
+
+#define CLVL_NOT_SPECIFIED INT_MIN
+
+#define CPRES_AUTO (-1)
+#define CPRES_NONE 0
+#define CPRES_ZLIB 1
+#define CPRES_ZLIBX 2
+#define CPRES_LZ4 3
+#define CPRES_ZSTD 4
+
+#define NSTR_CHECKSUM 0
+#define NSTR_COMPRESS 1
+
+struct name_num_item {
+ int num, flags;
+ const char *name;
+ struct name_num_item *main_nni;
+};
+
+struct name_num_obj {
+ const char *type;
+ struct name_num_item *negotiated_nni;
+ uchar *saw;
+ int saw_len;
+ struct name_num_item *list;
+};
+
+#ifdef EXTERNAL_ZLIB
+#define read_buf read_buf_
+#endif
+
+#ifndef __cplusplus
+#include "proto.h"
+#endif
+
+#ifndef SUPPORT_XATTRS
+#define x_stat(fn,fst,xst) do_stat(fn,fst)
+#define x_lstat(fn,fst,xst) do_lstat(fn,fst)
+#define x_fstat(fd,fst,xst) do_fstat(fd,fst)
+#endif
+
+/* We have replacement versions of these if they're missing. */
+#ifndef HAVE_ASPRINTF
+int asprintf(char **ptr, const char *format, ...);
+#endif
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **ptr, const char *format, va_list ap);
+#endif
+
+#if !defined HAVE_VSNPRINTF || !defined HAVE_C99_VSNPRINTF
+#define vsnprintf rsync_vsnprintf
+int vsnprintf(char *str, size_t count, const char *fmt, va_list args);
+#endif
+
+#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
+#define snprintf rsync_snprintf
+int snprintf(char *str, size_t count, const char *fmt,...);
+#endif
+
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+#define strerror(i) sys_errlist[i]
+#endif
+
+#ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+#endif
+
+#ifndef HAVE_ERRNO_DECL
+extern int errno;
+#endif
+
+#ifdef HAVE_READLINK
+#define SUPPORT_LINKS 1
+#if !defined NO_SYMLINK_XATTRS && !defined NO_SYMLINK_USER_XATTRS
+#define do_readlink(path, buf, bufsiz) readlink(path, buf, bufsiz)
+#endif
+#endif
+#ifdef HAVE_LINK
+#define SUPPORT_HARD_LINKS 1
+#endif
+
+#ifdef HAVE_SIGACTION
+#define SIGACTION(n,h) sigact.sa_handler=(h), sigaction((n),&sigact,NULL)
+#define signal(n,h) we_need_to_call_SIGACTION_not_signal(n,h)
+#else
+#define SIGACTION(n,h) signal(n,h)
+#endif
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+#ifndef S_IRUSR
+#define S_IRUSR 0400
+#endif
+
+#ifndef S_IWUSR
+#define S_IWUSR 0200
+#endif
+
+#ifndef ACCESSPERMS
+#define ACCESSPERMS 0777
+#endif
+
+#ifndef S_ISVTX
+#define S_ISVTX 0
+#endif
+
+#define CHMOD_BITS (S_ISUID | S_ISGID | S_ISVTX | ACCESSPERMS)
+
+#ifndef _S_IFMT
+#define _S_IFMT 0170000
+#endif
+
+#ifndef _S_IFLNK
+#define _S_IFLNK 0120000
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(mode) (((mode) & (_S_IFMT)) == (_S_IFLNK))
+#endif
+
+#ifndef S_ISBLK
+#define S_ISBLK(mode) (((mode) & (_S_IFMT)) == (_S_IFBLK))
+#endif
+
+#ifndef S_ISCHR
+#define S_ISCHR(mode) (((mode) & (_S_IFMT)) == (_S_IFCHR))
+#endif
+
+#ifndef S_ISSOCK
+#ifdef _S_IFSOCK
+#define S_ISSOCK(mode) (((mode) & (_S_IFMT)) == (_S_IFSOCK))
+#else
+#define S_ISSOCK(mode) (0)
+#endif
+#endif
+
+#ifndef S_ISFIFO
+#ifdef _S_IFIFO
+#define S_ISFIFO(mode) (((mode) & (_S_IFMT)) == (_S_IFIFO))
+#else
+#define S_ISFIFO(mode) (0)
+#endif
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR))
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG))
+#endif
+
+/* work out what fcntl flag to use for non-blocking */
+#ifdef O_NONBLOCK
+# define NONBLOCK_FLAG O_NONBLOCK
+#elif defined SYSV
+# define NONBLOCK_FLAG O_NDELAY
+#else
+# define NONBLOCK_FLAG FNDELAY
+#endif
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#define IS_SPECIAL(mode) (S_ISSOCK(mode) || S_ISFIFO(mode))
+#define IS_DEVICE(mode) (S_ISCHR(mode) || S_ISBLK(mode))
+
+/* Initial mask on permissions given to temporary files. Mask off setuid
+ bits and group access because of potential race-condition security
+ holes, and mask other access because mode 707 is bizarre */
+#define INITACCESSPERMS 0700
+
+/* handler for null strings in printf format */
+#define NS(s) ((s)?(s):"<NULL>")
+
+extern char *do_calloc;
+
+/* Convenient wrappers for malloc and realloc. Use them. */
+#define new(type) ((type*)my_alloc(NULL, sizeof (type), 1, __FILE__, __LINE__))
+#define new0(type) ((type*)my_alloc(do_calloc, sizeof (type), 1, __FILE__, __LINE__))
+#define realloc_buf(ptr, num) my_alloc((ptr), (num), 1, __FILE__, __LINE__)
+
+#define new_array(type, num) ((type*)my_alloc(NULL, (num), sizeof (type), __FILE__, __LINE__))
+#define new_array0(type, num) ((type*)my_alloc(do_calloc, (num), sizeof (type), __FILE__, __LINE__))
+#define realloc_array(ptr, type, num) ((type*)my_alloc((ptr), (num), sizeof (type), __FILE__, __LINE__))
+
+#undef strdup
+#define strdup(s) my_strdup(s, __FILE__, __LINE__)
+
+#define out_of_memory(msg) _out_of_memory(msg, __FILE__, __LINE__)
+#define overflow_exit(msg) _overflow_exit(msg, __FILE__, __LINE__)
+
+/* use magic gcc attributes to catch format errors */
+ void rprintf(enum logcode , const char *, ...)
+ __attribute__((format (printf, 2, 3)))
+;
+
+/* This is just like rprintf, but it also tries to print some
+ * representation of the error code. Normally errcode = errno. */
+void rsyserr(enum logcode, int, const char *, ...)
+ __attribute__((format (printf, 3, 4)))
+ ;
+
+/* Make sure that the O_BINARY flag is defined. */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat) ((int)(((stat)>>8)&0xFF))
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat) ((int)((stat)&0xFF) == 0)
+#endif
+
+#define exit_cleanup(code) _exit_cleanup(code, __FILE__, __LINE__)
+
+#ifdef HAVE_GETEUID
+#define MY_UID() geteuid()
+#else
+#define MY_UID() getuid()
+#endif
+
+#ifdef HAVE_GETEGID
+#define MY_GID() getegid()
+#else
+#define MY_GID() getgid()
+#endif
+
+#ifdef FORCE_FD_ZERO_MEMSET
+#undef FD_ZERO
+#define FD_ZERO(fdsetp) memset(fdsetp, 0, sizeof (fd_set))
+#endif
+
+extern short info_levels[], debug_levels[];
+
+#define INFO_GTE(flag, lvl) (info_levels[INFO_##flag] >= (lvl))
+#define INFO_EQ(flag, lvl) (info_levels[INFO_##flag] == (lvl))
+#define DEBUG_GTE(flag, lvl) (debug_levels[DEBUG_##flag] >= (lvl))
+#define DEBUG_EQ(flag, lvl) (debug_levels[DEBUG_##flag] == (lvl))
+
+#define INFO_BACKUP 0
+#define INFO_COPY (INFO_BACKUP+1)
+#define INFO_DEL (INFO_COPY+1)
+#define INFO_FLIST (INFO_DEL+1)
+#define INFO_MISC (INFO_FLIST+1)
+#define INFO_MOUNT (INFO_MISC+1)
+#define INFO_NAME (INFO_MOUNT+1)
+#define INFO_NONREG (INFO_NAME+1)
+#define INFO_PROGRESS (INFO_NONREG+1)
+#define INFO_REMOVE (INFO_PROGRESS+1)
+#define INFO_SKIP (INFO_REMOVE+1)
+#define INFO_STATS (INFO_SKIP+1)
+#define INFO_SYMSAFE (INFO_STATS+1)
+
+#define COUNT_INFO (INFO_SYMSAFE+1)
+
+#define DEBUG_ACL 0
+#define DEBUG_BACKUP (DEBUG_ACL+1)
+#define DEBUG_BIND (DEBUG_BACKUP+1)
+#define DEBUG_CHDIR (DEBUG_BIND+1)
+#define DEBUG_CONNECT (DEBUG_CHDIR+1)
+#define DEBUG_CMD (DEBUG_CONNECT+1)
+#define DEBUG_DEL (DEBUG_CMD+1)
+#define DEBUG_DELTASUM (DEBUG_DEL+1)
+#define DEBUG_DUP (DEBUG_DELTASUM+1)
+#define DEBUG_EXIT (DEBUG_DUP+1)
+#define DEBUG_FILTER (DEBUG_EXIT+1)
+#define DEBUG_FLIST (DEBUG_FILTER+1)
+#define DEBUG_FUZZY (DEBUG_FLIST+1)
+#define DEBUG_GENR (DEBUG_FUZZY+1)
+#define DEBUG_HASH (DEBUG_GENR+1)
+#define DEBUG_HLINK (DEBUG_HASH+1)
+#define DEBUG_ICONV (DEBUG_HLINK+1)
+#define DEBUG_IO (DEBUG_ICONV+1)
+#define DEBUG_NSTR (DEBUG_IO+1)
+#define DEBUG_OWN (DEBUG_NSTR+1)
+#define DEBUG_PROTO (DEBUG_OWN+1)
+#define DEBUG_RECV (DEBUG_PROTO+1)
+#define DEBUG_SEND (DEBUG_RECV+1)
+#define DEBUG_TIME (DEBUG_SEND+1)
+
+#define COUNT_DEBUG (DEBUG_TIME+1)
+
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+#ifndef HAVE_INET_PTON
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#ifndef HAVE_GETPASS
+char *getpass(const char *prompt);
+#endif
+
+#ifdef MAINTAINER_MODE
+const char *get_panic_action(void);
+#endif
+
+#define NOISY_DEATH(msg) do { \
+ fprintf(stderr, "%s in %s at line %d\n", msg, __FILE__, __LINE__); \
+ exit_cleanup(RERR_UNSUPPORTED); \
+} while (0)
+
+#ifdef HAVE_MALLINFO2
+#define MEM_ALLOC_INFO mallinfo2
+#elif defined HAVE_MALLINFO
+#define MEM_ALLOC_INFO mallinfo
+#endif
diff --git a/rsync3.txt b/rsync3.txt
new file mode 100644
index 0000000..e21f19f
--- /dev/null
+++ b/rsync3.txt
@@ -0,0 +1,467 @@
+-*- indented-text -*-
+
+Notes towards a new version of rsync
+Martin Pool <mbp@samba.org>, September 2001.
+
+
+Good things about the current implementation:
+
+ - Widely known and adopted.
+
+ - Fast/efficient, especially for moderately small sets of files over
+ slow links (transoceanic or modem.)
+
+ - Fairly reliable.
+
+ - The choice of running over a plain TCP socket or tunneling over
+ ssh.
+
+ - rsync operations are idempotent: you can always run the same
+ command twice to make sure it worked properly without any fear.
+ (Are there any exceptions?)
+
+ - Small changes to files cause small deltas.
+
+ - There is a way to evolve the protocol to some extent.
+
+ - rdiff and rsync --write-batch allow generation of standalone patch
+ sets. rsync+ is pretty cheesy, though. xdelta seems cleaner.
+
+ - Process triangle is creative, but seems to provoke OS bugs.
+
+ - "Morning-after property": you don't need to know anything on the
+ local machine about the state of the remote machine, or about
+ transfers that have been done in the past.
+
+ - You can easily push or pull simply by switching the order of
+ files.
+
+ - The "modules" system has some neat features compared to
+ e.g. Apache's per-directory configuration. In particular, because
+ you can set a userid and chroot directory, there is strong
+ protection between different modules. I haven't seen any calls
+ for a more flexible system.
+
+
+Bad things about the current implementation:
+
+ - Persistent and hard-to-diagnose hang bugs remain
+
+ - Protocol is sketchily documented, tied to this implementation, and
+ hard to modify/extend
+
+ - Both the program and the protocol assume a single non-interactive
+ one-way transfer
+
+ - A list of all files are held in memory for the entire transfer,
+ which cripples scalability to large file trees
+
+ - Opening a new socket for every operation causes problems,
+ especially when running over SSH with password authentication.
+
+ - Renamed files are not handled: the old file is removed, and the
+ new file created from scratch.
+
+ - The versioning approach assumes that future versions of the
+ program know about all previous versions, and will do the right
+ thing.
+
+ - People always get confused about ':' vs '::'
+
+ - Error messages can be cryptic.
+
+ - Default behaviour is not intuitive: in too many cases rsync will
+ happily do nothing. Perhaps -a should be the default?
+
+ - People get confused by trailing slashes, though it's hard to think
+ of another reasonable way to make this necessary distinction
+ between a directory and its contents.
+
+
+Protocol philosophy:
+
+ *The* big difference between protocols like HTTP, FTP, and NFS is
+ that their fundamental operations are "read this file", "delete
+ this file", and "make this directory", whereas rsync is "make this
+ directory like this one".
+
+
+Questionable features:
+
+ These are neat, but not necessarily clean or worth preserving.
+
+ - The remote rsync can be wrapped by some other program, such as in
+ tridge's rsync-mail scripts. The general feature of sending and
+ retrieving mail over rsync is good, but this is perhaps not the
+ right way to implement it.
+
+
+Desirable features:
+
+ These don't really require architectural changes; they're just
+ something to keep in mind.
+
+ - Synchronize ACLs and extended attributes
+
+ - Anonymous servers should be efficient
+
+ - Code should be portable to non-UNIX systems
+
+ - Should be possible to document the protocol in RFC form
+
+ - --dry-run option
+
+ - IPv6 support. Pretty straightforward.
+
+ - Allow the basis and destination files to be different. For
+ example, you could use this when you have a CD-ROM and want to
+ download an updated image onto a hard drive.
+
+ - Efficiently interrupt and restart a transfer. We can write a
+ checkpoint file that says where we're up to in the filesystem.
+ Alternatively, as long as transfers are idempotent, we can just
+ restart the whole thing. [NFSv4]
+
+ - Scripting support.
+
+ - Propagate atimes and do not modify them. This is very ugly on
+ Unix. It might be better to try to add O_NOATIME to kernels, and
+ call that.
+
+ - Unicode. Probably just use UTF-8 for everything.
+
+ - Open authentication system. Can we use PAM? Is SASL an adequate
+ mapping of PAM to the network, or useful in some other way?
+
+ - Resume interrupted transfers without the --partial flag. We need
+ to leave the temporary file behind, and then know to use it. This
+ leaves a risk of large temporary files accumulating, which is not
+ good. Perhaps it should be off by default.
+
+ - tcpwrappers support. Should be trivial; can already be done
+ through tcpd or inetd.
+
+ - Socks support built in. It's not clear this is any better than
+ just linking against the socks library, though.
+
+ - When run over SSH, invoke with predictable command-line arguments,
+ so that people can restrict what commands sshd will run. (Is this
+ really required?)
+
+ - Comparison mode: give a list of which files are new, gone, or
+ different. Set return code depending on whether anything has
+ changed.
+
+ - Internationalized messages (gettext?)
+
+ - Optionally use real regexps rather than globs?
+
+ - Show overall progress. Pretty hard to do, especially if we insist
+ on not scanning the directory tree up front.
+
+
+Regression testing:
+
+ - Support automatic testing.
+
+ - Have hard internal timeouts against hangs.
+
+ - Be deterministic.
+
+ - Measure performance.
+
+
+Hard links:
+
+ At the moment, we can recreate hard links, but it's a bit
+ inefficient: it depends on holding a list of all files in the tree.
+ Every time we see a file with a linkcount >1, we need to search for
+ another known name that has the same (fsid,inum) tuple. We could do
+ that more efficiently by keeping a list of only files with
+ linkcount>1, and removing files from that list as all their names
+ become known.
+
+
+Command-line options:
+
+ We have rather a lot at the moment. We might get more if the tool
+ becomes more flexible. Do we need a .rc or configuration file?
+ That wouldn't really fit with its pattern of use: cp and tar don't
+ have them, though ssh does.
+
+
+Scripting issues:
+
+ - Perhaps support multiple scripting languages: candidates include
+ Perl, Python, Tcl, Scheme (guile?), sh, ...
+
+ - Simply running a subprocess and looking at its stdout/exit code
+ might be sufficient, though it could also be pretty slow if it's
+ called often.
+
+ - There are security issues about running remote code, at least if
+ it's not running in the users own account. So we can either
+ disallow it, or use some kind of sandbox system.
+
+ - Python is a good language, but the syntax is not so good for
+ giving small fragments on the command line.
+
+ - Tcl is broken Lisp.
+
+ - Lots of sysadmins know Perl, though Perl can give some bizarre or
+ confusing errors. The built in stat operators and regexps might
+ be useful.
+
+ - Sadly probably not enough people know Scheme.
+
+ - sh is hard to embed.
+
+
+Scripting hooks:
+
+ - Whether to transfer a file
+
+ - What basis file to use
+
+ - Logging
+
+ - Whether to allow transfers (for public servers)
+
+ - Authentication
+
+ - Locking
+
+ - Cache
+
+ - Generating backup path/name.
+
+ - Post-processing of backups, e.g. to do compression.
+
+ - After transfer, before replacement: so that we can spit out a diff
+ of what was changed, or kick off some kind of reconciliation
+ process.
+
+
+VFS:
+
+ Rather than talking straight to the filesystem, rsyncd talks through
+ an internal API. Samba has one. Is it useful?
+
+ - Could be a tidy way to implement cached signatures.
+
+ - Keep files compressed on disk?
+
+
+Interactive interface:
+
+ - Something like ncFTP, or integration into GNOME-vfs. Probably
+ hold a single socket connection open.
+
+ - Can either call us as a separate process, or as a library.
+
+ - The standalone process needs to produce output in a form easily
+ digestible by a calling program, like the --emacs feature some
+ have. Same goes for output: rpm outputs a series of hash symbols,
+ which are easier for a GUI to handle than "\r30% complete"
+ strings.
+
+ - Yow! emacs support. (You could probably build that already, of
+ course.) I'd like to be able to write a simple script on a remote
+ machine that rsyncs it to my workstation, edits it there, then
+ pushes it back up.
+
+
+Pie-in-the-sky features:
+
+ These might have a severe impact on the protocol, and are not
+ clearly in our core requirements. It looks like in many of them
+ having scripting hooks will allow us
+
+ - Transport over UDP multicast. The hard part is handling multiple
+ destinations which have different basis files. We can look at
+ multicast-TFTP for inspiration.
+
+ - Conflict resolution. Possibly general scripting support will be
+ sufficient.
+
+ - Integrate with locking. It's hard to see a good general solution,
+ because Unix systems have several locking mechanisms, and grabbing
+ the lock from programs that don't expect it could cause deadlocks,
+ timeouts, or other problems. Scripting support might help.
+
+ - Replicate in place, rather than to a temporary file. This is
+ dangerous in the case of interruption, and it also means that the
+ delta can't refer to blocks that have already been overwritten.
+ On the other hand we could semi-trivially do this at first by
+ simply generating a delta with no copy instructions.
+
+ - Replicate block devices. Most of the difficulties here are to do
+ with replication in place, though on some systems we will also
+ have to do I/O on block boundaries.
+
+ - Peer to peer features. Flavour of the year. Can we think about
+ ways for clients to smoothly and voluntarily become servers for
+ content they receive?
+
+ - Imagine a situation where the destination has a much faster link
+ to the cloud than the source. In this case, Mojo Nation downloads
+ interleaved blocks from several slower servers. The general
+ situation might be a way for a master rsync process to farm out
+ tasks to several subjobs. In this particular case they'd need
+ different sockets. This might be related to multicast.
+
+
+Unlikely features:
+
+ - Allow remote source and destination. If this can be cleanly
+ designed into the protocol, perhaps with the remote machine acting
+ as a kind of echo, then it's good. It's uncommon enough that we
+ don't want to shape the whole protocol around it, though.
+
+ In fact, in a triangle of machines there are two possibilities:
+ all traffic passes from remote1 to remote2 through local, or local
+ just sets up the transfer and then remote1 talks to remote2. FTP
+ supports the second but it's not clearly good. There are some
+ security problems with being able to instruct one machine to open
+ a connection to another.
+
+
+In favour of evolving the protocol:
+
+ - Keeping compatibility with existing rsync servers will help with
+ adoption and testing.
+
+ - We should at the very least be able to fall back to the new
+ protocol.
+
+ - Error handling is not so good.
+
+
+In favour of using a new protocol:
+
+ - Maintaining compatibility might soak up development time that
+ would better go into improving a new protocol.
+
+ - If we start from scratch, it can be documented as we go, and we
+ can avoid design decisions that make the protocol complex or
+ implementation-bound.
+
+
+Error handling:
+
+ - Errors should come back reliably, and be clearly associated with
+ the particular file that caused the problem.
+
+ - Some errors ought to cause the whole transfer to abort; some are
+ just warnings. If any errors have occurred, then rsync ought to
+ return an error.
+
+
+Concurrency:
+
+ - We want to keep the CPU, filesystem, and network as full as
+ possible as much of the time as possible.
+
+ - We can do nonblocking network IO, but not so for disk.
+
+ - It makes sense to on the destination be generating signatures and
+ applying patches at the same time.
+
+ - Can structure this with nonblocking, threads, separate processes,
+ etc.
+
+
+Uses:
+
+ - Mirroring software distributions:
+
+ - Synchronizing laptop and desktop
+
+ - NFS filesystem migration/replication. See
+ http://www.ietf.org/proceedings/00jul/00july-133.htm#P24510_1276764
+
+ - Sync with PDA
+
+ - Network backup systems
+
+ - CVS filemover
+
+
+Conflict resolution:
+
+ - Requires application-specific knowledge. We want to provide
+ policy, rather than mechanism.
+
+ - Possibly allowing two-way migration across a single connection
+ would be useful.
+
+
+Moved files:
+
+ - There's no trivial way to detect renamed files, especially if they
+ move between directories.
+
+ - If we had a picture of the remote directory from last time on
+ either machine, then the inode numbers might give us a hint about
+ files which may have been renamed.
+
+ - Files that are renamed and not modified can be detected by
+ examining the directory listing, looking for files with the same
+ size/date as the origin.
+
+
+Filesystem migration:
+
+ NFSv4 probably wants to migrate file locks, but that's not really
+ our problem.
+
+
+Atomic updates:
+
+ The NFSv4 working group wants atomic migration. Most of the
+ responsibility for this lies on the NFS server or OS.
+
+ If migrating a whole tree, then we could do a nearly-atomic rename
+ at the end. This ties in to having separate basis and destination
+ files.
+
+ There's no way in Unix to replace a whole set of files atomically.
+ However, if we get them all onto the destination machine and then do
+ the updates quickly it would greatly reduce the window.
+
+
+Scalability:
+
+ We should aim to work well on machines in use in a year or two.
+ That probably means transfers of many millions of files in one
+ batch, and gigabytes or terabytes of data.
+
+ For argument's sake: at the low end, we want to sync ten files for a
+ total of 10kb across a 1kB/s link. At the high end, we want to sync
+ 1e9 files for 1TB of data across a 1GB/s link.
+
+ On the whole CPU usage is not normally a limiting factor, if only
+ because running over SSH burns a lot of cycles on encryption.
+
+ Perhaps have resource throttling without relying on rlimit.
+
+
+Streaming:
+
+ A big attraction of rsync is that there are few round-trip delays:
+ basically only one to get started, and then everything is
+ pipelined. This is a problem with FTP, and NFS (at least up to
+ v3). NFSv4 can pipeline operations, but building on that is
+ probably a bit complicated.
+
+
+Related work:
+
+ - mirror.pl
+
+ - ProFTPd
+
+ - Apache
+
+ - BitTorrent -- p2p mirroring
+ http://bitconjurer.org/BitTorrent/
diff --git a/rsyncd.conf.5 b/rsyncd.conf.5
new file mode 100644
index 0000000..249edd4
--- /dev/null
+++ b/rsyncd.conf.5
@@ -0,0 +1,1313 @@
+.TH "rsyncd.conf" "5" "20 Oct 2022" "rsyncd.conf from rsync 3.2.7" "User Commands"
+.\" prefix=/usr
+.P
+.SH "NAME"
+.P
+rsyncd.conf \- configuration file for rsync in daemon mode
+.P
+.SH "SYNOPSIS"
+.P
+rsyncd.conf
+.P
+The online version of this manpage (that includes cross-linking of topics)
+is available at https://download.samba.org/pub/rsync/rsyncd.conf.5.
+.P
+.SH "DESCRIPTION"
+.P
+The rsyncd.conf file is the runtime configuration file for rsync when run as an
+rsync daemon.
+.P
+The rsyncd.conf file controls authentication, access, logging and available
+modules.
+.P
+.SH "FILE FORMAT"
+.P
+The file consists of modules and parameters. A module begins with the name of
+the module in square brackets and continues until the next module begins.
+Modules contain parameters of the form \fBname\ =\ value\fP.
+.P
+The file is line-based\ \-\- that is, each newline-terminated line represents
+either a comment, a module name or a parameter.
+.P
+Only the first equals sign in a parameter is significant. Whitespace before or
+after the first equals sign is discarded. Leading, trailing and internal
+whitespace in module and parameter names is irrelevant. Leading and trailing
+whitespace in a parameter value is discarded. Internal whitespace within a
+parameter value is retained verbatim.
+.P
+Any line \fBbeginning\fP with a hash (\fB#\fP) is ignored, as are lines containing
+only whitespace. (If a hash occurs after anything other than leading
+whitespace, it is considered a part of the line's content.)
+.P
+Any line ending in a \fB\\\fP is "continued" on the next line in the customary UNIX
+fashion.
+.P
+The values following the equals sign in parameters are all either a string (no
+quotes needed) or a boolean, which may be given as yes/no, 0/1 or true/false.
+Case is not significant in boolean values, but is preserved in string values.
+.P
+.SH "LAUNCHING THE RSYNC DAEMON"
+.P
+The rsync daemon is launched by specifying the \fB\-\-daemon\fP option to rsync.
+.P
+The daemon must run with root privileges if you wish to use chroot, to bind to
+a port numbered under 1024 (as is the default 873), or to set file ownership.
+Otherwise, it must just have permission to read and write the appropriate data,
+log, and lock files.
+.P
+You can launch it either via inetd, as a stand-alone daemon, or from an rsync
+client via a remote shell. If run as a stand-alone daemon then just run the
+command "\fBrsync\ \-\-daemon\fP" from a suitable startup script.
+.P
+When run via inetd you should add a line like this to /etc/services:
+.RS 4
+.P
+.nf
+rsync 873/tcp
+.fi
+.RE
+.P
+and a single line something like this to /etc/inetd.conf:
+.RS 4
+.P
+.nf
+rsync stream tcp nowait root /usr/bin/rsync rsyncd --daemon
+.fi
+.RE
+.P
+Replace "/usr/bin/rsync" with the path to where you have rsync installed on
+your system. You will then need to send inetd a HUP signal to tell it to
+reread its config file.
+.P
+Note that you should \fBnot\fP send the rsync daemon a HUP signal to force it to
+reread the \fBrsyncd.conf\fP file. The file is re-read on each client connection.
+.P
+.SH "GLOBAL PARAMETERS"
+.P
+The first parameters in the file (before a [module] header) are the global
+parameters. Rsync also allows for the use of a "[global]" module name to
+indicate the start of one or more global-parameter sections (the name must be
+lower case).
+.P
+You may also include any module parameters in the global part of the config
+file in which case the supplied value will override the default for that
+parameter.
+.P
+You may use references to environment variables in the values of parameters.
+String parameters will have %VAR% references expanded as late as possible (when
+the string is first used in the program), allowing for the use of variables
+that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
+parameters (such as true/false settings) are expanded when read from the config
+file. If a variable does not exist in the environment, or if a sequence of
+characters is not a valid reference (such as an un-paired percent sign), the
+raw characters are passed through unchanged. This helps with backward
+compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
+string in a path could result in a very unsafe path). The safest way to insert
+a literal % into a value is to use %%.
+.P
+.IP "\fBmotd\ file\fP"
+This parameter allows you to specify a "message of the day" (MOTD) to display
+to clients on each connect. This usually contains site information and any
+legal notices. The default is no MOTD file. This can be overridden by the
+\fB\-\-dparam=motdfile=FILE\fP command-line option when starting the daemon.
+.IP "\fBpid\ file\fP"
+This parameter tells the rsync daemon to write its process ID to that file.
+The rsync keeps the file locked so that it can know when it is safe to
+overwrite an existing file.
+.IP
+The filename can be overridden by the \fB\-\-dparam=pidfile=FILE\fP command-line
+option when starting the daemon.
+.IP "\fBport\fP"
+You can override the default port the daemon will listen on by specifying
+this value (defaults to 873). This is ignored if the daemon is being run
+by inetd, and is superseded by the \fB\-\-port\fP command-line option.
+.IP "\fBaddress\fP"
+You can override the default IP address the daemon will listen on by
+specifying this value. This is ignored if the daemon is being run by
+inetd, and is superseded by the \fB\-\-address\fP command-line option.
+.IP "\fBsocket\ options\fP"
+This parameter can provide endless fun for people who like to tune their
+systems to the utmost degree. You can set all sorts of socket options which
+may make transfers faster (or slower!). Read the manpage for the
+\fBsetsockopt()\fP system call for details on some of the options you may be
+able to set. By default no special socket options are set. These settings
+can also be specified via the \fB\-\-sockopts\fP command-line option.
+.IP "\fBlisten\ backlog\fP"
+You can override the default backlog value when the daemon listens for
+connections. It defaults to 5.
+.P
+.SH "MODULE PARAMETERS"
+.P
+After the global parameters you should define a number of modules, each module
+exports a directory tree as a symbolic name. Modules are exported by specifying
+a module name in square brackets [module] followed by the parameters for that
+module. The module name cannot contain a slash or a closing square bracket.
+If the name contains whitespace, each internal sequence of whitespace will be
+changed into a single space, while leading or trailing whitespace will be
+discarded. Also, the name cannot be "global" as that exact name indicates that
+global parameters follow (see above).
+.P
+As with GLOBAL PARAMETERS, you may use references to environment variables in
+the values of parameters. See the GLOBAL PARAMETERS section for more details.
+.P
+.IP "\fBcomment\fP"
+This parameter specifies a description string that is displayed next to the
+module name when clients obtain a list of available modules. The default is
+no comment.
+.IP "\fBpath\fP"
+This parameter specifies the directory in the daemon's filesystem to make
+available in this module. You must specify this parameter for each module
+in \fBrsyncd.conf\fP.
+.IP
+If the value contains a "/./" element then the path will be divided at that
+point into a chroot dir and an inner-chroot subdir. If \fBuse\ chroot\fP
+is set to false, though, the extraneous dot dir is just cleaned out of the
+path. An example of this idiom is:
+.RS 4
+.IP
+.nf
+path = /var/rsync/./module1
+.fi
+.RE
+.IP
+This will (when chrooting) chroot to "/var/rsync" and set the inside-chroot
+path to "/module1".
+.IP
+You may base the path's value off of an environment variable by surrounding
+the variable name with percent signs. You can even reference a variable
+that is set by rsync when the user connects. For example, this would use
+the authorizing user's name in the path:
+.RS 4
+.IP
+.nf
+path = /home/%RSYNC_USER_NAME%
+.fi
+.RE
+.IP
+It is fine if the path includes internal spaces\ \-\- they will be retained
+verbatim (which means that you shouldn't try to escape them). If your
+final directory has a trailing space (and this is somehow not something you
+wish to fix), append a trailing slash to the path to avoid losing the
+trailing whitespace.
+.IP "\fBuse\ chroot\fP"
+If "use chroot" is true, the rsync daemon will chroot to the "path" before
+starting the file transfer with the client. This has the advantage of
+extra protection against possible implementation security holes, but it has
+the disadvantages of requiring super-user privileges, of not being able to
+follow symbolic links that are either absolute or outside of the new root
+path, and of complicating the preservation of users and groups by name (see
+below).
+.IP
+If \fBuse\ chroot\fP is not set, it defaults to trying to enable a chroot but
+allows the daemon to continue (after logging a warning) if it fails. The
+one exception to this is when a module's \fBpath\fP has a "/./" chroot
+divider in it\ \-\- this causes an unset value to be treated as true for that
+module.
+.IP
+Prior to rsync 3.2.7, the default value was "true". The new "unset"
+default makes it easier to setup an rsync daemon as a non-root user or to
+run a daemon on a system where chroot fails. Explicitly setting the value
+to "true" in rsyncd.conf will always require the chroot to succeed.
+.IP
+It is also possible to specify a dot-dir in the module's "path" to
+indicate that you want to chdir to the earlier part of the path and then
+serve files from inside the latter part of the path (with sanitizing and
+default symlink munging). This can be useful if you need some library dirs
+inside the chroot (typically for uid & gid lookups) but don't want to put
+the lib dir into the top of the served path (even though they can be hidden
+with an \fBexclude\fP directive). However, a better choice for a modern
+rsync setup is to use a \fBname\ converter\fP" and try to avoid inner lib
+dirs altogether. See also the \fBdaemon\ chroot\fP parameter, which causes
+rsync to chroot into its own chroot area before doing any path-related
+chrooting.
+.IP
+If the daemon is serving the "/" dir (either directly or due to being
+chrooted to the module's path), rsync does not do any path sanitizing or
+(default) munging.
+.IP
+When it has to limit access to a particular subdir (either due to chroot
+being disabled or having an inside-chroot path set), rsync will munge
+symlinks (by default) and sanitize paths. Those that dislike munged
+symlinks (and really, really trust their users to not break out of the
+subdir) can disable the symlink munging via the "munge symlinks"
+parameter.
+.IP
+When rsync is sanitizing paths, it trims ".." path elements from args that
+it believes would escape the module hierarchy. It also substitutes leading
+slashes in absolute paths with the module's path (so that options such as
+\fB\-\-backup-dir\fP & \fB\-\-compare-dest\fP interpret an absolute path as rooted in
+the module's "path" dir).
+.IP
+When a chroot is in effect \fIand\fP the "name converter" parameter is
+\fInot\fP set, the "numeric ids" parameter will default to being enabled
+(disabling name lookups). This means that if you manually setup
+name-lookup libraries in your chroot (instead of using a name converter)
+that you need to explicitly set \fBnumeric\ ids\ =\ false\fP for rsync to do name
+lookups.
+.IP
+If you copy library resources into the module's chroot area, you should
+protect them through your OS's normal user/group or ACL settings (to
+prevent the rsync module's user from being able to change them), and then
+hide them from the user's view via "exclude" (see how in the discussion of
+that parameter). However, it's easier and safer to setup a name converter.
+.IP "\fBdaemon\ chroot\fP"
+This parameter specifies a path to which the daemon will chroot before
+beginning communication with clients. Module paths (and any "use chroot"
+settings) will then be related to this one. This lets you choose if you
+want the whole daemon to be chrooted (with this setting), just the
+transfers to be chrooted (with "use chroot"), or both. Keep in mind that
+the "daemon chroot" area may need various OS/lib/etc files installed to
+allow the daemon to function. By default the daemon runs without any
+chrooting.
+.IP "\fBproxy\ protocol\fP"
+When this parameter is enabled, all incoming connections must start with a
+V1 or V2 proxy protocol header. If the header is not found, the connection
+is closed.
+.IP
+Setting this to \fBtrue\fP requires a proxy server to forward source IP
+information to rsync, allowing you to log proper IP/host info and make use
+of client-oriented IP restrictions. The default of \fBfalse\fP means that the
+IP information comes directly from the socket's metadata. If rsync is not
+behind a proxy, this should be disabled.
+.IP
+\fICAUTION\fP: using this option can be dangerous if you do not ensure that
+only the proxy is allowed to connect to the rsync port. If any non-proxied
+connections are allowed through, the client will be able to use a modified
+rsync to spoof any remote IP address that they desire. You can lock this
+down using something like iptables \fB\-uid-owner\ root\fP rules (for strict
+localhost access), various firewall rules, or you can require password
+authorization so that any spoofing by users will not grant extra access.
+.IP
+This setting is global. If you need some modules to require this and not
+others, then you will need to setup multiple rsync daemon processes on
+different ports.
+.IP "\fBname\ converter\fP"
+This parameter lets you specify a program that will be run by the rsync
+daemon to do user & group conversions between names & ids. This script
+is started prior to any chroot being setup, and runs as the daemon user
+(not the transfer user). You can specify a fully qualified pathname or
+a program name that is on the $PATH.
+.IP
+The program can be used to do normal user & group lookups without having to
+put any extra files into the chroot area of the module \fIor\fP you can do
+customized conversions.
+.IP
+The nameconvert program has access to all of the environment variables that
+are described in the section on \fBpre-xfer\ exec\fP. This is useful if you
+want to customize the conversion using information about the module and/or
+the copy request.
+.IP
+There is a sample python script in the support dir named "nameconvert" that
+implements the normal user & group lookups. Feel free to customize it or
+just use it as documentation to implement your own.
+.IP "\fBnumeric\ ids\fP"
+Enabling this parameter disables the mapping of users and groups by name
+for the current daemon module. This prevents the daemon from trying to
+load any user/group-related files or libraries. This enabling makes the
+transfer behave as if the client had passed the \fB\-\-numeric-ids\fP
+command-line option. By default, this parameter is enabled for chroot
+modules and disabled for non-chroot modules. Also keep in mind that
+uid/gid preservation requires the module to be running as root (see "uid")
+or for "fake super" to be configured.
+.IP
+A chroot-enabled module should not have this parameter set to false unless
+you're using a "name converter" program \fIor\fP you've taken steps to ensure
+that the module has the necessary resources it needs to translate names and
+that it is not possible for a user to change those resources.
+.IP "\fBmunge\ symlinks\fP"
+This parameter tells rsync to modify all symlinks in the same way as the
+(non-daemon-affecting) \fB\-\-munge-links\fP command-line option (using a method
+described below). This should help protect your files from user trickery
+when your daemon module is writable. The default is disabled when
+"use chroot" is on with an inside-chroot path of "/", OR if "daemon chroot"
+is on, otherwise it is enabled.
+.IP
+If you disable this parameter on a daemon that is not read-only, there are
+tricks that a user can play with uploaded symlinks to access
+daemon-excluded items (if your module has any), and, if "use chroot" is
+off, rsync can even be tricked into showing or changing data that is
+outside the module's path (as access-permissions allow).
+.IP
+The way rsync disables the use of symlinks is to prefix each one with the
+string "/rsyncd-munged/". This prevents the links from being used as long
+as that directory does not exist. When this parameter is enabled, rsync
+will refuse to run if that path is a directory or a symlink to a directory.
+When using the "munge symlinks" parameter in a chroot area that has an
+inside-chroot path of "/", you should add "/rsyncd-munged/" to the exclude
+setting for the module so that a user can't try to create it.
+.IP
+Note: rsync makes no attempt to verify that any pre-existing symlinks in
+the module's hierarchy are as safe as you want them to be (unless, of
+course, it just copied in the whole hierarchy). If you setup an rsync
+daemon on a new area or locally add symlinks, you can manually protect your
+symlinks from being abused by prefixing "/rsyncd-munged/" to the start of
+every symlink's value. There is a perl script in the support directory of
+the source code named "munge-symlinks" that can be used to add or remove
+this prefix from your symlinks.
+.IP
+When this parameter is disabled on a writable module and "use chroot" is
+off (or the inside-chroot path is not "/"), incoming symlinks will be
+modified to drop a leading slash and to remove ".." path elements that
+rsync believes will allow a symlink to escape the module's hierarchy.
+There are tricky ways to work around this, though, so you had better trust
+your users if you choose this combination of parameters.
+.IP "\fBcharset\fP"
+This specifies the name of the character set in which the module's
+filenames are stored. If the client uses an \fB\-\-iconv\fP option, the daemon
+will use the value of the "charset" parameter regardless of the character
+set the client actually passed. This allows the daemon to support charset
+conversion in a chroot module without extra files in the chroot area, and
+also ensures that name-translation is done in a consistent manner. If the
+"charset" parameter is not set, the \fB\-\-iconv\fP option is refused, just as if
+"iconv" had been specified via "refuse options".
+.IP
+If you wish to force users to always use \fB\-\-iconv\fP for a particular module,
+add "no-iconv" to the "refuse options" parameter. Keep in mind that this
+will restrict access to your module to very new rsync clients.
+.IP "\fBmax\ connections\fP"
+This parameter allows you to specify the maximum number of simultaneous
+connections you will allow. Any clients connecting when the maximum has
+been reached will receive a message telling them to try later. The default
+is 0, which means no limit. A negative value disables the module. See
+also the "lock file" parameter.
+.IP "\fBlog\ file\fP"
+When the "log file" parameter is set to a non-empty string, the rsync
+daemon will log messages to the indicated file rather than using syslog.
+This is particularly useful on systems (such as AIX) where \fBsyslog()\fP
+doesn't work for chrooted programs. The file is opened before \fBchroot()\fP
+is called, allowing it to be placed outside the transfer. If this value is
+set on a per-module basis instead of globally, the global log will still
+contain any authorization failures or config-file error messages.
+.IP
+If the daemon fails to open the specified file, it will fall back to using
+syslog and output an error about the failure. (Note that the failure to
+open the specified log file used to be a fatal error.)
+.IP
+This setting can be overridden by using the \fB\-\-log-file=FILE\fP or
+\fB\-\-dparam=logfile=FILE\fP command-line options. The former overrides all the
+log-file parameters of the daemon and all module settings. The latter sets
+the daemon's log file and the default for all the modules, which still
+allows modules to override the default setting.
+.IP "\fBsyslog\ facility\fP"
+This parameter allows you to specify the syslog facility name to use when
+logging messages from the rsync daemon. You may use any standard syslog
+facility name which is defined on your system. Common names are auth,
+authpriv, cron, daemon, ftp, kern, lpr, mail, news, security, syslog, user,
+uucp, local0, local1, local2, local3, local4, local5, local6 and local7.
+The default is daemon. This setting has no effect if the "log file"
+setting is a non-empty string (either set in the per-modules settings, or
+inherited from the global settings).
+.IP "\fBsyslog\ tag\fP"
+This parameter allows you to specify the syslog tag to use when logging
+messages from the rsync daemon. The default is "rsyncd". This setting has
+no effect if the "log file" setting is a non-empty string (either set in
+the per-modules settings, or inherited from the global settings).
+.IP
+For example, if you wanted each authenticated user's name to be included in
+the syslog tag, you could do something like this:
+.RS 4
+.IP
+.nf
+syslog tag = rsyncd.%RSYNC_USER_NAME%
+.fi
+.RE
+.IP "\fBmax\ verbosity\fP"
+This parameter allows you to control the maximum amount of verbose
+information that you'll allow the daemon to generate (since the information
+goes into the log file). The default is 1, which allows the client to
+request one level of verbosity.
+.IP
+This also affects the user's ability to request higher levels of \fB\-\-info\fP
+and \fB\-\-debug\fP logging. If the max value is 2, then no info and/or debug
+value that is higher than what would be set by \fB\-vv\fP will be honored by the
+daemon in its logging. To see how high of a verbosity level you need to
+accept for a particular info/debug level, refer to \fBrsync\ \-\-info=help\fP and
+\fBrsync\ \-\-debug=help\fP. For instance, it takes max-verbosity 4 to be able to
+output debug TIME2 and FLIST3.
+.IP "\fBlock\ file\fP"
+This parameter specifies the file to use to support the "max connections"
+parameter. The rsync daemon uses record locking on this file to ensure that
+the max connections limit is not exceeded for the modules sharing the lock
+file. The default is \fB/var/run/rsyncd.lock\fP.
+.IP "\fBread\ only\fP"
+This parameter determines whether clients will be able to upload files or
+not. If "read only" is true then any attempted uploads will fail. If
+"read only" is false then uploads will be possible if file permissions on
+the daemon side allow them. The default is for all modules to be read only.
+.IP
+Note that "auth users" can override this setting on a per-user basis.
+.IP "\fBwrite\ only\fP"
+This parameter determines whether clients will be able to download files or
+not. If "write only" is true then any attempted downloads will fail. If
+"write only" is false then downloads will be possible if file permissions
+on the daemon side allow them. The default is for this parameter to be
+disabled.
+.IP
+Helpful hint: you probably want to specify "refuse options = delete" for a
+write-only module.
+.IP "\fBopen\ noatime\fP"
+When set to True, this parameter tells the rsync daemon to open files with
+the O_NOATIME flag
+(on systems that support it) to avoid changing the access time of the files
+that are being transferred. If your OS does not support the O_NOATIME flag
+then rsync will silently ignore this option. Note also that some
+filesystems are mounted to avoid updating the atime on read access even
+without the O_NOATIME flag being set.
+.IP
+When set to False, this parameters ensures that files on the server are not
+opened with O_NOATIME.
+.IP
+When set to Unset (the default) the user controls the setting via
+\fB\-\-open-noatime\fP.
+.IP "\fBlist\fP"
+This parameter determines whether this module is listed when the client
+asks for a listing of available modules. In addition, if this is false,
+the daemon will pretend the module does not exist when a client denied by
+"hosts allow" or "hosts deny" attempts to access it. Realize that if
+"reverse lookup" is disabled globally but enabled for the module, the
+resulting reverse lookup to a potentially client-controlled DNS server may
+still reveal to the client that it hit an existing module. The default is
+for modules to be listable.
+.IP "\fBuid\fP"
+This parameter specifies the user name or user ID that file transfers to
+and from that module should take place as when the daemon was run as root.
+In combination with the "gid" parameter this determines what file
+permissions are available. The default when run by a super-user is to
+switch to the system's "nobody" user. The default for a non-super-user is
+to not try to change the user. See also the "gid" parameter.
+.IP
+The RSYNC_USER_NAME environment variable may be used to request that rsync
+run as the authorizing user. For example, if you want a rsync to run as
+the same user that was received for the rsync authentication, this setup is
+useful:
+.RS 4
+.IP
+.nf
+uid = %RSYNC_USER_NAME%
+gid = *
+.fi
+.RE
+.IP "\fBgid\fP"
+This parameter specifies one or more group names/IDs that will be used when
+accessing the module. The first one will be the default group, and any
+extra ones be set as supplemental groups. You may also specify a "\fB*\fP" as
+the first gid in the list, which will be replaced by all the normal groups
+for the transfer's user (see "uid"). The default when run by a super-user
+is to switch to your OS's "nobody" (or perhaps "nogroup") group with no
+other supplementary groups. The default for a non-super-user is to not
+change any group attributes (and indeed, your OS may not allow a
+non-super-user to try to change their group settings).
+.IP
+The specified list is normally split into tokens based on spaces and
+commas. However, if the list starts with a comma, then the list is only
+split on commas, which allows a group name to contain a space. In either
+case any leading and/or trailing whitespace is removed from the tokens and
+empty tokens are ignored.
+.IP "\fBdaemon\ uid\fP"
+This parameter specifies a uid under which the daemon will run. The daemon
+usually runs as user root, and when this is left unset the user is left
+unchanged. See also the "uid" parameter.
+.IP "\fBdaemon\ gid\fP"
+This parameter specifies a gid under which the daemon will run. The daemon
+usually runs as group root, and when this is left unset, the group is left
+unchanged. See also the "gid" parameter.
+.IP "\fBfake\ super\fP"
+Setting "fake super = yes" for a module causes the daemon side to behave as
+if the \fB\-\-fake-super\fP command-line option had been specified. This allows
+the full attributes of a file to be stored without having to have the
+daemon actually running as root.
+.IP "\fBfilter\fP"
+The daemon has its own filter chain that determines what files it will let
+the client access. This chain is not sent to the client and is independent
+of any filters the client may have specified. Files excluded by the daemon
+filter chain (\fBdaemon-excluded\fP files) are treated as non-existent if the
+client tries to pull them, are skipped with an error message if the client
+tries to push them (triggering exit code 23), and are never deleted from
+the module. You can use daemon filters to prevent clients from downloading
+or tampering with private administrative files, such as files you may add
+to support uid/gid name translations.
+.IP
+The daemon filter chain is built from the "filter", "include from",
+"include", "exclude from", and "exclude" parameters, in that order of
+priority. Anchored patterns are anchored at the root of the module. To
+prevent access to an entire subtree, for example, "\fB/secret\fP", you \fBmust\fP
+exclude everything in the subtree; the easiest way to do this is with a
+triple-star pattern like "\fB/secret/***\fP".
+.IP
+The "filter" parameter takes a space-separated list of daemon filter rules,
+though it is smart enough to know not to split a token at an internal space
+in a rule (e.g. "\fB\-\ /foo\ \ \-\ /bar\fP" is parsed as two rules). You may specify
+one or more merge-file rules using the normal syntax. Only one "filter"
+parameter can apply to a given module in the config file, so put all the
+rules you want in a single parameter. Note that per-directory merge-file
+rules do not provide as much protection as global rules, but they can be
+used to make \fB\-\-delete\fP work better during a client download operation if
+the per-dir merge files are included in the transfer and the client
+requests that they be used.
+.IP "\fBexclude\fP"
+This parameter takes a space-separated list of daemon exclude patterns. As
+with the client \fB\-\-exclude\fP option, patterns can be qualified with "\fB\-\ \fP" or
+"\fB+\ \fP" to explicitly indicate exclude/include. Only one "exclude" parameter
+can apply to a given module. See the "filter" parameter for a description
+of how excluded files affect the daemon.
+.IP "\fBinclude\fP"
+Use an "include" to override the effects of the "exclude" parameter. Only
+one "include" parameter can apply to a given module. See the "filter"
+parameter for a description of how excluded files affect the daemon.
+.IP "\fBexclude\ from\fP"
+This parameter specifies the name of a file on the daemon that contains
+daemon exclude patterns, one per line. Only one "exclude from" parameter
+can apply to a given module; if you have multiple exclude-from files, you
+can specify them as a merge file in the "filter" parameter. See the
+"filter" parameter for a description of how excluded files affect the
+daemon.
+.IP "\fBinclude\ from\fP"
+Analogue of "exclude from" for a file of daemon include patterns. Only one
+"include from" parameter can apply to a given module. See the "filter"
+parameter for a description of how excluded files affect the daemon.
+.IP "\fBincoming\ chmod\fP"
+This parameter allows you to specify a set of comma-separated chmod strings
+that will affect the permissions of all incoming files (files that are
+being received by the daemon). These changes happen after all other
+permission calculations, and this will even override destination-default
+and/or existing permissions when the client does not specify \fB\-\-perms\fP.
+See the description of the \fB\-\-chmod\fP rsync option and the \fBchmod\fP(1)
+manpage for information on the format of this string.
+.IP "\fBoutgoing\ chmod\fP"
+This parameter allows you to specify a set of comma-separated chmod strings
+that will affect the permissions of all outgoing files (files that are
+being sent out from the daemon). These changes happen first, making the
+sent permissions appear to be different than those stored in the filesystem
+itself. For instance, you could disable group write permissions on the
+server while having it appear to be on to the clients. See the description
+of the \fB\-\-chmod\fP rsync option and the \fBchmod\fP(1) manpage for information
+on the format of this string.
+.IP "\fBauth\ users\fP"
+This parameter specifies a comma and/or space-separated list of
+authorization rules. In its simplest form, you list the usernames that
+will be allowed to connect to this module. The usernames do not need to
+exist on the local system. The rules may contain shell wildcard characters
+that will be matched against the username provided by the client for
+authentication. If "auth users" is set then the client will be challenged
+to supply a username and password to connect to the module. A challenge
+response authentication protocol is used for this exchange. The plain text
+usernames and passwords are stored in the file specified by the
+"secrets file" parameter. The default is for all users to be able to
+connect without a password (this is called "anonymous rsync").
+.IP
+In addition to username matching, you can specify groupname matching via a
+\&'@' prefix. When using groupname matching, the authenticating username
+must be a real user on the system, or it will be assumed to be a member of
+no groups. For example, specifying "@rsync" will match the authenticating
+user if the named user is a member of the rsync group.
+.IP
+Finally, options may be specified after a colon (:). The options allow you
+to "deny" a user or a group, set the access to "ro" (read-only), or set the
+access to "rw" (read/write). Setting an auth-rule-specific ro/rw setting
+overrides the module's "read only" setting.
+.IP
+Be sure to put the rules in the order you want them to be matched, because
+the checking stops at the first matching user or group, and that is the
+only auth that is checked. For example:
+.RS 4
+.IP
+.nf
+auth users = joe:deny @guest:deny admin:rw @rsync:ro susan joe sam
+.fi
+.RE
+.IP
+In the above rule, user joe will be denied access no matter what. Any user
+that is in the group "guest" is also denied access. The user "admin" gets
+access in read/write mode, but only if the admin user is not in group
+"guest" (because the admin user-matching rule would never be reached if the
+user is in group "guest"). Any other user who is in group "rsync" will get
+read-only access. Finally, users susan, joe, and sam get the ro/rw setting
+of the module, but only if the user didn't match an earlier group-matching
+rule.
+.IP
+If you need to specify a user or group name with a space in it, start your
+list with a comma to indicate that the list should only be split on commas
+(though leading and trailing whitespace will also be removed, and empty
+entries are just ignored). For example:
+.RS 4
+.IP
+.nf
+auth users = , joe:deny, @Some Group:deny, admin:rw, @RO Group:ro
+.fi
+.RE
+.IP
+See the description of the secrets file for how you can have per-user
+passwords as well as per-group passwords. It also explains how a user can
+authenticate using their user password or (when applicable) a group
+password, depending on what rule is being authenticated.
+.IP
+See also the section entitled "USING RSYNC-DAEMON FEATURES VIA A REMOTE
+SHELL CONNECTION" in \fBrsync\fP(1) for information on how handle an
+rsyncd.conf-level username that differs from the remote-shell-level
+username when using a remote shell to connect to an rsync daemon.
+.IP "\fBsecrets\ file\fP"
+This parameter specifies the name of a file that contains the
+username:password and/or @groupname:password pairs used for authenticating
+this module. This file is only consulted if the "auth users" parameter is
+specified. The file is line-based and contains one name:password pair per
+line. Any line has a hash (#) as the very first character on the line is
+considered a comment and is skipped. The passwords can contain any
+characters but be warned that many operating systems limit the length of
+passwords that can be typed at the client end, so you may find that
+passwords longer than 8 characters don't work.
+.IP
+The use of group-specific lines are only relevant when the module is being
+authorized using a matching "@groupname" rule. When that happens, the user
+can be authorized via either their "username:password" line or the
+"@groupname:password" line for the group that triggered the authentication.
+.IP
+It is up to you what kind of password entries you want to include, either
+users, groups, or both. The use of group rules in "auth users" does not
+require that you specify a group password if you do not want to use shared
+passwords.
+.IP
+There is no default for the "secrets file" parameter, you must choose a
+name (such as \fB/etc/rsyncd.secrets\fP). The file must normally not be
+readable by "other"; see "strict modes". If the file is not found or is
+rejected, no logins for an "auth users" module will be possible.
+.IP "\fBstrict\ modes\fP"
+This parameter determines whether or not the permissions on the secrets
+file will be checked. If "strict modes" is true, then the secrets file
+must not be readable by any user ID other than the one that the rsync
+daemon is running under. If "strict modes" is false, the check is not
+performed. The default is true. This parameter was added to accommodate
+rsync running on the Windows operating system.
+.IP "\fBhosts\ allow\fP"
+This parameter allows you to specify a list of comma- and/or
+whitespace-separated patterns that are matched against a connecting
+client's hostname and IP address. If none of the patterns match, then the
+connection is rejected.
+.IP
+Each pattern can be in one of six forms:
+.IP
+.RS
+.IP o
+a dotted decimal IPv4 address of the form a.b.c.d, or an IPv6 address of
+the form a:b:c::d:e:f. In this case the incoming machine's IP address
+must match exactly.
+.IP o
+an address/mask in the form ipaddr/n where ipaddr is the IP address and n
+is the number of one bits in the netmask. All IP addresses which match
+the masked IP address will be allowed in.
+.IP o
+an address/mask in the form ipaddr/maskaddr where ipaddr is the IP
+address and maskaddr is the netmask in dotted decimal notation for IPv4,
+or similar for IPv6, e.g. ffff:ffff:ffff:ffff:: instead of /64. All IP
+addresses which match the masked IP address will be allowed in.
+.IP o
+a hostname pattern using wildcards. If the hostname of the connecting IP
+(as determined by a reverse lookup) matches the wildcarded name (using
+the same rules as normal Unix filename matching), the client is allowed
+in. This only works if "reverse lookup" is enabled (the default).
+.IP o
+a hostname. A plain hostname is matched against the reverse DNS of the
+connecting IP (if "reverse lookup" is enabled), and/or the IP of the
+given hostname is matched against the connecting IP (if "forward lookup"
+is enabled, as it is by default). Any match will be allowed in.
+.IP o
+an '@' followed by a netgroup name, which will match if the reverse DNS
+of the connecting IP is in the specified netgroup.
+.RE
+.IP
+Note IPv6 link-local addresses can have a scope in the address
+specification:
+.RS 4
+.IP
+.nf
+fe80::1%link1
+fe80::%link1/64
+fe80::%link1/ffff:ffff:ffff:ffff::
+.fi
+.RE
+.IP
+You can also combine "hosts allow" with "hosts deny" as a way to add
+exceptions to your deny list. When both parameters are specified, the
+"hosts allow" parameter is checked first and a match results in the client
+being able to connect. A non-allowed host is then matched against the
+"hosts deny" list to see if it should be rejected. A host that does not
+match either list is allowed to connect.
+.IP
+The default is no "hosts allow" parameter, which means all hosts can
+connect.
+.IP "\fBhosts\ deny\fP"
+This parameter allows you to specify a list of comma- and/or
+whitespace-separated patterns that are matched against a connecting clients
+hostname and IP address. If the pattern matches then the connection is
+rejected. See the "hosts allow" parameter for more information.
+.IP
+The default is no "hosts deny" parameter, which means all hosts can
+connect.
+.IP "\fBreverse\ lookup\fP"
+Controls whether the daemon performs a reverse lookup on the client's IP
+address to determine its hostname, which is used for "hosts allow" &
+"hosts deny" checks and the "%h" log escape. This is enabled by default,
+but you may wish to disable it to save time if you know the lookup will not
+return a useful result, in which case the daemon will use the name
+"UNDETERMINED" instead.
+.IP
+If this parameter is enabled globally (even by default), rsync performs the
+lookup as soon as a client connects, so disabling it for a module will not
+avoid the lookup. Thus, you probably want to disable it globally and then
+enable it for modules that need the information.
+.IP "\fBforward\ lookup\fP"
+Controls whether the daemon performs a forward lookup on any hostname
+specified in an hosts allow/deny setting. By default this is enabled,
+allowing the use of an explicit hostname that would not be returned by
+reverse DNS of the connecting IP.
+.IP "\fBignore\ errors\fP"
+This parameter tells rsyncd to ignore I/O errors on the daemon when
+deciding whether to run the delete phase of the transfer. Normally rsync
+skips the \fB\-\-delete\fP step if any I/O errors have occurred in order to
+prevent disastrous deletion due to a temporary resource shortage or other
+I/O error. In some cases this test is counter productive so you can use
+this parameter to turn off this behavior.
+.IP "\fBignore\ nonreadable\fP"
+This tells the rsync daemon to completely ignore files that are not
+readable by the user. This is useful for public archives that may have some
+non-readable files among the directories, and the sysadmin doesn't want
+those files to be seen at all.
+.IP "\fBtransfer\ logging\fP"
+This parameter enables per-file logging of downloads and uploads in a
+format somewhat similar to that used by ftp daemons. The daemon always
+logs the transfer at the end, so if a transfer is aborted, no mention will
+be made in the log file.
+.IP
+If you want to customize the log lines, see the "log format" parameter.
+.IP "\fBlog\ format\fP"
+This parameter allows you to specify the format used for logging file
+transfers when transfer logging is enabled. The format is a text string
+containing embedded single-character escape sequences prefixed with a
+percent (%) character. An optional numeric field width may also be
+specified between the percent and the escape letter (e.g.
+"\fB%\-50n\ %8l\ %07p\fP"). In addition, one or more apostrophes may be specified
+prior to a numerical escape to indicate that the numerical value should be
+made more human-readable. The 3 supported levels are the same as for the
+\fB\-\-human-readable\fP command-line option, though the default is for
+human-readability to be off. Each added apostrophe increases the level
+(e.g. "\fB%''l\ %'b\ %f\fP").
+.IP
+The default log format is "\fB%o\ %h\ [%a]\ %m\ (%u)\ %f\ %l\fP", and a "\fB%t\ [%p]\ \fP"
+is always prefixed when using the "log file" parameter. (A perl script
+that will summarize this default log format is included in the rsync source
+code distribution in the "support" subdirectory: rsyncstats.)
+.IP
+The single-character escapes that are understood are as follows:
+.IP
+.RS
+.IP o
+%a the remote IP address (only available for a daemon)
+.IP o
+%b the number of bytes actually transferred
+.IP o
+%B the permission bits of the file (e.g. rwxrwxrwt)
+.IP o
+%c the total size of the block checksums received for the basis file
+(only when sending)
+.IP o
+%C the full-file checksum if it is known for the file. For older rsync
+protocols/versions, the checksum was salted, and is thus not a useful
+value (and is not displayed when that is the case). For the checksum to
+output for a file, either the \fB\-\-checksum\fP option must be in-effect or
+the file must have been transferred without a salted checksum being used.
+See the \fB\-\-checksum-choice\fP option for a way to choose the algorithm.
+.IP o
+%f the filename (long form on sender; no trailing "/")
+.IP o
+%G the gid of the file (decimal) or "DEFAULT"
+.IP o
+%h the remote host name (only available for a daemon)
+.IP o
+%i an itemized list of what is being updated
+.IP o
+%l the length of the file in bytes
+.IP o
+%L the string "\fB\ \->\ SYMLINK\fP", "\fB\ =>\ HARDLINK\fP", or "" (where \fBSYMLINK\fP
+or \fBHARDLINK\fP is a filename)
+.IP o
+%m the module name
+.IP o
+%M the last-modified time of the file
+.IP o
+%n the filename (short form; trailing "/" on dir)
+.IP o
+%o the operation, which is "send", "recv", or "del." (the latter includes
+the trailing period)
+.IP o
+%p the process ID of this rsync session
+.IP o
+%P the module path
+.IP o
+%t the current date time
+.IP o
+%u the authenticated username or an empty string
+.IP o
+%U the uid of the file (decimal)
+.RE
+.IP
+For a list of what the characters mean that are output by "%i", see the
+\fB\-\-itemize-changes\fP option in the rsync manpage.
+.IP
+Note that some of the logged output changes when talking with older rsync
+versions. For instance, deleted files were only output as verbose messages
+prior to rsync 2.6.4.
+.IP "\fBtimeout\fP"
+This parameter allows you to override the clients choice for I/O timeout
+for this module. Using this parameter you can ensure that rsync won't wait
+on a dead client forever. The timeout is specified in seconds. A value of
+zero means no timeout and is the default. A good choice for anonymous rsync
+daemons may be 600 (giving a 10 minute timeout).
+.IP "\fBrefuse\ options\fP"
+This parameter allows you to specify a space-separated list of rsync
+command-line options that will be refused by your rsync daemon. You may
+specify the full option name, its one-letter abbreviation, or a wild-card
+string that matches multiple options. Beginning in 3.2.0, you can also
+negate a match term by starting it with a "!".
+.IP
+When an option is refused, the daemon prints an error message and exits.
+.IP
+For example, this would refuse \fB\-\-checksum\fP (\fB\-c\fP) and all the various
+delete options:
+.RS 4
+.IP
+.nf
+refuse options = c delete
+.fi
+.RE
+.IP
+The reason the above refuses all delete options is that the options imply
+\fB\-\-delete\fP, and implied options are refused just like explicit options.
+.IP
+The use of a negated match allows you to fine-tune your refusals after a
+wild-card, such as this:
+.RS 4
+.IP
+.nf
+refuse options = delete-* !delete-during
+.fi
+.RE
+.IP
+Negated matching can also turn your list of refused options into a list of
+accepted options. To do this, begin the list with a "\fB*\fP" (to refuse all
+options) and then specify one or more negated matches to accept. For
+example:
+.RS 4
+.IP
+.nf
+refuse options = * !a !v !compress*
+.fi
+.RE
+.IP
+Don't worry that the "\fB*\fP" will refuse certain vital options such as
+\fB\-\-dry-run\fP, \fB\-\-server\fP, \fB\-\-no-iconv\fP, \fB\-\-seclude-args\fP, etc. These
+important options are not matched by wild-card, so they must be overridden
+by their exact name. For instance, if you're forcing iconv transfers you
+could use something like this:
+.RS 4
+.IP
+.nf
+refuse options = * no-iconv !a !v
+.fi
+.RE
+.IP
+As an additional aid (beginning in 3.2.0), refusing (or "\fB!refusing\fP") the
+"a" or "archive" option also affects all the options that the \fB\-\-archive\fP
+option implies (\fB\-rdlptgoD\fP), but only if the option is matched explicitly
+(not using a wildcard). If you want to do something tricky, you can use
+"\fBarchive*\fP" to avoid this side-effect, but keep in mind that no normal
+rsync client ever sends the actual archive option to the server.
+.IP
+As an additional safety feature, the refusal of "delete" also refuses
+\fBremove-source-files\fP when the daemon is the sender; if you want the latter
+without the former, instead refuse "\fBdelete-*\fP" as that refuses all the
+delete modes without affecting \fB\-\-remove-source-files\fP. (Keep in mind that
+the client's \fB\-\-delete\fP option typically results in \fB\-\-delete-during\fP.)
+.IP
+When un-refusing delete options, you should either specify "\fB!delete*\fP" (to
+accept all delete options) or specify a limited set that includes "delete",
+such as:
+.RS 4
+.IP
+.nf
+refuse options = * !a !delete !delete-during
+.fi
+.RE
+.IP
+\&... whereas this accepts any delete option except \fB\-\-delete-after\fP:
+.RS 4
+.IP
+.nf
+refuse options = * !a !delete* delete-after
+.fi
+.RE
+.IP
+A note on refusing "compress": it may be better to set the "dont compress"
+daemon parameter to "\fB*\fP" and ensure that \fBRSYNC_COMPRESS_LIST=zlib\fP is set
+in the environment of the daemon in order to disable compression silently
+instead of returning an error that forces the client to remove the \fB\-z\fP
+option.
+.IP
+If you are un-refusing the compress option, you may want to match
+"\fB!compress*\fP" if you also want to allow the \fB\-\-compress-level\fP option.
+.IP
+Note that the "copy-devices" & "write-devices" options are refused by
+default, but they can be explicitly accepted with "\fB!copy-devices\fP" and/or
+"\fB!write-devices\fP". The options "log-file" and "log-file-format" are
+forcibly refused and cannot be accepted.
+.IP
+Here are all the options that are not matched by wild-cards:
+.IP
+.RS
+.IP o
+\fB\-\-server\fP: Required for rsync to even work.
+.IP o
+\fB\-\-rsh\fP, \fB\-e\fP: Required to convey compatibility flags to the server.
+.IP o
+\fB\-\-out-format\fP: This is required to convey output behavior to a remote
+receiver. While rsync passes the older alias \fB\-\-log-format\fP for
+compatibility reasons, this options should not be confused with
+\fB\-\-log-file-format\fP.
+.IP o
+\fB\-\-sender\fP: Use "write only" parameter instead of refusing this.
+.IP o
+\fB\-\-dry-run\fP, \fB\-n\fP: Who would want to disable this?
+.IP o
+\fB\-\-seclude-args\fP, \fB\-s\fP: Is the oldest arg-protection method.
+.IP o
+\fB\-\-from0\fP, \fB\-0\fP: Makes it easier to accept/refuse \fB\-\-files-from\fP without
+affecting this helpful modifier.
+.IP o
+\fB\-\-iconv\fP: This is auto-disabled based on "charset" parameter.
+.IP o
+\fB\-\-no-iconv\fP: Most transfers use this option.
+.IP o
+\fB\-\-checksum-seed\fP: Is a fairly rare, safe option.
+.IP o
+\fB\-\-write-devices\fP: Is non-wild but also auto-disabled.
+.RE
+.IP "\fBdont\ compress\fP"
+\fBNOTE:\fP This parameter currently has no effect except in one instance: if
+it is set to "\fB*\fP" then it minimizes or disables compression for all files
+(for those that don't want to refuse the \fB\-\-compress\fP option completely).
+.IP
+This parameter allows you to select filenames based on wildcard patterns
+that should not be compressed when pulling files from the daemon (no
+analogous parameter exists to govern the pushing of files to a daemon).
+Compression can be expensive in terms of CPU usage, so it is usually good
+to not try to compress files that won't compress well, such as already
+compressed files.
+.IP
+The "dont compress" parameter takes a space-separated list of
+case-insensitive wildcard patterns. Any source filename matching one of the
+patterns will be compressed as little as possible during the transfer. If
+the compression algorithm has an "off" level, then no compression occurs
+for those files. If an algorithms has the ability to change the level in
+mid-stream, it will be minimized to reduce the CPU usage as much as
+possible.
+.IP
+See the \fB\-\-skip-compress\fP parameter in the \fBrsync\fP(1) manpage for the
+list of file suffixes that are skipped by default if this parameter is not
+set.
+.IP "\fBearly\ exec\fP, \fBpre-xfer\ exec\fP, \fBpost-xfer\ exec\fP"
+You may specify a command to be run in the early stages of the connection,
+or right before and/or after the transfer. If the \fBearly\ exec\fP or
+\fBpre-xfer\ exec\fP command returns an error code, the transfer is aborted
+before it begins. Any output from the \fBpre-xfer\ exec\fP command on stdout
+(up to several KB) will be displayed to the user when aborting, but is
+\fInot\fP displayed if the script returns success. The other programs cannot
+send any text to the user. All output except for the \fBpre-xfer\ exec\fP
+stdout goes to the corresponding daemon's stdout/stderr, which is typically
+discarded. See the \fB\-\-no-detatch\fP option for a way to see the daemon's
+output, which can assist with debugging.
+.IP
+Note that the \fBearly\ exec\fP command runs before any part of the transfer
+request is known except for the module name. This helper script can be
+used to setup a disk mount or decrypt some data into a module dir, but you
+may need to use \fBlock\ file\fP and \fBmax\ connections\fP to avoid concurrency
+issues. If the client rsync specified the \fB\-\-early-input=FILE\fP option, it
+can send up to about 5K of data to the stdin of the early script. The
+stdin will otherwise be empty.
+.IP
+Note that the \fBpost-xfer\ exec\fP command is still run even if one of the
+other scripts returns an error code. The \fBpre-xfer\ exec\fP command will \fInot\fP
+be run, however, if the \fBearly\ exec\fP command fails.
+.IP
+The following environment variables will be set, though some are specific
+to the pre-xfer or the post-xfer environment:
+.IP
+.RS
+.IP o
+\fBRSYNC_MODULE_NAME\fP: The name of the module being accessed.
+.IP o
+\fBRSYNC_MODULE_PATH\fP: The path configured for the module.
+.IP o
+\fBRSYNC_HOST_ADDR\fP: The accessing host's IP address.
+.IP o
+\fBRSYNC_HOST_NAME\fP: The accessing host's name.
+.IP o
+\fBRSYNC_USER_NAME\fP: The accessing user's name (empty if no user).
+.IP o
+\fBRSYNC_PID\fP: A unique number for this transfer.
+.IP o
+\fBRSYNC_REQUEST\fP: (pre-xfer only) The module/path info specified by the
+user. Note that the user can specify multiple source files, so the
+request can be something like "mod/path1 mod/path2", etc.
+.IP o
+\fBRSYNC_ARG#\fP: (pre-xfer only) The pre-request arguments are set in these
+numbered values. RSYNC_ARG0 is always "rsyncd", followed by the options
+that were used in RSYNC_ARG1, and so on. There will be a value of "."
+indicating that the options are done and the path args are beginning\ \-\-
+these contain similar information to RSYNC_REQUEST, but with values
+separated and the module name stripped off.
+.IP o
+\fBRSYNC_EXIT_STATUS\fP: (post-xfer only) the server side's exit value. This
+will be 0 for a successful run, a positive value for an error that the
+server generated, or a \-1 if rsync failed to exit properly. Note that an
+error that occurs on the client side does not currently get sent to the
+server side, so this is not the final exit status for the whole transfer.
+.IP o
+\fBRSYNC_RAW_STATUS\fP: (post-xfer only) the raw exit value from
+\fBwaitpid()\fP.
+.RE
+.IP
+Even though the commands can be associated with a particular module, they
+are run using the permissions of the user that started the daemon (not the
+module's uid/gid setting) without any chroot restrictions.
+.IP
+These settings honor 2 environment variables: use RSYNC_SHELL to set a
+shell to use when running the command (which otherwise uses your
+\fBsystem()\fP call's default shell), and use RSYNC_NO_XFER_EXEC to disable
+both options completely.
+.P
+.SH "CONFIG DIRECTIVES"
+.P
+There are currently two config directives available that allow a config file to
+incorporate the contents of other files: \fB&include\fP and \fB&merge\fP. Both allow
+a reference to either a file or a directory. They differ in how segregated the
+file's contents are considered to be.
+.P
+The \fB&include\fP directive treats each file as more distinct, with each one
+inheriting the defaults of the parent file, starting the parameter parsing as
+globals/defaults, and leaving the defaults unchanged for the parsing of the
+rest of the parent file.
+.P
+The \fB&merge\fP directive, on the other hand, treats the file's contents as if it
+were simply inserted in place of the directive, and thus it can set parameters
+in a module started in another file, can affect the defaults for other files,
+etc.
+.P
+When an \fB&include\fP or \fB&merge\fP directive refers to a directory, it will read in
+all the \fB*.conf\fP or \fB*.inc\fP files (respectively) that are contained inside that
+directory (without any recursive scanning), with the files sorted into alpha
+order. So, if you have a directory named "rsyncd.d" with the files "foo.conf",
+"bar.conf", and "baz.conf" inside it, this directive:
+.RS 4
+.P
+.nf
+&include /path/rsyncd.d
+.fi
+.RE
+.P
+would be the same as this set of directives:
+.RS 4
+.P
+.nf
+&include /path/rsyncd.d/bar.conf
+&include /path/rsyncd.d/baz.conf
+&include /path/rsyncd.d/foo.conf
+.fi
+.RE
+.P
+except that it adjusts as files are added and removed from the directory.
+.P
+The advantage of the \fB&include\fP directive is that you can define one or more
+modules in a separate file without worrying about unintended side-effects
+between the self-contained module files.
+.P
+The advantage of the \fB&merge\fP directive is that you can load config snippets
+that can be included into multiple module definitions, and you can also set
+global values that will affect connections (such as \fBmotd\ file\fP), or globals
+that will affect other include files.
+.P
+For example, this is a useful /etc/rsyncd.conf file:
+.RS 4
+.P
+.nf
+port = 873
+log file = /var/log/rsync.log
+pid file = /var/lock/rsync.lock
+
+&merge /etc/rsyncd.d
+&include /etc/rsyncd.d
+.fi
+.RE
+.P
+This would merge any \fB/etc/rsyncd.d/*.inc\fP files (for global values that should
+stay in effect), and then include any \fB/etc/rsyncd.d/*.conf\fP files (defining
+modules without any global-value cross-talk).
+.P
+.SH "AUTHENTICATION STRENGTH"
+.P
+The authentication protocol used in rsync is a 128 bit MD4 based challenge
+response system. This is fairly weak protection, though (with at least one
+brute-force hash-finding algorithm publicly available), so if you want really
+top-quality security, then I recommend that you run rsync over ssh. (Yes, a
+future version of rsync will switch over to a stronger hashing method.)
+.P
+Also note that the rsync daemon protocol does not currently provide any
+encryption of the data that is transferred over the connection. Only
+authentication is provided. Use ssh as the transport if you want encryption.
+.P
+You can also make use of SSL/TLS encryption if you put rsync behind an
+SSL proxy.
+.P
+.SH "SSL/TLS Daemon Setup"
+.P
+When setting up an rsync daemon for access via SSL/TLS, you will need to
+configure a TCP proxy (such as haproxy or nginx) as the front-end that handles
+the encryption.
+.P
+.IP o
+You should limit the access to the backend-rsyncd port to only allow the
+proxy to connect. If it is on the same host as the proxy, then configuring
+it to only listen on localhost is a good idea.
+.IP o
+You should consider turning on the \fBproxy\ protocol\fP rsync-daemon parameter if
+your proxy supports sending that information. The examples below assume that
+this is enabled.
+.P
+An example haproxy setup is as follows:
+.RS 4
+.P
+.nf
+frontend fe_rsync-ssl
+ bind :::874 ssl crt /etc/letsencrypt/example.com/combined.pem
+ mode tcp
+ use_backend be_rsync
+
+backend be_rsync
+ mode tcp
+ server local-rsync 127.0.0.1:873 check send-proxy
+.fi
+.RE
+.P
+An example nginx proxy setup is as follows:
+.RS 4
+.P
+.nf
+stream {
+ server {
+ listen 874 ssl;
+ listen [::]:874 ssl;
+
+ ssl_certificate /etc/letsencrypt/example.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/example.com/privkey.pem;
+
+ proxy_pass localhost:873;
+ proxy_protocol on; # Requires rsyncd.conf "proxy protocol = true"
+ proxy_timeout 1m;
+ proxy_connect_timeout 5s;
+ }
+}
+.fi
+.RE
+.P
+.SH "DAEMON CONFIG EXAMPLES"
+.P
+A simple rsyncd.conf file that allow anonymous rsync to a ftp area at
+\fB/home/ftp\fP would be:
+.RS 4
+.P
+.nf
+[ftp]
+ path = /home/ftp
+ comment = ftp export area
+.fi
+.RE
+.P
+A more sophisticated example would be:
+.RS 4
+.P
+.nf
+uid = nobody
+gid = nobody
+use chroot = yes
+max connections = 4
+syslog facility = local5
+pid file = /var/run/rsyncd.pid
+
+[ftp]
+ path = /var/ftp/./pub
+ comment = whole ftp area (approx 6.1 GB)
+
+[sambaftp]
+ path = /var/ftp/./pub/samba
+ comment = Samba ftp area (approx 300 MB)
+
+[rsyncftp]
+ path = /var/ftp/./pub/rsync
+ comment = rsync ftp area (approx 6 MB)
+
+[sambawww]
+ path = /public_html/samba
+ comment = Samba WWW pages (approx 240 MB)
+
+[cvs]
+ path = /data/cvs
+ comment = CVS repository (requires authentication)
+ auth users = tridge, susan
+ secrets file = /etc/rsyncd.secrets
+.fi
+.RE
+.P
+The /etc/rsyncd.secrets file would look something like this:
+.RS 4
+.P
+.nf
+tridge:mypass
+susan:herpass
+.fi
+.RE
+.P
+.SH "FILES"
+.P
+/etc/rsyncd.conf or rsyncd.conf
+.P
+.SH "SEE ALSO"
+.P
+\fBrsync\fP(1), \fBrsync-ssl\fP(1)
+.P
+.SH "BUGS"
+.P
+Please report bugs! The rsync bug tracking system is online at
+https://rsync.samba.org/.
+.P
+.SH "VERSION"
+.P
+This manpage is current for version 3.2.7 of rsync.
+.P
+.SH "CREDITS"
+.P
+Rsync is distributed under the GNU General Public License. See the file
+COPYING for details.
+.P
+An rsync web site is available at https://rsync.samba.org/ and its github
+project is https://github.com/WayneD/rsync.
+.P
+.SH "THANKS"
+.P
+Thanks to Warren Stanley for his original idea and patch for the rsync daemon.
+Thanks to Karsten Thygesen for his many suggestions and documentation!
+.P
+.SH "AUTHOR"
+.P
+Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people have later contributed to it. It is currently maintained by Wayne
+Davison.
+.P
+Mailing lists for support and development are available at
+https://lists.samba.org/.
diff --git a/rsyncd.conf.5.html b/rsyncd.conf.5.html
new file mode 100644
index 0000000..02f67ed
--- /dev/null
+++ b/rsyncd.conf.5.html
@@ -0,0 +1,1205 @@
+<html><head>
+<title>rsyncd.conf(5) manpage</title>
+<meta charset="UTF-8"/>
+<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
+<style>
+body {
+ max-width: 50em;
+ margin: auto;
+}
+body, b, strong, u {
+ font-family: 'Roboto', sans-serif;
+}
+a.tgt { font-face: symbol; font-weight: 400; font-size: 70%; visibility: hidden; text-decoration: none; color: #ddd; padding: 0 4px; border: 0; }
+a.tgt:after { content: '🔗'; }
+a.tgt:hover { color: #444; background-color: #eaeaea; }
+h1:hover > a.tgt, h2:hover > a.tgt, h3:hover > a.tgt, dt:hover > a.tgt { visibility: visible; }
+code {
+ font-family: 'Roboto Mono', monospace;
+ font-weight: bold;
+ white-space: pre;
+}
+pre code {
+ display: block;
+ font-weight: normal;
+}
+blockquote pre code {
+ background: #f1f1f1;
+}
+dd p:first-of-type {
+ margin-block-start: 0em;
+}
+</style>
+</head><body>
+<h2 id="NAME">NAME<a href="#NAME" class="tgt"></a></h2>
+<p>rsyncd.conf -&#8288; configuration file for rsync in daemon mode</p>
+<h2 id="SYNOPSIS">SYNOPSIS<a href="#SYNOPSIS" class="tgt"></a></h2>
+<p>rsyncd.conf</p>
+<p>The online version of this manpage (that includes cross-linking of topics)
+is available at <a href="https://download.samba.org/pub/rsync/rsyncd.conf.5">https://download.samba.org/pub/rsync/rsyncd.conf.5</a>.</p>
+<h2 id="DESCRIPTION">DESCRIPTION<a href="#DESCRIPTION" class="tgt"></a></h2>
+<p>The rsyncd.conf file is the runtime configuration file for rsync when run as an
+rsync daemon.</p>
+<p>The rsyncd.conf file controls authentication, access, logging and available
+modules.</p>
+<h2 id="FILE_FORMAT">FILE FORMAT<a href="#FILE_FORMAT" class="tgt"></a></h2>
+<p>The file consists of modules and parameters. A module begins with the name of
+the module in square brackets and continues until the next module begins.
+Modules contain parameters of the form <code>name = value</code>.</p>
+<p>The file is line-based&nbsp;-&#8288;-&#8288; that is, each newline-terminated line represents
+either a comment, a module name or a parameter.</p>
+<p>Only the first equals sign in a parameter is significant. Whitespace before or
+after the first equals sign is discarded. Leading, trailing and internal
+whitespace in module and parameter names is irrelevant. Leading and trailing
+whitespace in a parameter value is discarded. Internal whitespace within a
+parameter value is retained verbatim.</p>
+<p>Any line <strong>beginning</strong> with a hash (<code>#</code>) is ignored, as are lines containing
+only whitespace. (If a hash occurs after anything other than leading
+whitespace, it is considered a part of the line's content.)</p>
+<p>Any line ending in a <code>\</code> is &quot;continued&quot; on the next line in the customary UNIX
+fashion.</p>
+<p>The values following the equals sign in parameters are all either a string (no
+quotes needed) or a boolean, which may be given as yes/no, 0/1 or true/false.
+Case is not significant in boolean values, but is preserved in string values.</p>
+<h2 id="LAUNCHING_THE_RSYNC_DAEMON">LAUNCHING THE RSYNC DAEMON<a href="#LAUNCHING_THE_RSYNC_DAEMON" class="tgt"></a></h2>
+<p>The rsync daemon is launched by specifying the <code>--daemon</code> option to rsync.</p>
+<p>The daemon must run with root privileges if you wish to use chroot, to bind to
+a port numbered under 1024 (as is the default 873), or to set file ownership.
+Otherwise, it must just have permission to read and write the appropriate data,
+log, and lock files.</p>
+<p>You can launch it either via inetd, as a stand-alone daemon, or from an rsync
+client via a remote shell. If run as a stand-alone daemon then just run the
+command &quot;<code>rsync --daemon</code>&quot; from a suitable startup script.</p>
+<p>When run via inetd you should add a line like this to /etc/services:</p>
+<blockquote>
+<pre><code>rsync 873/tcp
+</code></pre>
+</blockquote>
+<p>and a single line something like this to /etc/inetd.conf:</p>
+<blockquote>
+<pre><code>rsync stream tcp nowait root /usr/bin/rsync rsyncd --daemon
+</code></pre>
+</blockquote>
+<p>Replace &quot;/usr/bin/rsync&quot; with the path to where you have rsync installed on
+your system. You will then need to send inetd a HUP signal to tell it to
+reread its config file.</p>
+<p>Note that you should <strong>not</strong> send the rsync daemon a HUP signal to force it to
+reread the <code>rsyncd.conf</code> file. The file is re-read on each client connection.</p>
+<h2 id="GLOBAL_PARAMETERS">GLOBAL PARAMETERS<a href="#GLOBAL_PARAMETERS" class="tgt"></a></h2>
+<p>The first parameters in the file (before a [module] header) are the global
+parameters. Rsync also allows for the use of a &quot;[global]&quot; module name to
+indicate the start of one or more global-parameter sections (the name must be
+lower case).</p>
+<p>You may also include any module parameters in the global part of the config
+file in which case the supplied value will override the default for that
+parameter.</p>
+<p>You may use references to environment variables in the values of parameters.
+String parameters will have %VAR% references expanded as late as possible (when
+the string is first used in the program), allowing for the use of variables
+that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
+parameters (such as true/false settings) are expanded when read from the config
+file. If a variable does not exist in the environment, or if a sequence of
+characters is not a valid reference (such as an un-paired percent sign), the
+raw characters are passed through unchanged. This helps with backward
+compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
+string in a path could result in a very unsafe path). The safest way to insert
+a literal % into a value is to use %%.</p>
+<dl>
+
+<dt id="motd_file"><code>motd file</code><a href="#motd_file" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify a &quot;message of the day&quot; (MOTD) to display
+to clients on each connect. This usually contains site information and any
+legal notices. The default is no MOTD file. This can be overridden by the
+<code>--dparam=motdfile=FILE</code> command-line option when starting the daemon.</p>
+</dd>
+
+<dt id="pid_file"><code>pid file</code><a href="#pid_file" class="tgt"></a></dt><dd>
+<p>This parameter tells the rsync daemon to write its process ID to that file.
+The rsync keeps the file locked so that it can know when it is safe to
+overwrite an existing file.</p>
+<p>The filename can be overridden by the <code>--dparam=pidfile=FILE</code> command-line
+option when starting the daemon.</p>
+</dd>
+
+<dt id="port"><code>port</code><a href="#port" class="tgt"></a></dt><dd>
+<p>You can override the default port the daemon will listen on by specifying
+this value (defaults to 873). This is ignored if the daemon is being run
+by inetd, and is superseded by the <code>--port</code> command-line option.</p>
+</dd>
+
+<dt id="address"><code>address</code><a href="#address" class="tgt"></a></dt><dd>
+<p>You can override the default IP address the daemon will listen on by
+specifying this value. This is ignored if the daemon is being run by
+inetd, and is superseded by the <code>--address</code> command-line option.</p>
+</dd>
+
+<dt id="socket_options"><code>socket options</code><a href="#socket_options" class="tgt"></a></dt><dd>
+<p>This parameter can provide endless fun for people who like to tune their
+systems to the utmost degree. You can set all sorts of socket options which
+may make transfers faster (or slower!). Read the manpage for the
+<strong>setsockopt()</strong> system call for details on some of the options you may be
+able to set. By default no special socket options are set. These settings
+can also be specified via the <code>--sockopts</code> command-line option.</p>
+</dd>
+
+<dt id="listen_backlog"><code>listen backlog</code><a href="#listen_backlog" class="tgt"></a></dt><dd>
+<p>You can override the default backlog value when the daemon listens for
+connections. It defaults to 5.</p>
+</dd>
+</dl>
+<h2 id="MODULE_PARAMETERS">MODULE PARAMETERS<a href="#MODULE_PARAMETERS" class="tgt"></a></h2>
+<p>After the global parameters you should define a number of modules, each module
+exports a directory tree as a symbolic name. Modules are exported by specifying
+a module name in square brackets [module] followed by the parameters for that
+module. The module name cannot contain a slash or a closing square bracket.
+If the name contains whitespace, each internal sequence of whitespace will be
+changed into a single space, while leading or trailing whitespace will be
+discarded. Also, the name cannot be &quot;global&quot; as that exact name indicates that
+global parameters follow (see above).</p>
+<p>As with GLOBAL PARAMETERS, you may use references to environment variables in
+the values of parameters. See the GLOBAL PARAMETERS section for more details.</p>
+<dl>
+
+<dt id="comment"><code>comment</code><a href="#comment" class="tgt"></a></dt><dd>
+<p>This parameter specifies a description string that is displayed next to the
+module name when clients obtain a list of available modules. The default is
+no comment.</p>
+</dd>
+
+<dt id="path"><code>path</code><a href="#path" class="tgt"></a></dt><dd>
+<p>This parameter specifies the directory in the daemon's filesystem to make
+available in this module. You must specify this parameter for each module
+in <code>rsyncd.conf</code>.</p>
+<p>If the value contains a &quot;/./&quot; element then the path will be divided at that
+point into a chroot dir and an inner-chroot subdir. If <a href="#use_chroot"><code>use chroot</code></a>
+is set to false, though, the extraneous dot dir is just cleaned out of the
+path. An example of this idiom is:</p>
+<blockquote>
+<pre><code>path = /var/rsync/./module1
+</code></pre>
+</blockquote>
+<p>This will (when chrooting) chroot to &quot;/var/rsync&quot; and set the inside-chroot
+path to &quot;/module1&quot;.</p>
+<p>You may base the path's value off of an environment variable by surrounding
+the variable name with percent signs. You can even reference a variable
+that is set by rsync when the user connects. For example, this would use
+the authorizing user's name in the path:</p>
+<blockquote>
+<pre><code>path = /home/%RSYNC_USER_NAME%
+</code></pre>
+</blockquote>
+<p>It is fine if the path includes internal spaces&nbsp;-&#8288;-&#8288; they will be retained
+verbatim (which means that you shouldn't try to escape them). If your
+final directory has a trailing space (and this is somehow not something you
+wish to fix), append a trailing slash to the path to avoid losing the
+trailing whitespace.</p>
+</dd>
+
+<dt id="use_chroot"><code>use chroot</code><a href="#use_chroot" class="tgt"></a></dt><dd>
+<p>If &quot;use chroot&quot; is true, the rsync daemon will chroot to the &quot;<a href="#path">path</a>&quot; before
+starting the file transfer with the client. This has the advantage of
+extra protection against possible implementation security holes, but it has
+the disadvantages of requiring super-user privileges, of not being able to
+follow symbolic links that are either absolute or outside of the new root
+path, and of complicating the preservation of users and groups by name (see
+below).</p>
+<p>If <code>use chroot</code> is not set, it defaults to trying to enable a chroot but
+allows the daemon to continue (after logging a warning) if it fails. The
+one exception to this is when a module's <a href="#path"><code>path</code></a> has a &quot;/./&quot; chroot
+divider in it&nbsp;-&#8288;-&#8288; this causes an unset value to be treated as true for that
+module.</p>
+<p>Prior to rsync 3.2.7, the default value was &quot;true&quot;. The new &quot;unset&quot;
+default makes it easier to setup an rsync daemon as a non-root user or to
+run a daemon on a system where chroot fails. Explicitly setting the value
+to &quot;true&quot; in rsyncd.conf will always require the chroot to succeed.</p>
+<p>It is also possible to specify a dot-dir in the module's &quot;<a href="#path">path</a>&quot; to
+indicate that you want to chdir to the earlier part of the path and then
+serve files from inside the latter part of the path (with sanitizing and
+default symlink munging). This can be useful if you need some library dirs
+inside the chroot (typically for uid &amp; gid lookups) but don't want to put
+the lib dir into the top of the served path (even though they can be hidden
+with an <a href="#exclude"><code>exclude</code></a> directive). However, a better choice for a modern
+rsync setup is to use a <a href="#name_converter"><code>name converter</code></a>&quot; and try to avoid inner lib
+dirs altogether. See also the <a href="#daemon_chroot"><code>daemon chroot</code></a> parameter, which causes
+rsync to chroot into its own chroot area before doing any path-related
+chrooting.</p>
+<p>If the daemon is serving the &quot;/&quot; dir (either directly or due to being
+chrooted to the module's path), rsync does not do any path sanitizing or
+(default) munging.</p>
+<p>When it has to limit access to a particular subdir (either due to chroot
+being disabled or having an inside-chroot path set), rsync will munge
+symlinks (by default) and sanitize paths. Those that dislike munged
+symlinks (and really, really trust their users to not break out of the
+subdir) can disable the symlink munging via the &quot;<a href="#munge_symlinks">munge symlinks</a>&quot;
+parameter.</p>
+<p>When rsync is sanitizing paths, it trims &quot;..&quot; path elements from args that
+it believes would escape the module hierarchy. It also substitutes leading
+slashes in absolute paths with the module's path (so that options such as
+<code>--backup-dir</code> &amp; <code>--compare-dest</code> interpret an absolute path as rooted in
+the module's &quot;<a href="#path">path</a>&quot; dir).</p>
+<p>When a chroot is in effect <u>and</u> the &quot;<a href="#name_converter">name converter</a>&quot; parameter is
+<u>not</u> set, the &quot;<a href="#numeric_ids">numeric ids</a>&quot; parameter will default to being enabled
+(disabling name lookups). This means that if you manually setup
+name-lookup libraries in your chroot (instead of using a name converter)
+that you need to explicitly set <code>numeric ids = false</code> for rsync to do name
+lookups.</p>
+<p>If you copy library resources into the module's chroot area, you should
+protect them through your OS's normal user/group or ACL settings (to
+prevent the rsync module's user from being able to change them), and then
+hide them from the user's view via &quot;<a href="#exclude">exclude</a>&quot; (see how in the discussion of
+that parameter). However, it's easier and safer to setup a name converter.</p>
+</dd>
+
+<dt id="daemon_chroot"><code>daemon chroot</code><a href="#daemon_chroot" class="tgt"></a></dt><dd>
+<p>This parameter specifies a path to which the daemon will chroot before
+beginning communication with clients. Module paths (and any &quot;<a href="#use_chroot">use chroot</a>&quot;
+settings) will then be related to this one. This lets you choose if you
+want the whole daemon to be chrooted (with this setting), just the
+transfers to be chrooted (with &quot;<a href="#use_chroot">use chroot</a>&quot;), or both. Keep in mind that
+the &quot;daemon chroot&quot; area may need various OS/lib/etc files installed to
+allow the daemon to function. By default the daemon runs without any
+chrooting.</p>
+</dd>
+
+<dt id="proxy_protocol"><code>proxy protocol</code><a href="#proxy_protocol" class="tgt"></a></dt><dd>
+<p>When this parameter is enabled, all incoming connections must start with a
+V1 or V2 proxy protocol header. If the header is not found, the connection
+is closed.</p>
+<p>Setting this to <code>true</code> requires a proxy server to forward source IP
+information to rsync, allowing you to log proper IP/host info and make use
+of client-oriented IP restrictions. The default of <code>false</code> means that the
+IP information comes directly from the socket's metadata. If rsync is not
+behind a proxy, this should be disabled.</p>
+<p><u>CAUTION</u>: using this option can be dangerous if you do not ensure that
+only the proxy is allowed to connect to the rsync port. If any non-proxied
+connections are allowed through, the client will be able to use a modified
+rsync to spoof any remote IP address that they desire. You can lock this
+down using something like iptables <code>-uid-owner root</code> rules (for strict
+localhost access), various firewall rules, or you can require password
+authorization so that any spoofing by users will not grant extra access.</p>
+<p>This setting is global. If you need some modules to require this and not
+others, then you will need to setup multiple rsync daemon processes on
+different ports.</p>
+</dd>
+
+<dt id="name_converter"><code>name converter</code><a href="#name_converter" class="tgt"></a></dt><dd>
+<p>This parameter lets you specify a program that will be run by the rsync
+daemon to do user &amp; group conversions between names &amp; ids. This script
+is started prior to any chroot being setup, and runs as the daemon user
+(not the transfer user). You can specify a fully qualified pathname or
+a program name that is on the $PATH.</p>
+<p>The program can be used to do normal user &amp; group lookups without having to
+put any extra files into the chroot area of the module <u>or</u> you can do
+customized conversions.</p>
+<p>The nameconvert program has access to all of the environment variables that
+are described in the section on <code>pre-xfer exec</code>. This is useful if you
+want to customize the conversion using information about the module and/or
+the copy request.</p>
+<p>There is a sample python script in the support dir named &quot;nameconvert&quot; that
+implements the normal user &amp; group lookups. Feel free to customize it or
+just use it as documentation to implement your own.</p>
+</dd>
+
+<dt id="numeric_ids"><code>numeric ids</code><a href="#numeric_ids" class="tgt"></a></dt><dd>
+<p>Enabling this parameter disables the mapping of users and groups by name
+for the current daemon module. This prevents the daemon from trying to
+load any user/group-related files or libraries. This enabling makes the
+transfer behave as if the client had passed the <code>--numeric-ids</code>
+command-line option. By default, this parameter is enabled for chroot
+modules and disabled for non-chroot modules. Also keep in mind that
+uid/gid preservation requires the module to be running as root (see &quot;<a href="#uid">uid</a>&quot;)
+or for &quot;<a href="#fake_super">fake super</a>&quot; to be configured.</p>
+<p>A chroot-enabled module should not have this parameter set to false unless
+you're using a &quot;<a href="#name_converter">name converter</a>&quot; program <u>or</u> you've taken steps to ensure
+that the module has the necessary resources it needs to translate names and
+that it is not possible for a user to change those resources.</p>
+</dd>
+
+<dt id="munge_symlinks"><code>munge symlinks</code><a href="#munge_symlinks" class="tgt"></a></dt><dd>
+<p>This parameter tells rsync to modify all symlinks in the same way as the
+(non-daemon-affecting) <code>--munge-links</code> command-line option (using a method
+described below). This should help protect your files from user trickery
+when your daemon module is writable. The default is disabled when
+&quot;<a href="#use_chroot">use chroot</a>&quot; is on with an inside-chroot path of &quot;/&quot;, OR if &quot;<a href="#daemon_chroot">daemon chroot</a>&quot;
+is on, otherwise it is enabled.</p>
+<p>If you disable this parameter on a daemon that is not read-only, there are
+tricks that a user can play with uploaded symlinks to access
+daemon-excluded items (if your module has any), and, if &quot;<a href="#use_chroot">use chroot</a>&quot; is
+off, rsync can even be tricked into showing or changing data that is
+outside the module's path (as access-permissions allow).</p>
+<p>The way rsync disables the use of symlinks is to prefix each one with the
+string &quot;/rsyncd-munged/&quot;. This prevents the links from being used as long
+as that directory does not exist. When this parameter is enabled, rsync
+will refuse to run if that path is a directory or a symlink to a directory.
+When using the &quot;munge symlinks&quot; parameter in a chroot area that has an
+inside-chroot path of &quot;/&quot;, you should add &quot;/rsyncd-munged/&quot; to the exclude
+setting for the module so that a user can't try to create it.</p>
+<p>Note: rsync makes no attempt to verify that any pre-existing symlinks in
+the module's hierarchy are as safe as you want them to be (unless, of
+course, it just copied in the whole hierarchy). If you setup an rsync
+daemon on a new area or locally add symlinks, you can manually protect your
+symlinks from being abused by prefixing &quot;/rsyncd-munged/&quot; to the start of
+every symlink's value. There is a perl script in the support directory of
+the source code named &quot;munge-symlinks&quot; that can be used to add or remove
+this prefix from your symlinks.</p>
+<p>When this parameter is disabled on a writable module and &quot;<a href="#use_chroot">use chroot</a>&quot; is
+off (or the inside-chroot path is not &quot;/&quot;), incoming symlinks will be
+modified to drop a leading slash and to remove &quot;..&quot; path elements that
+rsync believes will allow a symlink to escape the module's hierarchy.
+There are tricky ways to work around this, though, so you had better trust
+your users if you choose this combination of parameters.</p>
+</dd>
+
+<dt id="charset"><code>charset</code><a href="#charset" class="tgt"></a></dt><dd>
+<p>This specifies the name of the character set in which the module's
+filenames are stored. If the client uses an <code>--iconv</code> option, the daemon
+will use the value of the &quot;charset&quot; parameter regardless of the character
+set the client actually passed. This allows the daemon to support charset
+conversion in a chroot module without extra files in the chroot area, and
+also ensures that name-translation is done in a consistent manner. If the
+&quot;charset&quot; parameter is not set, the <code>--iconv</code> option is refused, just as if
+&quot;iconv&quot; had been specified via &quot;<a href="#refuse_options">refuse options</a>&quot;.</p>
+<p>If you wish to force users to always use <code>--iconv</code> for a particular module,
+add &quot;no-iconv&quot; to the &quot;<a href="#refuse_options">refuse options</a>&quot; parameter. Keep in mind that this
+will restrict access to your module to very new rsync clients.</p>
+</dd>
+
+<dt id="max_connections"><code>max connections</code><a href="#max_connections" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify the maximum number of simultaneous
+connections you will allow. Any clients connecting when the maximum has
+been reached will receive a message telling them to try later. The default
+is 0, which means no limit. A negative value disables the module. See
+also the &quot;<a href="#lock_file">lock file</a>&quot; parameter.</p>
+</dd>
+
+<dt id="log_file"><code>log file</code><a href="#log_file" class="tgt"></a></dt><dd>
+<p>When the &quot;log file&quot; parameter is set to a non-empty string, the rsync
+daemon will log messages to the indicated file rather than using syslog.
+This is particularly useful on systems (such as AIX) where <strong>syslog()</strong>
+doesn't work for chrooted programs. The file is opened before <strong>chroot()</strong>
+is called, allowing it to be placed outside the transfer. If this value is
+set on a per-module basis instead of globally, the global log will still
+contain any authorization failures or config-file error messages.</p>
+<p>If the daemon fails to open the specified file, it will fall back to using
+syslog and output an error about the failure. (Note that the failure to
+open the specified log file used to be a fatal error.)</p>
+<p>This setting can be overridden by using the <code>--log-file=FILE</code> or
+<code>--dparam=logfile=FILE</code> command-line options. The former overrides all the
+log-file parameters of the daemon and all module settings. The latter sets
+the daemon's log file and the default for all the modules, which still
+allows modules to override the default setting.</p>
+</dd>
+
+<dt id="syslog_facility"><code>syslog facility</code><a href="#syslog_facility" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify the syslog facility name to use when
+logging messages from the rsync daemon. You may use any standard syslog
+facility name which is defined on your system. Common names are auth,
+authpriv, cron, daemon, ftp, kern, lpr, mail, news, security, syslog, user,
+uucp, local0, local1, local2, local3, local4, local5, local6 and local7.
+The default is daemon. This setting has no effect if the &quot;<a href="#log_file">log file</a>&quot;
+setting is a non-empty string (either set in the per-modules settings, or
+inherited from the global settings).</p>
+</dd>
+
+<dt id="syslog_tag"><code>syslog tag</code><a href="#syslog_tag" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify the syslog tag to use when logging
+messages from the rsync daemon. The default is &quot;rsyncd&quot;. This setting has
+no effect if the &quot;<a href="#log_file">log file</a>&quot; setting is a non-empty string (either set in
+the per-modules settings, or inherited from the global settings).</p>
+<p>For example, if you wanted each authenticated user's name to be included in
+the syslog tag, you could do something like this:</p>
+<blockquote>
+<pre><code>syslog tag = rsyncd.%RSYNC_USER_NAME%
+</code></pre>
+</blockquote>
+</dd>
+
+<dt id="max_verbosity"><code>max verbosity</code><a href="#max_verbosity" class="tgt"></a></dt><dd>
+<p>This parameter allows you to control the maximum amount of verbose
+information that you'll allow the daemon to generate (since the information
+goes into the log file). The default is 1, which allows the client to
+request one level of verbosity.</p>
+<p>This also affects the user's ability to request higher levels of <code>--info</code>
+and <code>--debug</code> logging. If the max value is 2, then no info and/or debug
+value that is higher than what would be set by <code>-vv</code> will be honored by the
+daemon in its logging. To see how high of a verbosity level you need to
+accept for a particular info/debug level, refer to <code>rsync --info=help</code> and
+<code>rsync --debug=help</code>. For instance, it takes max-verbosity 4 to be able to
+output debug TIME2 and FLIST3.</p>
+</dd>
+
+<dt id="lock_file"><code>lock file</code><a href="#lock_file" class="tgt"></a></dt><dd>
+<p>This parameter specifies the file to use to support the &quot;<a href="#max_connections">max connections</a>&quot;
+parameter. The rsync daemon uses record locking on this file to ensure that
+the max connections limit is not exceeded for the modules sharing the lock
+file. The default is <code>/var/run/rsyncd.lock</code>.</p>
+</dd>
+
+<dt id="read_only"><code>read only</code><a href="#read_only" class="tgt"></a></dt><dd>
+<p>This parameter determines whether clients will be able to upload files or
+not. If &quot;read only&quot; is true then any attempted uploads will fail. If
+&quot;read only&quot; is false then uploads will be possible if file permissions on
+the daemon side allow them. The default is for all modules to be read only.</p>
+<p>Note that &quot;<a href="#auth_users">auth users</a>&quot; can override this setting on a per-user basis.</p>
+</dd>
+
+<dt id="write_only"><code>write only</code><a href="#write_only" class="tgt"></a></dt><dd>
+<p>This parameter determines whether clients will be able to download files or
+not. If &quot;write only&quot; is true then any attempted downloads will fail. If
+&quot;write only&quot; is false then downloads will be possible if file permissions
+on the daemon side allow them. The default is for this parameter to be
+disabled.</p>
+<p>Helpful hint: you probably want to specify &quot;refuse options = delete&quot; for a
+write-only module.</p>
+</dd>
+
+<dt id="open_noatime"><code>open noatime</code><a href="#open_noatime" class="tgt"></a></dt><dd>
+<p>When set to True, this parameter tells the rsync daemon to open files with
+the O_NOATIME flag
+(on systems that support it) to avoid changing the access time of the files
+that are being transferred. If your OS does not support the O_NOATIME flag
+then rsync will silently ignore this option. Note also that some
+filesystems are mounted to avoid updating the atime on read access even
+without the O_NOATIME flag being set.</p>
+<p>When set to False, this parameters ensures that files on the server are not
+opened with O_NOATIME.</p>
+<p>When set to Unset (the default) the user controls the setting via
+<code>--open-noatime</code>.</p>
+</dd>
+
+<dt id="list"><code>list</code><a href="#list" class="tgt"></a></dt><dd>
+<p>This parameter determines whether this module is listed when the client
+asks for a listing of available modules. In addition, if this is false,
+the daemon will pretend the module does not exist when a client denied by
+&quot;<a href="#hosts_allow">hosts allow</a>&quot; or &quot;<a href="#hosts_deny">hosts deny</a>&quot; attempts to access it. Realize that if
+&quot;<a href="#reverse_lookup">reverse lookup</a>&quot; is disabled globally but enabled for the module, the
+resulting reverse lookup to a potentially client-controlled DNS server may
+still reveal to the client that it hit an existing module. The default is
+for modules to be listable.</p>
+</dd>
+
+<dt id="uid"><code>uid</code><a href="#uid" class="tgt"></a></dt><dd>
+<p>This parameter specifies the user name or user ID that file transfers to
+and from that module should take place as when the daemon was run as root.
+In combination with the &quot;<a href="#gid">gid</a>&quot; parameter this determines what file
+permissions are available. The default when run by a super-user is to
+switch to the system's &quot;nobody&quot; user. The default for a non-super-user is
+to not try to change the user. See also the &quot;<a href="#gid">gid</a>&quot; parameter.</p>
+<p>The RSYNC_USER_NAME environment variable may be used to request that rsync
+run as the authorizing user. For example, if you want a rsync to run as
+the same user that was received for the rsync authentication, this setup is
+useful:</p>
+<blockquote>
+<pre><code>uid = %RSYNC_USER_NAME%
+gid = *
+</code></pre>
+</blockquote>
+</dd>
+
+<dt id="gid"><code>gid</code><a href="#gid" class="tgt"></a></dt><dd>
+<p>This parameter specifies one or more group names/IDs that will be used when
+accessing the module. The first one will be the default group, and any
+extra ones be set as supplemental groups. You may also specify a &quot;<code>*</code>&quot; as
+the first gid in the list, which will be replaced by all the normal groups
+for the transfer's user (see &quot;<a href="#uid">uid</a>&quot;). The default when run by a super-user
+is to switch to your OS's &quot;nobody&quot; (or perhaps &quot;nogroup&quot;) group with no
+other supplementary groups. The default for a non-super-user is to not
+change any group attributes (and indeed, your OS may not allow a
+non-super-user to try to change their group settings).</p>
+<p>The specified list is normally split into tokens based on spaces and
+commas. However, if the list starts with a comma, then the list is only
+split on commas, which allows a group name to contain a space. In either
+case any leading and/or trailing whitespace is removed from the tokens and
+empty tokens are ignored.</p>
+</dd>
+
+<dt id="daemon_uid"><code>daemon uid</code><a href="#daemon_uid" class="tgt"></a></dt><dd>
+<p>This parameter specifies a uid under which the daemon will run. The daemon
+usually runs as user root, and when this is left unset the user is left
+unchanged. See also the &quot;<a href="#uid">uid</a>&quot; parameter.</p>
+</dd>
+
+<dt id="daemon_gid"><code>daemon gid</code><a href="#daemon_gid" class="tgt"></a></dt><dd>
+<p>This parameter specifies a gid under which the daemon will run. The daemon
+usually runs as group root, and when this is left unset, the group is left
+unchanged. See also the &quot;<a href="#gid">gid</a>&quot; parameter.</p>
+</dd>
+
+<dt id="fake_super"><code>fake super</code><a href="#fake_super" class="tgt"></a></dt><dd>
+<p>Setting &quot;fake super = yes&quot; for a module causes the daemon side to behave as
+if the <code>--fake-super</code> command-line option had been specified. This allows
+the full attributes of a file to be stored without having to have the
+daemon actually running as root.</p>
+</dd>
+
+<dt id="filter"><code>filter</code><a href="#filter" class="tgt"></a></dt><dd>
+<p>The daemon has its own filter chain that determines what files it will let
+the client access. This chain is not sent to the client and is independent
+of any filters the client may have specified. Files excluded by the daemon
+filter chain (<code>daemon-excluded</code> files) are treated as non-existent if the
+client tries to pull them, are skipped with an error message if the client
+tries to push them (triggering exit code 23), and are never deleted from
+the module. You can use daemon filters to prevent clients from downloading
+or tampering with private administrative files, such as files you may add
+to support uid/gid name translations.</p>
+<p>The daemon filter chain is built from the &quot;filter&quot;, &quot;<a href="#include_from">include from</a>&quot;,
+&quot;<a href="#include">include</a>&quot;, &quot;<a href="#exclude_from">exclude from</a>&quot;, and &quot;<a href="#exclude">exclude</a>&quot; parameters, in that order of
+priority. Anchored patterns are anchored at the root of the module. To
+prevent access to an entire subtree, for example, &quot;<code>/secret</code>&quot;, you <strong>must</strong>
+exclude everything in the subtree; the easiest way to do this is with a
+triple-star pattern like &quot;<code>/secret/***</code>&quot;.</p>
+<p>The &quot;filter&quot; parameter takes a space-separated list of daemon filter rules,
+though it is smart enough to know not to split a token at an internal space
+in a rule (e.g. &quot;<code>- /foo - /bar</code>&quot; is parsed as two rules). You may specify
+one or more merge-file rules using the normal syntax. Only one &quot;filter&quot;
+parameter can apply to a given module in the config file, so put all the
+rules you want in a single parameter. Note that per-directory merge-file
+rules do not provide as much protection as global rules, but they can be
+used to make <code>--delete</code> work better during a client download operation if
+the per-dir merge files are included in the transfer and the client
+requests that they be used.</p>
+</dd>
+
+<dt id="exclude"><code>exclude</code><a href="#exclude" class="tgt"></a></dt><dd>
+<p>This parameter takes a space-separated list of daemon exclude patterns. As
+with the client <code>--exclude</code> option, patterns can be qualified with &quot;<code>- </code>&quot; or
+&quot;<code>+ </code>&quot; to explicitly indicate exclude/include. Only one &quot;exclude&quot; parameter
+can apply to a given module. See the &quot;filter&quot; parameter for a description
+of how excluded files affect the daemon.</p>
+</dd>
+
+<dt id="include"><code>include</code><a href="#include" class="tgt"></a></dt><dd>
+<p>Use an &quot;include&quot; to override the effects of the &quot;<a href="#exclude">exclude</a>&quot; parameter. Only
+one &quot;include&quot; parameter can apply to a given module. See the &quot;<a href="#filter">filter</a>&quot;
+parameter for a description of how excluded files affect the daemon.</p>
+</dd>
+
+<dt id="exclude_from"><code>exclude from</code><a href="#exclude_from" class="tgt"></a></dt><dd>
+<p>This parameter specifies the name of a file on the daemon that contains
+daemon exclude patterns, one per line. Only one &quot;exclude from&quot; parameter
+can apply to a given module; if you have multiple exclude-from files, you
+can specify them as a merge file in the &quot;<a href="#filter">filter</a>&quot; parameter. See the
+&quot;<a href="#filter">filter</a>&quot; parameter for a description of how excluded files affect the
+daemon.</p>
+</dd>
+
+<dt id="include_from"><code>include from</code><a href="#include_from" class="tgt"></a></dt><dd>
+<p>Analogue of &quot;<a href="#exclude_from">exclude from</a>&quot; for a file of daemon include patterns. Only one
+&quot;include from&quot; parameter can apply to a given module. See the &quot;<a href="#filter">filter</a>&quot;
+parameter for a description of how excluded files affect the daemon.</p>
+</dd>
+
+<dt id="incoming_chmod"><code>incoming chmod</code><a href="#incoming_chmod" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify a set of comma-separated chmod strings
+that will affect the permissions of all incoming files (files that are
+being received by the daemon). These changes happen after all other
+permission calculations, and this will even override destination-default
+and/or existing permissions when the client does not specify <code>--perms</code>.
+See the description of the <code>--chmod</code> rsync option and the <strong>chmod</strong>(1)
+manpage for information on the format of this string.</p>
+</dd>
+
+<dt id="outgoing_chmod"><code>outgoing chmod</code><a href="#outgoing_chmod" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify a set of comma-separated chmod strings
+that will affect the permissions of all outgoing files (files that are
+being sent out from the daemon). These changes happen first, making the
+sent permissions appear to be different than those stored in the filesystem
+itself. For instance, you could disable group write permissions on the
+server while having it appear to be on to the clients. See the description
+of the <code>--chmod</code> rsync option and the <strong>chmod</strong>(1) manpage for information
+on the format of this string.</p>
+</dd>
+
+<dt id="auth_users"><code>auth users</code><a href="#auth_users" class="tgt"></a></dt><dd>
+<p>This parameter specifies a comma and/or space-separated list of
+authorization rules. In its simplest form, you list the usernames that
+will be allowed to connect to this module. The usernames do not need to
+exist on the local system. The rules may contain shell wildcard characters
+that will be matched against the username provided by the client for
+authentication. If &quot;auth users&quot; is set then the client will be challenged
+to supply a username and password to connect to the module. A challenge
+response authentication protocol is used for this exchange. The plain text
+usernames and passwords are stored in the file specified by the
+&quot;<a href="#secrets_file">secrets file</a>&quot; parameter. The default is for all users to be able to
+connect without a password (this is called &quot;anonymous rsync&quot;).</p>
+<p>In addition to username matching, you can specify groupname matching via a
+'@' prefix. When using groupname matching, the authenticating username
+must be a real user on the system, or it will be assumed to be a member of
+no groups. For example, specifying &quot;@rsync&quot; will match the authenticating
+user if the named user is a member of the rsync group.</p>
+<p>Finally, options may be specified after a colon (:). The options allow you
+to &quot;deny&quot; a user or a group, set the access to &quot;ro&quot; (read-only), or set the
+access to &quot;rw&quot; (read/write). Setting an auth-rule-specific ro/rw setting
+overrides the module's &quot;<a href="#read_only">read only</a>&quot; setting.</p>
+<p>Be sure to put the rules in the order you want them to be matched, because
+the checking stops at the first matching user or group, and that is the
+only auth that is checked. For example:</p>
+<blockquote>
+<pre><code>auth users = joe:deny @guest:deny admin:rw @rsync:ro susan joe sam
+</code></pre>
+</blockquote>
+<p>In the above rule, user joe will be denied access no matter what. Any user
+that is in the group &quot;guest&quot; is also denied access. The user &quot;admin&quot; gets
+access in read/write mode, but only if the admin user is not in group
+&quot;guest&quot; (because the admin user-matching rule would never be reached if the
+user is in group &quot;guest&quot;). Any other user who is in group &quot;rsync&quot; will get
+read-only access. Finally, users susan, joe, and sam get the ro/rw setting
+of the module, but only if the user didn't match an earlier group-matching
+rule.</p>
+<p>If you need to specify a user or group name with a space in it, start your
+list with a comma to indicate that the list should only be split on commas
+(though leading and trailing whitespace will also be removed, and empty
+entries are just ignored). For example:</p>
+<blockquote>
+<pre><code>auth users = , joe:deny, @Some Group:deny, admin:rw, @RO Group:ro
+</code></pre>
+</blockquote>
+<p>See the description of the secrets file for how you can have per-user
+passwords as well as per-group passwords. It also explains how a user can
+authenticate using their user password or (when applicable) a group
+password, depending on what rule is being authenticated.</p>
+<p>See also the section entitled &quot;USING RSYNC-DAEMON FEATURES VIA A REMOTE
+SHELL CONNECTION&quot; in <strong>rsync</strong>(1) for information on how handle an
+rsyncd.conf-level username that differs from the remote-shell-level
+username when using a remote shell to connect to an rsync daemon.</p>
+</dd>
+
+<dt id="secrets_file"><code>secrets file</code><a href="#secrets_file" class="tgt"></a></dt><dd>
+<p>This parameter specifies the name of a file that contains the
+username:password and/or @groupname:password pairs used for authenticating
+this module. This file is only consulted if the &quot;<a href="#auth_users">auth users</a>&quot; parameter is
+specified. The file is line-based and contains one name:password pair per
+line. Any line has a hash (#) as the very first character on the line is
+considered a comment and is skipped. The passwords can contain any
+characters but be warned that many operating systems limit the length of
+passwords that can be typed at the client end, so you may find that
+passwords longer than 8 characters don't work.</p>
+<p>The use of group-specific lines are only relevant when the module is being
+authorized using a matching &quot;@groupname&quot; rule. When that happens, the user
+can be authorized via either their &quot;username:password&quot; line or the
+&quot;@groupname:password&quot; line for the group that triggered the authentication.</p>
+<p>It is up to you what kind of password entries you want to include, either
+users, groups, or both. The use of group rules in &quot;<a href="#auth_users">auth users</a>&quot; does not
+require that you specify a group password if you do not want to use shared
+passwords.</p>
+<p>There is no default for the &quot;secrets file&quot; parameter, you must choose a
+name (such as <code>/etc/rsyncd.secrets</code>). The file must normally not be
+readable by &quot;other&quot;; see &quot;<a href="#strict_modes">strict modes</a>&quot;. If the file is not found or is
+rejected, no logins for an &quot;<a href="#auth_users">auth users</a>&quot; module will be possible.</p>
+</dd>
+
+<dt id="strict_modes"><code>strict modes</code><a href="#strict_modes" class="tgt"></a></dt><dd>
+<p>This parameter determines whether or not the permissions on the secrets
+file will be checked. If &quot;strict modes&quot; is true, then the secrets file
+must not be readable by any user ID other than the one that the rsync
+daemon is running under. If &quot;strict modes&quot; is false, the check is not
+performed. The default is true. This parameter was added to accommodate
+rsync running on the Windows operating system.</p>
+</dd>
+
+<dt id="hosts_allow"><code>hosts allow</code><a href="#hosts_allow" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify a list of comma- and/or
+whitespace-separated patterns that are matched against a connecting
+client's hostname and IP address. If none of the patterns match, then the
+connection is rejected.</p>
+<p>Each pattern can be in one of six forms:</p>
+<ul>
+<li>a dotted decimal IPv4 address of the form a.b.c.d, or an IPv6 address of
+the form a:b:c::d:e:f. In this case the incoming machine's IP address
+must match exactly.</li>
+<li>an address/mask in the form ipaddr/n where ipaddr is the IP address and n
+is the number of one bits in the netmask. All IP addresses which match
+the masked IP address will be allowed in.</li>
+<li>an address/mask in the form ipaddr/maskaddr where ipaddr is the IP
+address and maskaddr is the netmask in dotted decimal notation for IPv4,
+or similar for IPv6, e.g. ffff:ffff:ffff:ffff:: instead of /64. All IP
+addresses which match the masked IP address will be allowed in.</li>
+<li>a hostname pattern using wildcards. If the hostname of the connecting IP
+(as determined by a reverse lookup) matches the wildcarded name (using
+the same rules as normal Unix filename matching), the client is allowed
+in. This only works if &quot;<a href="#reverse_lookup">reverse lookup</a>&quot; is enabled (the default).</li>
+<li>a hostname. A plain hostname is matched against the reverse DNS of the
+connecting IP (if &quot;<a href="#reverse_lookup">reverse lookup</a>&quot; is enabled), and/or the IP of the
+given hostname is matched against the connecting IP (if &quot;<a href="#forward_lookup">forward lookup</a>&quot;
+is enabled, as it is by default). Any match will be allowed in.</li>
+<li>an '@' followed by a netgroup name, which will match if the reverse DNS
+of the connecting IP is in the specified netgroup.</li>
+</ul>
+<p>Note IPv6 link-local addresses can have a scope in the address
+specification:</p>
+<blockquote>
+<pre><code>fe80::1%link1
+fe80::%link1/64
+fe80::%link1/ffff:ffff:ffff:ffff::
+</code></pre>
+</blockquote>
+<p>You can also combine &quot;hosts allow&quot; with &quot;<a href="#hosts_deny">hosts deny</a>&quot; as a way to add
+exceptions to your deny list. When both parameters are specified, the
+&quot;hosts allow&quot; parameter is checked first and a match results in the client
+being able to connect. A non-allowed host is then matched against the
+&quot;<a href="#hosts_deny">hosts deny</a>&quot; list to see if it should be rejected. A host that does not
+match either list is allowed to connect.</p>
+<p>The default is no &quot;hosts allow&quot; parameter, which means all hosts can
+connect.</p>
+</dd>
+
+<dt id="hosts_deny"><code>hosts deny</code><a href="#hosts_deny" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify a list of comma- and/or
+whitespace-separated patterns that are matched against a connecting clients
+hostname and IP address. If the pattern matches then the connection is
+rejected. See the &quot;<a href="#hosts_allow">hosts allow</a>&quot; parameter for more information.</p>
+<p>The default is no &quot;hosts deny&quot; parameter, which means all hosts can
+connect.</p>
+</dd>
+
+<dt id="reverse_lookup"><code>reverse lookup</code><a href="#reverse_lookup" class="tgt"></a></dt><dd>
+<p>Controls whether the daemon performs a reverse lookup on the client's IP
+address to determine its hostname, which is used for &quot;<a href="#hosts_allow">hosts allow</a>&quot; &amp;
+&quot;<a href="#hosts_deny">hosts deny</a>&quot; checks and the &quot;%h&quot; log escape. This is enabled by default,
+but you may wish to disable it to save time if you know the lookup will not
+return a useful result, in which case the daemon will use the name
+&quot;UNDETERMINED&quot; instead.</p>
+<p>If this parameter is enabled globally (even by default), rsync performs the
+lookup as soon as a client connects, so disabling it for a module will not
+avoid the lookup. Thus, you probably want to disable it globally and then
+enable it for modules that need the information.</p>
+</dd>
+
+<dt id="forward_lookup"><code>forward lookup</code><a href="#forward_lookup" class="tgt"></a></dt><dd>
+<p>Controls whether the daemon performs a forward lookup on any hostname
+specified in an hosts allow/deny setting. By default this is enabled,
+allowing the use of an explicit hostname that would not be returned by
+reverse DNS of the connecting IP.</p>
+</dd>
+
+<dt id="ignore_errors"><code>ignore errors</code><a href="#ignore_errors" class="tgt"></a></dt><dd>
+<p>This parameter tells rsyncd to ignore I/O errors on the daemon when
+deciding whether to run the delete phase of the transfer. Normally rsync
+skips the <code>--delete</code> step if any I/O errors have occurred in order to
+prevent disastrous deletion due to a temporary resource shortage or other
+I/O error. In some cases this test is counter productive so you can use
+this parameter to turn off this behavior.</p>
+</dd>
+
+<dt id="ignore_nonreadable"><code>ignore nonreadable</code><a href="#ignore_nonreadable" class="tgt"></a></dt><dd>
+<p>This tells the rsync daemon to completely ignore files that are not
+readable by the user. This is useful for public archives that may have some
+non-readable files among the directories, and the sysadmin doesn't want
+those files to be seen at all.</p>
+</dd>
+
+<dt id="transfer_logging"><code>transfer logging</code><a href="#transfer_logging" class="tgt"></a></dt><dd>
+<p>This parameter enables per-file logging of downloads and uploads in a
+format somewhat similar to that used by ftp daemons. The daemon always
+logs the transfer at the end, so if a transfer is aborted, no mention will
+be made in the log file.</p>
+<p>If you want to customize the log lines, see the &quot;<a href="#log_format">log format</a>&quot; parameter.</p>
+</dd>
+
+<dt id="log_format"><code>log format</code><a href="#log_format" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify the format used for logging file
+transfers when transfer logging is enabled. The format is a text string
+containing embedded single-character escape sequences prefixed with a
+percent (%) character. An optional numeric field width may also be
+specified between the percent and the escape letter (e.g.
+&quot;<code>%-50n %8l %07p</code>&quot;). In addition, one or more apostrophes may be specified
+prior to a numerical escape to indicate that the numerical value should be
+made more human-readable. The 3 supported levels are the same as for the
+<code>--human-readable</code> command-line option, though the default is for
+human-readability to be off. Each added apostrophe increases the level
+(e.g. &quot;<code>%''l %'b %f</code>&quot;).</p>
+<p>The default log format is &quot;<code>%o %h [%a] %m (%u) %f %l</code>&quot;, and a &quot;<code>%t [%p] </code>&quot;
+is always prefixed when using the &quot;<a href="#log_file">log file</a>&quot; parameter. (A perl script
+that will summarize this default log format is included in the rsync source
+code distribution in the &quot;support&quot; subdirectory: rsyncstats.)</p>
+<p>The single-character escapes that are understood are as follows:</p>
+<ul>
+<li>%a the remote IP address (only available for a daemon)</li>
+<li>%b the number of bytes actually transferred</li>
+<li>%B the permission bits of the file (e.g. rwxrwxrwt)</li>
+<li>%c the total size of the block checksums received for the basis file
+(only when sending)</li>
+<li>%C the full-file checksum if it is known for the file. For older rsync
+protocols/versions, the checksum was salted, and is thus not a useful
+value (and is not displayed when that is the case). For the checksum to
+output for a file, either the <code>--checksum</code> option must be in-effect or
+the file must have been transferred without a salted checksum being used.
+See the <code>--checksum-choice</code> option for a way to choose the algorithm.</li>
+<li>%f the filename (long form on sender; no trailing &quot;/&quot;)</li>
+<li>%G the gid of the file (decimal) or &quot;DEFAULT&quot;</li>
+<li>%h the remote host name (only available for a daemon)</li>
+<li>%i an itemized list of what is being updated</li>
+<li>%l the length of the file in bytes</li>
+<li>%L the string &quot;<code> -&gt; SYMLINK</code>&quot;, &quot;<code> =&gt; HARDLINK</code>&quot;, or &quot;&quot; (where <code>SYMLINK</code>
+or <code>HARDLINK</code> is a filename)</li>
+<li>%m the module name</li>
+<li>%M the last-modified time of the file</li>
+<li>%n the filename (short form; trailing &quot;/&quot; on dir)</li>
+<li>%o the operation, which is &quot;send&quot;, &quot;recv&quot;, or &quot;del.&quot; (the latter includes
+the trailing period)</li>
+<li>%p the process ID of this rsync session</li>
+<li>%P the module path</li>
+<li>%t the current date time</li>
+<li>%u the authenticated username or an empty string</li>
+<li>%U the uid of the file (decimal)</li>
+</ul>
+<p>For a list of what the characters mean that are output by &quot;%i&quot;, see the
+<code>--itemize-changes</code> option in the rsync manpage.</p>
+<p>Note that some of the logged output changes when talking with older rsync
+versions. For instance, deleted files were only output as verbose messages
+prior to rsync 2.6.4.</p>
+</dd>
+
+<dt id="timeout"><code>timeout</code><a href="#timeout" class="tgt"></a></dt><dd>
+<p>This parameter allows you to override the clients choice for I/O timeout
+for this module. Using this parameter you can ensure that rsync won't wait
+on a dead client forever. The timeout is specified in seconds. A value of
+zero means no timeout and is the default. A good choice for anonymous rsync
+daemons may be 600 (giving a 10 minute timeout).</p>
+</dd>
+
+<dt id="refuse_options"><code>refuse options</code><a href="#refuse_options" class="tgt"></a></dt><dd>
+<p>This parameter allows you to specify a space-separated list of rsync
+command-line options that will be refused by your rsync daemon. You may
+specify the full option name, its one-letter abbreviation, or a wild-card
+string that matches multiple options. Beginning in 3.2.0, you can also
+negate a match term by starting it with a &quot;!&quot;.</p>
+<p>When an option is refused, the daemon prints an error message and exits.</p>
+<p>For example, this would refuse <code>--checksum</code> (<code>-c</code>) and all the various
+delete options:</p>
+<blockquote>
+<pre><code>refuse options = c delete
+</code></pre>
+</blockquote>
+<p>The reason the above refuses all delete options is that the options imply
+<code>--delete</code>, and implied options are refused just like explicit options.</p>
+<p>The use of a negated match allows you to fine-tune your refusals after a
+wild-card, such as this:</p>
+<blockquote>
+<pre><code>refuse options = delete-* !delete-during
+</code></pre>
+</blockquote>
+<p>Negated matching can also turn your list of refused options into a list of
+accepted options. To do this, begin the list with a &quot;<code>*</code>&quot; (to refuse all
+options) and then specify one or more negated matches to accept. For
+example:</p>
+<blockquote>
+<pre><code>refuse options = * !a !v !compress*
+</code></pre>
+</blockquote>
+<p>Don't worry that the &quot;<code>*</code>&quot; will refuse certain vital options such as
+<code>--dry-run</code>, <code>--server</code>, <code>--no-iconv</code>, <code>--seclude-args</code>, etc. These
+important options are not matched by wild-card, so they must be overridden
+by their exact name. For instance, if you're forcing iconv transfers you
+could use something like this:</p>
+<blockquote>
+<pre><code>refuse options = * no-iconv !a !v
+</code></pre>
+</blockquote>
+<p>As an additional aid (beginning in 3.2.0), refusing (or &quot;<code>!refusing</code>&quot;) the
+&quot;a&quot; or &quot;archive&quot; option also affects all the options that the <code>--archive</code>
+option implies (<code>-rdlptgoD</code>), but only if the option is matched explicitly
+(not using a wildcard). If you want to do something tricky, you can use
+&quot;<code>archive*</code>&quot; to avoid this side-effect, but keep in mind that no normal
+rsync client ever sends the actual archive option to the server.</p>
+<p>As an additional safety feature, the refusal of &quot;delete&quot; also refuses
+<code>remove-source-files</code> when the daemon is the sender; if you want the latter
+without the former, instead refuse &quot;<code>delete-*</code>&quot; as that refuses all the
+delete modes without affecting <code>--remove-source-files</code>. (Keep in mind that
+the client's <code>--delete</code> option typically results in <code>--delete-during</code>.)</p>
+<p>When un-refusing delete options, you should either specify &quot;<code>!delete*</code>&quot; (to
+accept all delete options) or specify a limited set that includes &quot;delete&quot;,
+such as:</p>
+<blockquote>
+<pre><code>refuse options = * !a !delete !delete-during
+</code></pre>
+</blockquote>
+<p>... whereas this accepts any delete option except <code>--delete-after</code>:</p>
+<blockquote>
+<pre><code>refuse options = * !a !delete* delete-after
+</code></pre>
+</blockquote>
+<p>A note on refusing &quot;compress&quot;: it may be better to set the &quot;<a href="#dont_compress">dont compress</a>&quot;
+daemon parameter to &quot;<code>*</code>&quot; and ensure that <code>RSYNC_COMPRESS_LIST=zlib</code> is set
+in the environment of the daemon in order to disable compression silently
+instead of returning an error that forces the client to remove the <code>-z</code>
+option.</p>
+<p>If you are un-refusing the compress option, you may want to match
+&quot;<code>!compress*</code>&quot; if you also want to allow the <code>--compress-level</code> option.</p>
+<p>Note that the &quot;copy-devices&quot; &amp; &quot;write-devices&quot; options are refused by
+default, but they can be explicitly accepted with &quot;<code>!copy-devices</code>&quot; and/or
+&quot;<code>!write-devices</code>&quot;. The options &quot;log-file&quot; and &quot;log-file-format&quot; are
+forcibly refused and cannot be accepted.</p>
+<p>Here are all the options that are not matched by wild-cards:</p>
+<ul>
+<li><code>--server</code>: Required for rsync to even work.</li>
+<li><code>--rsh</code>, <code>-e</code>: Required to convey compatibility flags to the server.</li>
+<li><code>--out-format</code>: This is required to convey output behavior to a remote
+receiver. While rsync passes the older alias <code>--log-format</code> for
+compatibility reasons, this options should not be confused with
+<code>--log-file-format</code>.</li>
+<li><code>--sender</code>: Use &quot;<a href="#write_only">write only</a>&quot; parameter instead of refusing this.</li>
+<li><code>--dry-run</code>, <code>-n</code>: Who would want to disable this?</li>
+<li><code>--seclude-args</code>, <code>-s</code>: Is the oldest arg-protection method.</li>
+<li><code>--from0</code>, <code>-0</code>: Makes it easier to accept/refuse <code>--files-from</code> without
+affecting this helpful modifier.</li>
+<li><code>--iconv</code>: This is auto-disabled based on &quot;<a href="#charset">charset</a>&quot; parameter.</li>
+<li><code>--no-iconv</code>: Most transfers use this option.</li>
+<li><code>--checksum-seed</code>: Is a fairly rare, safe option.</li>
+<li><code>--write-devices</code>: Is non-wild but also auto-disabled.</li>
+</ul>
+</dd>
+
+<dt id="dont_compress"><code>dont compress</code><a href="#dont_compress" class="tgt"></a></dt><dd>
+<p><strong>NOTE:</strong> This parameter currently has no effect except in one instance: if
+it is set to &quot;<code>*</code>&quot; then it minimizes or disables compression for all files
+(for those that don't want to refuse the <code>--compress</code> option completely).</p>
+<p>This parameter allows you to select filenames based on wildcard patterns
+that should not be compressed when pulling files from the daemon (no
+analogous parameter exists to govern the pushing of files to a daemon).
+Compression can be expensive in terms of CPU usage, so it is usually good
+to not try to compress files that won't compress well, such as already
+compressed files.</p>
+<p>The &quot;dont compress&quot; parameter takes a space-separated list of
+case-insensitive wildcard patterns. Any source filename matching one of the
+patterns will be compressed as little as possible during the transfer. If
+the compression algorithm has an &quot;off&quot; level, then no compression occurs
+for those files. If an algorithms has the ability to change the level in
+mid-stream, it will be minimized to reduce the CPU usage as much as
+possible.</p>
+<p>See the <code>--skip-compress</code> parameter in the <strong>rsync</strong>(1) manpage for the
+list of file suffixes that are skipped by default if this parameter is not
+set.</p>
+</dd>
+
+<span id="post-xfer_exec"></span><span id="pre-xfer_exec"></span><dt id="early_exec"><code>early exec</code>, <code>pre-xfer exec</code>, <code>post-xfer exec</code><a href="#early_exec" class="tgt"></a></dt><dd>
+<p>You may specify a command to be run in the early stages of the connection,
+or right before and/or after the transfer. If the <code>early exec</code> or
+<code>pre-xfer exec</code> command returns an error code, the transfer is aborted
+before it begins. Any output from the <code>pre-xfer exec</code> command on stdout
+(up to several KB) will be displayed to the user when aborting, but is
+<u>not</u> displayed if the script returns success. The other programs cannot
+send any text to the user. All output except for the <code>pre-xfer exec</code>
+stdout goes to the corresponding daemon's stdout/stderr, which is typically
+discarded. See the <code>--no-detatch</code> option for a way to see the daemon's
+output, which can assist with debugging.</p>
+<p>Note that the <code>early exec</code> command runs before any part of the transfer
+request is known except for the module name. This helper script can be
+used to setup a disk mount or decrypt some data into a module dir, but you
+may need to use <code>lock file</code> and <code>max connections</code> to avoid concurrency
+issues. If the client rsync specified the <code>--early-input=FILE</code> option, it
+can send up to about 5K of data to the stdin of the early script. The
+stdin will otherwise be empty.</p>
+<p>Note that the <code>post-xfer exec</code> command is still run even if one of the
+other scripts returns an error code. The <code>pre-xfer exec</code> command will <u>not</u>
+be run, however, if the <code>early exec</code> command fails.</p>
+<p>The following environment variables will be set, though some are specific
+to the pre-xfer or the post-xfer environment:</p>
+<ul>
+<li><code>RSYNC_MODULE_NAME</code>: The name of the module being accessed.</li>
+<li><code>RSYNC_MODULE_PATH</code>: The path configured for the module.</li>
+<li><code>RSYNC_HOST_ADDR</code>: The accessing host's IP address.</li>
+<li><code>RSYNC_HOST_NAME</code>: The accessing host's name.</li>
+<li><code>RSYNC_USER_NAME</code>: The accessing user's name (empty if no user).</li>
+<li><code>RSYNC_PID</code>: A unique number for this transfer.</li>
+<li><code>RSYNC_REQUEST</code>: (pre-xfer only) The module/path info specified by the
+user. Note that the user can specify multiple source files, so the
+request can be something like &quot;mod/path1 mod/path2&quot;, etc.</li>
+<li><code>RSYNC_ARG#</code>: (pre-xfer only) The pre-request arguments are set in these
+numbered values. RSYNC_ARG0 is always &quot;rsyncd&quot;, followed by the options
+that were used in RSYNC_ARG1, and so on. There will be a value of &quot;.&quot;
+indicating that the options are done and the path args are beginning&nbsp;-&#8288;-&#8288;
+these contain similar information to RSYNC_REQUEST, but with values
+separated and the module name stripped off.</li>
+<li><code>RSYNC_EXIT_STATUS</code>: (post-xfer only) the server side's exit value. This
+will be 0 for a successful run, a positive value for an error that the
+server generated, or a -&#8288;1 if rsync failed to exit properly. Note that an
+error that occurs on the client side does not currently get sent to the
+server side, so this is not the final exit status for the whole transfer.</li>
+<li><code>RSYNC_RAW_STATUS</code>: (post-xfer only) the raw exit value from
+<strong>waitpid()</strong>.</li>
+</ul>
+<p>Even though the commands can be associated with a particular module, they
+are run using the permissions of the user that started the daemon (not the
+module's uid/gid setting) without any chroot restrictions.</p>
+<p>These settings honor 2 environment variables: use RSYNC_SHELL to set a
+shell to use when running the command (which otherwise uses your
+<strong>system()</strong> call's default shell), and use RSYNC_NO_XFER_EXEC to disable
+both options completely.</p>
+</dd>
+</dl>
+<h2 id="CONFIG_DIRECTIVES">CONFIG DIRECTIVES<a href="#CONFIG_DIRECTIVES" class="tgt"></a></h2>
+<p>There are currently two config directives available that allow a config file to
+incorporate the contents of other files: <code>&amp;include</code> and <code>&amp;merge</code>. Both allow
+a reference to either a file or a directory. They differ in how segregated the
+file's contents are considered to be.</p>
+<p>The <code>&amp;include</code> directive treats each file as more distinct, with each one
+inheriting the defaults of the parent file, starting the parameter parsing as
+globals/defaults, and leaving the defaults unchanged for the parsing of the
+rest of the parent file.</p>
+<p>The <code>&amp;merge</code> directive, on the other hand, treats the file's contents as if it
+were simply inserted in place of the directive, and thus it can set parameters
+in a module started in another file, can affect the defaults for other files,
+etc.</p>
+<p>When an <code>&amp;include</code> or <code>&amp;merge</code> directive refers to a directory, it will read in
+all the <code>*.conf</code> or <code>*.inc</code> files (respectively) that are contained inside that
+directory (without any recursive scanning), with the files sorted into alpha
+order. So, if you have a directory named &quot;rsyncd.d&quot; with the files &quot;foo.conf&quot;,
+&quot;bar.conf&quot;, and &quot;baz.conf&quot; inside it, this directive:</p>
+<blockquote>
+<pre><code>&amp;include /path/rsyncd.d
+</code></pre>
+</blockquote>
+<p>would be the same as this set of directives:</p>
+<blockquote>
+<pre><code>&amp;include /path/rsyncd.d/bar.conf
+&amp;include /path/rsyncd.d/baz.conf
+&amp;include /path/rsyncd.d/foo.conf
+</code></pre>
+</blockquote>
+<p>except that it adjusts as files are added and removed from the directory.</p>
+<p>The advantage of the <code>&amp;include</code> directive is that you can define one or more
+modules in a separate file without worrying about unintended side-effects
+between the self-contained module files.</p>
+<p>The advantage of the <code>&amp;merge</code> directive is that you can load config snippets
+that can be included into multiple module definitions, and you can also set
+global values that will affect connections (such as <code>motd file</code>), or globals
+that will affect other include files.</p>
+<p>For example, this is a useful /etc/rsyncd.conf file:</p>
+<blockquote>
+<pre><code>port = 873
+log file = /var/log/rsync.log
+pid file = /var/lock/rsync.lock
+
+&amp;merge /etc/rsyncd.d
+&amp;include /etc/rsyncd.d
+</code></pre>
+</blockquote>
+<p>This would merge any <code>/etc/rsyncd.d/*.inc</code> files (for global values that should
+stay in effect), and then include any <code>/etc/rsyncd.d/*.conf</code> files (defining
+modules without any global-value cross-talk).</p>
+<h2 id="AUTHENTICATION_STRENGTH">AUTHENTICATION STRENGTH<a href="#AUTHENTICATION_STRENGTH" class="tgt"></a></h2>
+<p>The authentication protocol used in rsync is a 128 bit MD4 based challenge
+response system. This is fairly weak protection, though (with at least one
+brute-force hash-finding algorithm publicly available), so if you want really
+top-quality security, then I recommend that you run rsync over ssh. (Yes, a
+future version of rsync will switch over to a stronger hashing method.)</p>
+<p>Also note that the rsync daemon protocol does not currently provide any
+encryption of the data that is transferred over the connection. Only
+authentication is provided. Use ssh as the transport if you want encryption.</p>
+<p>You can also make use of SSL/TLS encryption if you put rsync behind an
+SSL proxy.</p>
+<h2 id="SSL_TLS_Daemon_Setup">SSL/TLS Daemon Setup<a href="#SSL_TLS_Daemon_Setup" class="tgt"></a></h2>
+<p>When setting up an rsync daemon for access via SSL/TLS, you will need to
+configure a TCP proxy (such as haproxy or nginx) as the front-end that handles
+the encryption.</p>
+<ul>
+<li>You should limit the access to the backend-rsyncd port to only allow the
+proxy to connect. If it is on the same host as the proxy, then configuring
+it to only listen on localhost is a good idea.</li>
+<li>You should consider turning on the <code>proxy protocol</code> rsync-daemon parameter if
+your proxy supports sending that information. The examples below assume that
+this is enabled.</li>
+</ul>
+<p>An example haproxy setup is as follows:</p>
+<blockquote>
+<pre><code>frontend fe_rsync-ssl
+ bind :::874 ssl crt /etc/letsencrypt/example.com/combined.pem
+ mode tcp
+ use_backend be_rsync
+
+backend be_rsync
+ mode tcp
+ server local-rsync 127.0.0.1:873 check send-proxy
+</code></pre>
+</blockquote>
+<p>An example nginx proxy setup is as follows:</p>
+<blockquote>
+<pre><code>stream {
+ server {
+ listen 874 ssl;
+ listen [::]:874 ssl;
+
+ ssl_certificate /etc/letsencrypt/example.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/example.com/privkey.pem;
+
+ proxy_pass localhost:873;
+ proxy_protocol on; # Requires rsyncd.conf &quot;proxy protocol = true&quot;
+ proxy_timeout 1m;
+ proxy_connect_timeout 5s;
+ }
+}
+</code></pre>
+</blockquote>
+<h2 id="DAEMON_CONFIG_EXAMPLES">DAEMON CONFIG EXAMPLES<a href="#DAEMON_CONFIG_EXAMPLES" class="tgt"></a></h2>
+<p>A simple rsyncd.conf file that allow anonymous rsync to a ftp area at
+<code>/home/ftp</code> would be:</p>
+<blockquote>
+<pre><code>[ftp]
+ path = /home/ftp
+ comment = ftp export area
+</code></pre>
+</blockquote>
+<p>A more sophisticated example would be:</p>
+<blockquote>
+<pre><code>uid = nobody
+gid = nobody
+use chroot = yes
+max connections = 4
+syslog facility = local5
+pid file = /var/run/rsyncd.pid
+
+[ftp]
+ path = /var/ftp/./pub
+ comment = whole ftp area (approx 6.1 GB)
+
+[sambaftp]
+ path = /var/ftp/./pub/samba
+ comment = Samba ftp area (approx 300 MB)
+
+[rsyncftp]
+ path = /var/ftp/./pub/rsync
+ comment = rsync ftp area (approx 6 MB)
+
+[sambawww]
+ path = /public_html/samba
+ comment = Samba WWW pages (approx 240 MB)
+
+[cvs]
+ path = /data/cvs
+ comment = CVS repository (requires authentication)
+ auth users = tridge, susan
+ secrets file = /etc/rsyncd.secrets
+</code></pre>
+</blockquote>
+<p>The /etc/rsyncd.secrets file would look something like this:</p>
+<blockquote>
+<pre><code>tridge:mypass
+susan:herpass
+</code></pre>
+</blockquote>
+<h2 id="FILES">FILES<a href="#FILES" class="tgt"></a></h2>
+<p>/etc/rsyncd.conf or rsyncd.conf</p>
+<h2 id="SEE_ALSO">SEE ALSO<a href="#SEE_ALSO" class="tgt"></a></h2>
+<p><a href="rsync.1"><strong>rsync</strong>(1)</a>, <a href="rsync-ssl.1"><strong>rsync-ssl</strong>(1)</a></p>
+<h2 id="BUGS">BUGS<a href="#BUGS" class="tgt"></a></h2>
+<p>Please report bugs! The rsync bug tracking system is online at
+<a href="https://rsync.samba.org/">https://rsync.samba.org/</a>.</p>
+<h2 id="VERSION">VERSION<a href="#VERSION" class="tgt"></a></h2>
+<p>This manpage is current for version 3.2.7 of rsync.</p>
+<h2 id="CREDITS">CREDITS<a href="#CREDITS" class="tgt"></a></h2>
+<p>Rsync is distributed under the GNU General Public License. See the file
+<a href="COPYING">COPYING</a> for details.</p>
+<p>An rsync web site is available at <a href="https://rsync.samba.org/">https://rsync.samba.org/</a> and its github
+project is <a href="https://github.com/WayneD/rsync">https://github.com/WayneD/rsync</a>.</p>
+<h2 id="THANKS">THANKS<a href="#THANKS" class="tgt"></a></h2>
+<p>Thanks to Warren Stanley for his original idea and patch for the rsync daemon.
+Thanks to Karsten Thygesen for his many suggestions and documentation!</p>
+<h2 id="AUTHOR">AUTHOR<a href="#AUTHOR" class="tgt"></a></h2>
+<p>Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people have later contributed to it. It is currently maintained by Wayne
+Davison.</p>
+<p>Mailing lists for support and development are available at
+<a href="https://lists.samba.org/">https://lists.samba.org/</a>.</p>
+<div style="float: right"><p><i>20 Oct 2022</i></p></div>
+</body></html>
diff --git a/rsyncd.conf.5.md b/rsyncd.conf.5.md
new file mode 100644
index 0000000..91aaf6f
--- /dev/null
+++ b/rsyncd.conf.5.md
@@ -0,0 +1,1273 @@
+## NAME
+
+rsyncd.conf - configuration file for rsync in daemon mode
+
+## SYNOPSIS
+
+rsyncd.conf
+
+The online version of this manpage (that includes cross-linking of topics)
+is available at <https://download.samba.org/pub/rsync/rsyncd.conf.5>.
+
+## DESCRIPTION
+
+The rsyncd.conf file is the runtime configuration file for rsync when run as an
+rsync daemon.
+
+The rsyncd.conf file controls authentication, access, logging and available
+modules.
+
+## FILE FORMAT
+
+The file consists of modules and parameters. A module begins with the name of
+the module in square brackets and continues until the next module begins.
+Modules contain parameters of the form `name = value`.
+
+The file is line-based -- that is, each newline-terminated line represents
+either a comment, a module name or a parameter.
+
+Only the first equals sign in a parameter is significant. Whitespace before or
+after the first equals sign is discarded. Leading, trailing and internal
+whitespace in module and parameter names is irrelevant. Leading and trailing
+whitespace in a parameter value is discarded. Internal whitespace within a
+parameter value is retained verbatim.
+
+Any line **beginning** with a hash (`#`) is ignored, as are lines containing
+only whitespace. (If a hash occurs after anything other than leading
+whitespace, it is considered a part of the line's content.)
+
+Any line ending in a `\` is "continued" on the next line in the customary UNIX
+fashion.
+
+The values following the equals sign in parameters are all either a string (no
+quotes needed) or a boolean, which may be given as yes/no, 0/1 or true/false.
+Case is not significant in boolean values, but is preserved in string values.
+
+## LAUNCHING THE RSYNC DAEMON
+
+The rsync daemon is launched by specifying the `--daemon` option to rsync.
+
+The daemon must run with root privileges if you wish to use chroot, to bind to
+a port numbered under 1024 (as is the default 873), or to set file ownership.
+Otherwise, it must just have permission to read and write the appropriate data,
+log, and lock files.
+
+You can launch it either via inetd, as a stand-alone daemon, or from an rsync
+client via a remote shell. If run as a stand-alone daemon then just run the
+command "`rsync --daemon`" from a suitable startup script.
+
+When run via inetd you should add a line like this to /etc/services:
+
+> rsync 873/tcp
+
+and a single line something like this to /etc/inetd.conf:
+
+> rsync stream tcp nowait root @BINDIR@/rsync rsyncd --daemon
+
+Replace "@BINDIR@/rsync" with the path to where you have rsync installed on
+your system. You will then need to send inetd a HUP signal to tell it to
+reread its config file.
+
+Note that you should **not** send the rsync daemon a HUP signal to force it to
+reread the `rsyncd.conf` file. The file is re-read on each client connection.
+
+## GLOBAL PARAMETERS
+
+The first parameters in the file (before a [module] header) are the global
+parameters. Rsync also allows for the use of a "[global]" module name to
+indicate the start of one or more global-parameter sections (the name must be
+lower case).
+
+You may also include any module parameters in the global part of the config
+file in which case the supplied value will override the default for that
+parameter.
+
+You may use references to environment variables in the values of parameters.
+String parameters will have %VAR% references expanded as late as possible (when
+the string is first used in the program), allowing for the use of variables
+that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
+parameters (such as true/false settings) are expanded when read from the config
+file. If a variable does not exist in the environment, or if a sequence of
+characters is not a valid reference (such as an un-paired percent sign), the
+raw characters are passed through unchanged. This helps with backward
+compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
+string in a path could result in a very unsafe path). The safest way to insert
+a literal % into a value is to use %%.
+
+[comment]: # (An OL starting at 0 is converted into a DL by the parser.)
+
+0. `motd file`
+
+ This parameter allows you to specify a "message of the day" (MOTD) to display
+ to clients on each connect. This usually contains site information and any
+ legal notices. The default is no MOTD file. This can be overridden by the
+ `--dparam=motdfile=FILE` command-line option when starting the daemon.
+
+0. `pid file`
+
+ This parameter tells the rsync daemon to write its process ID to that file.
+ The rsync keeps the file locked so that it can know when it is safe to
+ overwrite an existing file.
+
+ The filename can be overridden by the `--dparam=pidfile=FILE` command-line
+ option when starting the daemon.
+
+0. `port`
+
+ You can override the default port the daemon will listen on by specifying
+ this value (defaults to 873). This is ignored if the daemon is being run
+ by inetd, and is superseded by the `--port` command-line option.
+
+0. `address`
+
+ You can override the default IP address the daemon will listen on by
+ specifying this value. This is ignored if the daemon is being run by
+ inetd, and is superseded by the `--address` command-line option.
+
+0. `socket options`
+
+ This parameter can provide endless fun for people who like to tune their
+ systems to the utmost degree. You can set all sorts of socket options which
+ may make transfers faster (or slower!). Read the manpage for the
+ **setsockopt()** system call for details on some of the options you may be
+ able to set. By default no special socket options are set. These settings
+ can also be specified via the `--sockopts` command-line option.
+
+0. `listen backlog`
+
+ You can override the default backlog value when the daemon listens for
+ connections. It defaults to 5.
+
+## MODULE PARAMETERS
+
+After the global parameters you should define a number of modules, each module
+exports a directory tree as a symbolic name. Modules are exported by specifying
+a module name in square brackets [module] followed by the parameters for that
+module. The module name cannot contain a slash or a closing square bracket.
+If the name contains whitespace, each internal sequence of whitespace will be
+changed into a single space, while leading or trailing whitespace will be
+discarded. Also, the name cannot be "global" as that exact name indicates that
+global parameters follow (see above).
+
+As with GLOBAL PARAMETERS, you may use references to environment variables in
+the values of parameters. See the GLOBAL PARAMETERS section for more details.
+
+0. `comment`
+
+ This parameter specifies a description string that is displayed next to the
+ module name when clients obtain a list of available modules. The default is
+ no comment.
+
+0. `path`
+
+ This parameter specifies the directory in the daemon's filesystem to make
+ available in this module. You must specify this parameter for each module
+ in `rsyncd.conf`.
+
+ If the value contains a "/./" element then the path will be divided at that
+ point into a chroot dir and an inner-chroot subdir. If [`use chroot`](#)
+ is set to false, though, the extraneous dot dir is just cleaned out of the
+ path. An example of this idiom is:
+
+ > path = /var/rsync/./module1
+
+ This will (when chrooting) chroot to "/var/rsync" and set the inside-chroot
+ path to "/module1".
+
+ You may base the path's value off of an environment variable by surrounding
+ the variable name with percent signs. You can even reference a variable
+ that is set by rsync when the user connects. For example, this would use
+ the authorizing user's name in the path:
+
+ > path = /home/%RSYNC_USER_NAME%
+
+ It is fine if the path includes internal spaces -- they will be retained
+ verbatim (which means that you shouldn't try to escape them). If your
+ final directory has a trailing space (and this is somehow not something you
+ wish to fix), append a trailing slash to the path to avoid losing the
+ trailing whitespace.
+
+0. `use chroot`
+
+ If "use chroot" is true, the rsync daemon will chroot to the "[path](#)" before
+ starting the file transfer with the client. This has the advantage of
+ extra protection against possible implementation security holes, but it has
+ the disadvantages of requiring super-user privileges, of not being able to
+ follow symbolic links that are either absolute or outside of the new root
+ path, and of complicating the preservation of users and groups by name (see
+ below).
+
+ If `use chroot` is not set, it defaults to trying to enable a chroot but
+ allows the daemon to continue (after logging a warning) if it fails. The
+ one exception to this is when a module's [`path`](#) has a "/./" chroot
+ divider in it -- this causes an unset value to be treated as true for that
+ module.
+
+ Prior to rsync 3.2.7, the default value was "true". The new "unset"
+ default makes it easier to setup an rsync daemon as a non-root user or to
+ run a daemon on a system where chroot fails. Explicitly setting the value
+ to "true" in rsyncd.conf will always require the chroot to succeed.
+
+ It is also possible to specify a dot-dir in the module's "[path](#)" to
+ indicate that you want to chdir to the earlier part of the path and then
+ serve files from inside the latter part of the path (with sanitizing and
+ default symlink munging). This can be useful if you need some library dirs
+ inside the chroot (typically for uid & gid lookups) but don't want to put
+ the lib dir into the top of the served path (even though they can be hidden
+ with an [`exclude`](#) directive). However, a better choice for a modern
+ rsync setup is to use a [`name converter`](#)" and try to avoid inner lib
+ dirs altogether. See also the [`daemon chroot`](#) parameter, which causes
+ rsync to chroot into its own chroot area before doing any path-related
+ chrooting.
+
+ If the daemon is serving the "/" dir (either directly or due to being
+ chrooted to the module's path), rsync does not do any path sanitizing or
+ (default) munging.
+
+ When it has to limit access to a particular subdir (either due to chroot
+ being disabled or having an inside-chroot path set), rsync will munge
+ symlinks (by default) and sanitize paths. Those that dislike munged
+ symlinks (and really, really trust their users to not break out of the
+ subdir) can disable the symlink munging via the "[munge symlinks](#)"
+ parameter.
+
+ When rsync is sanitizing paths, it trims ".." path elements from args that
+ it believes would escape the module hierarchy. It also substitutes leading
+ slashes in absolute paths with the module's path (so that options such as
+ `--backup-dir` & `--compare-dest` interpret an absolute path as rooted in
+ the module's "[path](#)" dir).
+
+ When a chroot is in effect *and* the "[name converter](#)" parameter is
+ *not* set, the "[numeric ids](#)" parameter will default to being enabled
+ (disabling name lookups). This means that if you manually setup
+ name-lookup libraries in your chroot (instead of using a name converter)
+ that you need to explicitly set `numeric ids = false` for rsync to do name
+ lookups.
+
+ If you copy library resources into the module's chroot area, you should
+ protect them through your OS's normal user/group or ACL settings (to
+ prevent the rsync module's user from being able to change them), and then
+ hide them from the user's view via "[exclude](#)" (see how in the discussion of
+ that parameter). However, it's easier and safer to setup a name converter.
+
+0. `daemon chroot`
+
+ This parameter specifies a path to which the daemon will chroot before
+ beginning communication with clients. Module paths (and any "[use chroot](#)"
+ settings) will then be related to this one. This lets you choose if you
+ want the whole daemon to be chrooted (with this setting), just the
+ transfers to be chrooted (with "[use chroot](#)"), or both. Keep in mind that
+ the "daemon chroot" area may need various OS/lib/etc files installed to
+ allow the daemon to function. By default the daemon runs without any
+ chrooting.
+
+0. `proxy protocol`
+
+ When this parameter is enabled, all incoming connections must start with a
+ V1 or V2 proxy protocol header. If the header is not found, the connection
+ is closed.
+
+ Setting this to `true` requires a proxy server to forward source IP
+ information to rsync, allowing you to log proper IP/host info and make use
+ of client-oriented IP restrictions. The default of `false` means that the
+ IP information comes directly from the socket's metadata. If rsync is not
+ behind a proxy, this should be disabled.
+
+ _CAUTION_: using this option can be dangerous if you do not ensure that
+ only the proxy is allowed to connect to the rsync port. If any non-proxied
+ connections are allowed through, the client will be able to use a modified
+ rsync to spoof any remote IP address that they desire. You can lock this
+ down using something like iptables `-uid-owner root` rules (for strict
+ localhost access), various firewall rules, or you can require password
+ authorization so that any spoofing by users will not grant extra access.
+
+ This setting is global. If you need some modules to require this and not
+ others, then you will need to setup multiple rsync daemon processes on
+ different ports.
+
+0. `name converter`
+
+ This parameter lets you specify a program that will be run by the rsync
+ daemon to do user & group conversions between names & ids. This script
+ is started prior to any chroot being setup, and runs as the daemon user
+ (not the transfer user). You can specify a fully qualified pathname or
+ a program name that is on the $PATH.
+
+ The program can be used to do normal user & group lookups without having to
+ put any extra files into the chroot area of the module *or* you can do
+ customized conversions.
+
+ The nameconvert program has access to all of the environment variables that
+ are described in the section on `pre-xfer exec`. This is useful if you
+ want to customize the conversion using information about the module and/or
+ the copy request.
+
+ There is a sample python script in the support dir named "nameconvert" that
+ implements the normal user & group lookups. Feel free to customize it or
+ just use it as documentation to implement your own.
+
+0. `numeric ids`
+
+ Enabling this parameter disables the mapping of users and groups by name
+ for the current daemon module. This prevents the daemon from trying to
+ load any user/group-related files or libraries. This enabling makes the
+ transfer behave as if the client had passed the `--numeric-ids`
+ command-line option. By default, this parameter is enabled for chroot
+ modules and disabled for non-chroot modules. Also keep in mind that
+ uid/gid preservation requires the module to be running as root (see "[uid](#)")
+ or for "[fake super](#)" to be configured.
+
+ A chroot-enabled module should not have this parameter set to false unless
+ you're using a "[name converter](#)" program *or* you've taken steps to ensure
+ that the module has the necessary resources it needs to translate names and
+ that it is not possible for a user to change those resources.
+
+0. `munge symlinks`
+
+ This parameter tells rsync to modify all symlinks in the same way as the
+ (non-daemon-affecting) `--munge-links` command-line option (using a method
+ described below). This should help protect your files from user trickery
+ when your daemon module is writable. The default is disabled when
+ "[use chroot](#)" is on with an inside-chroot path of "/", OR if "[daemon chroot](#)"
+ is on, otherwise it is enabled.
+
+ If you disable this parameter on a daemon that is not read-only, there are
+ tricks that a user can play with uploaded symlinks to access
+ daemon-excluded items (if your module has any), and, if "[use chroot](#)" is
+ off, rsync can even be tricked into showing or changing data that is
+ outside the module's path (as access-permissions allow).
+
+ The way rsync disables the use of symlinks is to prefix each one with the
+ string "/rsyncd-munged/". This prevents the links from being used as long
+ as that directory does not exist. When this parameter is enabled, rsync
+ will refuse to run if that path is a directory or a symlink to a directory.
+ When using the "munge symlinks" parameter in a chroot area that has an
+ inside-chroot path of "/", you should add "/rsyncd-munged/" to the exclude
+ setting for the module so that a user can't try to create it.
+
+ Note: rsync makes no attempt to verify that any pre-existing symlinks in
+ the module's hierarchy are as safe as you want them to be (unless, of
+ course, it just copied in the whole hierarchy). If you setup an rsync
+ daemon on a new area or locally add symlinks, you can manually protect your
+ symlinks from being abused by prefixing "/rsyncd-munged/" to the start of
+ every symlink's value. There is a perl script in the support directory of
+ the source code named "munge-symlinks" that can be used to add or remove
+ this prefix from your symlinks.
+
+ When this parameter is disabled on a writable module and "[use chroot](#)" is
+ off (or the inside-chroot path is not "/"), incoming symlinks will be
+ modified to drop a leading slash and to remove ".." path elements that
+ rsync believes will allow a symlink to escape the module's hierarchy.
+ There are tricky ways to work around this, though, so you had better trust
+ your users if you choose this combination of parameters.
+
+0. `charset`
+
+ This specifies the name of the character set in which the module's
+ filenames are stored. If the client uses an `--iconv` option, the daemon
+ will use the value of the "charset" parameter regardless of the character
+ set the client actually passed. This allows the daemon to support charset
+ conversion in a chroot module without extra files in the chroot area, and
+ also ensures that name-translation is done in a consistent manner. If the
+ "charset" parameter is not set, the `--iconv` option is refused, just as if
+ "iconv" had been specified via "[refuse options](#)".
+
+ If you wish to force users to always use `--iconv` for a particular module,
+ add "no-iconv" to the "[refuse options](#)" parameter. Keep in mind that this
+ will restrict access to your module to very new rsync clients.
+
+0. `max connections`
+
+ This parameter allows you to specify the maximum number of simultaneous
+ connections you will allow. Any clients connecting when the maximum has
+ been reached will receive a message telling them to try later. The default
+ is 0, which means no limit. A negative value disables the module. See
+ also the "[lock file](#)" parameter.
+
+0. `log file`
+
+ When the "log file" parameter is set to a non-empty string, the rsync
+ daemon will log messages to the indicated file rather than using syslog.
+ This is particularly useful on systems (such as AIX) where **syslog()**
+ doesn't work for chrooted programs. The file is opened before **chroot()**
+ is called, allowing it to be placed outside the transfer. If this value is
+ set on a per-module basis instead of globally, the global log will still
+ contain any authorization failures or config-file error messages.
+
+ If the daemon fails to open the specified file, it will fall back to using
+ syslog and output an error about the failure. (Note that the failure to
+ open the specified log file used to be a fatal error.)
+
+ This setting can be overridden by using the `--log-file=FILE` or
+ `--dparam=logfile=FILE` command-line options. The former overrides all the
+ log-file parameters of the daemon and all module settings. The latter sets
+ the daemon's log file and the default for all the modules, which still
+ allows modules to override the default setting.
+
+0. `syslog facility`
+
+ This parameter allows you to specify the syslog facility name to use when
+ logging messages from the rsync daemon. You may use any standard syslog
+ facility name which is defined on your system. Common names are auth,
+ authpriv, cron, daemon, ftp, kern, lpr, mail, news, security, syslog, user,
+ uucp, local0, local1, local2, local3, local4, local5, local6 and local7.
+ The default is daemon. This setting has no effect if the "[log file](#)"
+ setting is a non-empty string (either set in the per-modules settings, or
+ inherited from the global settings).
+
+0. `syslog tag`
+
+ This parameter allows you to specify the syslog tag to use when logging
+ messages from the rsync daemon. The default is "rsyncd". This setting has
+ no effect if the "[log file](#)" setting is a non-empty string (either set in
+ the per-modules settings, or inherited from the global settings).
+
+ For example, if you wanted each authenticated user's name to be included in
+ the syslog tag, you could do something like this:
+
+ > syslog tag = rsyncd.%RSYNC_USER_NAME%
+
+0. `max verbosity`
+
+ This parameter allows you to control the maximum amount of verbose
+ information that you'll allow the daemon to generate (since the information
+ goes into the log file). The default is 1, which allows the client to
+ request one level of verbosity.
+
+ This also affects the user's ability to request higher levels of `--info`
+ and `--debug` logging. If the max value is 2, then no info and/or debug
+ value that is higher than what would be set by `-vv` will be honored by the
+ daemon in its logging. To see how high of a verbosity level you need to
+ accept for a particular info/debug level, refer to `rsync --info=help` and
+ `rsync --debug=help`. For instance, it takes max-verbosity 4 to be able to
+ output debug TIME2 and FLIST3.
+
+0. `lock file`
+
+ This parameter specifies the file to use to support the "[max connections](#)"
+ parameter. The rsync daemon uses record locking on this file to ensure that
+ the max connections limit is not exceeded for the modules sharing the lock
+ file. The default is `/var/run/rsyncd.lock`.
+
+0. `read only`
+
+ This parameter determines whether clients will be able to upload files or
+ not. If "read only" is true then any attempted uploads will fail. If
+ "read only" is false then uploads will be possible if file permissions on
+ the daemon side allow them. The default is for all modules to be read only.
+
+ Note that "[auth users](#)" can override this setting on a per-user basis.
+
+0. `write only`
+
+ This parameter determines whether clients will be able to download files or
+ not. If "write only" is true then any attempted downloads will fail. If
+ "write only" is false then downloads will be possible if file permissions
+ on the daemon side allow them. The default is for this parameter to be
+ disabled.
+
+ Helpful hint: you probably want to specify "refuse options = delete" for a
+ write-only module.
+
+0. `open noatime`
+
+ When set to True, this parameter tells the rsync daemon to open files with
+ the O_NOATIME flag
+ (on systems that support it) to avoid changing the access time of the files
+ that are being transferred. If your OS does not support the O_NOATIME flag
+ then rsync will silently ignore this option. Note also that some
+ filesystems are mounted to avoid updating the atime on read access even
+ without the O_NOATIME flag being set.
+
+ When set to False, this parameters ensures that files on the server are not
+ opened with O_NOATIME.
+
+ When set to Unset (the default) the user controls the setting via
+ `--open-noatime`.
+
+0. `list`
+
+ This parameter determines whether this module is listed when the client
+ asks for a listing of available modules. In addition, if this is false,
+ the daemon will pretend the module does not exist when a client denied by
+ "[hosts allow](#)" or "[hosts deny](#)" attempts to access it. Realize that if
+ "[reverse lookup](#)" is disabled globally but enabled for the module, the
+ resulting reverse lookup to a potentially client-controlled DNS server may
+ still reveal to the client that it hit an existing module. The default is
+ for modules to be listable.
+
+0. `uid`
+
+ This parameter specifies the user name or user ID that file transfers to
+ and from that module should take place as when the daemon was run as root.
+ In combination with the "[gid](#)" parameter this determines what file
+ permissions are available. The default when run by a super-user is to
+ switch to the system's "nobody" user. The default for a non-super-user is
+ to not try to change the user. See also the "[gid](#)" parameter.
+
+ The RSYNC_USER_NAME environment variable may be used to request that rsync
+ run as the authorizing user. For example, if you want a rsync to run as
+ the same user that was received for the rsync authentication, this setup is
+ useful:
+
+ > uid = %RSYNC_USER_NAME%
+ > gid = *
+
+0. `gid`
+
+ This parameter specifies one or more group names/IDs that will be used when
+ accessing the module. The first one will be the default group, and any
+ extra ones be set as supplemental groups. You may also specify a "`*`" as
+ the first gid in the list, which will be replaced by all the normal groups
+ for the transfer's user (see "[uid](#)"). The default when run by a super-user
+ is to switch to your OS's "nobody" (or perhaps "nogroup") group with no
+ other supplementary groups. The default for a non-super-user is to not
+ change any group attributes (and indeed, your OS may not allow a
+ non-super-user to try to change their group settings).
+
+ The specified list is normally split into tokens based on spaces and
+ commas. However, if the list starts with a comma, then the list is only
+ split on commas, which allows a group name to contain a space. In either
+ case any leading and/or trailing whitespace is removed from the tokens and
+ empty tokens are ignored.
+
+0. `daemon uid`
+
+ This parameter specifies a uid under which the daemon will run. The daemon
+ usually runs as user root, and when this is left unset the user is left
+ unchanged. See also the "[uid](#)" parameter.
+
+0. `daemon gid`
+
+ This parameter specifies a gid under which the daemon will run. The daemon
+ usually runs as group root, and when this is left unset, the group is left
+ unchanged. See also the "[gid](#)" parameter.
+
+0. `fake super`
+
+ Setting "fake super = yes" for a module causes the daemon side to behave as
+ if the `--fake-super` command-line option had been specified. This allows
+ the full attributes of a file to be stored without having to have the
+ daemon actually running as root.
+
+0. `filter`
+
+ The daemon has its own filter chain that determines what files it will let
+ the client access. This chain is not sent to the client and is independent
+ of any filters the client may have specified. Files excluded by the daemon
+ filter chain (`daemon-excluded` files) are treated as non-existent if the
+ client tries to pull them, are skipped with an error message if the client
+ tries to push them (triggering exit code 23), and are never deleted from
+ the module. You can use daemon filters to prevent clients from downloading
+ or tampering with private administrative files, such as files you may add
+ to support uid/gid name translations.
+
+ The daemon filter chain is built from the "filter", "[include from](#)",
+ "[include](#)", "[exclude from](#)", and "[exclude](#)" parameters, in that order of
+ priority. Anchored patterns are anchored at the root of the module. To
+ prevent access to an entire subtree, for example, "`/secret`", you **must**
+ exclude everything in the subtree; the easiest way to do this is with a
+ triple-star pattern like "`/secret/***`".
+
+ The "filter" parameter takes a space-separated list of daemon filter rules,
+ though it is smart enough to know not to split a token at an internal space
+ in a rule (e.g. "`- /foo - /bar`" is parsed as two rules). You may specify
+ one or more merge-file rules using the normal syntax. Only one "filter"
+ parameter can apply to a given module in the config file, so put all the
+ rules you want in a single parameter. Note that per-directory merge-file
+ rules do not provide as much protection as global rules, but they can be
+ used to make `--delete` work better during a client download operation if
+ the per-dir merge files are included in the transfer and the client
+ requests that they be used.
+
+0. `exclude`
+
+ This parameter takes a space-separated list of daemon exclude patterns. As
+ with the client `--exclude` option, patterns can be qualified with "`- `" or
+ "`+ `" to explicitly indicate exclude/include. Only one "exclude" parameter
+ can apply to a given module. See the "filter" parameter for a description
+ of how excluded files affect the daemon.
+
+0. `include`
+
+ Use an "include" to override the effects of the "[exclude](#)" parameter. Only
+ one "include" parameter can apply to a given module. See the "[filter](#)"
+ parameter for a description of how excluded files affect the daemon.
+
+0. `exclude from`
+
+ This parameter specifies the name of a file on the daemon that contains
+ daemon exclude patterns, one per line. Only one "exclude from" parameter
+ can apply to a given module; if you have multiple exclude-from files, you
+ can specify them as a merge file in the "[filter](#)" parameter. See the
+ "[filter](#)" parameter for a description of how excluded files affect the
+ daemon.
+
+0. `include from`
+
+ Analogue of "[exclude from](#)" for a file of daemon include patterns. Only one
+ "include from" parameter can apply to a given module. See the "[filter](#)"
+ parameter for a description of how excluded files affect the daemon.
+
+0. `incoming chmod`
+
+ This parameter allows you to specify a set of comma-separated chmod strings
+ that will affect the permissions of all incoming files (files that are
+ being received by the daemon). These changes happen after all other
+ permission calculations, and this will even override destination-default
+ and/or existing permissions when the client does not specify `--perms`.
+ See the description of the `--chmod` rsync option and the **chmod**(1)
+ manpage for information on the format of this string.
+
+0. `outgoing chmod`
+
+ This parameter allows you to specify a set of comma-separated chmod strings
+ that will affect the permissions of all outgoing files (files that are
+ being sent out from the daemon). These changes happen first, making the
+ sent permissions appear to be different than those stored in the filesystem
+ itself. For instance, you could disable group write permissions on the
+ server while having it appear to be on to the clients. See the description
+ of the `--chmod` rsync option and the **chmod**(1) manpage for information
+ on the format of this string.
+
+0. `auth users`
+
+ This parameter specifies a comma and/or space-separated list of
+ authorization rules. In its simplest form, you list the usernames that
+ will be allowed to connect to this module. The usernames do not need to
+ exist on the local system. The rules may contain shell wildcard characters
+ that will be matched against the username provided by the client for
+ authentication. If "auth users" is set then the client will be challenged
+ to supply a username and password to connect to the module. A challenge
+ response authentication protocol is used for this exchange. The plain text
+ usernames and passwords are stored in the file specified by the
+ "[secrets file](#)" parameter. The default is for all users to be able to
+ connect without a password (this is called "anonymous rsync").
+
+ In addition to username matching, you can specify groupname matching via a
+ '@' prefix. When using groupname matching, the authenticating username
+ must be a real user on the system, or it will be assumed to be a member of
+ no groups. For example, specifying "@rsync" will match the authenticating
+ user if the named user is a member of the rsync group.
+
+ Finally, options may be specified after a colon (:). The options allow you
+ to "deny" a user or a group, set the access to "ro" (read-only), or set the
+ access to "rw" (read/write). Setting an auth-rule-specific ro/rw setting
+ overrides the module's "[read only](#)" setting.
+
+ Be sure to put the rules in the order you want them to be matched, because
+ the checking stops at the first matching user or group, and that is the
+ only auth that is checked. For example:
+
+ > auth users = joe:deny @guest:deny admin:rw @rsync:ro susan joe sam
+
+ In the above rule, user joe will be denied access no matter what. Any user
+ that is in the group "guest" is also denied access. The user "admin" gets
+ access in read/write mode, but only if the admin user is not in group
+ "guest" (because the admin user-matching rule would never be reached if the
+ user is in group "guest"). Any other user who is in group "rsync" will get
+ read-only access. Finally, users susan, joe, and sam get the ro/rw setting
+ of the module, but only if the user didn't match an earlier group-matching
+ rule.
+
+ If you need to specify a user or group name with a space in it, start your
+ list with a comma to indicate that the list should only be split on commas
+ (though leading and trailing whitespace will also be removed, and empty
+ entries are just ignored). For example:
+
+ > auth users = , joe:deny, @Some Group:deny, admin:rw, @RO Group:ro
+
+ See the description of the secrets file for how you can have per-user
+ passwords as well as per-group passwords. It also explains how a user can
+ authenticate using their user password or (when applicable) a group
+ password, depending on what rule is being authenticated.
+
+ See also the section entitled "USING RSYNC-DAEMON FEATURES VIA A REMOTE
+ SHELL CONNECTION" in **rsync**(1) for information on how handle an
+ rsyncd.conf-level username that differs from the remote-shell-level
+ username when using a remote shell to connect to an rsync daemon.
+
+0. `secrets file`
+
+ This parameter specifies the name of a file that contains the
+ username:password and/or @groupname:password pairs used for authenticating
+ this module. This file is only consulted if the "[auth users](#)" parameter is
+ specified. The file is line-based and contains one name:password pair per
+ line. Any line has a hash (#) as the very first character on the line is
+ considered a comment and is skipped. The passwords can contain any
+ characters but be warned that many operating systems limit the length of
+ passwords that can be typed at the client end, so you may find that
+ passwords longer than 8 characters don't work.
+
+ The use of group-specific lines are only relevant when the module is being
+ authorized using a matching "@groupname" rule. When that happens, the user
+ can be authorized via either their "username:password" line or the
+ "@groupname:password" line for the group that triggered the authentication.
+
+ It is up to you what kind of password entries you want to include, either
+ users, groups, or both. The use of group rules in "[auth users](#)" does not
+ require that you specify a group password if you do not want to use shared
+ passwords.
+
+ There is no default for the "secrets file" parameter, you must choose a
+ name (such as `/etc/rsyncd.secrets`). The file must normally not be
+ readable by "other"; see "[strict modes](#)". If the file is not found or is
+ rejected, no logins for an "[auth users](#)" module will be possible.
+
+0. `strict modes`
+
+ This parameter determines whether or not the permissions on the secrets
+ file will be checked. If "strict modes" is true, then the secrets file
+ must not be readable by any user ID other than the one that the rsync
+ daemon is running under. If "strict modes" is false, the check is not
+ performed. The default is true. This parameter was added to accommodate
+ rsync running on the Windows operating system.
+
+0. `hosts allow`
+
+ This parameter allows you to specify a list of comma- and/or
+ whitespace-separated patterns that are matched against a connecting
+ client's hostname and IP address. If none of the patterns match, then the
+ connection is rejected.
+
+ Each pattern can be in one of six forms:
+
+ - a dotted decimal IPv4 address of the form a.b.c.d, or an IPv6 address of
+ the form a:b:c::d:e:f. In this case the incoming machine's IP address
+ must match exactly.
+ - an address/mask in the form ipaddr/n where ipaddr is the IP address and n
+ is the number of one bits in the netmask. All IP addresses which match
+ the masked IP address will be allowed in.
+ - an address/mask in the form ipaddr/maskaddr where ipaddr is the IP
+ address and maskaddr is the netmask in dotted decimal notation for IPv4,
+ or similar for IPv6, e.g. ffff:ffff:ffff:ffff:: instead of /64. All IP
+ addresses which match the masked IP address will be allowed in.
+ - a hostname pattern using wildcards. If the hostname of the connecting IP
+ (as determined by a reverse lookup) matches the wildcarded name (using
+ the same rules as normal Unix filename matching), the client is allowed
+ in. This only works if "[reverse lookup](#)" is enabled (the default).
+ - a hostname. A plain hostname is matched against the reverse DNS of the
+ connecting IP (if "[reverse lookup](#)" is enabled), and/or the IP of the
+ given hostname is matched against the connecting IP (if "[forward lookup](#)"
+ is enabled, as it is by default). Any match will be allowed in.
+ - an '@' followed by a netgroup name, which will match if the reverse DNS
+ of the connecting IP is in the specified netgroup.
+
+ Note IPv6 link-local addresses can have a scope in the address
+ specification:
+
+ > fe80::1%link1
+ > fe80::%link1/64
+ > fe80::%link1/ffff:ffff:ffff:ffff::
+
+ You can also combine "hosts allow" with "[hosts deny](#)" as a way to add
+ exceptions to your deny list. When both parameters are specified, the
+ "hosts allow" parameter is checked first and a match results in the client
+ being able to connect. A non-allowed host is then matched against the
+ "[hosts deny](#)" list to see if it should be rejected. A host that does not
+ match either list is allowed to connect.
+
+ The default is no "hosts allow" parameter, which means all hosts can
+ connect.
+
+0. `hosts deny`
+
+ This parameter allows you to specify a list of comma- and/or
+ whitespace-separated patterns that are matched against a connecting clients
+ hostname and IP address. If the pattern matches then the connection is
+ rejected. See the "[hosts allow](#)" parameter for more information.
+
+ The default is no "hosts deny" parameter, which means all hosts can
+ connect.
+
+0. `reverse lookup`
+
+ Controls whether the daemon performs a reverse lookup on the client's IP
+ address to determine its hostname, which is used for "[hosts allow](#)" &
+ "[hosts deny](#)" checks and the "%h" log escape. This is enabled by default,
+ but you may wish to disable it to save time if you know the lookup will not
+ return a useful result, in which case the daemon will use the name
+ "UNDETERMINED" instead.
+
+ If this parameter is enabled globally (even by default), rsync performs the
+ lookup as soon as a client connects, so disabling it for a module will not
+ avoid the lookup. Thus, you probably want to disable it globally and then
+ enable it for modules that need the information.
+
+0. `forward lookup`
+
+ Controls whether the daemon performs a forward lookup on any hostname
+ specified in an hosts allow/deny setting. By default this is enabled,
+ allowing the use of an explicit hostname that would not be returned by
+ reverse DNS of the connecting IP.
+
+0. `ignore errors`
+
+ This parameter tells rsyncd to ignore I/O errors on the daemon when
+ deciding whether to run the delete phase of the transfer. Normally rsync
+ skips the `--delete` step if any I/O errors have occurred in order to
+ prevent disastrous deletion due to a temporary resource shortage or other
+ I/O error. In some cases this test is counter productive so you can use
+ this parameter to turn off this behavior.
+
+0. `ignore nonreadable`
+
+ This tells the rsync daemon to completely ignore files that are not
+ readable by the user. This is useful for public archives that may have some
+ non-readable files among the directories, and the sysadmin doesn't want
+ those files to be seen at all.
+
+0. `transfer logging`
+
+ This parameter enables per-file logging of downloads and uploads in a
+ format somewhat similar to that used by ftp daemons. The daemon always
+ logs the transfer at the end, so if a transfer is aborted, no mention will
+ be made in the log file.
+
+ If you want to customize the log lines, see the "[log format](#)" parameter.
+
+0. `log format`
+
+ This parameter allows you to specify the format used for logging file
+ transfers when transfer logging is enabled. The format is a text string
+ containing embedded single-character escape sequences prefixed with a
+ percent (%) character. An optional numeric field width may also be
+ specified between the percent and the escape letter (e.g.
+ "`%-50n %8l %07p`"). In addition, one or more apostrophes may be specified
+ prior to a numerical escape to indicate that the numerical value should be
+ made more human-readable. The 3 supported levels are the same as for the
+ `--human-readable` command-line option, though the default is for
+ human-readability to be off. Each added apostrophe increases the level
+ (e.g. "`%''l %'b %f`").
+
+ The default log format is "`%o %h [%a] %m (%u) %f %l`", and a "`%t [%p] `"
+ is always prefixed when using the "[log file](#)" parameter. (A perl script
+ that will summarize this default log format is included in the rsync source
+ code distribution in the "support" subdirectory: rsyncstats.)
+
+ The single-character escapes that are understood are as follows:
+
+ - %a the remote IP address (only available for a daemon)
+ - %b the number of bytes actually transferred
+ - %B the permission bits of the file (e.g. rwxrwxrwt)
+ - %c the total size of the block checksums received for the basis file
+ (only when sending)
+ - %C the full-file checksum if it is known for the file. For older rsync
+ protocols/versions, the checksum was salted, and is thus not a useful
+ value (and is not displayed when that is the case). For the checksum to
+ output for a file, either the `--checksum` option must be in-effect or
+ the file must have been transferred without a salted checksum being used.
+ See the `--checksum-choice` option for a way to choose the algorithm.
+ - %f the filename (long form on sender; no trailing "/")
+ - %G the gid of the file (decimal) or "DEFAULT"
+ - %h the remote host name (only available for a daemon)
+ - %i an itemized list of what is being updated
+ - %l the length of the file in bytes
+ - %L the string "` -> SYMLINK`", "` => HARDLINK`", or "" (where `SYMLINK`
+ or `HARDLINK` is a filename)
+ - %m the module name
+ - %M the last-modified time of the file
+ - %n the filename (short form; trailing "/" on dir)
+ - %o the operation, which is "send", "recv", or "del." (the latter includes
+ the trailing period)
+ - %p the process ID of this rsync session
+ - %P the module path
+ - %t the current date time
+ - %u the authenticated username or an empty string
+ - %U the uid of the file (decimal)
+
+ For a list of what the characters mean that are output by "%i", see the
+ `--itemize-changes` option in the rsync manpage.
+
+ Note that some of the logged output changes when talking with older rsync
+ versions. For instance, deleted files were only output as verbose messages
+ prior to rsync 2.6.4.
+
+0. `timeout`
+
+ This parameter allows you to override the clients choice for I/O timeout
+ for this module. Using this parameter you can ensure that rsync won't wait
+ on a dead client forever. The timeout is specified in seconds. A value of
+ zero means no timeout and is the default. A good choice for anonymous rsync
+ daemons may be 600 (giving a 10 minute timeout).
+
+0. `refuse options`
+
+ This parameter allows you to specify a space-separated list of rsync
+ command-line options that will be refused by your rsync daemon. You may
+ specify the full option name, its one-letter abbreviation, or a wild-card
+ string that matches multiple options. Beginning in 3.2.0, you can also
+ negate a match term by starting it with a "!".
+
+ When an option is refused, the daemon prints an error message and exits.
+
+ For example, this would refuse `--checksum` (`-c`) and all the various
+ delete options:
+
+ > refuse options = c delete
+
+ The reason the above refuses all delete options is that the options imply
+ `--delete`, and implied options are refused just like explicit options.
+
+ The use of a negated match allows you to fine-tune your refusals after a
+ wild-card, such as this:
+
+ > refuse options = delete-* !delete-during
+
+ Negated matching can also turn your list of refused options into a list of
+ accepted options. To do this, begin the list with a "`*`" (to refuse all
+ options) and then specify one or more negated matches to accept. For
+ example:
+
+ > refuse options = * !a !v !compress*
+
+ Don't worry that the "`*`" will refuse certain vital options such as
+ `--dry-run`, `--server`, `--no-iconv`, `--seclude-args`, etc. These
+ important options are not matched by wild-card, so they must be overridden
+ by their exact name. For instance, if you're forcing iconv transfers you
+ could use something like this:
+
+ > refuse options = * no-iconv !a !v
+
+ As an additional aid (beginning in 3.2.0), refusing (or "`!refusing`") the
+ "a" or "archive" option also affects all the options that the `--archive`
+ option implies (`-rdlptgoD`), but only if the option is matched explicitly
+ (not using a wildcard). If you want to do something tricky, you can use
+ "`archive*`" to avoid this side-effect, but keep in mind that no normal
+ rsync client ever sends the actual archive option to the server.
+
+ As an additional safety feature, the refusal of "delete" also refuses
+ `remove-source-files` when the daemon is the sender; if you want the latter
+ without the former, instead refuse "`delete-*`" as that refuses all the
+ delete modes without affecting `--remove-source-files`. (Keep in mind that
+ the client's `--delete` option typically results in `--delete-during`.)
+
+ When un-refusing delete options, you should either specify "`!delete*`" (to
+ accept all delete options) or specify a limited set that includes "delete",
+ such as:
+
+ > refuse options = * !a !delete !delete-during
+
+ ... whereas this accepts any delete option except `--delete-after`:
+
+ > refuse options = * !a !delete* delete-after
+
+ A note on refusing "compress": it may be better to set the "[dont compress](#)"
+ daemon parameter to "`*`" and ensure that `RSYNC_COMPRESS_LIST=zlib` is set
+ in the environment of the daemon in order to disable compression silently
+ instead of returning an error that forces the client to remove the `-z`
+ option.
+
+ If you are un-refusing the compress option, you may want to match
+ "`!compress*`" if you also want to allow the `--compress-level` option.
+
+ Note that the "copy-devices" & "write-devices" options are refused by
+ default, but they can be explicitly accepted with "`!copy-devices`" and/or
+ "`!write-devices`". The options "log-file" and "log-file-format" are
+ forcibly refused and cannot be accepted.
+
+ Here are all the options that are not matched by wild-cards:
+
+ - `--server`: Required for rsync to even work.
+ - `--rsh`, `-e`: Required to convey compatibility flags to the server.
+ - `--out-format`: This is required to convey output behavior to a remote
+ receiver. While rsync passes the older alias `--log-format` for
+ compatibility reasons, this options should not be confused with
+ `--log-file-format`.
+ - `--sender`: Use "[write only](#)" parameter instead of refusing this.
+ - `--dry-run`, `-n`: Who would want to disable this?
+ - `--seclude-args`, `-s`: Is the oldest arg-protection method.
+ - `--from0`, `-0`: Makes it easier to accept/refuse `--files-from` without
+ affecting this helpful modifier.
+ - `--iconv`: This is auto-disabled based on "[charset](#)" parameter.
+ - `--no-iconv`: Most transfers use this option.
+ - `--checksum-seed`: Is a fairly rare, safe option.
+ - `--write-devices`: Is non-wild but also auto-disabled.
+
+0. `dont compress`
+
+ **NOTE:** This parameter currently has no effect except in one instance: if
+ it is set to "`*`" then it minimizes or disables compression for all files
+ (for those that don't want to refuse the `--compress` option completely).
+
+ This parameter allows you to select filenames based on wildcard patterns
+ that should not be compressed when pulling files from the daemon (no
+ analogous parameter exists to govern the pushing of files to a daemon).
+ Compression can be expensive in terms of CPU usage, so it is usually good
+ to not try to compress files that won't compress well, such as already
+ compressed files.
+
+ The "dont compress" parameter takes a space-separated list of
+ case-insensitive wildcard patterns. Any source filename matching one of the
+ patterns will be compressed as little as possible during the transfer. If
+ the compression algorithm has an "off" level, then no compression occurs
+ for those files. If an algorithms has the ability to change the level in
+ mid-stream, it will be minimized to reduce the CPU usage as much as
+ possible.
+
+ See the `--skip-compress` parameter in the **rsync**(1) manpage for the
+ list of file suffixes that are skipped by default if this parameter is not
+ set.
+
+0. `early exec`, `pre-xfer exec`, `post-xfer exec`
+
+ You may specify a command to be run in the early stages of the connection,
+ or right before and/or after the transfer. If the `early exec` or
+ `pre-xfer exec` command returns an error code, the transfer is aborted
+ before it begins. Any output from the `pre-xfer exec` command on stdout
+ (up to several KB) will be displayed to the user when aborting, but is
+ _not_ displayed if the script returns success. The other programs cannot
+ send any text to the user. All output except for the `pre-xfer exec`
+ stdout goes to the corresponding daemon's stdout/stderr, which is typically
+ discarded. See the `--no-detatch` option for a way to see the daemon's
+ output, which can assist with debugging.
+
+ Note that the `early exec` command runs before any part of the transfer
+ request is known except for the module name. This helper script can be
+ used to setup a disk mount or decrypt some data into a module dir, but you
+ may need to use `lock file` and `max connections` to avoid concurrency
+ issues. If the client rsync specified the `--early-input=FILE` option, it
+ can send up to about 5K of data to the stdin of the early script. The
+ stdin will otherwise be empty.
+
+ Note that the `post-xfer exec` command is still run even if one of the
+ other scripts returns an error code. The `pre-xfer exec` command will _not_
+ be run, however, if the `early exec` command fails.
+
+ The following environment variables will be set, though some are specific
+ to the pre-xfer or the post-xfer environment:
+
+ - `RSYNC_MODULE_NAME`: The name of the module being accessed.
+ - `RSYNC_MODULE_PATH`: The path configured for the module.
+ - `RSYNC_HOST_ADDR`: The accessing host's IP address.
+ - `RSYNC_HOST_NAME`: The accessing host's name.
+ - `RSYNC_USER_NAME`: The accessing user's name (empty if no user).
+ - `RSYNC_PID`: A unique number for this transfer.
+ - `RSYNC_REQUEST`: (pre-xfer only) The module/path info specified by the
+ user. Note that the user can specify multiple source files, so the
+ request can be something like "mod/path1 mod/path2", etc.
+ - `RSYNC_ARG#`: (pre-xfer only) The pre-request arguments are set in these
+ numbered values. RSYNC_ARG0 is always "rsyncd", followed by the options
+ that were used in RSYNC_ARG1, and so on. There will be a value of "."
+ indicating that the options are done and the path args are beginning --
+ these contain similar information to RSYNC_REQUEST, but with values
+ separated and the module name stripped off.
+ - `RSYNC_EXIT_STATUS`: (post-xfer only) the server side's exit value. This
+ will be 0 for a successful run, a positive value for an error that the
+ server generated, or a -1 if rsync failed to exit properly. Note that an
+ error that occurs on the client side does not currently get sent to the
+ server side, so this is not the final exit status for the whole transfer.
+ - `RSYNC_RAW_STATUS`: (post-xfer only) the raw exit value from
+ **waitpid()**.
+
+ Even though the commands can be associated with a particular module, they
+ are run using the permissions of the user that started the daemon (not the
+ module's uid/gid setting) without any chroot restrictions.
+
+ These settings honor 2 environment variables: use RSYNC_SHELL to set a
+ shell to use when running the command (which otherwise uses your
+ **system()** call's default shell), and use RSYNC_NO_XFER_EXEC to disable
+ both options completely.
+
+## CONFIG DIRECTIVES
+
+There are currently two config directives available that allow a config file to
+incorporate the contents of other files: `&include` and `&merge`. Both allow
+a reference to either a file or a directory. They differ in how segregated the
+file's contents are considered to be.
+
+The `&include` directive treats each file as more distinct, with each one
+inheriting the defaults of the parent file, starting the parameter parsing as
+globals/defaults, and leaving the defaults unchanged for the parsing of the
+rest of the parent file.
+
+The `&merge` directive, on the other hand, treats the file's contents as if it
+were simply inserted in place of the directive, and thus it can set parameters
+in a module started in another file, can affect the defaults for other files,
+etc.
+
+When an `&include` or `&merge` directive refers to a directory, it will read in
+all the `*.conf` or `*.inc` files (respectively) that are contained inside that
+directory (without any recursive scanning), with the files sorted into alpha
+order. So, if you have a directory named "rsyncd.d" with the files "foo.conf",
+"bar.conf", and "baz.conf" inside it, this directive:
+
+> &include /path/rsyncd.d
+
+would be the same as this set of directives:
+
+> &include /path/rsyncd.d/bar.conf
+> &include /path/rsyncd.d/baz.conf
+> &include /path/rsyncd.d/foo.conf
+
+except that it adjusts as files are added and removed from the directory.
+
+The advantage of the `&include` directive is that you can define one or more
+modules in a separate file without worrying about unintended side-effects
+between the self-contained module files.
+
+The advantage of the `&merge` directive is that you can load config snippets
+that can be included into multiple module definitions, and you can also set
+global values that will affect connections (such as `motd file`), or globals
+that will affect other include files.
+
+For example, this is a useful /etc/rsyncd.conf file:
+
+> port = 873
+> log file = /var/log/rsync.log
+> pid file = /var/lock/rsync.lock
+>
+> &merge /etc/rsyncd.d
+> &include /etc/rsyncd.d
+
+This would merge any `/etc/rsyncd.d/*.inc` files (for global values that should
+stay in effect), and then include any `/etc/rsyncd.d/*.conf` files (defining
+modules without any global-value cross-talk).
+
+## AUTHENTICATION STRENGTH
+
+The authentication protocol used in rsync is a 128 bit MD4 based challenge
+response system. This is fairly weak protection, though (with at least one
+brute-force hash-finding algorithm publicly available), so if you want really
+top-quality security, then I recommend that you run rsync over ssh. (Yes, a
+future version of rsync will switch over to a stronger hashing method.)
+
+Also note that the rsync daemon protocol does not currently provide any
+encryption of the data that is transferred over the connection. Only
+authentication is provided. Use ssh as the transport if you want encryption.
+
+You can also make use of SSL/TLS encryption if you put rsync behind an
+SSL proxy.
+
+## SSL/TLS Daemon Setup
+
+When setting up an rsync daemon for access via SSL/TLS, you will need to
+configure a TCP proxy (such as haproxy or nginx) as the front-end that handles
+the encryption.
+
+- You should limit the access to the backend-rsyncd port to only allow the
+ proxy to connect. If it is on the same host as the proxy, then configuring
+ it to only listen on localhost is a good idea.
+- You should consider turning on the `proxy protocol` rsync-daemon parameter if
+ your proxy supports sending that information. The examples below assume that
+ this is enabled.
+
+An example haproxy setup is as follows:
+
+> ```
+> frontend fe_rsync-ssl
+> bind :::874 ssl crt /etc/letsencrypt/example.com/combined.pem
+> mode tcp
+> use_backend be_rsync
+>
+> backend be_rsync
+> mode tcp
+> server local-rsync 127.0.0.1:873 check send-proxy
+> ```
+
+An example nginx proxy setup is as follows:
+
+> ```
+> stream {
+> server {
+> listen 874 ssl;
+> listen [::]:874 ssl;
+>
+> ssl_certificate /etc/letsencrypt/example.com/fullchain.pem;
+> ssl_certificate_key /etc/letsencrypt/example.com/privkey.pem;
+>
+> proxy_pass localhost:873;
+> proxy_protocol on; # Requires rsyncd.conf "proxy protocol = true"
+> proxy_timeout 1m;
+> proxy_connect_timeout 5s;
+> }
+> }
+> ```
+
+## DAEMON CONFIG EXAMPLES
+
+A simple rsyncd.conf file that allow anonymous rsync to a ftp area at
+`/home/ftp` would be:
+
+> ```
+> [ftp]
+> path = /home/ftp
+> comment = ftp export area
+> ```
+
+A more sophisticated example would be:
+
+> ```
+> uid = nobody
+> gid = nobody
+> use chroot = yes
+> max connections = 4
+> syslog facility = local5
+> pid file = /var/run/rsyncd.pid
+>
+> [ftp]
+> path = /var/ftp/./pub
+> comment = whole ftp area (approx 6.1 GB)
+>
+> [sambaftp]
+> path = /var/ftp/./pub/samba
+> comment = Samba ftp area (approx 300 MB)
+>
+> [rsyncftp]
+> path = /var/ftp/./pub/rsync
+> comment = rsync ftp area (approx 6 MB)
+>
+> [sambawww]
+> path = /public_html/samba
+> comment = Samba WWW pages (approx 240 MB)
+>
+> [cvs]
+> path = /data/cvs
+> comment = CVS repository (requires authentication)
+> auth users = tridge, susan
+> secrets file = /etc/rsyncd.secrets
+> ```
+
+The /etc/rsyncd.secrets file would look something like this:
+
+> tridge:mypass
+> susan:herpass
+
+## FILES
+
+/etc/rsyncd.conf or rsyncd.conf
+
+## SEE ALSO
+
+[**rsync**(1)](rsync.1), [**rsync-ssl**(1)](rsync-ssl.1)
+
+## BUGS
+
+Please report bugs! The rsync bug tracking system is online at
+<https://rsync.samba.org/>.
+
+## VERSION
+
+This manpage is current for version @VERSION@ of rsync.
+
+## CREDITS
+
+Rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
+
+An rsync web site is available at <https://rsync.samba.org/> and its github
+project is <https://github.com/WayneD/rsync>.
+
+## THANKS
+
+Thanks to Warren Stanley for his original idea and patch for the rsync daemon.
+Thanks to Karsten Thygesen for his many suggestions and documentation!
+
+## AUTHOR
+
+Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
+people have later contributed to it. It is currently maintained by Wayne
+Davison.
+
+Mailing lists for support and development are available at
+<https://lists.samba.org/>.
diff --git a/rsyncsh.txt b/rsyncsh.txt
new file mode 100644
index 0000000..93932dc
--- /dev/null
+++ b/rsyncsh.txt
@@ -0,0 +1,26 @@
+rsyncsh
+Copyright (C) 2001 by Martin Pool
+
+This is a quick hack to build an interactive shell around rsync, the
+same way we have the ftp, lftp and ncftp programs for the FTP
+protocol. The key application for this is connecting to a public
+rsync server, such as rsync.kernel.org, change down through and list
+directories, and finally pull down the file you want.
+
+rsync is somewhat ill-at-ease as an interactive operation, since every
+network connection is used to carry out exactly one operation. rsync
+kind of "forks across the network" passing the options and filenames
+to operate upon, and the connection is closed when the transfer is
+complete. (This might be fixed in the future, either by adapting the
+current protocol to allow chained operations over a single socket, or
+by writing a new protocol that better supports interactive use.)
+
+So, rsyncsh runs a new rsync command and opens a new socket for every
+(network-based) command you type.
+
+This has two consequences. Firstly, there is more command latency
+than is really desirable. More seriously, if the connection cannot be
+done automatically, because for example it uses SSH with a password,
+then you will need to enter the password every time. We might even
+fix this in the future, though, by having a way to automatically feed
+the password to SSH if it's entered once.
diff --git a/runtests.sh b/runtests.sh
new file mode 100755
index 0000000..0c463be
--- /dev/null
+++ b/runtests.sh
@@ -0,0 +1,360 @@
+#! /bin/sh
+
+# Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
+# Copyright (C) 2003-2022 Wayne Davison
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version
+# 2 as published by the Free Software Foundation.
+#
+# 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# -------------------------------------------------------------------------
+
+# rsync top-level test script -- this invokes all the other more
+# detailed tests in order. This script can either be called by `make
+# check' or `make installcheck'. `check' runs against the copies of
+# the program and other files in the build directory, and
+# `installcheck' against the installed copy of the program.
+
+# It can also be called on a single test file using a run like this:
+#
+# preserve_scratch=yes whichtests=itemize.test ./runtests.sh
+
+# In either case we need to also be able to find the source directory,
+# since we read test scripts and possibly other information from
+# there.
+
+# Whenever possible, informational messages are written to stdout and
+# error messages to stderr. They're separated out by the build farm
+# display scripts.
+
+# According to the GNU autoconf manual, the only valid place to set up
+# directory locations is through Make, since users are allowed to (try
+# to) change their mind on the Make command line. So, Make has to
+# pass in all the values we need.
+
+# For other configured settings we read ./config.sh, which tells us
+# about shell commands on this machine and similar things.
+
+# rsync_bin gives the location of the rsync binary. This is either
+# builddir/rsync if we're testing an uninstalled copy, or
+# install_prefix/bin/rsync if we're testing an installed copy. On the
+# build farm rsync will be installed, but into a scratch /usr.
+
+# srcdir gives the location of the source tree, which lets us find the
+# build scripts. At the moment we assume we are invoked from the
+# source directory.
+
+# This script must be invoked from the build directory.
+
+# A scratch directory, 'testtmp', is used in the build directory to
+# hold per-test subdirectories.
+
+# This script also uses the $loglevel environment variable. 1 is the
+# default value, and 10 the most verbose. You can set this from the
+# Make command line. It's also set by the build farm to give more
+# detail for failing builds.
+
+# -------------------------------------------------------------------------
+
+# NOTES FOR TEST CASES:
+
+# Each test case runs in its own shell.
+
+# Exit codes from tests:
+
+# 1 tests failed
+# 2 error in starting tests
+# 77 this test skipped (random value unlikely to happen by chance, same as
+# automake)
+
+# HOWEVER, the overall exit code to the farm is different: we return
+# the *number of tests that failed*, so that it will show up nicely in
+# the overall summary.
+
+# rsync.fns contains some general setup functions and definitions.
+
+# -------------------------------------------------------------------------
+
+# NOTES ON PORTABILITY:
+
+# Both this script and the Makefile have to be pretty conservative
+# about which Unix features they use.
+
+# We cannot count on Make exporting variables to commands, unless
+# they're explicitly given on the command line.
+
+# Also, we can't count on 'cp -a' or 'mkdir -p', although they're
+# pretty handy (see function makepath for the latter).
+
+# I think some of the GNU documentation suggests that we shouldn't
+# rely on shell functions. However, the Bash manual seems to say that
+# they're in POSIX 1003.2, and since the build farm relies on them
+# they're probably working on most machines we really care about.
+
+# You cannot use "function foo {" syntax, but must instead say "foo()
+# {", or it breaks on FreeBSD.
+
+# BSD machines tend not to have "head" or "seq".
+
+# You cannot do "export VAR=VALUE" all on one line; the export must be
+# separate from the assignment. (SCO SysV)
+
+# Don't rely on grep -q, as that doesn't work everywhere -- just redirect
+# stdout to /dev/null to keep it quiet.
+
+# -------------------------------------------------------------------------
+
+# STILL TO DO:
+
+# We need a good protection against tests that hang indefinitely.
+# Perhaps some combination of starting them in the background, wait,
+# and kill?
+
+# Perhaps we need a common way to cleanup tests. At the moment just
+# clobbering the directory when we're done should be enough.
+
+# If any of the targets fail, then (GNU?) Make returns 2, instead of
+# the return code from the failing command. This is fine, but it
+# means that the build farm just shows "2" for failed tests, not the
+# number of tests that actually failed. For more details we might
+# need to grovel through the log files to find a line saying how many
+# failed.
+
+
+set -e
+
+. "./shconfig"
+
+RUNSHFLAGS='-e'
+export RUNSHFLAGS
+
+# for Solaris
+if [ -d /usr/xpg4/bin ]; then
+ PATH="/usr/xpg4/bin/:$PATH"
+ export PATH
+fi
+
+if [ "x$loglevel" != x ] && [ "$loglevel" -gt 8 ]; then
+ if set -x; then
+ # If it doesn't work the first time, don't keep trying.
+ RUNSHFLAGS="$RUNSHFLAGS -x"
+ fi
+fi
+
+POSIXLY_CORRECT=1
+if test x"$TOOLDIR" = x; then
+ TOOLDIR=`pwd`
+fi
+srcdir=`dirname $0`
+if test x"$srcdir" = x || test x"$srcdir" = x.; then
+ srcdir="$TOOLDIR"
+fi
+if test x"$rsync_bin" = x; then
+ rsync_bin="$TOOLDIR/rsync"
+fi
+
+# This allows the user to specify extra rsync options -- use carefully!
+RSYNC="$rsync_bin $*"
+#RSYNC="valgrind $rsync_bin $*"
+
+TLS_ARGS=''
+if grep -E '^#define HAVE_LUTIMES 1' config.h >/dev/null; then
+ TLS_ARGS="$TLS_ARGS -l"
+fi
+if grep -E '#undef CHOWN_MODIFIES_SYMLINK' config.h >/dev/null; then
+ TLS_ARGS="$TLS_ARGS -L"
+fi
+
+export POSIXLY_CORRECT TOOLDIR srcdir RSYNC TLS_ARGS
+
+echo "============================================================"
+echo "$0 running in $TOOLDIR"
+echo " rsync_bin=$RSYNC"
+echo " srcdir=$srcdir"
+echo " TLS_ARGS=$TLS_ARGS"
+
+if [ -f /usr/bin/whoami ]; then
+ testuser=`/usr/bin/whoami`
+elif [ -f /usr/ucb/whoami ]; then
+ testuser=`/usr/ucb/whoami`
+elif [ -f /bin/whoami ]; then
+ testuser=`/bin/whoami`
+else
+ testuser=`id -un 2>/dev/null || echo ${LOGNAME:-${USERNAME:-${USER:-'UNKNOWN'}}}`
+fi
+
+echo " testuser=$testuser"
+echo " os=`uname -a`"
+
+# It must be "yes", not just nonnull
+if [ "x$preserve_scratch" = xyes ]; then
+ echo " preserve_scratch=yes"
+else
+ echo " preserve_scratch=no"
+fi
+
+# Check if setacl/setfacl is around and if it supports the -k or -s option.
+if setacl -k u::7,g::5,o:5 testsuite 2>/dev/null; then
+ setfacl_nodef='setacl -k'
+elif setfacl --help 2>&1 | grep ' -k,\|\[-[a-z]*k' >/dev/null; then
+ setfacl_nodef='setfacl -k'
+elif setfacl -s u::7,g::5,o:5 testsuite 2>/dev/null; then
+ setfacl_nodef='setfacl -s u::7,g::5,o:5'
+else
+ # The "true" command runs successfully, but does nothing.
+ setfacl_nodef=true
+fi
+
+export setfacl_nodef
+
+if [ ! -f "$rsync_bin" ]; then
+ echo "rsync_bin $rsync_bin is not a file" >&2
+ exit 2
+fi
+
+if [ ! -d "$srcdir" ]; then
+ echo "srcdir $srcdir is not a directory" >&2
+ exit 2
+fi
+
+expect_skipped="${RSYNC_EXPECT_SKIPPED-IGNORE}"
+skipped_list=''
+skipped=0
+missing=0
+passed=0
+failed=0
+
+# Directory that holds the other test subdirs. We create separate dirs
+# inside for each test case, so that they can be left behind in case of
+# failure to aid investigation. We don't remove the testtmp subdir at
+# the end so that it can be configured as a symlink to a filesystem that
+# has ACLs and xattr support enabled (if desired).
+scratchbase="${scratchbase:-$TOOLDIR}"/testtmp
+echo " scratchbase=$scratchbase"
+[ -d "$scratchbase" ] || mkdir "$scratchbase"
+
+suitedir="$srcdir/testsuite"
+TESTRUN_TIMEOUT=300
+
+export scratchdir suitedir TESTRUN_TIMEOUT
+
+prep_scratch() {
+ [ -d "$scratchdir" ] && chmod -R u+rwX "$scratchdir" && rm -rf "$scratchdir"
+ mkdir "$scratchdir"
+ # Get rid of default ACLs and dir-setgid to avoid confusing some tests.
+ $setfacl_nodef "$scratchdir" 2>/dev/null || true
+ chmod g-s "$scratchdir"
+ case "$srcdir" in
+ /*) ln -s "$srcdir" "$scratchdir/src" ;;
+ *) ln -s "$TOOLDIR/$srcdir" "$scratchdir/src" ;;
+ esac
+ return 0
+}
+
+maybe_discard_scratch() {
+ [ x"$preserve_scratch" != xyes ] && [ -d "$scratchdir" ] && rm -rf "$scratchdir"
+ return 0
+}
+
+if [ "x$whichtests" = x ]; then
+ whichtests="*.test"
+ full_run=yes
+else
+ full_run=no
+fi
+
+for testscript in $suitedir/$whichtests; do
+ testbase=`echo $testscript | sed -e 's!.*/!!' -e 's/.test\$//'`
+ scratchdir="$scratchbase/$testbase"
+
+ prep_scratch
+
+ case "$testscript" in
+ *hardlinks*) TESTRUN_TIMEOUT=600 ;;
+ *) TESTRUN_TIMEOUT=300 ;;
+ esac
+
+ set +e
+ "$TOOLDIR/"testrun $RUNSHFLAGS "$testscript" >"$scratchdir/test.log" 2>&1
+ result=$?
+ set -e
+
+ if [ "x$always_log" = xyes ] || ( [ $result != 0 ] && [ $result != 77 ] && [ $result != 78 ] )
+ then
+ echo "----- $testbase log follows"
+ cat "$scratchdir/test.log"
+ echo "----- $testbase log ends"
+ if [ -f "$scratchdir/rsyncd.log" ]; then
+ echo "----- $testbase rsyncd.log follows"
+ cat "$scratchdir/rsyncd.log"
+ echo "----- $testbase rsyncd.log ends"
+ fi
+ fi
+
+ case $result in
+ 0)
+ echo "PASS $testbase"
+ passed=`expr $passed + 1`
+ maybe_discard_scratch
+ ;;
+ 77)
+ # backticks will fill the whole file onto one line, which is a feature
+ whyskipped=`cat "$scratchdir/whyskipped"`
+ echo "SKIP $testbase ($whyskipped)"
+ skipped_list="$skipped_list,$testbase"
+ skipped=`expr $skipped + 1`
+ maybe_discard_scratch
+ ;;
+ 78)
+ # It failed, but we expected that. don't dump out error logs,
+ # because most users won't want to see them. But do leave
+ # the working directory around.
+ echo "XFAIL $testbase"
+ failed=`expr $failed + 1`
+ ;;
+ *)
+ echo "FAIL $testbase"
+ failed=`expr $failed + 1`
+ if [ "x$nopersist" = xyes ]; then
+ exit 1
+ fi
+ esac
+done
+
+echo '------------------------------------------------------------'
+echo "----- overall results:"
+echo " $passed passed"
+[ "$failed" -gt 0 ] && echo " $failed failed"
+[ "$skipped" -gt 0 ] && echo " $skipped skipped"
+[ "$missing" -gt 0 ] && echo " $missing missing"
+if [ "$full_run" = yes ] && [ "$expect_skipped" != IGNORE ]; then
+ skipped_list=`echo "$skipped_list" | sed 's/^,//'`
+ echo "----- skipped results:"
+ echo " expected: $expect_skipped"
+ echo " got: $skipped_list"
+else
+ skipped_list=''
+ expect_skipped=''
+fi
+echo '------------------------------------------------------------'
+
+# OK, so expr exits with 0 if the result is neither null nor zero; and
+# 1 if the expression is null or zero. This is the opposite of what
+# we want, and if we just call expr then this script will always fail,
+# because -e is set.
+
+result=`expr $failed + $missing || true`
+if [ "$result" = 0 ] && [ "$skipped_list" != "$expect_skipped" ]; then
+ result=1
+fi
+echo "overall result is $result"
+exit $result
diff --git a/sender.c b/sender.c
new file mode 100644
index 0000000..3d4f052
--- /dev/null
+++ b/sender.c
@@ -0,0 +1,461 @@
+/*
+ * Routines only used by the sending process.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "inums.h"
+
+extern int do_xfers;
+extern int am_server;
+extern int am_daemon;
+extern int local_server;
+extern int inc_recurse;
+extern int log_before_transfer;
+extern int stdout_format_has_i;
+extern int logfile_format_has_i;
+extern int want_xattr_optim;
+extern int csum_length;
+extern int append_mode;
+extern int copy_links;
+extern int io_error;
+extern int flist_eof;
+extern int whole_file;
+extern int allowed_lull;
+extern int copy_devices;
+extern int preserve_xattrs;
+extern int protocol_version;
+extern int remove_source_files;
+extern int updating_basis_file;
+extern int make_backups;
+extern int inplace;
+extern int inplace_partial;
+extern int batch_fd;
+extern int write_batch;
+extern int file_old_total;
+extern BOOL want_progress_now;
+extern struct stats stats;
+extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern char num_dev_ino_buf[4 + 8 + 8];
+
+BOOL extra_flist_sending_enabled;
+
+/**
+ * @file
+ *
+ * The sender gets checksums from the generator, calculates deltas,
+ * and transmits them to the receiver. The sender process runs on the
+ * machine holding the source files.
+ **/
+
+/**
+ * Receive the checksums for a buffer
+ **/
+static struct sum_struct *receive_sums(int f)
+{
+ struct sum_struct *s = new(struct sum_struct);
+ int lull_mod = protocol_version >= 31 ? 0 : allowed_lull * 5;
+ OFF_T offset = 0;
+ int32 i;
+
+ read_sum_head(f, s);
+
+ s->sums = NULL;
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO, "count=%s n=%ld rem=%ld\n",
+ big_num(s->count), (long)s->blength, (long)s->remainder);
+ }
+
+ if (append_mode > 0) {
+ s->flength = (OFF_T)s->count * s->blength;
+ if (s->remainder)
+ s->flength -= s->blength - s->remainder;
+ return s;
+ }
+
+ if (s->count == 0)
+ return(s);
+
+ s->sums = new_array(struct sum_buf, s->count);
+
+ for (i = 0; i < s->count; i++) {
+ s->sums[i].sum1 = read_int(f);
+ read_buf(f, s->sums[i].sum2, s->s2length);
+
+ s->sums[i].offset = offset;
+ s->sums[i].flags = 0;
+
+ if (i == s->count-1 && s->remainder != 0)
+ s->sums[i].len = s->remainder;
+ else
+ s->sums[i].len = s->blength;
+ offset += s->sums[i].len;
+
+ if (lull_mod && !(i % lull_mod))
+ maybe_send_keepalive(time(NULL), True);
+
+ if (DEBUG_GTE(DELTASUM, 3)) {
+ rprintf(FINFO,
+ "chunk[%d] len=%d offset=%s sum1=%08x\n",
+ i, s->sums[i].len, big_num(s->sums[i].offset),
+ s->sums[i].sum1);
+ }
+ }
+
+ s->flength = offset;
+
+ return s;
+}
+
+void successful_send(int ndx)
+{
+ char fname[MAXPATHLEN];
+ char *failed_op;
+ struct file_struct *file;
+ struct file_list *flist;
+ STRUCT_STAT st;
+
+ if (!remove_source_files)
+ return;
+
+ flist = flist_for_ndx(ndx, "successful_send");
+ file = flist->files[ndx - flist->ndx_start];
+ if (!change_pathname(file, NULL, 0))
+ return;
+ f_name(file, fname);
+
+ if ((copy_links ? do_stat(fname, &st) : do_lstat(fname, &st)) < 0) {
+ failed_op = "re-lstat";
+ goto failed;
+ }
+
+ if (local_server
+ && (int64)st.st_dev == IVAL64(num_dev_ino_buf, 4)
+ && (int64)st.st_ino == IVAL64(num_dev_ino_buf, 4 + 8)) {
+ rprintf(FERROR_XFER, "ERROR: Skipping sender remove of destination file: %s\n", fname);
+ return;
+ }
+
+ if (st.st_size != F_LENGTH(file) || st.st_mtime != file->modtime
+#ifdef ST_MTIME_NSEC
+ || (NSEC_BUMP(file) && (uint32)st.ST_MTIME_NSEC != F_MOD_NSEC(file))
+#endif
+ ) {
+ rprintf(FERROR_XFER, "ERROR: Skipping sender remove for changed file: %s\n", fname);
+ return;
+ }
+
+ if (do_unlink(fname) < 0) {
+ failed_op = "remove";
+ failed:
+ if (errno == ENOENT)
+ rprintf(FINFO, "sender file already removed: %s\n", fname);
+ else
+ rsyserr(FERROR_XFER, errno, "sender failed to %s %s", failed_op, fname);
+ } else {
+ if (INFO_GTE(REMOVE, 1))
+ rprintf(FINFO, "sender removed %s\n", fname);
+ }
+}
+
+static void write_ndx_and_attrs(int f_out, int ndx, int iflags,
+ const char *fname, struct file_struct *file,
+ uchar fnamecmp_type, char *buf, int len)
+{
+ write_ndx(f_out, ndx);
+ if (protocol_version < 29)
+ return;
+ write_shortint(f_out, iflags);
+ if (iflags & ITEM_BASIS_TYPE_FOLLOWS)
+ write_byte(f_out, fnamecmp_type);
+ if (iflags & ITEM_XNAME_FOLLOWS)
+ write_vstring(f_out, buf, len);
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers
+ && !(want_xattr_optim && BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE)))
+ send_xattr_request(fname, file, f_out);
+#endif
+}
+
+void send_files(int f_in, int f_out)
+{
+ int fd = -1;
+ struct sum_struct *s;
+ struct map_struct *mbuf = NULL;
+ STRUCT_STAT st;
+ char fname[MAXPATHLEN], xname[MAXPATHLEN];
+ const char *path, *slash;
+ uchar fnamecmp_type;
+ int iflags, xlen;
+ struct file_struct *file;
+ int phase = 0, max_phase = protocol_version >= 29 ? 2 : 1;
+ int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
+ enum logcode log_code = log_before_transfer ? FLOG : FINFO;
+ int f_xfer = write_batch < 0 ? batch_fd : f_out;
+ int save_io_error = io_error;
+ int ndx, j;
+
+ if (DEBUG_GTE(SEND, 1))
+ rprintf(FINFO, "send_files starting\n");
+
+ if (whole_file < 0)
+ whole_file = 0;
+
+ progress_init();
+
+ while (1) {
+ if (inc_recurse) {
+ send_extra_file_list(f_out, MIN_FILECNT_LOOKAHEAD);
+ extra_flist_sending_enabled = !flist_eof;
+ }
+
+ /* This call also sets cur_flist. */
+ ndx = read_ndx_and_attrs(f_in, f_out, &iflags, &fnamecmp_type,
+ xname, &xlen);
+ extra_flist_sending_enabled = False;
+
+ if (ndx == NDX_DONE) {
+ if (!am_server && cur_flist) {
+ set_current_file_index(NULL, 0);
+ if (INFO_GTE(PROGRESS, 2))
+ end_progress(0);
+ }
+ if (inc_recurse && first_flist) {
+ file_old_total -= first_flist->used;
+ flist_free(first_flist);
+ if (first_flist) {
+ if (first_flist == cur_flist)
+ file_old_total = cur_flist->used;
+ write_ndx(f_out, NDX_DONE);
+ continue;
+ }
+ }
+ if (++phase > max_phase)
+ break;
+ if (DEBUG_GTE(SEND, 1))
+ rprintf(FINFO, "send_files phase=%d\n", phase);
+ write_ndx(f_out, NDX_DONE);
+ continue;
+ }
+
+ if (inc_recurse)
+ send_extra_file_list(f_out, MIN_FILECNT_LOOKAHEAD);
+
+ if (ndx - cur_flist->ndx_start >= 0)
+ file = cur_flist->files[ndx - cur_flist->ndx_start];
+ else
+ file = dir_flist->files[cur_flist->parent_ndx];
+ if (F_PATHNAME(file)) {
+ path = F_PATHNAME(file);
+ slash = "/";
+ } else {
+ path = slash = "";
+ }
+ if (!change_pathname(file, NULL, 0))
+ continue;
+ f_name(file, fname);
+
+ if (DEBUG_GTE(SEND, 1))
+ rprintf(FINFO, "send_files(%d, %s%s%s)\n", ndx, path,slash,fname);
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers
+ && !(want_xattr_optim && BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE)))
+ recv_xattr_request(file, f_in);
+#endif
+
+ if (!(iflags & ITEM_TRANSFER)) {
+ maybe_log_item(file, iflags, itemizing, xname);
+ write_ndx_and_attrs(f_out, ndx, iflags, fname, file, fnamecmp_type, xname, xlen);
+ if (iflags & ITEM_IS_NEW) {
+ stats.created_files++;
+ if (S_ISREG(file->mode)) {
+ /* Nothing further to count. */
+ } else if (S_ISDIR(file->mode))
+ stats.created_dirs++;
+#ifdef SUPPORT_LINKS
+ else if (S_ISLNK(file->mode))
+ stats.created_symlinks++;
+#endif
+ else if (IS_DEVICE(file->mode))
+ stats.created_devices++;
+ else
+ stats.created_specials++;
+ }
+ continue;
+ }
+ if (phase == 2) {
+ rprintf(FERROR,
+ "got transfer request in phase 2 [%s]\n",
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (file->flags & FLAG_FILE_SENT) {
+ if (csum_length == SHORT_SUM_LENGTH) {
+ /* For inplace: redo phase turns off the backup
+ * flag so that we do a regular inplace send. */
+ make_backups = -make_backups;
+ append_mode = -append_mode;
+ csum_length = SUM_LENGTH;
+ }
+ } else {
+ if (csum_length != SHORT_SUM_LENGTH) {
+ make_backups = -make_backups;
+ append_mode = -append_mode;
+ csum_length = SHORT_SUM_LENGTH;
+ }
+ if (iflags & ITEM_IS_NEW)
+ stats.created_files++;
+ }
+
+ updating_basis_file = (inplace_partial && fnamecmp_type == FNAMECMP_PARTIAL_DIR)
+ || (inplace && (protocol_version >= 29 ? fnamecmp_type == FNAMECMP_FNAME : make_backups <= 0));
+
+ if (!am_server)
+ set_current_file_index(file, ndx);
+ stats.xferred_files++;
+ stats.total_transferred_size += F_LENGTH(file);
+
+ remember_initial_stats();
+
+ if (!do_xfers) { /* log the transfer */
+ log_item(FCLIENT, file, iflags, NULL);
+ write_ndx_and_attrs(f_out, ndx, iflags, fname, file, fnamecmp_type, xname, xlen);
+ continue;
+ }
+
+ if (!(s = receive_sums(f_in))) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "receive_sums failed\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ fd = do_open(fname, O_RDONLY, 0);
+ if (fd == -1) {
+ if (errno == ENOENT) {
+ enum logcode c = am_daemon && protocol_version < 28 ? FERROR : FWARNING;
+ io_error |= IOERR_VANISHED;
+ rprintf(c, "file has vanished: %s\n",
+ full_fname(fname));
+ } else {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno,
+ "send_files failed to open %s",
+ full_fname(fname));
+ }
+ free_sums(s);
+ if (protocol_version >= 30)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ /* map the local file */
+ if (do_fstat(fd, &st) != 0) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "fstat failed");
+ free_sums(s);
+ close(fd);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (IS_DEVICE(st.st_mode)) {
+ if (!copy_devices) {
+ rprintf(FERROR, "attempt to copy device contents without --copy-devices\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (st.st_size == 0)
+ st.st_size = get_device_size(fd, fname);
+ }
+
+ if (append_mode > 0 && st.st_size < F_LENGTH(file)) {
+ rprintf(FWARNING, "skipped diminished file: %s\n",
+ full_fname(fname));
+ free_sums(s);
+ close(fd);
+ if (protocol_version >= 30)
+ send_msg_int(MSG_NO_SEND, ndx);
+ continue;
+ }
+
+ if (st.st_size) {
+ int32 read_size = MAX(s->blength * 3, MAX_MAP_SIZE);
+ mbuf = map_file(fd, st.st_size, read_size, s->blength);
+ } else
+ mbuf = NULL;
+
+ if (DEBUG_GTE(DELTASUM, 2)) {
+ rprintf(FINFO, "send_files mapped %s%s%s of size %s\n",
+ path,slash,fname, big_num(st.st_size));
+ }
+
+ write_ndx_and_attrs(f_out, ndx, iflags, fname, file, fnamecmp_type, xname, xlen);
+ write_sum_head(f_xfer, s);
+
+ if (DEBUG_GTE(DELTASUM, 2))
+ rprintf(FINFO, "calling match_sums %s%s%s\n", path,slash,fname);
+
+ if (log_before_transfer)
+ log_item(FCLIENT, file, iflags, NULL);
+ else if (!am_server && INFO_GTE(NAME, 1) && INFO_EQ(PROGRESS, 1))
+ rprintf(FCLIENT, "%s\n", fname);
+
+ set_compression(fname);
+
+ match_sums(f_xfer, s, mbuf, st.st_size);
+ if (INFO_GTE(PROGRESS, 1))
+ end_progress(st.st_size);
+ else if (want_progress_now)
+ instant_progress(fname);
+
+ log_item(log_code, file, iflags, NULL);
+
+ if (mbuf) {
+ j = unmap_file(mbuf);
+ if (j) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, j,
+ "read errors mapping %s",
+ full_fname(fname));
+ }
+ }
+ close(fd);
+
+ free_sums(s);
+
+ if (DEBUG_GTE(SEND, 1))
+ rprintf(FINFO, "sender finished %s%s%s\n", path,slash,fname);
+
+ /* Flag that we actually sent this entry. */
+ file->flags |= FLAG_FILE_SENT;
+ }
+ if (make_backups < 0)
+ make_backups = -make_backups;
+
+ if (io_error != save_io_error && protocol_version >= 30)
+ send_msg_int(MSG_IO_ERROR, io_error);
+
+ if (DEBUG_GTE(SEND, 1))
+ rprintf(FINFO, "send files finished\n");
+
+ match_report();
+
+ write_ndx(f_out, NDX_DONE);
+}
diff --git a/shconfig.in b/shconfig.in
new file mode 100755
index 0000000..5d1fdc5
--- /dev/null
+++ b/shconfig.in
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+# config.sh.in
+
+# This file is processed by config.status to produce config.status,
+# containing autoconf-determined values needed by the test scripts.
+
+ECHO_T="@ECHO_T@"
+ECHO_N="@ECHO_N@"
+ECHO_C="@ECHO_C@"
+HOST_OS="@host_os@"
+SHELL_PATH="@SHELL_PATH@"
+FAKEROOT_PATH="@FAKEROOT_PATH@"
+
+export ECHO_T ECHO_N ECHO_C HOST_OS SHELL_PATH FAKEROOT_PATH
diff --git a/simd-checksum-avx2.S b/simd-checksum-avx2.S
new file mode 100644
index 0000000..549cc3e
--- /dev/null
+++ b/simd-checksum-avx2.S
@@ -0,0 +1,177 @@
+#include "config.h"
+
+#ifdef USE_ROLL_ASM /* { */
+
+#define CHAR_OFFSET 0 /* Keep this the same as rsync.h, which isn't likely to change. */
+
+#ifdef __APPLE__
+#define get_checksum1_avx2_asm _get_checksum1_avx2_asm
+#endif
+
+.intel_syntax noprefix
+.text
+
+ .p2align 5
+ .globl get_checksum1_avx2_asm
+
+# rdi=*buf, esi=len, edx=i, rcx= *ps1, r8= *ps2
+get_checksum1_avx2_asm:
+ vmovd xmm6,[rcx] # load *ps1
+ lea eax, [rsi-128] # at least 128 bytes to process?
+ cmp edx, eax
+ jg .exit
+ lea rax, .mul_T2[rip]
+ vmovntdqa ymm7, [rax] # load T2 multiplication constants
+ vmovntdqa ymm12,[rax+32]# from memory.
+ vpcmpeqd ymm15, ymm15, ymm15 # set all elements to -1.
+
+#if CHAR_OFFSET != 0
+ mov eax, 32*CHAR_OFFSET
+ vmovd xmm10, eax
+ vpbroadcastd ymm10, xmm10
+ mov eax, 528*CHAR_OFFSET
+ vmovd xmm13, eax
+ vpbroadcastd ymm13, xmm13
+#endif
+ vpabsb ymm15, ymm15 # set all byte size elements to 1.
+ add rdi, rdx
+ vmovdqu ymm2, [rdi] # preload the first 64 bytes.
+ vmovdqu ymm3, [rdi+32]
+ and esi, ~63 # only needed during final reduction,
+ # done here to avoid a longer nop for
+ # alignment below.
+ add edx, esi
+ shr rsi, 6 # longer opcode for alignment
+ add rdi, 64
+ vpxor xmm1, xmm1, xmm1 # reset both partial sums accumulators.
+ vpxor xmm4, xmm4, xmm4
+ mov eax, [r8]
+ .p2align 4 # should fit into the LSD allocation queue.
+.loop:
+ vpmaddubsw ymm0, ymm15, ymm2 # s1 partial sums
+ vpmaddubsw ymm5, ymm15, ymm3
+ vmovdqu ymm8, [rdi] # preload the next
+ vmovdqu ymm9, [rdi+32] # 64 bytes.
+ add rdi, 64
+ vpaddd ymm4, ymm4, ymm6
+ vpaddw ymm5, ymm5, ymm0
+ vpsrld ymm0, ymm5, 16
+ vpaddw ymm5, ymm0, ymm5
+ vpaddd ymm6, ymm5, ymm6
+ vpmaddubsw ymm2, ymm7, ymm2 # s2 partial sums
+ vpmaddubsw ymm3, ymm12, ymm3
+ prefetcht0 [rdi+384] # prefetch 6 cachelines ahead.
+ vpaddw ymm3, ymm2, ymm3
+ vpsrldq ymm2, ymm3, 2
+ vpaddd ymm3, ymm2, ymm3
+ vpaddd ymm1, ymm1, ymm3
+
+#if CHAR_OFFSET != 0
+ vpaddd ymm6, ymm10, ymm6 # 32*CHAR_OFFSET
+ vpaddd ymm1, ymm13, ymm1 # 528*CHAR_OFFSET
+#endif
+ vmovdqa ymm2, ymm8 # move the next 64 bytes
+ vmovdqa ymm3, ymm9 # into the right registers
+ sub esi, 1
+ jnz .loop
+
+ # now we reduce the partial sums.
+ vpslld ymm3, ymm4, 6
+ vpsrldq ymm2, ymm6, 4
+
+ vpaddd ymm0, ymm3, ymm1
+ vpaddd ymm6, ymm2, ymm6
+ vpsrlq ymm3, ymm0, 32
+
+ vpsrldq ymm2, ymm6, 8
+ vpaddd ymm0, ymm3, ymm0
+ vpsrldq ymm3, ymm0, 8
+ vpaddd ymm6, ymm2, ymm6
+ vpaddd ymm0, ymm3, ymm0
+ vextracti128 xmm2, ymm6, 0x1
+ vextracti128 xmm1, ymm0, 0x1
+ vpaddd xmm6, xmm2, xmm6
+ vmovd [rcx], xmm6
+ vpaddd xmm1, xmm1, xmm0
+ vmovd ecx, xmm1
+ add eax, ecx
+ mov [r8], eax
+.exit:
+ vzeroupper
+ mov eax, edx
+ ret
+
+#ifdef __APPLE__
+.data
+ .align 6
+#else
+.section .rodata
+ .p2align 6
+#endif
+.mul_T2:
+ .byte 64
+ .byte 63
+ .byte 62
+ .byte 61
+ .byte 60
+ .byte 59
+ .byte 58
+ .byte 57
+ .byte 56
+ .byte 55
+ .byte 54
+ .byte 53
+ .byte 52
+ .byte 51
+ .byte 50
+ .byte 49
+ .byte 48
+ .byte 47
+ .byte 46
+ .byte 45
+ .byte 44
+ .byte 43
+ .byte 42
+ .byte 41
+ .byte 40
+ .byte 39
+ .byte 38
+ .byte 37
+ .byte 36
+ .byte 35
+ .byte 34
+ .byte 33
+ .byte 32
+ .byte 31
+ .byte 30
+ .byte 29
+ .byte 28
+ .byte 27
+ .byte 26
+ .byte 25
+ .byte 24
+ .byte 23
+ .byte 22
+ .byte 21
+ .byte 20
+ .byte 19
+ .byte 18
+ .byte 17
+ .byte 16
+ .byte 15
+ .byte 14
+ .byte 13
+ .byte 12
+ .byte 11
+ .byte 10
+ .byte 9
+ .byte 8
+ .byte 7
+ .byte 6
+ .byte 5
+ .byte 4
+ .byte 3
+ .byte 2
+ .byte 1
+
+#endif /* } USE_ROLL_ASM */
diff --git a/simd-checksum-x86_64.cpp b/simd-checksum-x86_64.cpp
new file mode 100644
index 0000000..33f26e9
--- /dev/null
+++ b/simd-checksum-x86_64.cpp
@@ -0,0 +1,553 @@
+/*
+ * SSE2/SSSE3/AVX2-optimized routines to support checksumming of bytes.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2004-2020 Wayne Davison
+ * Copyright (C) 2020 Jorrit Jongma
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+/*
+ * Optimization target for get_checksum1() was the Intel Atom D2700, the
+ * slowest CPU in the test set and the most likely to be CPU limited during
+ * transfers. The combination of intrinsics was chosen specifically for the
+ * most gain on that CPU, other combinations were occasionally slightly
+ * faster on the others.
+ *
+ * While on more modern CPUs transfers are less likely to be CPU limited
+ * (at least by this specific function), lower CPU usage is always better.
+ * Improvements may still be seen when matching chunks from NVMe storage
+ * even on newer CPUs.
+ *
+ * Benchmarks (in MB/s) C SSE2 SSSE3 AVX2
+ * - Intel Atom D2700 550 750 1000 N/A
+ * - Intel i7-7700hq 1850 2550 4050 6200
+ * - AMD ThreadRipper 2950x 2900 5600 8950 8100
+ *
+ * Curiously the AMD is slower with AVX2 than SSSE3, while the Intel is
+ * significantly faster. AVX2 is kept because it's more likely to relieve
+ * the bottleneck on the slower CPU.
+ *
+ * This optimization for get_checksum1() is intentionally limited to x86-64
+ * as no 32-bit CPU was available for testing. As 32-bit CPUs only have half
+ * the available xmm registers, this optimized version may not be faster than
+ * the pure C version anyway. Note that all x86-64 CPUs support at least SSE2.
+ *
+ * This file is compiled using GCC 4.8+/clang 6+'s C++ front end to allow the
+ * use of the target attribute, selecting the fastest code path based on
+ * dispatch priority (GCC 5) or runtime detection of CPU capabilities (GCC 6+).
+ * GCC 4.x are not supported to ease configure.ac logic.
+ */
+
+#ifdef __x86_64__ /* { */
+#ifdef __cplusplus /* { */
+
+#include "rsync.h"
+
+#ifdef USE_ROLL_SIMD /* { */
+
+#include <immintrin.h>
+
+/* Some clang versions don't like it when you use static with multi-versioned functions: linker errors */
+#ifdef __clang__
+#define MVSTATIC
+#else
+#define MVSTATIC static
+#endif
+
+// Missing from the headers on gcc 6 and older, clang 8 and older
+typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(1)));
+typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(1)));
+
+/* Compatibility macros to let our SSSE3 algorithm run with only SSE2.
+ These used to be neat individual functions with target attributes switching between SSE2 and SSSE3 implementations
+ as needed, but though this works perfectly with GCC, clang fails to inline those properly leading to a near 50%
+ performance drop - combined with static and inline modifiers gets you linker errors and even compiler crashes...
+*/
+
+#define SSE2_INTERLEAVE_ODD_EPI16(a, b) _mm_packs_epi32(_mm_srai_epi32(a, 16), _mm_srai_epi32(b, 16))
+#define SSE2_INTERLEAVE_EVEN_EPI16(a, b) SSE2_INTERLEAVE_ODD_EPI16(_mm_slli_si128(a, 2), _mm_slli_si128(b, 2))
+#define SSE2_MULU_ODD_EPI8(a, b) _mm_mullo_epi16(_mm_srli_epi16(a, 8), _mm_srai_epi16(b, 8))
+#define SSE2_MULU_EVEN_EPI8(a, b) _mm_mullo_epi16(_mm_and_si128(a, _mm_set1_epi16(0xFF)), _mm_srai_epi16(_mm_slli_si128(b, 1), 8))
+
+#define SSE2_HADDS_EPI16(a, b) _mm_adds_epi16(SSE2_INTERLEAVE_EVEN_EPI16(a, b), SSE2_INTERLEAVE_ODD_EPI16(a, b))
+#define SSE2_MADDUBS_EPI16(a, b) _mm_adds_epi16(SSE2_MULU_EVEN_EPI8(a, b), SSE2_MULU_ODD_EPI8(a, b))
+
+#ifndef USE_ROLL_ASM
+__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
+#endif
+__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_ssse3_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
+__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
+
+/*
+ Original loop per 4 bytes:
+ s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + 10*CHAR_OFFSET;
+ s1 += buf[i] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET;
+
+ SSE2/SSSE3 loop per 32 bytes:
+ int16 t1[8];
+ int16 t2[8];
+ for (int j = 0; j < 8; j++) {
+ t1[j] = buf[j*4 + i] + buf[j*4 + i+1] + buf[j*4 + i+2] + buf[j*4 + i+3];
+ t2[j] = 4*buf[j*4 + i] + 3*buf[j*4 + i+1] + 2*buf[j*4 + i+2] + buf[j*4 + i+3];
+ }
+ s2 += 32*s1 + (uint32)(
+ 28*t1[0] + 24*t1[1] + 20*t1[2] + 16*t1[3] + 12*t1[4] + 8*t1[5] + 4*t1[6] +
+ t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7]
+ ) + 528*CHAR_OFFSET;
+ s1 += (uint32)(t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7]) +
+ 32*CHAR_OFFSET;
+ */
+__attribute__ ((target("ssse3"))) MVSTATIC int32 get_checksum1_ssse3_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
+{
+ if (len > 32) {
+ int aligned = ((uintptr_t)buf & 15) == 0;
+
+ uint32 x[4] = {0};
+ x[0] = *ps1;
+ __m128i ss1 = _mm_loadu_si128((__m128i_u*)x);
+ x[0] = *ps2;
+ __m128i ss2 = _mm_loadu_si128((__m128i_u*)x);
+
+ const int16 mul_t1_buf[8] = {28, 24, 20, 16, 12, 8, 4, 0};
+ __m128i mul_t1 = _mm_loadu_si128((__m128i_u*)mul_t1_buf);
+
+ for (; i < (len-32); i+=32) {
+ // Load ... 2*[int8*16]
+ __m128i in8_1, in8_2;
+ if (!aligned) {
+ // Synonymous with _mm_loadu_si128 on all but a handful of old CPUs
+ in8_1 = _mm_lddqu_si128((__m128i_u*)&buf[i]);
+ in8_2 = _mm_lddqu_si128((__m128i_u*)&buf[i + 16]);
+ } else {
+ in8_1 = _mm_load_si128((__m128i_u*)&buf[i]);
+ in8_2 = _mm_load_si128((__m128i_u*)&buf[i + 16]);
+ }
+
+ // (1*buf[i] + 1*buf[i+1]), (1*buf[i+2], 1*buf[i+3]), ... 2*[int16*8]
+ // Fastest, even though multiply by 1
+ __m128i mul_one = _mm_set1_epi8(1);
+ __m128i add16_1 = _mm_maddubs_epi16(mul_one, in8_1);
+ __m128i add16_2 = _mm_maddubs_epi16(mul_one, in8_2);
+
+ // (4*buf[i] + 3*buf[i+1]), (2*buf[i+2], buf[i+3]), ... 2*[int16*8]
+ __m128i mul_const = _mm_set1_epi32(4 + (3 << 8) + (2 << 16) + (1 << 24));
+ __m128i mul_add16_1 = _mm_maddubs_epi16(mul_const, in8_1);
+ __m128i mul_add16_2 = _mm_maddubs_epi16(mul_const, in8_2);
+
+ // s2 += 32*s1
+ ss2 = _mm_add_epi32(ss2, _mm_slli_epi32(ss1, 5));
+
+ // [sum(t1[0]..t1[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ // Shifting left, then shifting right again and shuffling (rather than just
+ // shifting right as with mul32 below) to cheaply end up with the correct sign
+ // extension as we go from int16 to int32.
+ __m128i sum_add32 = _mm_add_epi16(add16_1, add16_2);
+ sum_add32 = _mm_add_epi16(sum_add32, _mm_slli_si128(sum_add32, 2));
+ sum_add32 = _mm_add_epi16(sum_add32, _mm_slli_si128(sum_add32, 4));
+ sum_add32 = _mm_add_epi16(sum_add32, _mm_slli_si128(sum_add32, 8));
+ sum_add32 = _mm_srai_epi32(sum_add32, 16);
+ sum_add32 = _mm_shuffle_epi32(sum_add32, 3);
+
+ // [sum(t2[0]..t2[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ __m128i sum_mul_add32 = _mm_add_epi16(mul_add16_1, mul_add16_2);
+ sum_mul_add32 = _mm_add_epi16(sum_mul_add32, _mm_slli_si128(sum_mul_add32, 2));
+ sum_mul_add32 = _mm_add_epi16(sum_mul_add32, _mm_slli_si128(sum_mul_add32, 4));
+ sum_mul_add32 = _mm_add_epi16(sum_mul_add32, _mm_slli_si128(sum_mul_add32, 8));
+ sum_mul_add32 = _mm_srai_epi32(sum_mul_add32, 16);
+ sum_mul_add32 = _mm_shuffle_epi32(sum_mul_add32, 3);
+
+ // s1 += t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7]
+ ss1 = _mm_add_epi32(ss1, sum_add32);
+
+ // s2 += t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7]
+ ss2 = _mm_add_epi32(ss2, sum_mul_add32);
+
+ // [t1[0] + t1[1], t1[2] + t1[3] ...] [int16*8]
+ // We could've combined this with generating sum_add32 above and
+ // save an instruction but benchmarking shows that as being slower
+ __m128i add16 = _mm_hadds_epi16(add16_1, add16_2);
+
+ // [t1[0], t1[1], ...] -> [t1[0]*28 + t1[1]*24, ...] [int32*4]
+ __m128i mul32 = _mm_madd_epi16(add16, mul_t1);
+
+ // [sum(mul32), X, X, X] [int32*4]; faster than multiple _mm_hadd_epi32
+ mul32 = _mm_add_epi32(mul32, _mm_srli_si128(mul32, 4));
+ mul32 = _mm_add_epi32(mul32, _mm_srli_si128(mul32, 8));
+
+ // s2 += 28*t1[0] + 24*t1[1] + 20*t1[2] + 16*t1[3] + 12*t1[4] + 8*t1[5] + 4*t1[6]
+ ss2 = _mm_add_epi32(ss2, mul32);
+
+#if CHAR_OFFSET != 0
+ // s1 += 32*CHAR_OFFSET
+ __m128i char_offset_multiplier = _mm_set1_epi32(32 * CHAR_OFFSET);
+ ss1 = _mm_add_epi32(ss1, char_offset_multiplier);
+
+ // s2 += 528*CHAR_OFFSET
+ char_offset_multiplier = _mm_set1_epi32(528 * CHAR_OFFSET);
+ ss2 = _mm_add_epi32(ss2, char_offset_multiplier);
+#endif
+ }
+
+ _mm_store_si128((__m128i_u*)x, ss1);
+ *ps1 = x[0];
+ _mm_store_si128((__m128i_u*)x, ss2);
+ *ps2 = x[0];
+ }
+ return i;
+}
+
+/*
+ Same as SSSE3 version, but using macros defined above to emulate SSSE3 calls that are not available with SSE2.
+ For GCC-only the SSE2 and SSSE3 versions could be a single function calling other functions with the right
+ target attributes to emulate SSSE3 calls on SSE2 if needed, but clang doesn't inline those properly leading
+ to a near 50% performance drop.
+ */
+__attribute__ ((target("sse2"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
+{
+ if (len > 32) {
+ int aligned = ((uintptr_t)buf & 15) == 0;
+
+ uint32 x[4] = {0};
+ x[0] = *ps1;
+ __m128i ss1 = _mm_loadu_si128((__m128i_u*)x);
+ x[0] = *ps2;
+ __m128i ss2 = _mm_loadu_si128((__m128i_u*)x);
+
+ const int16 mul_t1_buf[8] = {28, 24, 20, 16, 12, 8, 4, 0};
+ __m128i mul_t1 = _mm_loadu_si128((__m128i_u*)mul_t1_buf);
+
+ for (; i < (len-32); i+=32) {
+ // Load ... 2*[int8*16]
+ __m128i in8_1, in8_2;
+ if (!aligned) {
+ in8_1 = _mm_loadu_si128((__m128i_u*)&buf[i]);
+ in8_2 = _mm_loadu_si128((__m128i_u*)&buf[i + 16]);
+ } else {
+ in8_1 = _mm_load_si128((__m128i_u*)&buf[i]);
+ in8_2 = _mm_load_si128((__m128i_u*)&buf[i + 16]);
+ }
+
+ // (1*buf[i] + 1*buf[i+1]), (1*buf[i+2], 1*buf[i+3]), ... 2*[int16*8]
+ // Fastest, even though multiply by 1
+ __m128i mul_one = _mm_set1_epi8(1);
+ __m128i add16_1 = SSE2_MADDUBS_EPI16(mul_one, in8_1);
+ __m128i add16_2 = SSE2_MADDUBS_EPI16(mul_one, in8_2);
+
+ // (4*buf[i] + 3*buf[i+1]), (2*buf[i+2], buf[i+3]), ... 2*[int16*8]
+ __m128i mul_const = _mm_set1_epi32(4 + (3 << 8) + (2 << 16) + (1 << 24));
+ __m128i mul_add16_1 = SSE2_MADDUBS_EPI16(mul_const, in8_1);
+ __m128i mul_add16_2 = SSE2_MADDUBS_EPI16(mul_const, in8_2);
+
+ // s2 += 32*s1
+ ss2 = _mm_add_epi32(ss2, _mm_slli_epi32(ss1, 5));
+
+ // [sum(t1[0]..t1[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ // Shifting left, then shifting right again and shuffling (rather than just
+ // shifting right as with mul32 below) to cheaply end up with the correct sign
+ // extension as we go from int16 to int32.
+ __m128i sum_add32 = _mm_add_epi16(add16_1, add16_2);
+ sum_add32 = _mm_add_epi16(sum_add32, _mm_slli_si128(sum_add32, 2));
+ sum_add32 = _mm_add_epi16(sum_add32, _mm_slli_si128(sum_add32, 4));
+ sum_add32 = _mm_add_epi16(sum_add32, _mm_slli_si128(sum_add32, 8));
+ sum_add32 = _mm_srai_epi32(sum_add32, 16);
+ sum_add32 = _mm_shuffle_epi32(sum_add32, 3);
+
+ // [sum(t2[0]..t2[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ __m128i sum_mul_add32 = _mm_add_epi16(mul_add16_1, mul_add16_2);
+ sum_mul_add32 = _mm_add_epi16(sum_mul_add32, _mm_slli_si128(sum_mul_add32, 2));
+ sum_mul_add32 = _mm_add_epi16(sum_mul_add32, _mm_slli_si128(sum_mul_add32, 4));
+ sum_mul_add32 = _mm_add_epi16(sum_mul_add32, _mm_slli_si128(sum_mul_add32, 8));
+ sum_mul_add32 = _mm_srai_epi32(sum_mul_add32, 16);
+ sum_mul_add32 = _mm_shuffle_epi32(sum_mul_add32, 3);
+
+ // s1 += t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7]
+ ss1 = _mm_add_epi32(ss1, sum_add32);
+
+ // s2 += t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7]
+ ss2 = _mm_add_epi32(ss2, sum_mul_add32);
+
+ // [t1[0] + t1[1], t1[2] + t1[3] ...] [int16*8]
+ // We could've combined this with generating sum_add32 above and
+ // save an instruction but benchmarking shows that as being slower
+ __m128i add16 = SSE2_HADDS_EPI16(add16_1, add16_2);
+
+ // [t1[0], t1[1], ...] -> [t1[0]*28 + t1[1]*24, ...] [int32*4]
+ __m128i mul32 = _mm_madd_epi16(add16, mul_t1);
+
+ // [sum(mul32), X, X, X] [int32*4]; faster than multiple _mm_hadd_epi32
+ mul32 = _mm_add_epi32(mul32, _mm_srli_si128(mul32, 4));
+ mul32 = _mm_add_epi32(mul32, _mm_srli_si128(mul32, 8));
+
+ // s2 += 28*t1[0] + 24*t1[1] + 20*t1[2] + 16*t1[3] + 12*t1[4] + 8*t1[5] + 4*t1[6]
+ ss2 = _mm_add_epi32(ss2, mul32);
+
+#if CHAR_OFFSET != 0
+ // s1 += 32*CHAR_OFFSET
+ __m128i char_offset_multiplier = _mm_set1_epi32(32 * CHAR_OFFSET);
+ ss1 = _mm_add_epi32(ss1, char_offset_multiplier);
+
+ // s2 += 528*CHAR_OFFSET
+ char_offset_multiplier = _mm_set1_epi32(528 * CHAR_OFFSET);
+ ss2 = _mm_add_epi32(ss2, char_offset_multiplier);
+#endif
+ }
+
+ _mm_store_si128((__m128i_u*)x, ss1);
+ *ps1 = x[0];
+ _mm_store_si128((__m128i_u*)x, ss2);
+ *ps2 = x[0];
+ }
+ return i;
+}
+
+#ifdef USE_ROLL_ASM /* { */
+
+extern "C" __attribute__ ((target("avx2"))) int32 get_checksum1_avx2_asm(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2);
+
+#else /* } { */
+
+/*
+ AVX2 loop per 64 bytes:
+ int16 t1[16];
+ int16 t2[16];
+ for (int j = 0; j < 16; j++) {
+ t1[j] = buf[j*4 + i] + buf[j*4 + i+1] + buf[j*4 + i+2] + buf[j*4 + i+3];
+ t2[j] = 4*buf[j*4 + i] + 3*buf[j*4 + i+1] + 2*buf[j*4 + i+2] + buf[j*4 + i+3];
+ }
+ s2 += 64*s1 + (uint32)(
+ 60*t1[0] + 56*t1[1] + 52*t1[2] + 48*t1[3] + 44*t1[4] + 40*t1[5] + 36*t1[6] + 32*t1[7] + 28*t1[8] + 24*t1[9] + 20*t1[10] + 16*t1[11] + 12*t1[12] + 8*t1[13] + 4*t1[14] +
+ t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7] + t2[8] + t2[9] + t2[10] + t2[11] + t2[12] + t2[13] + t2[14] + t2[15]
+ ) + 2080*CHAR_OFFSET;
+ s1 += (uint32)(t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7] + t1[8] + t1[9] + t1[10] + t1[11] + t1[12] + t1[13] + t1[14] + t1[15]) +
+ 64*CHAR_OFFSET;
+ */
+
+__attribute__ ((target("avx2"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
+{
+ if (len > 64) {
+
+ uint32 x[4] = {0};
+ __m128i ss1 = _mm_cvtsi32_si128(*ps1);
+ __m128i ss2 = _mm_cvtsi32_si128(*ps2);
+
+ const char mul_t1_buf[16] = {60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0};
+ __m128i tmp = _mm_load_si128((__m128i*) mul_t1_buf);
+ __m256i mul_t1 = _mm256_cvtepu8_epi16(tmp);
+ __m256i mul_const = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(4 | (3 << 8) | (2 << 16) | (1 << 24)));
+ __m256i mul_one;
+ mul_one = _mm256_abs_epi8(_mm256_cmpeq_epi16(mul_one,mul_one)); // set all vector elements to 1
+
+ for (; i < (len-64); i+=64) {
+ // Load ... 4*[int8*16]
+ __m256i in8_1, in8_2;
+ __m128i in8_1_low, in8_2_low, in8_1_high, in8_2_high;
+ in8_1_low = _mm_loadu_si128((__m128i_u*)&buf[i]);
+ in8_2_low = _mm_loadu_si128((__m128i_u*)&buf[i+16]);
+ in8_1_high = _mm_loadu_si128((__m128i_u*)&buf[i+32]);
+ in8_2_high = _mm_loadu_si128((__m128i_u*)&buf[i+48]);
+ in8_1 = _mm256_inserti128_si256(_mm256_castsi128_si256(in8_1_low), in8_1_high,1);
+ in8_2 = _mm256_inserti128_si256(_mm256_castsi128_si256(in8_2_low), in8_2_high,1);
+
+ // (1*buf[i] + 1*buf[i+1]), (1*buf[i+2], 1*buf[i+3]), ... 2*[int16*8]
+ // Fastest, even though multiply by 1
+ __m256i add16_1 = _mm256_maddubs_epi16(mul_one, in8_1);
+ __m256i add16_2 = _mm256_maddubs_epi16(mul_one, in8_2);
+
+ // (4*buf[i] + 3*buf[i+1]), (2*buf[i+2], buf[i+3]), ... 2*[int16*8]
+ __m256i mul_add16_1 = _mm256_maddubs_epi16(mul_const, in8_1);
+ __m256i mul_add16_2 = _mm256_maddubs_epi16(mul_const, in8_2);
+
+ // s2 += 64*s1
+ ss2 = _mm_add_epi32(ss2, _mm_slli_epi32(ss1, 6));
+
+ // [sum(t1[0]..t1[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ __m256i sum_add32 = _mm256_add_epi16(add16_1, add16_2);
+ sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_epi32(sum_add32, 16));
+ sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_si256(sum_add32, 4));
+ sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_si256(sum_add32, 8));
+
+ // [sum(t2[0]..t2[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
+ __m256i sum_mul_add32 = _mm256_add_epi16(mul_add16_1, mul_add16_2);
+ sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_epi32(sum_mul_add32, 16));
+ sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_si256(sum_mul_add32, 4));
+ sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_si256(sum_mul_add32, 8));
+
+ // s1 += t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7]
+ __m128i sum_add32_hi = _mm256_extracti128_si256(sum_add32, 0x1);
+ ss1 = _mm_add_epi32(ss1, _mm256_castsi256_si128(sum_add32));
+ ss1 = _mm_add_epi32(ss1, sum_add32_hi);
+
+ // s2 += t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7]
+ __m128i sum_mul_add32_hi = _mm256_extracti128_si256(sum_mul_add32, 0x1);
+ ss2 = _mm_add_epi32(ss2, _mm256_castsi256_si128(sum_mul_add32));
+ ss2 = _mm_add_epi32(ss2, sum_mul_add32_hi);
+
+ // [t1[0] + t1[1], t1[2] + t1[3] ...] [int16*8]
+ // We could've combined this with generating sum_add32 above and
+ // save an instruction but benchmarking shows that as being slower
+ __m256i add16 = _mm256_hadds_epi16(add16_1, add16_2);
+
+ // [t1[0], t1[1], ...] -> [t1[0]*28 + t1[1]*24, ...] [int32*4]
+ __m256i mul32 = _mm256_madd_epi16(add16, mul_t1);
+
+ // [sum(mul32), X, X, X] [int32*4]; faster than multiple _mm_hadd_epi32
+ mul32 = _mm256_add_epi32(mul32, _mm256_srli_si256(mul32, 4));
+ mul32 = _mm256_add_epi32(mul32, _mm256_srli_si256(mul32, 8));
+ // prefetch 2 cacheline ahead
+ _mm_prefetch(&buf[i + 160], _MM_HINT_T0);
+
+ // s2 += 28*t1[0] + 24*t1[1] + 20*t1[2] + 16*t1[3] + 12*t1[4] + 8*t1[5] + 4*t1[6]
+ __m128i mul32_hi = _mm256_extracti128_si256(mul32, 0x1);
+ ss2 = _mm_add_epi32(ss2, _mm256_castsi256_si128(mul32));
+ ss2 = _mm_add_epi32(ss2, mul32_hi);
+
+#if CHAR_OFFSET != 0
+ // s1 += 32*CHAR_OFFSET
+ __m128i char_offset_multiplier = _mm_set1_epi32(32 * CHAR_OFFSET);
+ ss1 = _mm_add_epi32(ss1, char_offset_multiplier);
+
+ // s2 += 528*CHAR_OFFSET
+ char_offset_multiplier = _mm_set1_epi32(528 * CHAR_OFFSET);
+ ss2 = _mm_add_epi32(ss2, char_offset_multiplier);
+#endif
+ }
+
+ _mm_store_si128((__m128i_u*)x, ss1);
+ *ps1 = x[0];
+ _mm_store_si128((__m128i_u*)x, ss2);
+ *ps2 = x[0];
+ }
+ return i;
+}
+
+#endif /* } !USE_ROLL_ASM */
+
+static int32 get_checksum1_default_1(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
+{
+ uint32 s1 = *ps1;
+ uint32 s2 = *ps2;
+ for (; i < (len-4); i+=4) {
+ s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + 10*CHAR_OFFSET;
+ s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
+ }
+ for (; i < len; i++) {
+ s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
+ }
+ *ps1 = s1;
+ *ps2 = s2;
+ return i;
+}
+
+/* With GCC 10 putting this implementation inside 'extern "C"' causes an
+ assembler error. That worked fine on GCC 5-9 and clang 6-10...
+ */
+static inline uint32 get_checksum1_cpp(char *buf1, int32 len)
+{
+ int32 i = 0;
+ uint32 s1 = 0;
+ uint32 s2 = 0;
+
+ // multiples of 64 bytes using AVX2 (if available)
+#ifdef USE_ROLL_ASM
+ i = get_checksum1_avx2_asm((schar*)buf1, len, i, &s1, &s2);
+#else
+ i = get_checksum1_avx2_64((schar*)buf1, len, i, &s1, &s2);
+#endif
+
+ // multiples of 32 bytes using SSSE3 (if available)
+ i = get_checksum1_ssse3_32((schar*)buf1, len, i, &s1, &s2);
+
+ // multiples of 32 bytes using SSE2 (if available)
+ i = get_checksum1_sse2_32((schar*)buf1, len, i, &s1, &s2);
+
+ // whatever is left
+ i = get_checksum1_default_1((schar*)buf1, len, i, &s1, &s2);
+
+ return (s1 & 0xffff) + (s2 << 16);
+}
+
+extern "C" {
+
+uint32 get_checksum1(char *buf1, int32 len)
+{
+ return get_checksum1_cpp(buf1, len);
+}
+
+} // extern "C"
+
+#ifdef BENCHMARK_SIMD_CHECKSUM1
+#pragma clang optimize off
+#pragma GCC push_options
+#pragma GCC optimize ("O0")
+
+#define ROUNDS 1024
+#define BLOCK_LEN 1024*1024
+
+#ifndef CLOCK_MONOTONIC_RAW
+#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
+#endif
+
+static void benchmark(const char* desc, int32 (*func)(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2), schar* buf, int32 len) {
+ struct timespec start, end;
+ uint64_t us;
+ uint32_t cs, s1, s2;
+ int i, next;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+ for (i = 0; i < ROUNDS; i++) {
+ s1 = s2 = 0;
+ next = func((schar*)buf, len, 0, &s1, &s2);
+ get_checksum1_default_1((schar*)buf, len, next, &s1, &s2);
+ }
+ clock_gettime(CLOCK_MONOTONIC_RAW, &end);
+ us = next == 0 ? 0 : (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
+ cs = next == 0 ? 0 : (s1 & 0xffff) + (s2 << 16);
+ printf("%-5s :: %5.0f MB/s :: %08x\n", desc, us ? (float)(len / (1024 * 1024) * ROUNDS) / ((float)us / 1000000.0f) : 0, cs);
+}
+
+static int32 get_checksum1_auto(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) {
+ uint32 cs = get_checksum1((char*)buf, len);
+ *ps1 = cs & 0xffff;
+ *ps2 = cs >> 16;
+ return len;
+}
+
+int main() {
+ int i;
+ unsigned char* buf = (unsigned char*)aligned_alloc(64,BLOCK_LEN);
+ for (i = 0; i < BLOCK_LEN; i++) buf[i] = (i + (i % 3) + (i % 11)) % 256;
+
+ benchmark("Auto", get_checksum1_auto, (schar*)buf, BLOCK_LEN);
+ benchmark("Raw-C", get_checksum1_default_1, (schar*)buf, BLOCK_LEN);
+ benchmark("SSE2", get_checksum1_sse2_32, (schar*)buf, BLOCK_LEN);
+ benchmark("SSSE3", get_checksum1_ssse3_32, (schar*)buf, BLOCK_LEN);
+#ifdef USE_ROLL_ASM
+ benchmark("AVX2-ASM", get_checksum1_avx2_asm, (schar*)buf, BLOCK_LEN);
+#else
+ benchmark("AVX2", get_checksum1_avx2_64, (schar*)buf, BLOCK_LEN);
+#endif
+
+ free(buf);
+ return 0;
+}
+
+#pragma GCC pop_options
+#pragma clang optimize on
+#endif /* BENCHMARK_SIMD_CHECKSUM1 */
+
+#endif /* } USE_ROLL_SIMD */
+#endif /* } __cplusplus */
+#endif /* } __x86_64__ */
diff --git a/socket.c b/socket.c
new file mode 100644
index 0000000..c2075ad
--- /dev/null
+++ b/socket.c
@@ -0,0 +1,841 @@
+/*
+ * Socket functions used in rsync.
+ *
+ * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/* This file is now converted to use the new-style getaddrinfo()
+ * interface, which supports IPv6 but is also supported on recent
+ * IPv4-only machines. On systems that don't have that interface, we
+ * emulate it using the KAME implementation. */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "ifuncs.h"
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#include <netinet/tcp.h>
+
+extern char *bind_address;
+extern char *sockopts;
+extern int default_af_hint;
+extern int connect_timeout;
+extern int pid_file_fd;
+
+#ifdef HAVE_SIGACTION
+static struct sigaction sigact;
+#endif
+
+static int sock_exec(const char *prog);
+
+/* Establish a proxy connection on an open socket to a web proxy by using the
+ * CONNECT method. If proxy_user and proxy_pass are not NULL, they are used to
+ * authenticate to the proxy using the "Basic" proxy-authorization protocol. */
+static int establish_proxy_connection(int fd, char *host, int port, char *proxy_user, char *proxy_pass)
+{
+ char *cp, buffer[1024];
+ char *authhdr, authbuf[1024];
+ int len;
+
+ if (proxy_user && proxy_pass) {
+ stringjoin(buffer, sizeof buffer,
+ proxy_user, ":", proxy_pass, NULL);
+ len = strlen(buffer);
+
+ if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
+ rprintf(FERROR,
+ "authentication information is too long\n");
+ return -1;
+ }
+
+ base64_encode(buffer, len, authbuf, 1);
+ authhdr = "\r\nProxy-Authorization: Basic ";
+ } else {
+ *authbuf = '\0';
+ authhdr = "";
+ }
+
+ len = snprintf(buffer, sizeof buffer, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
+ assert(len > 0 && len < (int)sizeof buffer);
+ if (write(fd, buffer, len) != len) {
+ rsyserr(FERROR, errno, "failed to write to proxy");
+ return -1;
+ }
+
+ for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
+ if (read(fd, cp, 1) != 1) {
+ rsyserr(FERROR, errno, "failed to read from proxy");
+ return -1;
+ }
+ if (*cp == '\n')
+ break;
+ }
+
+ if (*cp != '\n')
+ cp++;
+ *cp-- = '\0';
+ if (*cp == '\r')
+ *cp = '\0';
+ if (strncmp(buffer, "HTTP/", 5) != 0) {
+ rprintf(FERROR, "bad response from proxy -- %s\n",
+ buffer);
+ return -1;
+ }
+ for (cp = &buffer[5]; isDigit(cp) || *cp == '.'; cp++) {}
+ while (*cp == ' ')
+ cp++;
+ if (*cp != '2') {
+ rprintf(FERROR, "bad response from proxy -- %s\n",
+ buffer);
+ return -1;
+ }
+ /* throw away the rest of the HTTP header */
+ while (1) {
+ for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
+ if (read(fd, cp, 1) != 1) {
+ rsyserr(FERROR, errno,
+ "failed to read from proxy");
+ return -1;
+ }
+ if (*cp == '\n')
+ break;
+ }
+ if (cp > buffer && *cp == '\n')
+ cp--;
+ if (cp == buffer && (*cp == '\n' || *cp == '\r'))
+ break;
+ }
+ return 0;
+}
+
+
+/* Try to set the local address for a newly-created socket.
+ * Return -1 if this fails. */
+int try_bind_local(int s, int ai_family, int ai_socktype,
+ const char *bind_addr)
+{
+ int error;
+ struct addrinfo bhints, *bres_all, *r;
+
+ memset(&bhints, 0, sizeof bhints);
+ bhints.ai_family = ai_family;
+ bhints.ai_socktype = ai_socktype;
+ bhints.ai_flags = AI_PASSIVE;
+ if ((error = getaddrinfo(bind_addr, NULL, &bhints, &bres_all))) {
+ rprintf(FERROR, RSYNC_NAME ": getaddrinfo %s: %s\n",
+ bind_addr, gai_strerror(error));
+ return -1;
+ }
+
+ for (r = bres_all; r; r = r->ai_next) {
+ if (bind(s, r->ai_addr, r->ai_addrlen) == -1)
+ continue;
+ freeaddrinfo(bres_all);
+ return s;
+ }
+
+ /* no error message; there might be some problem that allows
+ * creation of the socket but not binding, perhaps if the
+ * machine has no ipv6 address of this name. */
+ freeaddrinfo(bres_all);
+ return -1;
+}
+
+/* connect() timeout handler based on alarm() */
+static void contimeout_handler(UNUSED(int val))
+{
+ connect_timeout = -1;
+}
+
+/* Open a socket to a tcp remote host with the specified port.
+ *
+ * Based on code from Warren. Proxy support by Stephen Rothwell.
+ * getaddrinfo() rewrite contributed by KAME.net.
+ *
+ * Now that we support IPv6 we need to look up the remote machine's address
+ * first, using af_hint to set a preference for the type of address. Then
+ * depending on whether it has v4 or v6 addresses we try to open a connection.
+ *
+ * The loop allows for machines with some addresses which may not be reachable,
+ * perhaps because we can't e.g. route ipv6 to that network but we can get ip4
+ * packets through.
+ *
+ * bind_addr: local address to use. Normally NULL to bind the wildcard address.
+ *
+ * af_hint: address family, e.g. AF_INET or AF_INET6. */
+int open_socket_out(char *host, int port, const char *bind_addr, int af_hint)
+{
+ int type = SOCK_STREAM;
+ int error, s, j, addr_cnt, *errnos;
+ struct addrinfo hints, *res0, *res;
+ char portbuf[10];
+ char *h, *cp;
+ int proxied = 0;
+ char buffer[1024];
+ char *proxy_user = NULL, *proxy_pass = NULL;
+
+ /* if we have a RSYNC_PROXY env variable then redirect our
+ * connection via a web proxy at the given address. */
+ h = getenv("RSYNC_PROXY");
+ proxied = h != NULL && *h != '\0';
+
+ if (proxied) {
+ strlcpy(buffer, h, sizeof buffer);
+
+ /* Is the USER:PASS@ prefix present? */
+ if ((cp = strrchr(buffer, '@')) != NULL) {
+ *cp++ = '\0';
+ /* The remainder is the HOST:PORT part. */
+ h = cp;
+
+ if ((cp = strchr(buffer, ':')) == NULL) {
+ rprintf(FERROR,
+ "invalid proxy specification: should be USER:PASS@HOST:PORT\n");
+ return -1;
+ }
+ *cp++ = '\0';
+
+ proxy_user = buffer;
+ proxy_pass = cp;
+ } else {
+ /* The whole buffer is the HOST:PORT part. */
+ h = buffer;
+ }
+
+ if ((cp = strchr(h, ':')) == NULL) {
+ rprintf(FERROR,
+ "invalid proxy specification: should be HOST:PORT\n");
+ return -1;
+ }
+ *cp++ = '\0';
+ strlcpy(portbuf, cp, sizeof portbuf);
+ if (DEBUG_GTE(CONNECT, 1)) {
+ rprintf(FINFO, "connection via http proxy %s port %s\n",
+ h, portbuf);
+ }
+ } else {
+ snprintf(portbuf, sizeof portbuf, "%d", port);
+ h = host;
+ }
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = af_hint;
+ hints.ai_socktype = type;
+ error = getaddrinfo(h, portbuf, &hints, &res0);
+ if (error) {
+ rprintf(FERROR, RSYNC_NAME ": getaddrinfo: %s %s: %s\n",
+ h, portbuf, gai_strerror(error));
+ return -1;
+ }
+
+ for (res = res0, addr_cnt = 0; res; res = res->ai_next, addr_cnt++) {}
+ errnos = new_array0(int, addr_cnt);
+
+ s = -1;
+ /* Try to connect to all addresses for this machine until we get
+ * through. It might e.g. be multi-homed, or have both IPv4 and IPv6
+ * addresses. We need to create a socket for each record, since the
+ * address record tells us what protocol to use to try to connect. */
+ for (res = res0, j = 0; res; res = res->ai_next, j++) {
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s < 0)
+ continue;
+
+ if (bind_addr
+ && try_bind_local(s, res->ai_family, type,
+ bind_addr) == -1) {
+ close(s);
+ s = -1;
+ continue;
+ }
+ if (connect_timeout > 0) {
+ SIGACTION(SIGALRM, contimeout_handler);
+ alarm(connect_timeout);
+ }
+
+ set_socket_options(s, sockopts);
+ while (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
+ if (connect_timeout < 0)
+ exit_cleanup(RERR_CONTIMEOUT);
+ if (errno == EINTR)
+ continue;
+ close(s);
+ s = -1;
+ break;
+ }
+
+ if (connect_timeout > 0)
+ alarm(0);
+
+ if (s < 0) {
+ errnos[j] = errno;
+ continue;
+ }
+
+ if (proxied && establish_proxy_connection(s, host, port, proxy_user, proxy_pass) != 0) {
+ close(s);
+ s = -1;
+ continue;
+ }
+ if (DEBUG_GTE(CONNECT, 2)) {
+ char buf[2048];
+ if ((error = getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof buf, NULL, 0, NI_NUMERICHOST)) != 0)
+ snprintf(buf, sizeof buf, "*getnameinfo failure: %s*", gai_strerror(error));
+ rprintf(FINFO, "Connected to %s (%s)\n", h, buf);
+ }
+ break;
+ }
+
+ if (s < 0 || DEBUG_GTE(CONNECT, 2)) {
+ char buf[2048];
+ for (res = res0, j = 0; res; res = res->ai_next, j++) {
+ if (errnos[j] == 0)
+ continue;
+ if ((error = getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof buf, NULL, 0, NI_NUMERICHOST)) != 0)
+ snprintf(buf, sizeof buf, "*getnameinfo failure: %s*", gai_strerror(error));
+ rsyserr(FERROR, errnos[j], "failed to connect to %s (%s)", h, buf);
+ }
+ if (s < 0)
+ s = -1;
+ }
+
+ freeaddrinfo(res0);
+ free(errnos);
+
+ return s;
+}
+
+
+/* Open an outgoing socket, but allow for it to be intercepted by
+ * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
+ * socketpair rather than really opening a socket.
+ *
+ * We use this primarily in testing to detect TCP flow bugs, but not
+ * cause security problems by really opening remote connections.
+ *
+ * This is based on the Samba LIBSMB_PROG feature.
+ *
+ * bind_addr: local address to use. Normally NULL to get the stack default. */
+int open_socket_out_wrapped(char *host, int port, const char *bind_addr, int af_hint)
+{
+ char *prog = getenv("RSYNC_CONNECT_PROG");
+
+ if (prog && strchr(prog, '%')) {
+ int hlen = strlen(host);
+ int len = strlen(prog) + 1;
+ char *f, *t;
+ for (f = prog; *f; f++) {
+ if (*f != '%')
+ continue;
+ /* Compute more than enough room. */
+ if (f[1] == '%')
+ f++;
+ else
+ len += hlen;
+ }
+ f = prog;
+ prog = new_array(char, len);
+ for (t = prog; *f; f++) {
+ if (*f == '%') {
+ switch (*++f) {
+ case '%':
+ /* Just skips the extra '%'. */
+ break;
+ case 'H':
+ memcpy(t, host, hlen);
+ t += hlen;
+ continue;
+ default:
+ f--; /* pass % through */
+ break;
+ }
+ }
+ *t++ = *f;
+ }
+ *t = '\0';
+ }
+
+ if (DEBUG_GTE(CONNECT, 1)) {
+ rprintf(FINFO, "%sopening tcp connection to %s port %d\n",
+ prog ? "Using RSYNC_CONNECT_PROG instead of " : "",
+ host, port);
+ }
+ if (prog)
+ return sock_exec(prog);
+ return open_socket_out(host, port, bind_addr, af_hint);
+}
+
+
+/* Open one or more sockets for incoming data using the specified type,
+ * port, and address.
+ *
+ * The getaddrinfo() call may return several address results, e.g. for
+ * the machine's IPv4 and IPv6 name.
+ *
+ * We return an array of file-descriptors to the sockets, with a trailing
+ * -1 value to indicate the end of the list.
+ *
+ * bind_addr: local address to bind, or NULL to allow it to default. */
+static int *open_socket_in(int type, int port, const char *bind_addr,
+ int af_hint)
+{
+ int one = 1;
+ int s, *socks, maxs, i, ecnt;
+ struct addrinfo hints, *all_ai, *resp;
+ char portbuf[10], **errmsgs;
+ int error;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = af_hint;
+ hints.ai_socktype = type;
+ hints.ai_flags = AI_PASSIVE;
+ snprintf(portbuf, sizeof portbuf, "%d", port);
+ error = getaddrinfo(bind_addr, portbuf, &hints, &all_ai);
+ if (error) {
+ rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
+ bind_addr, gai_strerror(error));
+ return NULL;
+ }
+
+ /* Count max number of sockets we might open. */
+ for (maxs = 0, resp = all_ai; resp; resp = resp->ai_next, maxs++) {}
+
+ socks = new_array(int, maxs + 1);
+ errmsgs = new_array(char *, maxs);
+
+ /* We may not be able to create the socket, if for example the
+ * machine knows about IPv6 in the C library, but not in the
+ * kernel. */
+ for (resp = all_ai, i = ecnt = 0; resp; resp = resp->ai_next) {
+ s = socket(resp->ai_family, resp->ai_socktype,
+ resp->ai_protocol);
+
+ if (s == -1) {
+ int r = asprintf(&errmsgs[ecnt++],
+ "socket(%d,%d,%d) failed: %s\n",
+ (int)resp->ai_family, (int)resp->ai_socktype,
+ (int)resp->ai_protocol, strerror(errno));
+ if (r < 0)
+ out_of_memory("open_socket_in");
+ /* See if there's another address that will work... */
+ continue;
+ }
+
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&one, sizeof one);
+ if (sockopts)
+ set_socket_options(s, sockopts);
+ else
+ set_socket_options(s, lp_socket_options());
+
+#ifdef IPV6_V6ONLY
+ if (resp->ai_family == AF_INET6) {
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof one) < 0
+ && default_af_hint != AF_INET6) {
+ close(s);
+ continue;
+ }
+ }
+#endif
+
+ /* Now we've got a socket - we need to bind it. */
+ if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) {
+ /* Nope, try another */
+ int r = asprintf(&errmsgs[ecnt++],
+ "bind() failed: %s (address-family %d)\n",
+ strerror(errno), (int)resp->ai_family);
+ if (r < 0)
+ out_of_memory("open_socket_in");
+ close(s);
+ continue;
+ }
+
+ socks[i++] = s;
+ }
+ socks[i] = -1;
+
+ if (all_ai)
+ freeaddrinfo(all_ai);
+
+ /* Only output the socket()/bind() messages if we were totally
+ * unsuccessful, or if the daemon is being run with -vv. */
+ for (s = 0; s < ecnt; s++) {
+ if (!i || DEBUG_GTE(BIND, 1))
+ rwrite(FLOG, errmsgs[s], strlen(errmsgs[s]), 0);
+ free(errmsgs[s]);
+ }
+ free(errmsgs);
+
+ if (!i) {
+ rprintf(FERROR,
+ "unable to bind any inbound sockets on port %d\n",
+ port);
+ free(socks);
+ return NULL;
+ }
+ return socks;
+}
+
+
+/* Determine if a file descriptor is in fact a socket. */
+int is_a_socket(int fd)
+{
+ int v;
+ socklen_t l = sizeof (int);
+
+ /* Parameters to getsockopt, setsockopt etc are very
+ * unstandardized across platforms, so don't be surprised if
+ * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
+ * It seems they all eventually get the right idea.
+ *
+ * Debian says: ``The fifth argument of getsockopt and
+ * setsockopt is in reality an int [*] (and this is what BSD
+ * 4.* and libc4 and libc5 have). Some POSIX confusion
+ * resulted in the present socklen_t. The draft standard has
+ * not been adopted yet, but glibc2 already follows it and
+ * also has socklen_t [*]. See also accept(2).''
+ *
+ * We now return to your regularly scheduled programming. */
+ return getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0;
+}
+
+
+static void sigchld_handler(UNUSED(int val))
+{
+#ifdef WNOHANG
+ while (waitpid(-1, NULL, WNOHANG) > 0) {}
+#endif
+#ifndef HAVE_SIGACTION
+ signal(SIGCHLD, sigchld_handler);
+#endif
+}
+
+
+void start_accept_loop(int port, int (*fn)(int, int))
+{
+ fd_set deffds;
+ int *sp, maxfd, i;
+
+#ifdef HAVE_SIGACTION
+ sigact.sa_flags = SA_NOCLDSTOP;
+#endif
+
+ /* open an incoming socket */
+ sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
+ if (sp == NULL)
+ exit_cleanup(RERR_SOCKETIO);
+
+ /* ready to listen */
+ FD_ZERO(&deffds);
+ for (i = 0, maxfd = -1; sp[i] >= 0; i++) {
+ if (listen(sp[i], lp_listen_backlog()) < 0) {
+ rsyserr(FERROR, errno, "listen() on socket failed");
+#ifdef INET6
+ if (errno == EADDRINUSE && i > 0) {
+ rprintf(FINFO, "Try using --ipv4 or --ipv6 to avoid this listen() error.\n");
+ }
+#endif
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ FD_SET(sp[i], &deffds);
+ if (maxfd < sp[i])
+ maxfd = sp[i];
+ }
+
+ /* now accept incoming connections - forking a new process
+ * for each incoming connection */
+ while (1) {
+ fd_set fds;
+ pid_t pid;
+ int fd;
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof addr;
+
+ /* close log file before the potentially very long select so
+ * file can be trimmed by another process instead of growing
+ * forever */
+ logfile_close();
+
+#ifdef FD_COPY
+ FD_COPY(&deffds, &fds);
+#else
+ fds = deffds;
+#endif
+
+ if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 1)
+ continue;
+
+ for (i = 0, fd = -1; sp[i] >= 0; i++) {
+ if (FD_ISSET(sp[i], &fds)) {
+ fd = accept(sp[i], (struct sockaddr *)&addr, &addrlen);
+ break;
+ }
+ }
+
+ if (fd < 0)
+ continue;
+
+ SIGACTION(SIGCHLD, sigchld_handler);
+
+ if ((pid = fork()) == 0) {
+ int ret;
+ if (pid_file_fd >= 0)
+ close(pid_file_fd);
+ for (i = 0; sp[i] >= 0; i++)
+ close(sp[i]);
+ /* Re-open log file in child before possibly giving
+ * up privileges (see logfile_close() above). */
+ logfile_reopen();
+ ret = fn(fd, fd);
+ close_all();
+ _exit(ret);
+ } else if (pid < 0) {
+ rsyserr(FERROR, errno,
+ "could not create child server process");
+ close(fd);
+ /* This might have happened because we're
+ * overloaded. Sleep briefly before trying to
+ * accept again. */
+ sleep(2);
+ } else {
+ /* Parent doesn't need this fd anymore. */
+ close(fd);
+ }
+ }
+}
+
+
+enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
+
+struct
+{
+ char *name;
+ int level;
+ int option;
+ int value;
+ int opttype;
+} socket_options[] = {
+ {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
+ {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
+#ifdef SO_BROADCAST
+ {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
+#endif
+#ifdef TCP_NODELAY
+ {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
+#endif
+#ifdef IPTOS_LOWDELAY
+ {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
+#endif
+#ifdef IPTOS_THROUGHPUT
+ {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
+#endif
+#ifdef SO_SNDBUF
+ {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
+#endif
+#ifdef SO_RCVBUF
+ {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
+#endif
+#ifdef SO_SNDLOWAT
+ {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_RCVLOWAT
+ {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_SNDTIMEO
+ {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
+#endif
+#ifdef SO_RCVTIMEO
+ {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
+#endif
+ {NULL,0,0,0,0}
+};
+
+
+/* Set user socket options. */
+void set_socket_options(int fd, char *options)
+{
+ char *tok;
+
+ if (!options || !*options)
+ return;
+
+ options = strdup(options);
+
+ for (tok = strtok(options, " \t,"); tok; tok = strtok(NULL," \t,")) {
+ int ret=0,i;
+ int value = 1;
+ char *p;
+ int got_value = 0;
+
+ if ((p = strchr(tok,'='))) {
+ *p = 0;
+ value = atoi(p+1);
+ got_value = 1;
+ }
+
+ for (i = 0; socket_options[i].name; i++) {
+ if (strcmp(socket_options[i].name,tok)==0)
+ break;
+ }
+
+ if (!socket_options[i].name) {
+ rprintf(FERROR,"Unknown socket option %s\n",tok);
+ continue;
+ }
+
+ switch (socket_options[i].opttype) {
+ case OPT_BOOL:
+ case OPT_INT:
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,
+ (char *)&value, sizeof (int));
+ break;
+
+ case OPT_ON:
+ if (got_value)
+ rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
+
+ {
+ int on = socket_options[i].value;
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,
+ (char *)&on, sizeof (int));
+ }
+ break;
+ }
+
+ if (ret != 0) {
+ rsyserr(FERROR, errno,
+ "failed to set socket option %s", tok);
+ }
+ }
+
+ free(options);
+}
+
+
+/* This is like socketpair but uses tcp. The function guarantees that nobody
+ * else can attach to the socket, or if they do that this function fails and
+ * the socket gets closed. Returns 0 on success, -1 on failure. The resulting
+ * file descriptors are symmetrical. Currently only for RSYNC_CONNECT_PROG. */
+static int socketpair_tcp(int fd[2])
+{
+ int listener;
+ struct sockaddr_in sock;
+ struct sockaddr_in sock2;
+ socklen_t socklen = sizeof sock;
+ int connect_done = 0;
+
+ fd[0] = fd[1] = listener = -1;
+
+ memset(&sock, 0, sizeof sock);
+
+ if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+ goto failed;
+
+ memset(&sock2, 0, sizeof sock2);
+#ifdef HAVE_SOCKADDR_IN_LEN
+ sock2.sin_len = sizeof sock2;
+#endif
+ sock2.sin_family = PF_INET;
+ sock2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bind(listener, (struct sockaddr *)&sock2, sizeof sock2) != 0
+ || listen(listener, 1) != 0
+ || getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0
+ || (fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+ goto failed;
+
+ set_nonblocking(fd[1]);
+
+ sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) == -1) {
+ if (errno != EINPROGRESS)
+ goto failed;
+ } else
+ connect_done = 1;
+
+ if ((fd[0] = accept(listener, (struct sockaddr *)&sock2, &socklen)) == -1)
+ goto failed;
+
+ close(listener);
+ listener = -1;
+
+ set_blocking(fd[1]);
+
+ if (connect_done == 0) {
+ if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) != 0 && errno != EISCONN)
+ goto failed;
+ }
+
+ /* all OK! */
+ return 0;
+
+ failed:
+ if (fd[0] != -1)
+ close(fd[0]);
+ if (fd[1] != -1)
+ close(fd[1]);
+ if (listener != -1)
+ close(listener);
+ return -1;
+}
+
+
+/* Run a program on a local tcp socket, so that we can talk to it's stdin and
+ * stdout. This is used to fake a connection to a daemon for testing -- not
+ * for the normal case of running SSH.
+ *
+ * Returns a socket which is attached to a subprocess running "prog". stdin and
+ * stdout are attached. stderr is left attached to the original stderr. */
+static int sock_exec(const char *prog)
+{
+ pid_t pid;
+ int fd[2];
+
+ if (socketpair_tcp(fd) != 0) {
+ rsyserr(FERROR, errno, "socketpair_tcp failed");
+ return -1;
+ }
+ if (DEBUG_GTE(CMD, 1))
+ rprintf(FINFO, "Running socket program: \"%s\"\n", prog);
+
+ pid = fork();
+ if (pid < 0) {
+ rsyserr(FERROR, errno, "fork");
+ exit_cleanup(RERR_IPC);
+ }
+
+ if (pid == 0) {
+ close(fd[0]);
+ if (dup2(fd[1], STDIN_FILENO) < 0
+ || dup2(fd[1], STDOUT_FILENO) < 0) {
+ fprintf(stderr, "Failed to run \"%s\"\n", prog);
+ exit(1);
+ }
+ exit(shell_exec(prog));
+ }
+
+ close(fd[1]);
+ return fd[0];
+}
diff --git a/stunnel-rsyncd.conf.in b/stunnel-rsyncd.conf.in
new file mode 100644
index 0000000..b3fd240
--- /dev/null
+++ b/stunnel-rsyncd.conf.in
@@ -0,0 +1,30 @@
+# This config for stunnel will start up rsync for an incoming ssl connection.
+foreground = no
+#output = /var/log/stunnel-rsyncd.log
+pid = /var/run/stunnel-rsyncd.pid
+socket = l:TCP_NODELAY=1
+socket = r:TCP_NODELAY=1
+#compression = rle
+# This must be root for rsync to use chroot -- rsync will drop permissions:
+setuid = root
+setgid = root
+
+[rsync]
+accept = 874
+# You can set the cert to a combo *.pem file and omit the key, if you like.
+cert = /etc/rsync-ssl/certs/server.crt
+key = /etc/rsync-ssl/certs/server.key
+client = no
+
+# To allow anyone to try an ssl connection, use this:
+verify = 0
+CAfile = /etc/ssl/certs/ca-certificates.crt
+
+# To allow only cert-authorized clients, use something like this instead of the above:
+#verify = 3
+#CAfile = /etc/rsync-ssl/certs/allowed-clients.cert.pem
+
+exec = @bindir@/rsync
+# You can either share the same config as a normal daemon, or specify a separate config:
+execargs = rsync --server --daemon .
+#execargs = rsync --server --daemon --config=/etc/rsync-ssl/rsyncd.conf .
diff --git a/support/Makefile b/support/Makefile
new file mode 100644
index 0000000..c6a1e30
--- /dev/null
+++ b/support/Makefile
@@ -0,0 +1,6 @@
+all: savetransfer
+
+savetransfer: savetransfer.o
+
+clean:
+ rm -f *.o savetransfer
diff --git a/support/atomic-rsync b/support/atomic-rsync
new file mode 100755
index 0000000..1964090
--- /dev/null
+++ b/support/atomic-rsync
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+# This script lets you update a hierarchy of files in an atomic way by
+# first creating a new hierarchy using rsync's --link-dest option, and
+# then swapping the hierarchy into place. **See the usage message for
+# more details and some important caveats!**
+
+import os, sys, re, subprocess, shutil
+
+ALT_DEST_ARG_RE = re.compile('^--[a-z][^ =]+-dest(=|$)')
+
+RSYNC_PROG = '/usr/bin/rsync'
+
+def main():
+ cmd_args = sys.argv[1:]
+ if '--help' in cmd_args:
+ usage_and_exit()
+
+ if len(cmd_args) < 2:
+ usage_and_exit(True)
+
+ dest_dir = cmd_args[-1].rstrip('/')
+ if dest_dir == '' or dest_dir.startswith('-'):
+ usage_and_exit(True)
+
+ if not os.path.isdir(dest_dir):
+ die(dest_dir, "is not a directory or a symlink to a dir.\nUse --help for help.")
+
+ bad_args = [ arg for arg in cmd_args if ALT_DEST_ARG_RE.match(arg) ]
+ if bad_args:
+ die("You cannot use the", ' or '.join(bad_args), "option with atomic-rsync.\nUse --help for help.")
+
+ # We ignore exit-code 24 (file vanished) by default.
+ allowed_exit_codes = '0 ' + os.environ.get('ATOMIC_RSYNC_OK_CODES', '24')
+ try:
+ allowed_exit_codes = set(int(num) for num in re.split(r'[, ]+', allowed_exit_codes) if num != '')
+ except ValueError:
+ die('Invalid integer in ATOMIC_RSYNC_OK_CODES:', allowed_exit_codes[2:])
+
+ symlink_content = os.readlink(dest_dir) if os.path.islink(dest_dir) else None
+
+ dest_arg = dest_dir
+ dest_dir = os.path.realpath(dest_dir) # The real destination dir with all symlinks dereferenced
+ if dest_dir == '/':
+ die('You must not use "/" as the destination directory.\nUse --help for help.')
+
+ old_dir = new_dir = None
+ if symlink_content is not None and dest_dir.endswith(('-1','-2')):
+ if not symlink_content.endswith(dest_dir[-2:]):
+ die("Symlink suffix out of sync with dest_dir name:", symlink_content, 'vs', dest_dir)
+ num = 3 - int(dest_dir[-1]);
+ old_dir = None
+ new_dir = dest_dir[:-1] + str(num)
+ symlink_content = symlink_content[:-1] + str(num)
+ else:
+ old_dir = dest_dir + '~old~'
+ new_dir = dest_dir + '~new~'
+
+ cmd_args[-1] = new_dir + '/'
+
+ if old_dir is not None and os.path.isdir(old_dir):
+ shutil.rmtree(old_dir)
+ if os.path.isdir(new_dir):
+ shutil.rmtree(new_dir)
+
+ child = subprocess.run([RSYNC_PROG, '--link-dest=' + dest_dir, *cmd_args])
+ if child.returncode not in allowed_exit_codes:
+ die('The rsync copy failed with code', child.returncode, exitcode=child.returncode)
+
+ if not os.path.isdir(new_dir):
+ die('The rsync copy failed to create:', new_dir)
+
+ if old_dir is None:
+ atomic_symlink(symlink_content, dest_arg)
+ else:
+ os.rename(dest_dir, old_dir)
+ os.rename(new_dir, dest_dir)
+
+
+def atomic_symlink(target, link):
+ newlink = link + "~new~"
+ try:
+ os.unlink(newlink); # Just in case
+ except OSError:
+ pass
+ os.symlink(target, newlink)
+ os.rename(newlink, link)
+
+
+def usage_and_exit(use_stderr=False):
+ usage_msg = """\
+Usage: atomic-rsync [RSYNC-OPTIONS] [HOST:]/SOURCE/DIR/ /DEST/DIR/
+ atomic-rsync [RSYNC-OPTIONS] HOST::MOD/DIR/ /DEST/DIR/
+
+This script lets you update a hierarchy of files in an atomic way by first
+creating a new hierarchy (using hard-links to leverage the existing files),
+and then swapping the new hierarchy into place. You must be pulling files
+to a local directory, and that directory must already exist. For example:
+
+ mkdir /local/files-1
+ ln -s files-1 /local/files
+ atomic-rsync -aiv host:/remote/files/ /local/files/
+
+If /local/files is a symlink to a directory that ends in -1 or -2, the copy
+will go to the alternate suffix and the symlink will be changed to point to
+the new dir. This is a fully atomic update. If the destination is not a
+symlink (or not a symlink to a *-1 or a *-2 directory), this will instead
+create a directory with "~new~" suffixed, move the current directory to a
+name with "~old~" suffixed, and then move the ~new~ directory to the original
+destination name (this double rename is not fully atomic, but is rapid). In
+both cases, the prior destintaion directory will be preserved until the next
+update, at which point it will be deleted.
+
+By default, rsync exit-code 24 (file vanished) is allowed without halting the
+atomic update. If you want to change that, specify the environment variable
+ATOMIC_RSYNC_OK_CODES with numeric values separated by spaces and/or commas.
+Specify an empty string to only allow a successful copy. An override example:
+
+ ATOMIC_RSYNC_OK_CODES='23 24' atomic-rsync -aiv host:src/ dest/
+
+See the errcode.h file for a list of all the exit codes.
+
+See the "rsync" command for its list of options. You may not use the
+--link-dest, --compare-dest, or --copy-dest options (since this script
+uses --link-dest to make the transfer efficient).
+"""
+ print(usage_msg, file=sys.stderr if use_stderr else sys.stdout)
+ sys.exit(1 if use_stderr else 0)
+
+
+def die(*args, exitcode=1):
+ print(*args, file=sys.stderr)
+ sys.exit(exitcode)
+
+
+if __name__ == '__main__':
+ main()
+
+# vim: sw=4 et
diff --git a/support/cvs2includes b/support/cvs2includes
new file mode 100755
index 0000000..fc7f78f
--- /dev/null
+++ b/support/cvs2includes
@@ -0,0 +1,42 @@
+#!/usr/bin/env perl
+#
+# This script finds all CVS/Entries files in the current directory and below
+# and creates a local .cvsinclude file with non-inherited rules including each
+# checked-in file. Then, use this option whenever using --cvs-exclude (-C):
+#
+# -f ': .cvsinclude'
+#
+# That ensures that all checked-in files/dirs are included in the transfer.
+# (You could alternately put ": .cvsinclude" into an .rsync-filter file and
+# use the -F option, which is easier to type.)
+#
+# The downside is that you need to remember to re-run cvs2includes whenever
+# you add a new file to the project.
+use strict;
+
+open(FIND, 'find . -name CVS -type d |') or die $!;
+while (<FIND>) {
+ chomp;
+ s#^\./##;
+
+ my $entries = "$_/Entries";
+ s/CVS$/.cvsinclude/;
+ my $filter = $_;
+
+ open(ENTRIES, $entries) or die "Unable to open $entries: $!\n";
+ my @includes;
+ while (<ENTRIES>) {
+ push(@includes, $1) if m#/(.+?)/#;
+ }
+ close ENTRIES;
+ if (@includes) {
+ open(FILTER, ">$filter") or die "Unable to write $filter: $!\n";
+ print FILTER map "+ /$_\n", @includes;
+ close FILTER;
+ print "Updated $filter\n";
+ } elsif (-f $filter) {
+ unlink($filter);
+ print "Removed $filter\n";
+ }
+}
+close FIND;
diff --git a/support/deny-rsync b/support/deny-rsync
new file mode 100755
index 0000000..bd4da9e
--- /dev/null
+++ b/support/deny-rsync
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+# Send an error message via the rsync-protocol to a non-daemon client rsync.
+#
+# Usage: deny-rsync "message"
+
+protocol_version=29
+exit_code=4 # same as a daemon that refuses an option
+
+# e.g. byte_escape 29 => \035
+function byte_escape {
+ echo -ne "\\0$(printf "%o" $1)"
+}
+
+msg="$1"
+if [ "${#msg}" -gt 254 ]; then
+ # truncate a message that is too long for this naive script to handle
+ msg="${msg:0:251}..."
+fi
+msglen=$(( ${#msg} + 1 )) # add 1 for the newline we append below
+
+# Send protocol version. All numbers are LSB-first 4-byte ints.
+echo -ne "$(byte_escape $protocol_version)\\000\\000\\000"
+
+# Send a zero checksum seed.
+echo -ne "\\000\\000\\000\\000"
+
+# The following is equivalent to rprintf(FERROR_XFER, "%s\n", $msg).
+# 1. Message header: ((MPLEX_BASE + FERROR_XFER) << 24) + $msglen.
+echo -ne "$(byte_escape $msglen)\\000\\000\\010"
+# 2. The actual data.
+echo -E "$msg"
+
+# Make sure the client gets our message, not a write failure.
+sleep 1
+
+exit $exit_code
diff --git a/support/file-attr-restore b/support/file-attr-restore
new file mode 100755
index 0000000..2e4a21b
--- /dev/null
+++ b/support/file-attr-restore
@@ -0,0 +1,173 @@
+#!/usr/bin/env perl
+# This script will parse the output of "find ARG [ARG...] -ls" and
+# apply (at your discretion) the permissions, owner, and group info
+# it reads onto any existing files and dirs (it doesn't try to affect
+# symlinks). Run this with --help (-h) for a usage summary.
+
+use strict;
+use Getopt::Long;
+
+our($p_opt, $o_opt, $g_opt, $map_file, $dry_run, $verbosity, $help_opt);
+
+&Getopt::Long::Configure('bundling');
+&usage if !&GetOptions(
+ 'all|a' => sub { $p_opt = $o_opt = $g_opt = 1 },
+ 'perms|p' => \$p_opt,
+ 'owner|o' => \$o_opt,
+ 'groups|g' => \$g_opt,
+ 'map|m=s' => \$map_file,
+ 'dry-run|n' => \$dry_run,
+ 'help|h' => \$help_opt,
+ 'verbose|v+' => \$verbosity,
+) || $help_opt;
+
+our(%uid_hash, %gid_hash);
+
+$" = ', '; # How to join arrays referenced in double-quotes.
+
+&parse_map_file($map_file) if defined $map_file;
+
+my $detail_line = qr{
+ ^ \s* \d+ \s+ # ignore inode
+ \d+ \s+ # ignore size
+ ([-bcdlps]) # 1. File type
+ ( [-r][-w][-xsS] # 2. user-permissions
+ [-r][-w][-xsS] # group-permissions
+ [-r][-w][-xtT] ) \s+ # other-permissions
+ \d+ \s+ # ignore number of links
+ (\S+) \s+ # 3. owner
+ (\S+) \s+ # 4. group
+ (?: \d+ \s+ )? # ignore size (when present)
+ \w+ \s+ \d+ \s+ # ignore month and date
+ \d+ (?: : \d+ )? \s+ # ignore time or year
+ ([^\r\n]+) $ # 5. name
+}x;
+
+while (<>) {
+ my($type, $perms, $owner, $group, $name) = /$detail_line/;
+ die "Invalid input line $.:\n$_" unless defined $name;
+ die "A filename is not properly escaped:\n$_" unless $name =~ /^[^"\\]*(\\(\d\d\d|\D)[^"\\]*)*$/;
+ my $fn = $name;
+ $fn =~ s/\\(\d+|.)/ eval "\"\\$1\"" /eg;
+ if ($type eq '-') {
+ undef $type unless -f $fn;
+ } elsif ($type eq 'd') {
+ undef $type unless -d $fn;
+ } elsif ($type eq 'b') {
+ undef $type unless -b $fn;
+ } elsif ($type eq 'c') {
+ undef $type unless -c $fn;
+ } elsif ($type eq 'p') {
+ undef $type unless -p $fn;
+ } elsif ($type eq 's') {
+ undef $type unless -S $fn;
+ } else {
+ if ($verbosity) {
+ if ($type eq 'l') {
+ $name =~ s/ -> .*//;
+ $type = 'symlink';
+ } else {
+ $type = "type '$type'";
+ }
+ print "Skipping $name ($type ignored)\n";
+ }
+ next;
+ }
+ if (!defined $type) {
+ my $reason = -e _ ? "types don't match" : 'missing';
+ print "Skipping $name ($reason)\n";
+ next;
+ }
+ my($cur_mode, $cur_uid, $cur_gid) = (stat(_))[2,4,5];
+ $cur_mode &= 07777;
+ my $highs = join('', $perms =~ /..(.)..(.)..(.)/);
+ $highs =~ tr/-rwxSTst/00001111/;
+ $perms =~ tr/-STrwxst/00011111/;
+ my $mode = $p_opt ? oct('0b' . $highs . $perms) : $cur_mode;
+ my $uid = $o_opt ? $uid_hash{$owner} : $cur_uid;
+ if (!defined $uid) {
+ if ($owner =~ /^\d+$/) {
+ $uid = $owner;
+ } else {
+ $uid = getpwnam($owner);
+ }
+ $uid_hash{$owner} = $uid;
+ }
+ my $gid = $g_opt ? $gid_hash{$group} : $cur_gid;
+ if (!defined $gid) {
+ if ($group =~ /^\d+$/) {
+ $gid = $group;
+ } else {
+ $gid = getgrnam($group);
+ }
+ $gid_hash{$group} = $gid;
+ }
+
+ my @changes;
+ if ($mode != $cur_mode) {
+ push(@changes, 'permissions');
+ if (!$dry_run && !chmod($mode, $fn)) {
+ warn "chmod($mode, \"$name\") failed: $!\n";
+ }
+ }
+ if ($uid != $cur_uid || $gid != $cur_gid) {
+ push(@changes, 'owner') if $uid != $cur_uid;
+ push(@changes, 'group') if $gid != $cur_gid;
+ if (!$dry_run) {
+ if (!chown($uid, $gid, $fn)) {
+ warn "chown($uid, $gid, \"$name\") failed: $!\n";
+ }
+ if (($mode & 06000) && !chmod($mode, $fn)) {
+ warn "post-chown chmod($mode, \"$name\") failed: $!\n";
+ }
+ }
+ }
+ if (@changes) {
+ print "$name: changed @changes\n";
+ } elsif ($verbosity) {
+ print "$name: OK\n";
+ }
+}
+exit;
+
+sub parse_map_file
+{
+ my($fn) = @_;
+ open(IN, $fn) or die "Unable to open $fn: $!\n";
+ while (<IN>) {
+ if (/^user\s+(\S+)\s+(\S+)/) {
+ $uid_hash{$1} = $2;
+ } elsif (/^group\s+(\S+)\s+(\S+)/) {
+ $gid_hash{$1} = $2;
+ } else {
+ die "Invalid line #$. in mapfile `$fn':\n$_";
+ }
+ }
+ close IN;
+}
+
+sub usage
+{
+ die <<EOT;
+Usage: file-attr-restore [OPTIONS] FILE [FILE...]
+ -a, --all Restore all the attributes (-pog)
+ -p, --perms Restore the permissions
+ -o, --owner Restore the ownership
+ -g, --groups Restore the group
+ -m, --map=FILE Read user/group mappings from FILE
+ -n, --dry-run Don't actually make the changes
+ -v, --verbose Increase verbosity
+ -h, --help Show this help text
+
+The FILE arg(s) should have been created by running the "find"
+program with "-ls" as the output specifier.
+
+The input file for the --map option must be in this format:
+
+ user FROM TO
+ group FROM TO
+
+The "FROM" should be an user/group mentioned in the input, and the TO
+should be either a uid/gid number, or a local user/group name.
+EOT
+}
diff --git a/support/files-to-excludes b/support/files-to-excludes
new file mode 100755
index 0000000..a28955c
--- /dev/null
+++ b/support/files-to-excludes
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+# This script takes an input of filenames and outputs a set of
+# include/exclude directives that can be used by rsync to copy
+# just the indicated files using an --exclude-from=FILE option.
+use strict;
+
+my %hash;
+
+while (<>) {
+ chomp;
+ s#^/+##;
+ my $path = '/';
+ while (m#([^/]+/)/*#g) {
+ $path .= $1;
+ print "+ $path\n" unless $hash{$path}++;
+ }
+ if (m#([^/]+)$#) {
+ print "+ $path$1\n";
+ } else {
+ delete $hash{$path};
+ }
+}
+
+foreach (sort keys %hash) {
+ print "- $_*\n";
+}
+print "- /*\n";
diff --git a/support/git-set-file-times b/support/git-set-file-times
new file mode 100755
index 0000000..e06f073
--- /dev/null
+++ b/support/git-set-file-times
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+
+import os, re, argparse, subprocess
+from datetime import datetime
+
+NULL_COMMIT_RE = re.compile(r'\0\0commit [a-f0-9]{40}$|\0$')
+
+def main():
+ if not args.git_dir:
+ cmd = 'git rev-parse --show-toplevel 2>/dev/null || echo .'
+ top_dir = subprocess.check_output(cmd, shell=True, encoding='utf-8').strip()
+ args.git_dir = os.path.join(top_dir, '.git')
+ if not args.prefix:
+ os.chdir(top_dir)
+
+ git = [ 'git', '--git-dir=' + args.git_dir ]
+
+ if args.tree:
+ cmd = git + 'ls-tree -z -r --name-only'.split() + [ args.tree ]
+ else:
+ cmd = git + 'ls-files -z'.split()
+
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding='utf-8')
+ out = proc.communicate()[0]
+ ls = set(out.split('\0'))
+ ls.discard('')
+
+ if not args.tree:
+ # All modified files keep their current mtime.
+ proc = subprocess.Popen(git + 'status -z --no-renames'.split(), stdout=subprocess.PIPE, encoding='utf-8')
+ out = proc.communicate()[0]
+ for fn in out.split('\0'):
+ if fn == '' or (fn[0] != 'M' and fn[1] != 'M'):
+ continue
+ fn = fn[3:]
+ if args.list:
+ mtime = os.lstat(fn).st_mtime
+ print_line(fn, mtime, mtime)
+ ls.discard(fn)
+
+ cmd = git + 'log -r --name-only --format=%x00commit%x20%H%n%x00commit_time%x20%ct%n --no-renames -z'.split()
+ if args.tree:
+ cmd.append(args.tree)
+ cmd += ['--'] + args.files
+
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding='utf-8')
+ for line in proc.stdout:
+ line = line.strip()
+ m = re.match(r'^\0commit_time (\d+)$', line)
+ if m:
+ commit_time = int(m[1])
+ elif NULL_COMMIT_RE.search(line):
+ line = NULL_COMMIT_RE.sub('', line)
+ files = set(fn for fn in line.split('\0') if fn in ls)
+ if not files:
+ continue
+ for fn in files:
+ if args.prefix:
+ fn = args.prefix + fn
+ mtime = os.lstat(fn).st_mtime
+ if args.list:
+ print_line(fn, mtime, commit_time)
+ elif mtime != commit_time:
+ if not args.quiet:
+ print(f"Setting {fn}")
+ os.utime(fn, (commit_time, commit_time), follow_symlinks = False)
+ ls -= files
+ if not ls:
+ break
+ proc.communicate()
+
+
+def print_line(fn, mtime, commit_time):
+ if args.list > 1:
+ ts = str(commit_time).rjust(10)
+ else:
+ ts = datetime.utcfromtimestamp(commit_time).strftime("%Y-%m-%d %H:%M:%S")
+ chg = '.' if mtime == commit_time else '*'
+ print(chg, ts, fn)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Set the times of the files in the current git checkout to their last-changed time.", add_help=False)
+ parser.add_argument('--git-dir', metavar='GIT_DIR', help="The git dir to query (defaults to affecting the current git checkout).")
+ parser.add_argument('--tree', metavar='TREE-ISH', help="The tree-ish to query (defaults to the current branch).")
+ parser.add_argument('--prefix', metavar='PREFIX_STR', help="Prepend the PREFIX_STR to each filename we tweak (defaults to the top of current checkout).")
+ parser.add_argument('--quiet', '-q', action='store_true', help="Don't output the changed-file information.")
+ parser.add_argument('--list', '-l', action='count', help="List files & times instead of changing them. Repeat for Unix timestamp instead of human readable.")
+ parser.add_argument('files', metavar='FILE', nargs='*', help="Specify a subset of checked-out files to tweak.")
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/instant-rsyncd b/support/instant-rsyncd
new file mode 100755
index 0000000..8bcfd00
--- /dev/null
+++ b/support/instant-rsyncd
@@ -0,0 +1,126 @@
+#!/usr/bin/env bash
+
+# instant-rsyncd lets you quickly set up and start a simple, unprivileged rsync
+# daemon with a single module in the current directory. I've found it
+# invaluable for quick testing, and I use it when writing a list of commands
+# that people can paste into a terminal to reproduce a daemon-related bug.
+# Sysadmins deploying an rsync daemon for the first time may find it helpful as
+# a starting point.
+#
+# Usage: instant-rsyncd MODULE PORT RSYNCD-USERNAME [RSYNC-PATH]
+# The script asks for the rsyncd user's password twice on stdin, once to set it
+# and once to log in to test the daemon.
+# -- Matt McCutchen <matt@mattmccutchen.net>
+
+set -e
+
+dir="$(pwd)"
+
+echo
+echo "This will setup an rsync daemon in $dir"
+
+if [ $# = 0 ]; then
+ IFS='' read -p 'Module name to create (or return to exit): ' module
+ [ ! "$module" ] && exit
+else
+ module="$1"
+ shift
+fi
+
+if [ $# = 0 ]; then
+ IFS='' read -p 'Port number the daemon should listen on [873]: ' port
+else
+ port="$1"
+ shift
+fi
+[ "$port" ] || port=873
+
+if [ $# = 0 ]; then
+ IFS='' read -p 'User name for authentication (empty for none): ' user
+else
+ user="$1"
+ shift
+fi
+
+if [ "$user" ]; then
+ IFS='' read -s -p 'Desired password: ' password
+ echo
+fi
+
+rsync="$1"
+[ "$rsync" ] || rsync=rsync
+
+moduledir="${dir%/}/$module"
+
+mkdir "$module"
+
+cat >rsyncd.conf <<EOF
+log file = rsyncd.log
+pid file = rsyncd.pid
+port = $port
+use chroot = no
+
+[$module]
+ path = $module
+ read only = false
+EOF
+
+if [ "$user" ]; then
+ cat >>rsyncd.conf <<-EOF
+ auth users = $user
+ secrets file = $module.secrets
+ EOF
+ touch "$module".secrets
+ chmod go-rwx "$module".secrets
+ echo "$user:$password" >"$module".secrets
+ user="$user@"
+fi
+
+cat >start <<EOF
+#!/bin/bash
+set -e
+cd \`dirname \$0\`
+! [ -e rsyncd.pid ] || {
+ echo "Is the daemon already running? If not, delete rsyncd.pid."
+ exit 1
+}
+$rsync --daemon --config=rsyncd.conf
+EOF
+chmod +x start
+
+cat >stop <<"EOF"
+#!/bin/bash
+set -e
+cd `dirname $0`
+! [ -e rsyncd.pid ] || kill -s SIGTERM $(< rsyncd.pid)
+EOF
+chmod +x stop
+
+path="rsync://$user$(hostname):$port/$module/"
+
+if ./start; then
+ sleep .2
+ echo
+ echo "I ran the start command for the daemon. The log file rsyncd.log says:"
+ echo
+ cat rsyncd.log
+ echo
+ echo "You can start and stop it with ./start and ./stop respectively."
+ echo "You can customize the configuration file rsyncd.conf."
+ echo
+ echo "Give rsync the following path to access the module:"
+ echo " $path"
+ echo
+ if [ "$user" ]; then
+ echo "Let's test the daemon now. Enter the password you chose at the prompt."
+ else
+ echo "Let's test the daemon now."
+ fi
+ echo
+ echo '$' $rsync --list-only "$path"
+ $rsync --list-only "$path"
+ echo
+ echo "You should see an empty folder; it's $moduledir."
+else
+ echo "Something went wrong. Do you see an error message?"
+fi
diff --git a/support/json-rsync-version b/support/json-rsync-version
new file mode 100755
index 0000000..31fed7f
--- /dev/null
+++ b/support/json-rsync-version
@@ -0,0 +1,93 @@
+#!/usr/bin/python3
+
+import sys, argparse, subprocess, json
+
+TWEAK_NAME = {
+ 'asm': 'asm_roll',
+ 'ASM': 'asm_roll',
+ 'hardlink_special': 'hardlink_specials',
+ 'protect_args': 'secluded_args',
+ 'protected_args': 'secluded_args',
+ 'SIMD': 'SIMD_roll',
+ }
+
+MOVE_OPTIM = set('asm_roll SIMD_roll'.split())
+
+def main():
+ if not args.rsync or args.rsync == '-':
+ ver_out = sys.stdin.read().strip()
+ else:
+ ver_out = subprocess.check_output([args.rsync, '--version', '--version'], encoding='utf-8').strip()
+ if ver_out.startswith('{'):
+ print(ver_out)
+ return
+ info = { }
+ misplaced_optims = { }
+ for line in ver_out.splitlines():
+ if line.startswith('rsync '):
+ prog, vstr, ver, pstr, vstr2, proto = line.split()
+ info['program'] = prog
+ if ver.startswith('v'):
+ ver = ver[1:]
+ info[vstr] = ver
+ if '.' not in proto:
+ proto += '.0'
+ else:
+ proto = proto.replace('.PR', '.')
+ info[pstr] = proto
+ elif line.startswith('Copyright '):
+ info['copyright'] = line[10:]
+ elif line.startswith('Web site: '):
+ info['url'] = line[10:]
+ elif line.startswith(' '):
+ if not saw_comma and ',' in line:
+ saw_comma = True
+ info[sect_name] = { }
+ if saw_comma:
+ for x in line.strip(' ,').split(', '):
+ if ' ' in x:
+ val, var = x.split(' ', 1)
+ if val == 'no':
+ val = False
+ elif val.endswith('-bit'):
+ var = var[:-1] + '_bits'
+ val = int(val.split('-')[0])
+ else:
+ var = x
+ val = True
+ var = var.replace(' ', '_').replace('-', '_')
+ if var in TWEAK_NAME:
+ var = TWEAK_NAME[var]
+ if sect_name[0] != 'o' and var in MOVE_OPTIM:
+ misplaced_optims[var] = val
+ else:
+ info[sect_name][var] = val
+ else:
+ info[sect_name] += [ x for x in line.split() if not x.startswith('(') ]
+ elif line == '':
+ break
+ else:
+ sect_name = line.strip(' :').replace(' ', '_').lower()
+ info[sect_name] = [ ]
+ saw_comma = False
+ for chk in 'capabilities optimizations'.split():
+ if chk not in info:
+ info[chk] = { }
+ if misplaced_optims:
+ info['optimizations'].update(misplaced_optims)
+ for chk in 'checksum_list compress_list daemon_auth_list'.split():
+ if chk not in info:
+ info[chk] = [ ]
+ info['license'] = 'GPLv3' if ver[0] == '3' else 'GPLv2'
+ info['caveat'] = 'rsync comes with ABSOLUTELY NO WARRANTY'
+ print(json.dumps(info))
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Output rsync's version data in JSON format, even if the rsync doesn't support a native json-output method.", add_help=False)
+ parser.add_argument('rsync', nargs='?', help="Specify an rsync command to run. Otherwise stdin is consumed.")
+ parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/logfilter b/support/logfilter
new file mode 100755
index 0000000..29cfe69
--- /dev/null
+++ b/support/logfilter
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+# Filter the rsync daemon log messages by module name. The log file can be
+# in either syslog format or rsync's own log-file format. Note that the
+# MODULE_NAME parameter is used in a regular-expression match in order to
+# allow regex wildcards to be used. You can also limit the output by
+# directory hierarchy in a module. Examples:
+#
+# logfilter foo /var/log/rsyncd.log # output lines for module foo
+# logfilter foo/dir /var/log/syslog # limit lines to those in dir of foo
+
+use strict;
+
+my $match = shift;
+die "Usage: logfilter MODULE_NAME [LOGFILE ...]\n" unless defined $match;
+
+my $syslog_prefix = '\w\w\w +\d+ \d\d:\d\d:\d\d \S+ rsyncd';
+my $rsyncd_prefix = '\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d ';
+
+my %pids;
+
+while (<>) {
+ my($pid,$msg) = /^(?:$syslog_prefix|$rsyncd_prefix)\[(\d+)\]:? (.*)/o;
+ next unless defined $pid;
+ my($mod_spec) = $msg =~ /^rsync (?:on|to) (\S+) from /;
+ if (defined $mod_spec) {
+ if ($mod_spec =~ /^$match(\/\S*)?$/o) {
+ $pids{$pid} = 1;
+ } else {
+ delete $pids{$pid};
+ }
+ }
+ next unless $pids{$pid};
+ print $_;
+}
diff --git a/support/lsh b/support/lsh
new file mode 100755
index 0000000..7b3c065
--- /dev/null
+++ b/support/lsh
@@ -0,0 +1,108 @@
+#!/usr/bin/env perl
+# This is a "local shell" command that works like a remote shell but only for
+# the local host. See the usage message for more details.
+
+use strict;
+use warnings;
+use Getopt::Long;
+use English '-no_match_vars';
+
+&Getopt::Long::Configure('bundling');
+&Getopt::Long::Configure('require_order');
+GetOptions(
+ 'l=s' => \( my $login_name ),
+ '1|2|4|6|A|a|C|f|g|k|M|N|n|q|s|T|t|V|v|X|x|Y' => sub { }, # Ignore
+ 'b|c|D|e|F|i|L|m|O|o|p|R|S|w=s' => sub { }, # Ignore
+ 'no-cd' => \( my $no_chdir ),
+ 'sudo' => \( my $use_sudo ),
+ 'rrsync=s' => \( my $rrsync_dir ),
+ 'rropts=s' => \( my $rrsync_opts ),
+) or &usage;
+&usage unless @ARGV > 1;
+
+my $host = shift;
+if ($host =~ s/^([^@]+)\@//) {
+ $login_name = $1;
+}
+if ($host eq 'lh') {
+ $no_chdir = 1;
+} elsif ($host ne 'localhost') {
+ die "lsh: unable to connect to host $host\n";
+}
+
+my ($home_dir, @cmd);
+if ($login_name) {
+ my ($uid, $gid);
+ if ($login_name =~ /\D/) {
+ $uid = getpwnam($login_name);
+ die "Unknown user: $login_name\n" unless defined $uid;
+ } else {
+ $uid = $login_name;
+ }
+ ($login_name, $gid, $home_dir) = (getpwuid($uid))[0,3,7];
+ if ($use_sudo) {
+ unshift @ARGV, "cd '$home_dir' &&" unless $no_chdir;
+ unshift @cmd, qw( sudo -H -u ), $login_name;
+ $no_chdir = 1;
+ } else {
+ my $groups = "$gid $gid";
+ while (my ($grgid, $grmembers) = (getgrent)[2,3]) {
+ if ($grgid != $gid && $grmembers =~ /(^|\s)\Q$login_name\E(\s|$)/o) {
+ $groups .= " $grgid";
+ }
+ }
+
+ my ($ruid, $euid) = ($UID, $EUID);
+ $GID = $EGID = $groups;
+ $UID = $EUID = $uid;
+ die "Cannot set ruid: $! (use --sudo?)\n" if $UID == $ruid && $ruid != $uid;
+ die "Cannot set euid: $! (use --sudo?)\n" if $EUID == $euid && $euid != $uid;
+
+ $ENV{USER} = $ENV{USERNAME} = $login_name;
+ $ENV{HOME} = $home_dir;
+ }
+} else {
+ $home_dir = (getpwuid($UID))[7];
+}
+
+unless ($no_chdir) {
+ chdir $home_dir or die "Unable to chdir to $home_dir: $!\n";
+}
+
+if ($rrsync_dir) {
+ $ENV{SSH_ORIGINAL_COMMAND} = join(' ', @ARGV);
+ push @cmd, 'rrsync';
+ if ($rrsync_opts) {
+ foreach my $opt (split(/[ ,]+/, $rrsync_opts)) {
+ $opt = "-$opt" unless $opt =~ /^-/;
+ push @cmd, $opt;
+ }
+ }
+ push @cmd, $rrsync_dir;
+} else {
+ push @cmd, '/bin/sh', '-c', "@ARGV";
+}
+exec @cmd;
+die "Failed to exec: $!\n";
+
+sub usage
+{
+ die <<EOT;
+Usage: lsh [OPTIONS] localhost|lh COMMAND [...]
+
+This is a "local shell" command that works like a remote shell but only for the
+local host. This is useful for rsync testing or for running a local copy where
+the sender and the receiver need to use different options (e.g. --fake-super).
+
+Options:
+
+-l USER Choose the USER that lsh tries to become.
+--no-cd Skip the chdir \$HOME (the default with hostname "lh")
+--sudo Use sudo -H -l USER to become root or the specified USER.
+--rrsync=DIR Test rrsync restricted copying without using ssh.
+--rropts=STR The string "munge,no-del,no-lock" would pass 3 options to
+ rrsync (must be combined with --rrsync=DIR).
+
+The script also ignores a bunch of single-letter ssh options.
+EOT
+}
diff --git a/support/lsh.sh b/support/lsh.sh
new file mode 100755
index 0000000..db03422
--- /dev/null
+++ b/support/lsh.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# This script can be used as a "remote shell" command that is only
+# capable of pretending to connect to "localhost". This is useful
+# for testing or for running a local copy where the sender and the
+# receiver needs to use different options (e.g. --fake-super). If
+# we get a -l USER option, we try to use "sudo -u USER" to run the
+# command. Supports only the hostnames "localhost" and "lh", with
+# the latter implying the --no-cd option.
+
+user=''
+do_cd=y # Default path is user's home dir (just like ssh) unless host is "lh".
+
+while : ; do
+ case "$1" in
+ -l) user="$2"; shift; shift ;;
+ -l*) user=`echo "$1" | sed 's/^-l//'`; shift ;;
+ --no-cd) do_cd=n; shift ;;
+ -*) shift ;;
+ localhost) shift; break ;;
+ lh) do_cd=n; shift; break ;;
+ *) echo "lsh: unable to connect to host $1" 1>&2; exit 1 ;;
+ esac
+done
+
+if [ "$user" ]; then
+ prefix=''
+ if [ $do_cd = y ]; then
+ home=`perl -e "print((getpwnam('$user'))[7])"`
+ prefix="cd '$home' &&"
+ fi
+ sudo -H -u "$user" sh -c "$prefix $*"
+else
+ if [ $do_cd = y ]; then
+ cd || exit 1
+ fi
+ eval "${@}"
+fi
diff --git a/support/mapfrom b/support/mapfrom
new file mode 100755
index 0000000..88946bc
--- /dev/null
+++ b/support/mapfrom
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+# This helper script makes it easy to use a passwd or group file to map
+# values in a LOCAL transfer. For instance, if you mount a backup that
+# does not have the same passwd setup as the local machine, you can do
+# a copy FROM the backup area as follows and get the differing ID values
+# mapped just like a remote transfer FROM the backed-up machine would do:
+#
+# rsync -av --usermap=`mapfrom /mnt/backup/etc/passwd` \
+# --groupmap=`mapfrom /mnt/backup/etc/group` \
+# /mnt/backup/some/src/ /some/dest/
+
+while (<>) {
+ push @_, "$2:$1" if /^(\w+):[^:]+:(\d+)/;
+}
+print join(',', @_), "\n";
diff --git a/support/mapto b/support/mapto
new file mode 100755
index 0000000..9588752
--- /dev/null
+++ b/support/mapto
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+# This helper script makes it easy to use a passwd or group file to map
+# values in a LOCAL transfer. For instance, if you mount a backup that
+# does not have the same passwd setup as the local machine, you can do
+# a copy TO the backup area as follows and get the differing ID values
+# mapped just like a remote transfer TO the backed-up machine would do:
+#
+# rsync -av --usermap=`mapto /mnt/backup/etc/passwd` \
+# --groupmap=`mapto /mnt/backup/etc/group` \
+# /some/src/ /mnt/backup/some/dest/
+
+while (<>) {
+ push @_, "$1:$2" if /^(\w+):[^:]+:(\d+)/;
+}
+print join(',', @_), "\n";
diff --git a/support/mnt-excl b/support/mnt-excl
new file mode 100755
index 0000000..ed7b49b
--- /dev/null
+++ b/support/mnt-excl
@@ -0,0 +1,49 @@
+#!/usr/bin/env perl
+# This script takes a command-line arg of a source directory
+# that will be passed to rsync, and generates a set of excludes
+# that will exclude all mount points from the list. This is
+# useful if you have "bind" mounts since the --one-file-system
+# option won't notice the transition to a different spot on
+# the same disk. For example:
+#
+# mnt-excl /dir | rsync --exclude-from=- ... /dir /dest/
+# mnt-excl /dir/ | rsync --exclude-from=- ... /dir/ /dest/
+# ssh host mnt-excl /dir | rsync --exclude-from=- ... host:/dir /dest/
+#
+# Imagine that /dir/foo is a mount point: the first invocation of
+# mnt-excl would have output /dir/foo, while the second would have
+# output /foo (which are the properly anchored excludes).
+#
+# NOTE: This script expects /proc/mounts to exist, but could be
+# easily adapted to read /etc/mtab or similar.
+#
+# ADDENDUM: The addition of the --filter option (which has support for
+# absolute-anchored excludes) can make this script unneeded in some
+# scenarios. If you don't need delete protection on the receiving side
+# (or if the destination path is identical to the source path), then you
+# can exclude some absolute paths from the transfer based on the mount
+# dirs. For instance:
+#
+# awk '{print $2}' /proc/mounts | grep -v '^/$' | \
+# rsync -avf 'merge,/- -' /dir host:/dest/
+
+use strict;
+use warnings;
+use Cwd 'abs_path';
+
+my $file = '/proc/mounts';
+my $dir = shift || '/';
+my $trailing_slash = $dir =~ m{./$} ? '/' : '';
+$dir = abs_path($dir) . $trailing_slash;
+$dir =~ s{([^/]*)$}{};
+my $trailing = $1;
+$trailing = '' if $trailing eq '.' || !-d "$dir$trailing";
+$trailing .= '/' if $trailing ne '';
+
+open(IN, $file) or die "Unable to open $file: $!\n";
+while (<IN>) {
+ $_ = (split)[1];
+ next unless s{^\Q$dir$trailing\E}{}o && $_ ne '';
+ print "- /$trailing$_\n";
+}
+close IN;
diff --git a/support/munge-symlinks b/support/munge-symlinks
new file mode 100755
index 0000000..e7a5474
--- /dev/null
+++ b/support/munge-symlinks
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+# This script will either prefix all symlink values with the string
+# "/rsyncd-munged/" or remove that prefix.
+
+import os, sys, argparse
+
+SYMLINK_PREFIX = '/rsyncd-munged/'
+PREFIX_LEN = len(SYMLINK_PREFIX)
+
+def main():
+ for arg in args.names:
+ if os.path.islink(arg):
+ process_one_arg(arg)
+ elif os.path.isdir(arg):
+ for fn in find_symlinks(arg):
+ process_one_arg(fn)
+ else:
+ print("Arg is not a symlink or a dir:", arg, file=sys.stderr)
+
+
+def find_symlinks(path):
+ for entry in os.scandir(path):
+ if entry.is_symlink():
+ yield entry.path
+ elif entry.is_dir(follow_symlinks=False):
+ yield from find_symlinks(entry.path)
+
+
+def process_one_arg(fn):
+ lnk = os.readlink(fn)
+ if args.unmunge:
+ if not lnk.startswith(SYMLINK_PREFIX):
+ return
+ lnk = lnk[PREFIX_LEN:]
+ while args.all and lnk.startswith(SYMLINK_PREFIX):
+ lnk = lnk[PREFIX_LEN:]
+ else:
+ if not args.all and lnk.startswith(SYMLINK_PREFIX):
+ return
+ lnk = SYMLINK_PREFIX + lnk
+
+ try:
+ os.unlink(fn)
+ except OSError as e:
+ print("Unable to unlink symlink:", str(e), file=sys.stderr)
+ return
+ try:
+ os.symlink(lnk, fn)
+ except OSError as e:
+ print("Unable to recreate symlink", fn, '->', lnk + ':', str(e), file=sys.stderr)
+ return
+ print(fn, '->', lnk)
+
+
+if __name__ == '__main__':
+ our_desc = """\
+Adds or removes the %s prefix to/from the start of each symlink's value.
+When given the name of a directory, affects all the symlinks in that directory hierarchy.
+""" % SYMLINK_PREFIX
+ epilog = 'See the "munge symlinks" option in the rsyncd.conf manpage for more details.'
+ parser = argparse.ArgumentParser(description=our_desc, epilog=epilog, add_help=False)
+ uniq_group = parser.add_mutually_exclusive_group()
+ uniq_group.add_argument('--munge', action='store_true', help="Add the prefix to symlinks (the default).")
+ uniq_group.add_argument('--unmunge', action='store_true', help="Remove the prefix from symlinks.")
+ parser.add_argument('--all', action='store_true', help="Always adds the prefix when munging (even if already munged) or removes multiple instances of the prefix when unmunging.")
+ parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
+ parser.add_argument('names', metavar='NAME', nargs='+', help="One or more directories and/or symlinks to process.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/nameconvert b/support/nameconvert
new file mode 100755
index 0000000..ecfe28d
--- /dev/null
+++ b/support/nameconvert
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+
+# This implements a simple protocol to do user & group conversions between
+# names & ids. All input and output consists of simple strings with a
+# terminating newline.
+#
+# The requests can be:
+#
+# uid ID_NUM\n -> NAME\n
+# gid ID_NUM\n -> NAME\n
+# usr NAME\n -> ID_NUM\n
+# grp NAME\n -> ID_NUM\n
+#
+# An unknown ID_NUM or NAME results in an empty return value.
+#
+# This is used by an rsync daemon when configured with the "name converter" and
+# (often) "use chroot = true". While this converter uses real user & group
+# lookups you could change it to use any mapping idiom you'd like.
+
+import sys, argparse, pwd, grp
+
+def main():
+ for line in sys.stdin:
+ try:
+ req, arg = line.rstrip().split(' ', 1)
+ except:
+ req = None
+ try:
+ if req == 'uid':
+ ans = pwd.getpwuid(int(arg)).pw_name
+ elif req == 'gid':
+ ans = grp.getgrgid(int(arg)).gr_name
+ elif req == 'usr':
+ ans = pwd.getpwnam(arg).pw_uid
+ elif req == 'grp':
+ ans = grp.getgrnam(arg).gr_gid
+ else:
+ print("Invalid request", file=sys.stderr)
+ sys.exit(1)
+ except KeyError:
+ ans = ''
+ print(ans, flush=True)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Convert users & groups between names & numbers for an rsync daemon.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
diff --git a/support/rrsync b/support/rrsync
new file mode 100755
index 0000000..94c85f5
--- /dev/null
+++ b/support/rrsync
@@ -0,0 +1,379 @@
+#!/usr/bin/env python3
+
+# Restricts rsync to subdirectory declared in .ssh/authorized_keys. See
+# the rrsync man page for details of how to make use of this script.
+
+# NOTE: install python3 braceexpand to support brace expansion in the args!
+
+# Originally a perl script by: Joe Smith <js-cgi@inwap.com> 30-Sep-2004
+# Python version by: Wayne Davison <wayne@opencoder.net>
+
+# You may configure these 2 values to your liking. See also the section of
+# short & long options if you want to disable any options that rsync accepts.
+RSYNC = '/usr/bin/rsync'
+LOGFILE = 'rrsync.log' # NOTE: the file must exist for a line to be appended!
+
+# The following options are mainly the options that a client rsync can send
+# to the server, and usually just in the one option format that the stock
+# rsync produces. However, there are some additional convenience options
+# added as well, and thus a few options are present in both the short and
+# long lists (such as --group, --owner, and --perms).
+
+# NOTE when disabling: check for both a short & long version of the option!
+
+### START of options data produced by the cull-options script. ###
+
+# To disable a short-named option, add its letter to this string:
+short_disabled = 's'
+
+# These are also disabled when the restricted dir is not "/":
+short_disabled_subdir = 'KLk'
+
+# These are all possible short options that we will accept (when not disabled above):
+short_no_arg = 'ACDEHIJKLNORSUWXbcdgklmnopqrstuvxyz' # DO NOT REMOVE ANY
+short_with_num = '@B' # DO NOT REMOVE ANY
+
+# To disable a long-named option, change its value to a -1. The values mean:
+# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
+# check the arg when receiving; and 3 = always check the arg.
+long_opts = {
+ 'append': 0,
+ 'backup-dir': 2,
+ 'block-size': 1,
+ 'bwlimit': 1,
+ 'checksum-choice': 1,
+ 'checksum-seed': 1,
+ 'compare-dest': 2,
+ 'compress-choice': 1,
+ 'compress-level': 1,
+ 'copy-dest': 2,
+ 'copy-devices': -1,
+ 'copy-unsafe-links': 0,
+ 'daemon': -1,
+ 'debug': 1,
+ 'delay-updates': 0,
+ 'delete': 0,
+ 'delete-after': 0,
+ 'delete-before': 0,
+ 'delete-delay': 0,
+ 'delete-during': 0,
+ 'delete-excluded': 0,
+ 'delete-missing-args': 0,
+ 'existing': 0,
+ 'fake-super': 0,
+ 'files-from': 3,
+ 'force': 0,
+ 'from0': 0,
+ 'fsync': 0,
+ 'fuzzy': 0,
+ 'group': 0,
+ 'groupmap': 1,
+ 'hard-links': 0,
+ 'iconv': 1,
+ 'ignore-errors': 0,
+ 'ignore-existing': 0,
+ 'ignore-missing-args': 0,
+ 'ignore-times': 0,
+ 'info': 1,
+ 'inplace': 0,
+ 'link-dest': 2,
+ 'links': 0,
+ 'list-only': 0,
+ 'log-file': 3,
+ 'log-format': 1,
+ 'max-alloc': 1,
+ 'max-delete': 1,
+ 'max-size': 1,
+ 'min-size': 1,
+ 'mkpath': 0,
+ 'modify-window': 1,
+ 'msgs2stderr': 0,
+ 'munge-links': 0,
+ 'new-compress': 0,
+ 'no-W': 0,
+ 'no-implied-dirs': 0,
+ 'no-msgs2stderr': 0,
+ 'no-munge-links': -1,
+ 'no-r': 0,
+ 'no-relative': 0,
+ 'no-specials': 0,
+ 'numeric-ids': 0,
+ 'old-compress': 0,
+ 'one-file-system': 0,
+ 'only-write-batch': 1,
+ 'open-noatime': 0,
+ 'owner': 0,
+ 'partial': 0,
+ 'partial-dir': 2,
+ 'perms': 0,
+ 'preallocate': 0,
+ 'recursive': 0,
+ 'remove-sent-files': 0,
+ 'remove-source-files': 0,
+ 'safe-links': 0,
+ 'sender': 0,
+ 'server': 0,
+ 'size-only': 0,
+ 'skip-compress': 1,
+ 'specials': 0,
+ 'stats': 0,
+ 'stderr': 1,
+ 'suffix': 1,
+ 'super': 0,
+ 'temp-dir': 2,
+ 'timeout': 1,
+ 'times': 0,
+ 'use-qsort': 0,
+ 'usermap': 1,
+ 'write-devices': -1,
+}
+
+### END of options data produced by the cull-options script. ###
+
+import os, sys, re, argparse, glob, socket, time, subprocess
+from argparse import RawTextHelpFormatter
+
+try:
+ from braceexpand import braceexpand
+except:
+ braceexpand = lambda x: [ DE_BACKSLASH_RE.sub(r'\1', x) ]
+
+HAS_DOT_DOT_RE = re.compile(r'(^|/)\.\.(/|$)')
+LONG_OPT_RE = re.compile(r'^--([^=]+)(?:=(.*))?$')
+DE_BACKSLASH_RE = re.compile(r'\\(.)')
+
+def main():
+ if not os.path.isdir(args.dir):
+ die("Restricted directory does not exist!")
+
+ # The format of the environment variables set by sshd:
+ # SSH_ORIGINAL_COMMAND:
+ # rsync --server -vlogDtpre.iLsfxCIvu --etc . ARG # push
+ # rsync --server --sender -vlogDtpre.iLsfxCIvu --etc . ARGS # pull
+ # SSH_CONNECTION (client_ip client_port server_ip server_port):
+ # 192.168.1.100 64106 192.168.1.2 22
+
+ command = os.environ.get('SSH_ORIGINAL_COMMAND', None)
+ if not command:
+ die("Not invoked via sshd")
+ command = command.split(' ', 2)
+ if command[0:1] != ['rsync']:
+ die("SSH_ORIGINAL_COMMAND does not run rsync")
+ if command[1:2] != ['--server']:
+ die("--server option is not the first arg")
+ command = '' if len(command) < 3 else command[2]
+
+ global am_sender
+ am_sender = command.startswith("--sender ") # Restrictive on purpose!
+ if args.ro and not am_sender:
+ die("sending to read-only server is not allowed")
+ if args.wo and am_sender:
+ die("reading from write-only server is not allowed")
+
+ if args.wo or not am_sender:
+ long_opts['sender'] = -1
+ if args.no_del:
+ for opt in long_opts:
+ if opt.startswith(('remove', 'delete')):
+ long_opts[opt] = -1
+ if args.ro:
+ long_opts['log-file'] = -1
+
+ if args.dir != '/':
+ global short_disabled
+ short_disabled += short_disabled_subdir
+
+ short_no_arg_re = short_no_arg
+ short_with_num_re = short_with_num
+ if short_disabled:
+ for ltr in short_disabled:
+ short_no_arg_re = short_no_arg_re.replace(ltr, '')
+ short_with_num_re = short_with_num_re.replace(ltr, '')
+ short_disabled_re = re.compile(r'^-[%s]*([%s])' % (short_no_arg_re, short_disabled))
+ short_no_arg_re = re.compile(r'^-(?=.)[%s]*(e\d*\.\w*)?$' % short_no_arg_re)
+ short_with_num_re = re.compile(r'^-[%s]\d+$' % short_with_num_re)
+
+ log_fh = open(LOGFILE, 'a') if os.path.isfile(LOGFILE) else None
+
+ try:
+ os.chdir(args.dir)
+ except OSError as e:
+ die('unable to chdir to restricted dir:', str(e))
+
+ rsync_opts = [ '--server' ]
+ rsync_args = [ ]
+ saw_the_dot_arg = False
+ last_opt = check_type = None
+
+ for arg in re.findall(r'(?:[^\s\\]+|\\.[^\s\\]*)+', command):
+ if check_type:
+ rsync_opts.append(validated_arg(last_opt, arg, check_type))
+ check_type = None
+ elif saw_the_dot_arg:
+ # NOTE: an arg that starts with a '-' is safe due to our use of "--" in the cmd tuple.
+ try:
+ b_e = braceexpand(arg) # Also removes backslashes
+ except: # Handle errors such as unbalanced braces by just de-backslashing the arg:
+ b_e = [ DE_BACKSLASH_RE.sub(r'\1', arg) ]
+ for xarg in b_e:
+ rsync_args += validated_arg('arg', xarg, wild=True)
+ else: # parsing the option args
+ if arg == '.':
+ saw_the_dot_arg = True
+ continue
+ rsync_opts.append(arg)
+ if short_no_arg_re.match(arg) or short_with_num_re.match(arg):
+ continue
+ disabled = False
+ m = LONG_OPT_RE.match(arg)
+ if m:
+ opt = m.group(1)
+ opt_arg = m.group(2)
+ ct = long_opts.get(opt, None)
+ if ct is None:
+ break # Generate generic failure due to unfinished arg parsing
+ if ct == 0:
+ continue
+ opt = '--' + opt
+ if ct > 0:
+ if opt_arg is not None:
+ rsync_opts[-1] = opt + '=' + validated_arg(opt, opt_arg, ct)
+ else:
+ check_type = ct
+ last_opt = opt
+ continue
+ disabled = True
+ elif short_disabled:
+ m = short_disabled_re.match(arg)
+ if m:
+ disabled = True
+ opt = '-' + m.group(1)
+
+ if disabled:
+ die("option", opt, "has been disabled on this server.")
+ break # Generate a generic failure
+
+ if not saw_the_dot_arg:
+ die("invalid rsync-command syntax or options")
+
+ if args.munge:
+ rsync_opts.append('--munge-links')
+
+ if not rsync_args:
+ rsync_args = [ '.' ]
+
+ cmd = (RSYNC, *rsync_opts, '--', '.', *rsync_args)
+
+ if log_fh:
+ now = time.localtime()
+ host = os.environ.get('SSH_CONNECTION', 'unknown').split()[0] # Drop everything after the IP addr
+ if host.startswith('::ffff:'):
+ host = host[7:]
+ try:
+ host = socket.gethostbyaddr(socket.inet_aton(host))
+ except:
+ pass
+ log_fh.write("%02d:%02d:%02d %-16s %s\n" % (now.tm_hour, now.tm_min, now.tm_sec, host, str(cmd)))
+ log_fh.close()
+
+ # NOTE: This assumes that the rsync protocol will not be maliciously hijacked.
+ if args.no_lock:
+ os.execlp(RSYNC, *cmd)
+ die("execlp(", RSYNC, *cmd, ') failed')
+ child = subprocess.run(cmd)
+ if child.returncode != 0:
+ sys.exit(child.returncode)
+
+
+def validated_arg(opt, arg, typ=3, wild=False):
+ if opt != 'arg': # arg values already have their backslashes removed.
+ arg = DE_BACKSLASH_RE.sub(r'\1', arg)
+
+ orig_arg = arg
+ if arg.startswith('./'):
+ arg = arg[1:]
+ arg = arg.replace('//', '/')
+ if args.dir != '/':
+ if HAS_DOT_DOT_RE.search(arg):
+ die("do not use .. in", opt, "(anchor the path at the root of your restricted dir)")
+ if arg.startswith('/'):
+ arg = args.dir + arg
+
+ if wild:
+ got = glob.glob(arg)
+ if not got:
+ got = [ arg ]
+ else:
+ got = [ arg ]
+
+ ret = [ ]
+ for arg in got:
+ if args.dir != '/' and arg != '.' and (typ == 3 or (typ == 2 and not am_sender)):
+ arg_has_trailing_slash = arg.endswith('/')
+ if arg_has_trailing_slash:
+ arg = arg[:-1]
+ else:
+ arg_has_trailing_slash_dot = arg.endswith('/.')
+ if arg_has_trailing_slash_dot:
+ arg = arg[:-2]
+ real_arg = os.path.realpath(arg)
+ if arg != real_arg and not real_arg.startswith(args.dir_slash):
+ die('unsafe arg:', orig_arg, [arg, real_arg])
+ if arg_has_trailing_slash:
+ arg += '/'
+ elif arg_has_trailing_slash_dot:
+ arg += '/.'
+ if opt == 'arg' and arg.startswith(args.dir_slash):
+ arg = arg[args.dir_slash_len:]
+ if arg == '':
+ arg = '.'
+ ret.append(arg)
+
+ return ret if wild else ret[0]
+
+
+def lock_or_die(dirname):
+ import fcntl
+ global lock_handle
+ lock_handle = os.open(dirname, os.O_RDONLY)
+ try:
+ fcntl.flock(lock_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except:
+ die('Another instance of rrsync is already accessing this directory.')
+
+
+def die(*msg):
+ print(sys.argv[0], 'error:', *msg, file=sys.stderr)
+ if sys.stdin.isatty():
+ arg_parser.print_help(sys.stderr)
+ sys.exit(1)
+
+
+# This class displays the --help to the user on argparse error IFF they're running it interactively.
+class OurArgParser(argparse.ArgumentParser):
+ def error(self, msg):
+ die(msg)
+
+
+if __name__ == '__main__':
+ our_desc = """Use "man rrsync" to learn how to restrict ssh users to using a restricted rsync command."""
+ arg_parser = OurArgParser(description=our_desc, add_help=False)
+ only_group = arg_parser.add_mutually_exclusive_group()
+ only_group.add_argument('-ro', action='store_true', help="Allow only reading from the DIR. Implies -no-del and -no-lock.")
+ only_group.add_argument('-wo', action='store_true', help="Allow only writing to the DIR.")
+ arg_parser.add_argument('-munge', action='store_true', help="Enable rsync's --munge-links on the server side.")
+ arg_parser.add_argument('-no-del', action='store_true', help="Disable rsync's --delete* and --remove* options.")
+ arg_parser.add_argument('-no-lock', action='store_true', help="Avoid the single-run (per-user) lock check.")
+ arg_parser.add_argument('-help', '-h', action='help', help="Output this help message and exit.")
+ arg_parser.add_argument('dir', metavar='DIR', help="The restricted directory to use.")
+ args = arg_parser.parse_args()
+ args.dir = os.path.realpath(args.dir)
+ args.dir_slash = args.dir + '/'
+ args.dir_slash_len = len(args.dir_slash)
+ if args.ro:
+ args.no_del = True
+ elif not args.no_lock:
+ lock_or_die(args.dir)
+ main()
+
+# vim: sw=4 et
diff --git a/support/rrsync.1.md b/support/rrsync.1.md
new file mode 100644
index 0000000..98f2cab
--- /dev/null
+++ b/support/rrsync.1.md
@@ -0,0 +1,166 @@
+## NAME
+
+rrsync - a script to setup restricted rsync users via ssh logins
+
+## SYNOPSIS
+
+```
+rrsync [-ro|-rw] [-munge] [-no-del] [-no-lock] DIR
+```
+
+The single non-option argument specifies the restricted _DIR_ to use. It can be
+relative to the user's home directory or an absolute path.
+
+The online version of this manpage (that includes cross-linking of topics)
+is available at <https://download.samba.org/pub/rsync/rrsync.1>.
+
+## DESCRIPTION
+
+A user's ssh login can be restricted to only allow the running of an rsync
+transfer in one of two easy ways:
+
+* forcing the running of the rrsync script
+* forcing the running of an rsync daemon-over-ssh command.
+
+Both of these setups use a feature of ssh that allows a command to be forced to
+run instead of an interactive shell. However, if the user's home shell is bash,
+please see [BASH SECURITY ISSUE](#) for a potential issue.
+
+To use the rrsync script, edit the user's `~/.ssh/authorized_keys` file and add
+a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:
+
+> ```
+> command="rrsync DIR"
+> command="rrsync -ro DIR"
+> command="rrsync -munge -no-del DIR"
+> ```
+
+Then, ensure that the rrsync script has your desired option restrictions. You
+may want to copy the script to a local bin dir with a unique name if you want
+to have multiple configurations. One or more rrsync options can be specified
+prior to the _DIR_ if you want to further restrict the transfer.
+
+To use an rsync daemon setup, edit the user's `~/.ssh/authorized_keys` file and
+add a prefix like one of the following (followed by a space) in front of each
+ssh-key line that should be restricted:
+
+> ```
+> command="rsync --server --daemon ."
+> command="rsync --server --daemon --config=/PATH/TO/rsyncd.conf ."
+> ```
+
+Then, ensure that the rsyncd.conf file is created with one or more module names
+with the appropriate path and option restrictions. If rsync's
+[`--config`](rsync.1#dopt) option is omitted, it defaults to `~/rsyncd.conf`.
+See the [**rsyncd.conf**(5)](rsyncd.conf.5) manpage for details of how to
+configure an rsync daemon.
+
+When using rrsync, there can be just one restricted dir per authorized key. A
+daemon setup, on the other hand, allows multiple module names inside the config
+file, each one with its own path setting.
+
+The remainder of this manpage is dedicated to using the rrsync script.
+
+## OPTIONS
+
+0. `-ro`
+
+ Allow only reading from the DIR. Implies [`-no-del`](#opt) and
+ [`-no-lock`](#opt).
+
+0. `-wo`
+
+ Allow only writing to the DIR.
+
+0. `-munge`
+
+ Enable rsync's [`--munge-links`](rsync.1#opt) on the server side.
+
+0. `-no-del`
+
+ Disable rsync's `--delete*` and `--remove*` options.
+
+0. `-no-lock`
+
+ Avoid the single-run (per-user) lock check. Useful with [`-munge`](#opt).
+
+0. `-help`, `-h`
+
+ Output this help message and exit.
+
+## SECURITY RESTRICTIONS
+
+The rrsync script validates the path arguments it is sent to try to restrict
+them to staying within the specified DIR.
+
+The rrsync script rejects rsync's [`--copy-links`](rsync.1#opt) option (by
+default) so that a copy cannot dereference a symlink within the DIR to get to a
+file outside the DIR.
+
+The rrsync script rejects rsync's [`--protect-args`](rsync.1#opt) (`-s`) option
+because it would allow options to be sent to the server-side that the script
+cannot check. If you want to support `--protect-args`, use a daemon-over-ssh
+setup.
+
+The rrsync script accepts just a subset of rsync's options that the real rsync
+uses when running the server command. A few extra convenience options are also
+included to help it to interact with BackupPC and accept some convenient user
+overrides.
+
+The script (or a copy of it) can be manually edited if you want it to customize
+the option handling.
+
+## BASH SECURITY ISSUE
+
+If your users have bash set as their home shell, bash may try to be overly
+helpful and ensure that the user's login bashrc files are run prior to
+executing the forced command. This can be a problem if the user can somehow
+update their home bashrc files, perhaps via the restricted copy, a shared home
+directory, or something similar.
+
+One simple way to avoid the issue is to switch the user to a simpler shell,
+such as dash. When choosing the new home shell, make sure that you're not
+choosing bash in disguise, as it is unclear if it avoids the security issue.
+
+Another potential fix is to ensure that the user's home directory is not a
+shared mount and that they have no means of copying files outside of their
+restricted directories. This may require you to force the enabling of symlink
+munging on the server side.
+
+A future version of openssh may have a change to the handling of forced
+commands that allows it to avoid using the user's home shell.
+
+## EXAMPLES
+
+The `~/.ssh/authorized_keys` file might have lines in it like this:
+
+> ```
+> command="rrsync client/logs" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzG...
+> command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmk...
+> ```
+
+## FILES
+
+~/.ssh/authorized_keys
+
+## SEE ALSO
+
+[**rsync**(1)](rsync.1), [**rsyncd.conf**(5)](rsyncd.conf.5)
+
+## VERSION
+
+This manpage is current for version @VERSION@ of rsync.
+
+## CREDITS
+
+rsync is distributed under the GNU General Public License. See the file
+[COPYING](COPYING) for details.
+
+An rsync web site is available at <https://rsync.samba.org/> and its github
+project is <https://github.com/WayneD/rsync>.
+
+## AUTHOR
+
+The original rrsync perl script was written by Joe Smith. Many people have
+later contributed to it. The python version was created by Wayne Davison.
diff --git a/support/rsync-no-vanished b/support/rsync-no-vanished
new file mode 100755
index 0000000..b31a5d2
--- /dev/null
+++ b/support/rsync-no-vanished
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+REAL_RSYNC=/usr/bin/rsync
+IGNOREEXIT=24
+IGNOREOUT='^(file has vanished: |rsync warning: some files vanished before they could be transferred)'
+
+# If someone installs this as "rsync", make sure we don't affect a server run.
+for arg in "${@}"; do
+ if [[ "$arg" == --server ]]; then
+ exec $REAL_RSYNC "${@}"
+ exit $? # Not reached
+ fi
+done
+
+set -o pipefail
+
+# This filters stderr without merging it with stdout:
+{ $REAL_RSYNC "${@}" 2>&1 1>&3 3>&- | grep -E -v "$IGNOREOUT"; ret=${PIPESTATUS[0]}; } 3>&1 1>&2
+
+if [[ $ret == $IGNOREEXIT ]]; then
+ ret=0
+fi
+
+exit $ret
diff --git a/support/rsync-slash-strip b/support/rsync-slash-strip
new file mode 100755
index 0000000..b57e61c
--- /dev/null
+++ b/support/rsync-slash-strip
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# This script can be used as an rsync command-line filter that strips a single
+# trailing slash from each arg. That treats "src/" the same as "src", thus
+# you need to use "src/." or "src//" for just the contents of the "src" dir.
+# (Note that command-line dir-excludes would need to use "excl//" too.)
+#
+# To use this, name it something like "rs", put it somewhere in your path, and
+# then use "rs" in place of "rsync" when you are typing your copy commands.
+
+REAL_RSYNC=/usr/bin/rsync
+
+args=()
+for arg in "${@}"; do
+ if [[ "$arg" == --server ]]; then
+ exec $REAL_RSYNC "${@}"
+ exit $? # Not reached
+ fi
+ if [[ "$arg" == / ]]; then
+ args=("${args[@]}" /)
+ else
+ args=("${args[@]}" "${arg%/}")
+ fi
+done
+exec $REAL_RSYNC "${args[@]}"
diff --git a/support/rsyncstats b/support/rsyncstats
new file mode 100755
index 0000000..99fd545
--- /dev/null
+++ b/support/rsyncstats
@@ -0,0 +1,312 @@
+#!/usr/bin/env perl
+#
+# This script parses the default logfile format produced by rsync when running
+# as a daemon with transfer logging enabled. It also parses a slightly tweaked
+# version of the default format where %o has been replaced with %i.
+#
+# This script is derived from the xferstats script that comes with wuftpd. See
+# the usage message at the bottom for the options it takes.
+#
+# Andrew Tridgell, October 1998
+
+use Getopt::Long;
+
+# You may wish to edit the next line to customize for your default log file.
+$usage_file = "/var/log/rsyncd.log";
+
+# Edit the following lines for default report settings.
+# Entries defined here will be over-ridden by the command line.
+
+$hourly_report = 0;
+$domain_report = 0;
+$total_report = 0;
+$depth_limit = 9999;
+$only_section = '';
+
+&Getopt::Long::Configure('bundling');
+&usage if !&GetOptions(
+ 'hourly-report|h' => \$hourly_report,
+ 'domain-report|d' => \$domain_report,
+ 'domain|D:s' => \$only_domain,
+ 'total-report|t' => \$total_report,
+ 'depth-limit|l:i' => \$depth_limit,
+ 'real|r' => \$real,
+ 'anon|a' => \$anon,
+ 'section|s:s' => \$only_section,
+ 'file|f:s' => \$usage_file,
+);
+
+$anon = 1 if !$real && !$anon;
+
+open(LOG, $usage_file) || die "Error opening usage log file: $usage_file\n";
+
+if ($only_domain) {
+ print "Transfer Totals include the '$only_domain' domain only.\n";
+ print "All other domains are filtered out for this report.\n\n";
+}
+
+if ($only_section) {
+ print "Transfer Totals include the '$only_section' section only.\n";
+ print "All other sections are filtered out for this report.\n\n";
+}
+
+line: while (<LOG>) {
+
+my $syslog_prefix = '\w\w\w +\d+ \d\d:\d\d:\d\d \S+ rsyncd';
+my $rsyncd_prefix = '\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d ';
+
+ next unless ($day,$time,$op,$host,$module,$file,$bytes)
+ = m{^
+ ( \w\w\w\s+\d+ | \d+/\d\d/\d\d ) \s+ # day
+ (\d\d:\d\d:\d\d) \s+ # time
+ [^[]* \[\d+\]:? \s+ # pid (ignored)
+ (send|recv|[<>]f\S+) \s+ # op (%o or %i)
+ (\S+) \s+ # host
+ \[\d+\.\d+\.\d+\.\d+\] \s+ # IP (ignored)
+ (\S+) \s+ # module
+ \(\S*\) \s+ # user (ignored)
+ (.*) \s+ # file name
+ (\d+) # file length in bytes
+ $ }x;
+
+ # TODO actually divide the data by into send/recv categories
+ if ($op =~ /^>/) {
+ $op = 'send';
+ } elsif ($op =~ /^</) {
+ $op = 'recv';
+ }
+
+ $daytime = $day;
+ $hour = substr($time,0,2);
+
+ $file = $module . "/" . $file;
+
+ $file =~ s|//|/|mg;
+
+ @path = split(/\//, $file);
+
+ $pathkey = "";
+ for ($i=0; $i <= $#path && $i <= $depth_limit; $i++) {
+ $pathkey = $pathkey . "/" . $path[$i];
+ }
+
+ if ($only_section ne '') {
+ next unless (substr($pathkey,0,length($only_section)) eq $only_section);
+ }
+
+ $host =~ tr/A-Z/a-z/;
+
+ @address = split(/\./, $host);
+
+ $domain = $address[$#address];
+ if ( int($address[0]) > 0 || $#address < 2 )
+ { $domain = "unresolved"; }
+
+ if ($only_domain ne '') {
+ next unless (substr($domain,0,length($only_domain)) eq $only_domain);
+ }
+
+
+# printf("c=%d day=%s bytes=%d file=%s path=%s\n",
+# $#line, $daytime, $bytes, $file, $pathkey);
+
+ $xferfiles++; # total files sent
+ $xfertfiles++; # total files sent
+ $xferfiles{$daytime}++; # files per day
+ $groupfiles{$pathkey}++; # per-group accesses
+ $domainfiles{$domain}++;
+
+ $xferbytes{$daytime} += $bytes; # bytes per day
+ $domainbytes{$domain} += $bytes; # xmit bytes to domain
+ $xferbytes += $bytes; # total bytes sent
+ $groupbytes{$pathkey} += $bytes; # per-group bytes sent
+
+ $xfertfiles{$hour}++; # files per hour
+ $xfertbytes{$hour} += $bytes; # bytes per hour
+ $xfertbytes += $bytes; # total bytes sent
+}
+close LOG;
+
+#@syslist = keys %systemfiles;
+@dates = sort datecompare keys %xferbytes;
+
+if ($xferfiles == 0) {die "There was no data to process.\n";}
+
+
+print "TOTALS FOR SUMMARY PERIOD ", $dates[0], " TO ", $dates[$#dates], "\n\n";
+printf("Files Transmitted During Summary Period %12.0f\n", $xferfiles);
+printf("Bytes Transmitted During Summary Period %12.0f\n", $xferbytes);
+#printf("Systems Using Archives %12.0f\n\n", $#syslist+1);
+
+printf("Average Files Transmitted Daily %12.0f\n",
+ $xferfiles / ($#dates + 1));
+printf("Average Bytes Transmitted Daily %12.0f\n",
+ $xferbytes / ($#dates + 1));
+
+format top1 =
+
+Daily Transmission Statistics
+
+ Number Of Number of Percent Of Percent Of
+ Date Files Sent MB Sent Files Sent Bytes Sent
+--------------- ---------- ----------- ---------- ----------
+.
+
+format line1 =
+@<<<<<<<<<<<<<< @>>>>>>>>> @>>>>>>>>>> @>>>>>>> @>>>>>>>
+$date, $nfiles, $nbytes/(1024*1024), $pctfiles, $pctbytes
+.
+
+$^ = top1;
+$~ = line1;
+
+foreach $date (sort datecompare keys %xferbytes) {
+
+ $nfiles = $xferfiles{$date};
+ $nbytes = $xferbytes{$date};
+ $pctfiles = sprintf("%8.2f", 100*$xferfiles{$date} / $xferfiles);
+ $pctbytes = sprintf("%8.2f", 100*$xferbytes{$date} / $xferbytes);
+ write;
+}
+
+if ($total_report) {
+format top2 =
+
+Total Transfers from each Archive Section (By bytes)
+
+ - Percent -
+ Archive Section NFiles MB Files Bytes
+------------------------------------- ------- ----------- ----- -------
+.
+
+format line2 =
+@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>> @>>>>>>>>>> @>>>> @>>>>
+$section, $files, $bytes/(1024*1024), $pctfiles, $pctbytes
+.
+
+$| = 1;
+$- = 0;
+$^ = top2;
+$~ = line2;
+
+foreach $section (sort bytecompare keys %groupfiles) {
+
+ $files = $groupfiles{$section};
+ $bytes = $groupbytes{$section};
+ $pctbytes = sprintf("%8.2f", 100 * $groupbytes{$section} / $xferbytes);
+ $pctfiles = sprintf("%8.2f", 100 * $groupfiles{$section} / $xferfiles);
+ write;
+
+}
+
+if ( $xferfiles < 1 ) { $xferfiles = 1; }
+if ( $xferbytes < 1 ) { $xferbytes = 1; }
+}
+
+if ($domain_report) {
+format top3 =
+
+Total Transfer Amount By Domain
+
+ Number Of Number of Percent Of Percent Of
+Domain Name Files Sent MB Sent Files Sent Bytes Sent
+----------- ---------- ------------ ---------- ----------
+.
+
+format line3 =
+@<<<<<<<<<< @>>>>>>>>> @>>>>>>>>>>> @>>>>>>> @>>>>>>>
+$domain, $files, $bytes/(1024*1024), $pctfiles, $pctbytes
+.
+
+$- = 0;
+$^ = top3;
+$~ = line3;
+
+foreach $domain (sort domnamcompare keys %domainfiles) {
+
+ if ( $domainsecs{$domain} < 1 ) { $domainsecs{$domain} = 1; }
+
+ $files = $domainfiles{$domain};
+ $bytes = $domainbytes{$domain};
+ $pctfiles = sprintf("%8.2f", 100 * $domainfiles{$domain} / $xferfiles);
+ $pctbytes = sprintf("%8.2f", 100 * $domainbytes{$domain} / $xferbytes);
+ write;
+
+}
+
+}
+
+if ($hourly_report) {
+
+format top8 =
+
+Hourly Transmission Statistics
+
+ Number Of Number of Percent Of Percent Of
+ Time Files Sent MB Sent Files Sent Bytes Sent
+--------------- ---------- ----------- ---------- ----------
+.
+
+format line8 =
+@<<<<<<<<<<<<<< @>>>>>>>>> @>>>>>>>>>> @>>>>>>> @>>>>>>>
+$hour, $nfiles, $nbytes/(1024*1024), $pctfiles, $pctbytes
+.
+
+
+$| = 1;
+$- = 0;
+$^ = top8;
+$~ = line8;
+
+foreach $hour (sort keys %xfertbytes) {
+
+ $nfiles = $xfertfiles{$hour};
+ $nbytes = $xfertbytes{$hour};
+ $pctfiles = sprintf("%8.2f", 100*$xfertfiles{$hour} / $xferfiles);
+ $pctbytes = sprintf("%8.2f", 100*$xfertbytes{$hour} / $xferbytes);
+ write;
+}
+}
+exit(0);
+
+sub datecompare {
+ $a cmp $b;
+}
+
+sub domnamcompare {
+
+ $sdiff = length($a) - length($b);
+ ($sdiff < 0) ? -1 : ($sdiff > 0) ? 1 : $a cmp $b;
+
+}
+
+sub bytecompare {
+
+ $bdiff = $groupbytes{$b} - $groupbytes{$a};
+ ($bdiff < 0) ? -1 : ($bdiff > 0) ? 1 : $a cmp $b;
+
+}
+
+sub faccompare {
+
+ $fdiff = $fac{$b} - $fac{$a};
+ ($fdiff < 0) ? -1 : ($fdiff > 0) ? 1 : $a cmp $b;
+
+}
+
+sub usage
+{
+ die <<EOT;
+USAGE: rsyncstats [options]
+
+OPTIONS:
+ -f FILENAME Use FILENAME for the log file.
+ -h Include report on hourly traffic.
+ -d Include report on domain traffic.
+ -t Report on total traffic by section.
+ -D DOMAIN Report only on traffic from DOMAIN.
+ -l DEPTH Set DEPTH of path detail for sections.
+ -s SECTION Set SECTION to report on. For example, "-s /pub"
+ will report only on paths under "/pub".
+EOT
+}
diff --git a/support/savetransfer.c b/support/savetransfer.c
new file mode 100644
index 0000000..808a6f2
--- /dev/null
+++ b/support/savetransfer.c
@@ -0,0 +1,175 @@
+/* This program can record the stream of data flowing to or from a program.
+ * This allows it to be used to check that rsync's data that is flowing
+ * through a remote shell is not being corrupted (for example).
+ *
+ * Usage: savetransfer [-i|-o] OUTPUT_FILE PROGRAM [ARGS...]
+ * -i Save the input going to PROGRAM to the OUTPUT_FILE
+ * -o Save the output coming from PROGRAM to the OUTPUT_FILE
+ *
+ * If you want to capture the flow of data for an rsync command, use one of
+ * the following commands (the resulting files should be identical):
+ *
+ * rsync -av --rsh="savetransfer -i /tmp/to.server ssh"
+ * --rsync-path="savetransfer -i /tmp/from.client rsync" SOURCE DEST
+ *
+ * rsync -av --rsh="savetransfer -o /tmp/from.server ssh"
+ * --rsync-path="savetransfer -o /tmp/to.client rsync" SOURCE DEST
+ *
+ * Note that this program aborts after 30 seconds of inactivity, so you'll need
+ * to change it if that is not enough dead time for your transfer. Also, some
+ * of the above commands will not notice that the transfer is done (if we're
+ * saving the input to a PROGRAM and the PROGRAM goes away: we won't notice
+ * that it's gone unless more data comes in) -- when this happens it will delay
+ * at the end of the transfer until the timeout period expires.
+ */
+
+#include "../rsync.h"
+
+#define TIMEOUT_SECONDS 30
+
+#ifdef HAVE_SIGACTION
+static struct sigaction sigact;
+#endif
+
+void run_program(char **command);
+
+char buf[4096];
+int save_data_from_program = 0;
+
+int
+main(int argc, char *argv[])
+{
+ int fd_file, len;
+ struct timeval tv;
+ fd_set fds;
+
+ argv++;
+ if (--argc && argv[0][0] == '-') {
+ if (argv[0][1] == 'o')
+ save_data_from_program = 1;
+ else if (argv[0][1] == 'i')
+ save_data_from_program = 0;
+ else {
+ fprintf(stderr, "Unknown option: %s\n", argv[0]);
+ exit(1);
+ }
+ argv++;
+ argc--;
+ }
+ if (argc < 2) {
+ fprintf(stderr, "Usage: savetransfer [-i|-o] OUTPUT_FILE PROGRAM [ARGS...]\n");
+ fprintf(stderr, "-i Save the input going to PROGRAM to the OUTPUT_FILE\n");
+ fprintf(stderr, "-o Save the output coming from PROGRAM to the OUTPUT_FILE\n");
+ exit(1);
+ }
+ if ((fd_file = open(*argv, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644)) < 0) {
+ fprintf(stderr, "Unable to write to `%s': %s\n", *argv, strerror(errno));
+ exit(1);
+ }
+ set_blocking(fd_file);
+
+ SIGACTION(SIGPIPE, SIG_IGN);
+
+ run_program(argv + 1);
+
+#if defined HAVE_SETMODE && O_BINARY
+ setmode(STDIN_FILENO, O_BINARY);
+ setmode(STDOUT_FILENO, O_BINARY);
+#endif
+ set_nonblocking(STDIN_FILENO);
+ set_blocking(STDOUT_FILENO);
+
+ while (1) {
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ tv.tv_sec = TIMEOUT_SECONDS;
+ tv.tv_usec = 0;
+ if (!select(STDIN_FILENO+1, &fds, NULL, NULL, &tv))
+ break;
+ if (!FD_ISSET(STDIN_FILENO, &fds))
+ break;
+ if ((len = read(STDIN_FILENO, buf, sizeof buf)) <= 0)
+ break;
+ if (write(STDOUT_FILENO, buf, len) != len) {
+ fprintf(stderr, "Failed to write data to stdout: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (write(fd_file, buf, len) != len) {
+ fprintf(stderr, "Failed to write data to fd_file: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ return 0;
+}
+
+void
+run_program(char **command)
+{
+ int pipe_fds[2], ret;
+ pid_t pid;
+
+ if (pipe(pipe_fds) < 0) {
+ fprintf(stderr, "pipe failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if ((pid = fork()) < 0) {
+ fprintf(stderr, "fork failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (pid == 0) {
+ if (save_data_from_program)
+ ret = dup2(pipe_fds[1], STDOUT_FILENO);
+ else
+ ret = dup2(pipe_fds[0], STDIN_FILENO);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to dup (in child): %s\n", strerror(errno));
+ exit(1);
+ }
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ set_blocking(STDIN_FILENO);
+ set_blocking(STDOUT_FILENO);
+ execvp(command[0], command);
+ fprintf(stderr, "Failed to exec %s: %s\n", command[0], strerror(errno));
+ exit(1);
+ }
+
+ if (save_data_from_program)
+ ret = dup2(pipe_fds[0], STDIN_FILENO);
+ else
+ ret = dup2(pipe_fds[1], STDOUT_FILENO);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to dup (in parent): %s\n", strerror(errno));
+ exit(1);
+ }
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+}
+
+void
+set_nonblocking(int fd)
+{
+ int val;
+
+ if ((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return;
+ if (!(val & NONBLOCK_FLAG)) {
+ val |= NONBLOCK_FLAG;
+ fcntl(fd, F_SETFL, val);
+ }
+}
+
+void
+set_blocking(int fd)
+{
+ int val;
+
+ if ((val = fcntl(fd, F_GETFL, 0)) < 0)
+ return;
+ if (val & NONBLOCK_FLAG) {
+ val &= ~NONBLOCK_FLAG;
+ fcntl(fd, F_SETFL, val);
+ }
+}
diff --git a/syscall.c b/syscall.c
new file mode 100644
index 0000000..d92074a
--- /dev/null
+++ b/syscall.c
@@ -0,0 +1,714 @@
+/*
+ * Syscall wrappers to ensure that nothing gets done in dry_run mode
+ * and to handle system peculiarities.
+ *
+ * Copyright (C) 1998 Andrew Tridgell
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+#if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_ATTR_H
+#include <sys/attr.h>
+#endif
+
+#if defined HAVE_SYS_FALLOCATE && !defined HAVE_FALLOCATE
+#include <sys/syscall.h>
+#endif
+
+extern int dry_run;
+extern int am_root;
+extern int am_sender;
+extern int read_only;
+extern int list_only;
+extern int inplace;
+extern int preallocate_files;
+extern int preserve_perms;
+extern int preserve_executability;
+extern int open_noatime;
+
+#ifndef S_BLKSIZE
+# if defined hpux || defined __hpux__ || defined __hpux
+# define S_BLKSIZE 1024
+# elif defined _AIX && defined _I386
+# define S_BLKSIZE 4096
+# else
+# define S_BLKSIZE 512
+# endif
+#endif
+
+#ifdef SUPPORT_CRTIMES
+#ifdef HAVE_GETATTRLIST
+#pragma pack(push, 4)
+struct create_time {
+ uint32 length;
+ struct timespec crtime;
+};
+#pragma pack(pop)
+#elif defined __CYGWIN__
+#include <windows.h>
+#endif
+#endif
+
+#define RETURN_ERROR_IF(x,e) \
+ do { \
+ if (x) { \
+ errno = (e); \
+ return -1; \
+ } \
+ } while (0)
+
+#define RETURN_ERROR_IF_RO_OR_LO RETURN_ERROR_IF(read_only || list_only, EROFS)
+
+int do_unlink(const char *path)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+ return unlink(path);
+}
+
+#ifdef SUPPORT_LINKS
+int do_symlink(const char *lnk, const char *path)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+#if defined NO_SYMLINK_XATTRS || defined NO_SYMLINK_USER_XATTRS
+ /* For --fake-super, we create a normal file with mode 0600
+ * and write the lnk into it. */
+ if (am_root < 0) {
+ int ok, len = strlen(lnk);
+ int fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
+ if (fd < 0)
+ return -1;
+ ok = write(fd, lnk, len) == len;
+ if (close(fd) < 0)
+ ok = 0;
+ return ok ? 0 : -1;
+ }
+#endif
+
+ return symlink(lnk, path);
+}
+
+#if defined NO_SYMLINK_XATTRS || defined NO_SYMLINK_USER_XATTRS
+ssize_t do_readlink(const char *path, char *buf, size_t bufsiz)
+{
+ /* For --fake-super, we read the link from the file. */
+ if (am_root < 0) {
+ int fd = do_open_nofollow(path, O_RDONLY);
+ if (fd >= 0) {
+ int len = read(fd, buf, bufsiz);
+ close(fd);
+ return len;
+ }
+ if (errno != ELOOP)
+ return -1;
+ /* A real symlink needs to be turned into a fake one on the receiving
+ * side, so tell the generator that the link has no length. */
+ if (!am_sender)
+ return 0;
+ /* Otherwise fall through and let the sender report the real length. */
+ }
+
+ return readlink(path, buf, bufsiz);
+}
+#endif
+#endif
+
+#if defined HAVE_LINK || defined HAVE_LINKAT
+int do_link(const char *old_path, const char *new_path)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+#ifdef HAVE_LINKAT
+ return linkat(AT_FDCWD, old_path, AT_FDCWD, new_path, 0);
+#else
+ return link(old_path, new_path);
+#endif
+}
+#endif
+
+int do_lchown(const char *path, uid_t owner, gid_t group)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+#ifndef HAVE_LCHOWN
+#define lchown chown
+#endif
+ return lchown(path, owner, group);
+}
+
+int do_mknod(const char *pathname, mode_t mode, dev_t dev)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ /* For --fake-super, we create a normal file with mode 0600. */
+ if (am_root < 0) {
+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
+ if (fd < 0 || close(fd) < 0)
+ return -1;
+ return 0;
+ }
+
+#if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
+ if (S_ISFIFO(mode))
+ return mkfifo(pathname, mode);
+#endif
+#if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H
+ if (S_ISSOCK(mode)) {
+ int sock;
+ struct sockaddr_un saddr;
+ unsigned int len = strlcpy(saddr.sun_path, pathname, sizeof saddr.sun_path);
+ if (len >= sizeof saddr.sun_path) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+#ifdef HAVE_SOCKADDR_UN_LEN
+ saddr.sun_len = len + 1;
+#endif
+ saddr.sun_family = AF_UNIX;
+
+ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0
+ || (unlink(pathname) < 0 && errno != ENOENT)
+ || (bind(sock, (struct sockaddr*)&saddr, sizeof saddr)) < 0)
+ return -1;
+ close(sock);
+#ifdef HAVE_CHMOD
+ return do_chmod(pathname, mode);
+#else
+ return 0;
+#endif
+ }
+#endif
+#ifdef HAVE_MKNOD
+ return mknod(pathname, mode, dev);
+#else
+ return -1;
+#endif
+}
+
+int do_rmdir(const char *pathname)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+ return rmdir(pathname);
+}
+
+int do_open(const char *pathname, int flags, mode_t mode)
+{
+ if (flags != O_RDONLY) {
+ RETURN_ERROR_IF(dry_run, 0);
+ RETURN_ERROR_IF_RO_OR_LO;
+ }
+
+#ifdef O_NOATIME
+ if (open_noatime)
+ flags |= O_NOATIME;
+#endif
+
+ return open(pathname, flags | O_BINARY, mode);
+}
+
+#ifdef HAVE_CHMOD
+int do_chmod(const char *path, mode_t mode)
+{
+ static int switch_step = 0;
+ int code;
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ switch (switch_step) {
+#ifdef HAVE_LCHMOD
+ case 0:
+ if ((code = lchmod(path, mode & CHMOD_BITS)) == 0)
+ break;
+ if (errno == ENOSYS)
+ switch_step++;
+ else if (errno != ENOTSUP)
+ break;
+#endif
+ /* FALLTHROUGH */
+ default:
+ if (S_ISLNK(mode)) {
+# if defined HAVE_SETATTRLIST
+ struct attrlist attrList;
+ uint32_t m = mode & CHMOD_BITS; /* manpage is wrong: not mode_t! */
+
+ memset(&attrList, 0, sizeof attrList);
+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrList.commonattr = ATTR_CMN_ACCESSMASK;
+ if ((code = setattrlist(path, &attrList, &m, sizeof m, FSOPT_NOFOLLOW)) == 0)
+ break;
+ if (errno == ENOTSUP)
+ code = 1;
+# else
+ code = 1;
+# endif
+ } else
+ code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
+ break;
+ }
+ if (code != 0 && (preserve_perms || preserve_executability))
+ return code;
+ return 0;
+}
+#endif
+
+int do_rename(const char *old_path, const char *new_path)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+ return rename(old_path, new_path);
+}
+
+#ifdef HAVE_FTRUNCATE
+int do_ftruncate(int fd, OFF_T size)
+{
+ int ret;
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ do {
+ ret = ftruncate(fd, size);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+#endif
+
+void trim_trailing_slashes(char *name)
+{
+ int l;
+ /* Some BSD systems cannot make a directory if the name
+ * contains a trailing slash.
+ * <http://www.opensource.apple.com/bugs/X/BSD%20Kernel/2734739.html> */
+
+ /* Don't change empty string; and also we can't improve on
+ * "/" */
+
+ l = strlen(name);
+ while (l > 1) {
+ if (name[--l] != '/')
+ break;
+ name[l] = '\0';
+ }
+}
+
+int do_mkdir(char *path, mode_t mode)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+ trim_trailing_slashes(path);
+ return mkdir(path, mode);
+}
+
+/* like mkstemp but forces permissions */
+int do_mkstemp(char *template, mode_t perms)
+{
+ RETURN_ERROR_IF(dry_run, 0);
+ RETURN_ERROR_IF(read_only, EROFS);
+ perms |= S_IWUSR;
+
+#if defined HAVE_SECURE_MKSTEMP && defined HAVE_FCHMOD && (!defined HAVE_OPEN64 || defined HAVE_MKSTEMP64)
+ {
+ int fd = mkstemp(template);
+ if (fd == -1)
+ return -1;
+ if (fchmod(fd, perms) != 0 && preserve_perms) {
+ int errno_save = errno;
+ close(fd);
+ unlink(template);
+ errno = errno_save;
+ return -1;
+ }
+#if defined HAVE_SETMODE && O_BINARY
+ setmode(fd, O_BINARY);
+#endif
+ return fd;
+ }
+#else
+ if (!mktemp(template))
+ return -1;
+ return do_open(template, O_RDWR|O_EXCL|O_CREAT, perms);
+#endif
+}
+
+int do_stat(const char *path, STRUCT_STAT *st)
+{
+#ifdef USE_STAT64_FUNCS
+ return stat64(path, st);
+#else
+ return stat(path, st);
+#endif
+}
+
+int do_lstat(const char *path, STRUCT_STAT *st)
+{
+#ifdef SUPPORT_LINKS
+# ifdef USE_STAT64_FUNCS
+ return lstat64(path, st);
+# else
+ return lstat(path, st);
+# endif
+#else
+ return do_stat(path, st);
+#endif
+}
+
+int do_fstat(int fd, STRUCT_STAT *st)
+{
+#ifdef USE_STAT64_FUNCS
+ return fstat64(fd, st);
+#else
+ return fstat(fd, st);
+#endif
+}
+
+OFF_T do_lseek(int fd, OFF_T offset, int whence)
+{
+#ifdef HAVE_LSEEK64
+#if !SIZEOF_OFF64_T
+ OFF_T lseek64();
+#else
+ off64_t lseek64();
+#endif
+ return lseek64(fd, offset, whence);
+#else
+ return lseek(fd, offset, whence);
+#endif
+}
+
+#ifdef HAVE_SETATTRLIST
+int do_setattrlist_times(const char *path, STRUCT_STAT *stp)
+{
+ struct attrlist attrList;
+ struct timespec ts[2];
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ /* Yes, this is in the opposite order of utime and similar. */
+ ts[0].tv_sec = stp->st_mtime;
+ ts[0].tv_nsec = stp->ST_MTIME_NSEC;
+
+ ts[1].tv_sec = stp->st_atime;
+ ts[1].tv_nsec = stp->ST_ATIME_NSEC;
+
+ memset(&attrList, 0, sizeof attrList);
+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrList.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
+ return setattrlist(path, &attrList, ts, sizeof ts, FSOPT_NOFOLLOW);
+}
+
+#ifdef SUPPORT_CRTIMES
+int do_setattrlist_crtime(const char *path, time_t crtime)
+{
+ struct attrlist attrList;
+ struct timespec ts;
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ ts.tv_sec = crtime;
+ ts.tv_nsec = 0;
+
+ memset(&attrList, 0, sizeof attrList);
+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrList.commonattr = ATTR_CMN_CRTIME;
+ return setattrlist(path, &attrList, &ts, sizeof ts, FSOPT_NOFOLLOW);
+}
+#endif
+#endif /* HAVE_SETATTRLIST */
+
+#ifdef SUPPORT_CRTIMES
+time_t get_create_time(const char *path, STRUCT_STAT *stp)
+{
+#ifdef HAVE_GETATTRLIST
+ static struct create_time attrBuf;
+ struct attrlist attrList;
+
+ (void)stp;
+ memset(&attrList, 0, sizeof attrList);
+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrList.commonattr = ATTR_CMN_CRTIME;
+ if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0)
+ return 0;
+ return attrBuf.crtime.tv_sec;
+#elif defined __CYGWIN__
+ (void)path;
+ return stp->st_birthtime;
+#else
+#error Unknown crtimes implementation
+#endif
+}
+
+#if defined __CYGWIN__
+int do_SetFileTime(const char *path, time_t crtime)
+{
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ int cnt = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
+ if (cnt == 0)
+ return -1;
+ WCHAR *pathw = new_array(WCHAR, cnt);
+ if (!pathw)
+ return -1;
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw, cnt);
+ HANDLE handle = CreateFileW(pathw, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ free(pathw);
+ if (handle == INVALID_HANDLE_VALUE)
+ return -1;
+ int64 temp_time = Int32x32To64(crtime, 10000000) + 116444736000000000LL;
+ FILETIME birth_time;
+ birth_time.dwLowDateTime = (DWORD)temp_time;
+ birth_time.dwHighDateTime = (DWORD)(temp_time >> 32);
+ int ok = SetFileTime(handle, &birth_time, NULL, NULL);
+ CloseHandle(handle);
+ return ok ? 0 : -1;
+}
+#endif
+#endif /* SUPPORT_CRTIMES */
+
+#ifdef HAVE_UTIMENSAT
+int do_utimensat(const char *path, STRUCT_STAT *stp)
+{
+ struct timespec t[2];
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ t[0].tv_sec = stp->st_atime;
+#ifdef ST_ATIME_NSEC
+ t[0].tv_nsec = stp->ST_ATIME_NSEC;
+#else
+ t[0].tv_nsec = 0;
+#endif
+ t[1].tv_sec = stp->st_mtime;
+#ifdef ST_MTIME_NSEC
+ t[1].tv_nsec = stp->ST_MTIME_NSEC;
+#else
+ t[1].tv_nsec = 0;
+#endif
+ return utimensat(AT_FDCWD, path, t, AT_SYMLINK_NOFOLLOW);
+}
+#endif
+
+#ifdef HAVE_LUTIMES
+int do_lutimes(const char *path, STRUCT_STAT *stp)
+{
+ struct timeval t[2];
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ t[0].tv_sec = stp->st_atime;
+#ifdef ST_ATIME_NSEC
+ t[0].tv_usec = stp->ST_ATIME_NSEC / 1000;
+#else
+ t[0].tv_usec = 0;
+#endif
+ t[1].tv_sec = stp->st_mtime;
+#ifdef ST_MTIME_NSEC
+ t[1].tv_usec = stp->ST_MTIME_NSEC / 1000;
+#else
+ t[1].tv_usec = 0;
+#endif
+ return lutimes(path, t);
+}
+#endif
+
+#ifdef HAVE_UTIMES
+int do_utimes(const char *path, STRUCT_STAT *stp)
+{
+ struct timeval t[2];
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ t[0].tv_sec = stp->st_atime;
+#ifdef ST_ATIME_NSEC
+ t[0].tv_usec = stp->ST_ATIME_NSEC / 1000;
+#else
+ t[0].tv_usec = 0;
+#endif
+ t[1].tv_sec = stp->st_mtime;
+#ifdef ST_MTIME_NSEC
+ t[1].tv_usec = stp->ST_MTIME_NSEC / 1000;
+#else
+ t[1].tv_usec = 0;
+#endif
+ return utimes(path, t);
+}
+
+#elif defined HAVE_UTIME
+int do_utime(const char *path, STRUCT_STAT *stp)
+{
+#ifdef HAVE_STRUCT_UTIMBUF
+ struct utimbuf tbuf;
+#else
+ time_t t[2];
+#endif
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+# ifdef HAVE_STRUCT_UTIMBUF
+ tbuf.actime = stp->st_atime;
+ tbuf.modtime = stp->st_mtime;
+ return utime(path, &tbuf);
+# else
+ t[0] = stp->st_atime;
+ t[1] = stp->st_mtime;
+ return utime(path, t);
+# endif
+}
+
+#else
+#error Need utimes or utime function.
+#endif
+
+#ifdef SUPPORT_PREALLOCATION
+#ifdef FALLOC_FL_KEEP_SIZE
+#define DO_FALLOC_OPTIONS FALLOC_FL_KEEP_SIZE
+#else
+#define DO_FALLOC_OPTIONS 0
+#endif
+
+OFF_T do_fallocate(int fd, OFF_T offset, OFF_T length)
+{
+ int opts = inplace || preallocate_files ? DO_FALLOC_OPTIONS : 0;
+ int ret;
+ RETURN_ERROR_IF(dry_run, 0);
+ RETURN_ERROR_IF_RO_OR_LO;
+ if (length & 1) /* make the length not match the desired length */
+ length++;
+ else
+ length--;
+#if defined HAVE_FALLOCATE
+ ret = fallocate(fd, opts, offset, length);
+#elif defined HAVE_SYS_FALLOCATE
+ ret = syscall(SYS_fallocate, fd, opts, (loff_t)offset, (loff_t)length);
+#elif defined HAVE_EFFICIENT_POSIX_FALLOCATE
+ ret = posix_fallocate(fd, offset, length);
+#else
+#error Coding error in SUPPORT_PREALLOCATION logic.
+#endif
+ if (ret < 0)
+ return ret;
+ if (opts == 0) {
+ STRUCT_STAT st;
+ if (do_fstat(fd, &st) < 0)
+ return length;
+ return st.st_blocks * S_BLKSIZE;
+ }
+ return 0;
+}
+#endif
+
+/* Punch a hole at pos for len bytes. The current file position must be at pos and will be
+ * changed to be at pos + len. */
+int do_punch_hole(int fd, OFF_T pos, OFF_T len)
+{
+#ifdef HAVE_FALLOCATE
+# ifdef HAVE_FALLOC_FL_PUNCH_HOLE
+ if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, pos, len) == 0) {
+ if (do_lseek(fd, len, SEEK_CUR) != pos + len)
+ return -1;
+ return 0;
+ }
+# endif
+# ifdef HAVE_FALLOC_FL_ZERO_RANGE
+ if (fallocate(fd, FALLOC_FL_ZERO_RANGE, pos, len) == 0) {
+ if (do_lseek(fd, len, SEEK_CUR) != pos + len)
+ return -1;
+ return 0;
+ }
+# endif
+#else
+ (void)pos;
+#endif
+ {
+ char zeros[4096];
+ memset(zeros, 0, sizeof zeros);
+ while (len > 0) {
+ int chunk = len > (int)sizeof zeros ? (int)sizeof zeros : len;
+ int wrote = write(fd, zeros, chunk);
+ if (wrote <= 0) {
+ if (wrote < 0 && errno == EINTR)
+ continue;
+ return -1;
+ }
+ len -= wrote;
+ }
+ }
+ return 0;
+}
+
+int do_open_nofollow(const char *pathname, int flags)
+{
+#ifndef O_NOFOLLOW
+ STRUCT_STAT f_st, l_st;
+#endif
+ int fd;
+
+ if (flags != O_RDONLY) {
+ RETURN_ERROR_IF(dry_run, 0);
+ RETURN_ERROR_IF_RO_OR_LO;
+#ifndef O_NOFOLLOW
+ /* This function doesn't support write attempts w/o O_NOFOLLOW. */
+ errno = EINVAL;
+ return -1;
+#endif
+ }
+
+#ifdef O_NOFOLLOW
+ fd = open(pathname, flags|O_NOFOLLOW);
+#else
+ if (do_lstat(pathname, &l_st) < 0)
+ return -1;
+ if (S_ISLNK(l_st.st_mode)) {
+ errno = ELOOP;
+ return -1;
+ }
+ if ((fd = open(pathname, flags)) < 0)
+ return fd;
+ if (do_fstat(fd, &f_st) < 0) {
+ close_and_return_error:
+ {
+ int save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ }
+ return -1;
+ }
+ if (l_st.st_dev != f_st.st_dev || l_st.st_ino != f_st.st_ino) {
+ errno = EINVAL;
+ goto close_and_return_error;
+ }
+#endif
+
+ return fd;
+}
diff --git a/t_stub.c b/t_stub.c
new file mode 100644
index 0000000..085378a
--- /dev/null
+++ b/t_stub.c
@@ -0,0 +1,113 @@
+/*
+ * This file contains really simple implementations for rsync global
+ * functions, so that module test harnesses can run standalone.
+ *
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+int do_fsync = 0;
+int inplace = 0;
+int modify_window = 0;
+int preallocate_files = 0;
+int protect_args = 0;
+int module_id = -1;
+int relative_paths = 0;
+int module_dirlen = 0;
+int preserve_xattrs = 0;
+int preserve_perms = 0;
+int preserve_executability = 0;
+int omit_link_times = 0;
+int open_noatime = 0;
+size_t max_alloc = 0; /* max_alloc is needed when combined with util2.o */
+char *partial_dir;
+char *module_dir;
+filter_rule_list daemon_filter_list;
+
+ void rprintf(UNUSED(enum logcode code), const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
+ void rsyserr(UNUSED(enum logcode code), int errcode, const char *format, ...)
+{
+ va_list ap;
+ fputs(RSYNC_NAME ": ", stderr);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fprintf(stderr, ": %s (%d)\n", strerror(errcode), errcode);
+}
+
+ void _exit_cleanup(int code, const char *file, int line)
+{
+ fprintf(stderr, "exit(%d): %s(%d)\n",
+ code, file, line);
+ exit(code);
+}
+
+ int check_filter(UNUSED(filter_rule_list *listp), UNUSED(enum logcode code),
+ UNUSED(const char *name), UNUSED(int name_is_dir))
+{
+ /* This function doesn't really get called in this test context, so
+ * just return 0. */
+ return 0;
+}
+
+ int copy_xattrs(UNUSED(const char *source), UNUSED(const char *dest))
+{
+ return -1;
+}
+
+ void free_xattr(UNUSED(stat_x *sxp))
+{
+ return;
+}
+
+ void free_acl(UNUSED(stat_x *sxp))
+{
+ return;
+}
+
+ char *lp_name(UNUSED(int mod))
+{
+ return NULL;
+}
+
+ BOOL lp_use_chroot(UNUSED(int mod))
+{
+ return 0;
+}
+
+ const char *who_am_i(void)
+{
+ return "tester";
+}
+
+ int csum_len_for_type(int cst, int flg)
+{
+ return cst || !flg ? 16 : 1;
+}
+
+ int canonical_checksum(int cst)
+{
+ return cst ? 0 : 0;
+}
diff --git a/t_unsafe.c b/t_unsafe.c
new file mode 100644
index 0000000..010cac5
--- /dev/null
+++ b/t_unsafe.c
@@ -0,0 +1,44 @@
+/*
+ * Test harness for unsafe_symlink(). Not linked into rsync itself.
+ *
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/* Prints either "safe" or "unsafe" depending on the two arguments.
+ * Always returns 0 unless something extraordinary happens. */
+
+#include "rsync.h"
+
+int dry_run = 0;
+int am_root = 0;
+int am_sender = 1;
+int read_only = 0;
+int list_only = 0;
+short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
+
+int
+main(int argc, char **argv)
+{
+ if (argc != 3) {
+ fprintf(stderr, "usage: t_unsafe LINKDEST SRCDIR\n");
+ return 1;
+ }
+
+ printf("%s\n", unsafe_symlink(argv[1], argv[2]) ? "unsafe" : "safe");
+
+ return 0;
+}
diff --git a/tech_report.tex b/tech_report.tex
new file mode 100644
index 0000000..4144990
--- /dev/null
+++ b/tech_report.tex
@@ -0,0 +1,310 @@
+\documentclass[a4paper]{article}
+\begin{document}
+
+
+\title{The rsync algorithm}
+
+\author{Andrew Tridgell \quad\quad Paul Mackerras\\
+Department of Computer Science \\
+Australian National University \\
+Canberra, ACT 0200, Australia}
+
+\maketitle
+
+\begin{abstract}
+ This report presents an algorithm for updating a file on one machine
+ to be identical to a file on another machine. We assume that the
+ two machines are connected by a low-bandwidth high-latency
+ bi-directional communications link. The algorithm identifies parts
+ of the source file which are identical to some part of the
+ destination file, and only sends those parts which cannot be matched
+ in this way. Effectively, the algorithm computes a set of
+ differences without having both files on the same machine. The
+ algorithm works best when the files are similar, but will also
+ function correctly and reasonably efficiently when the files are
+ quite different.
+\end{abstract}
+
+\section{The problem}
+
+Imagine you have two files, $A$ and $B$, and you wish to update $B$ to be
+the same as $A$. The obvious method is to copy $A$ onto $B$.
+
+Now imagine that the two files are on machines connected by a slow
+communications link, for example a dialup IP link. If $A$ is large,
+copying $A$ onto $B$ will be slow. To make it faster you could
+compress $A$ before sending it, but that will usually only gain a
+factor of 2 to 4.
+
+Now assume that $A$ and $B$ are quite similar, perhaps both derived
+from the same original file. To really speed things up you would need
+to take advantage of this similarity. A common method is to send just
+the differences between $A$ and $B$ down the link and then use this
+list of differences to reconstruct the file.
+
+The problem is that the normal methods for creating a set of
+differences between two files rely on being able to read both files.
+Thus they require that both files are available beforehand at one end
+of the link. If they are not both available on the same machine,
+these algorithms cannot be used (once you had copied the file over,
+you wouldn't need the differences). This is the problem that rsync
+addresses.
+
+The rsync algorithm efficiently computes which parts of a source file
+match some part of an existing destination file. These parts need not
+be sent across the link; all that is needed is a reference to the part
+of the destination file. Only parts of the source file which are not
+matched in this way need to be sent verbatim. The receiver can then
+construct a copy of the source file using the references to parts of
+the existing destination file and the verbatim material.
+
+Trivially, the data sent to the receiver can be compressed using any
+of a range of common compression algorithms, for further speed
+improvements.
+
+\section{The rsync algorithm}
+
+Suppose we have two general purpose computers $\alpha$ and $\beta$.
+Computer $\alpha$ has access to a file $A$ and $\beta$ has access to
+file $B$, where $A$ and $B$ are ``similar''. There is a slow
+communications link between $\alpha$ and $\beta$.
+
+The rsync algorithm consists of the following steps:
+
+\begin{enumerate}
+\item $\beta$ splits the file $B$ into a series of non-overlapping
+ fixed-sized blocks of size S bytes\footnote{We have found that
+ values of S between 500 and 1000 are quite good for most purposes}.
+ The last block may be shorter than $S$ bytes.
+
+\item For each of these blocks $\beta$ calculates two checksums:
+ a weak ``rolling'' 32-bit checksum (described below) and a strong
+ 128-bit MD4 checksum.
+
+\item $\beta$ sends these checksums to $\alpha$.
+
+\item $\alpha$ searches through $A$ to find all blocks of length $S$
+ bytes (at any offset, not just multiples of $S$) that have the same
+ weak and strong checksum as one of the blocks of $B$. This can be
+ done in a single pass very quickly using a special property of the
+ rolling checksum described below.
+
+\item $\alpha$ sends $\beta$ a sequence of instructions for
+ constructing a copy of $A$. Each instruction is either a reference
+ to a block of $B$, or literal data. Literal data is sent only for
+ those sections of $A$ which did not match any of the blocks of $B$.
+\end{enumerate}
+
+The end result is that $\beta$ gets a copy of $A$, but only the pieces
+of $A$ that are not found in $B$ (plus a small amount of data for
+checksums and block indexes) are sent over the link. The algorithm
+also only requires one round trip, which minimises the impact of the
+link latency.
+
+The most important details of the algorithm are the rolling checksum
+and the associated multi-alternate search mechanism which allows the
+all-offsets checksum search to proceed very quickly. These will be
+discussed in greater detail below.
+
+\section{Rolling checksum}
+
+The weak rolling checksum used in the rsync algorithm needs to have
+the property that it is very cheap to calculate the checksum of a
+buffer $X_2 .. X_{n+1}$ given the checksum of buffer $X_1 .. X_n$ and
+the values of the bytes $X_1$ and $X_{n+1}$.
+
+The weak checksum algorithm we used in our implementation was inspired
+by Mark Adler's adler-32 checksum. Our checksum is defined by
+$$ a(k,l) = (\sum_{i=k}^l X_i) \bmod M $$
+$$ b(k,l) = (\sum_{i=k}^l (l-i+1)X_i) \bmod M $$
+$$ s(k,l) = a(k,l) + 2^{16} b(k,l) $$
+
+where $s(k,l)$ is the rolling checksum of the bytes $X_k \ldots X_l$.
+For simplicity and speed, we use $M = 2^{16}$.
+
+The important property of this checksum is that successive values can
+be computed very efficiently using the recurrence relations
+
+$$ a(k+1,l+1) = (a(k,l) - X_k + X_{l+1}) \bmod M $$
+$$ b(k+1,l+1) = (b(k,l) - (l-k+1) X_k + a(k+1,l+1)) \bmod M $$
+
+Thus the checksum can be calculated for blocks of length S at all
+possible offsets within a file in a ``rolling'' fashion, with very
+little computation at each point.
+
+Despite its simplicity, this checksum was found to be quite adequate as
+a first-level check for a match of two file blocks. We have found in
+practice that the probability of this checksum matching when the
+blocks are not equal is quite low. This is important because the much
+more expensive strong checksum must be calculated for each block where
+the weak checksum matches.
+
+\section{Checksum searching}
+
+Once $\alpha$ has received the list of checksums of the blocks of $B$,
+it must search $A$ for any blocks at any offset that match the
+checksum of some block of $B$. The basic strategy is to compute the
+32-bit rolling checksum for a block of length $S$ starting at each
+byte of $A$ in turn, and for each checksum, search the list for a
+match. To do this our implementation uses a
+simple 3 level searching scheme.
+
+The first level uses a 16-bit hash of the 32-bit rolling checksum and
+a $2^{16}$ entry hash table. The list of checksum values (i.e., the
+checksums from the blocks of $B$) is sorted according to the 16-bit
+hash of the 32-bit rolling checksum. Each entry in the hash table
+points to the first element of the list for that hash value, or
+contains a null value if no element of the list has that hash value.
+
+At each offset in the file the 32-bit rolling checksum and its 16-bit
+hash are calculated. If the hash table entry for that hash value is
+not a null value, the second-level check is invoked.
+
+The second-level check involves scanning the sorted checksum list
+starting with the entry pointed to by the hash table entry, looking
+for an entry whose 32-bit rolling checksum matches the current value.
+The scan terminates when it reaches an entry whose 16-bit hash
+differs. If this search finds a match, the third-level check is
+invoked.
+
+The third-level check involves calculating the strong checksum for the
+current offset in the file and comparing it with the strong checksum
+value in the current list entry. If the two strong checksums match,
+we assume that we have found a block of $A$ which matches a block of
+$B$. In fact the blocks could be different, but the probability of
+this is microscopic, and in practice this is a reasonable assumption.
+
+When a match is found, $\alpha$ sends $\beta$ the data in $A$ between
+the current file offset and the end of the previous match, followed by
+the index of the block in $B$ that matched. This data is sent
+immediately a match is found, which allows us to overlap the
+communication with further computation.
+
+If no match is found at a given offset in the file, the rolling
+checksum is updated to the next offset and the search proceeds. If a
+match is found, the search is restarted at the end of the matched
+block. This strategy saves a considerable amount of computation for
+the common case where the two files are nearly identical. In
+addition, it would be a simple matter to encode the block indexes as
+runs, for the common case where a portion of $A$ matches a series of
+blocks of $B$ in order.
+
+\section{Pipelining}
+
+The above sections describe the process for constructing a copy of one
+file on a remote system. If we have a several files to copy, we can
+gain a considerable latency advantage by pipelining the process.
+
+This involves $\beta$ initiating two independent processes. One of the
+processes generates and sends the checksums to $\alpha$ while the
+other receives the difference information from $\alpha$ and
+reconstructs the files.
+
+If the communications link is buffered then these two processes can
+proceed independently and the link should be kept fully utilised in
+both directions for most of the time.
+
+\section{Results}
+
+To test the algorithm, tar files were created of the Linux kernel
+sources for two versions of the kernel. The two kernel versions were
+1.99.10 and 2.0.0. These tar files are approximately 24MB in size and
+are separated by 5 released patch levels.
+
+Out of the 2441 files in the 1.99.10 release, 291 files had changed in
+the 2.0.0 release, 19 files had been removed and 25 files had been
+added.
+
+A ``diff'' of the two tar files using the standard GNU diff utility
+produced over 32 thousand lines of output totalling 2.1 MB.
+
+The following table shows the results for rsync between the two files
+with a varying block size.\footnote{All the tests in this section were
+ carried out using rsync version 0.5}
+
+\vspace*{5mm}
+\begin{tabular}{|l|l|l|l|l|l|l|} \hline
+{\bf block} & {\bf matches} & {\bf tag} & {\bf false} & {\bf data} & {\bf written} & {\bf read} \\
+{\bf size} & & {\bf hits} & {\bf alarms} & & & \\ \hline \hline
+
+300 & 64247 & 3817434 & 948 & 5312200 & 5629158 & 1632284 \\ \hline
+500 & 46989 & 620013 & 64 & 1091900 & 1283906 & 979384 \\ \hline
+700 & 33255 & 571970 & 22 & 1307800 & 1444346 & 699564 \\ \hline
+900 & 25686 & 525058 & 24 & 1469500 & 1575438 & 544124 \\ \hline
+1100 & 20848 & 496844 & 21 & 1654500 & 1740838 & 445204 \\ \hline
+\end{tabular}
+\vspace*{5mm}
+
+In each case, the CPU time taken was less than the
+time it takes to run ``diff'' on the two files.\footnote{The wall
+ clock time was approximately 2 minutes per run on a 50 MHz SPARC 10
+ running SunOS, using rsh over loopback for communication. GNU diff
+ took about 4 minutes.}
+
+The columns in the table are as follows:
+
+\begin{description}
+\item [block size] The size in bytes of the checksummed blocks.
+\item [matches] The number of times a block of $B$ was found in $A$.
+\item [tag hits] The number of times the 16-bit hash of the rolling
+ checksum matched a hash of one of the checksums from $B$.
+\item [false alarms] The number of times the 32-bit rolling checksum
+ matched but the strong checksum didn't.
+\item [data] The amount of file data transferred verbatim, in bytes.
+\item [written] The total number of bytes written by $\alpha$,
+ including protocol overheads. This is almost all file data.
+\item [read] The total number of bytes read by $\alpha$, including
+ protocol overheads. This is almost all checksum information.
+\end{description}
+
+The results demonstrate that for block sizes above 300 bytes, only a
+small fraction (around 5\%) of the file was transferred. The amount
+transferred was also considerably less than the size of the diff file
+that would have been transferred if the diff/patch method of updating
+a remote file was used.
+
+The checksums themselves took up a considerable amount of space,
+although much less than the size of the data transferred in each
+case. Each pair of checksums consumes 20 bytes: 4 bytes for the
+rolling checksum plus 16 bytes for the 128-bit MD4 checksum.
+
+The number of false alarms was less than $1/1000$ of the number of
+true matches, indicating that the 32-bit rolling checksum is quite
+good at screening out false matches.
+
+The number of tag hits indicates that the second level of the
+checksum search algorithm was invoked about once every 50
+characters. This is quite high because the total number of blocks in
+the file is a large fraction of the size of the tag hash table. For
+smaller files we would expect the tag hit rate to be much closer to
+the number of matches. For extremely large files, we should probably
+increase the size of the hash table.
+
+The next table shows similar results for a much smaller set of files.
+In this case the files were not packed into a tar file first. Rather,
+rsync was invoked with an option to recursively descend the directory
+tree. The files used were from two source releases of another software
+package called Samba. The total source code size is 1.7 MB and the
+diff between the two releases is 4155 lines long totalling 120 kB.
+
+\vspace*{5mm}
+\begin{tabular}{|l|l|l|l|l|l|l|} \hline
+{\bf block} & {\bf matches} & {\bf tag} & {\bf false} & {\bf data} & {\bf written} & {\bf read} \\
+{\bf size} & & {\bf hits} & {\bf alarms} & & & \\ \hline \hline
+
+300 & 3727 & 3899 & 0 & 129775 & 153999 & 83948 \\ \hline
+500 & 2158 & 2325 & 0 & 171574 & 189330 & 50908 \\ \hline
+700 & 1517 & 1649 & 0 & 195024 & 210144 & 36828 \\ \hline
+900 & 1156 & 1281 & 0 & 222847 & 236471 & 29048 \\ \hline
+1100 & 921 & 1049 & 0 & 250073 & 262725 & 23988 \\ \hline
+\end{tabular}
+\vspace*{5mm}
+
+
+\section{Availability}
+
+An implementation of rsync which provides a convenient interface
+similar to the common UNIX command rcp has been written and is
+available for download from http://rsync.samba.org/
+
+\end{document}
diff --git a/testhelp/maketree.py b/testhelp/maketree.py
new file mode 100644
index 0000000..19ae71d
--- /dev/null
+++ b/testhelp/maketree.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python2
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version
+# 2 as published by the Free Software Foundation.
+#
+# 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Populate a tree with pseudo-randomly distributed files to test
+# rsync.
+
+from __future__ import generators
+import random, string, os, os.path
+
+nfiles = 10000
+depth = 5
+n_children = 20
+n_files = 20
+n_symlinks = 10
+
+name_chars = string.digits + string.letters
+
+abuffer = 'a' * 1024
+
+def random_name_chars():
+ a = ""
+ for i in range(10):
+ a = a + random.choice(name_chars)
+ return a
+
+
+def generate_names():
+ n = 0
+ while 1:
+ yield "%05d_%s" % (n, random_name_chars())
+ n += 1
+
+
+class TreeBuilder:
+ def __init__(self):
+ self.n_children = 20
+ self.n_files = 100
+ self.total_entries = 100000 # long(1e8)
+ self.actual_size = 0
+ self.name_gen = generate_names()
+ self.all_files = []
+ self.all_dirs = []
+ self.all_symlinks = []
+
+
+ def random_size(self):
+ return random.lognormvariate(4, 4)
+
+
+ def random_symlink_target(self):
+ what = random.choice(['directory', 'file', 'symlink', 'none'])
+ try:
+ if what == 'directory':
+ return random.choice(self.all_dirs)
+ elif what == 'file':
+ return random.choice(self.all_files)
+ elif what == 'symlink':
+ return random.choice(self.all_symlinks)
+ elif what == 'none':
+ return self.name_gen.next()
+ except IndexError:
+ return self.name_gen.next()
+
+
+ def can_continue(self):
+ self.total_entries -= 1
+ return self.total_entries > 0
+
+
+ def build_tree(self, prefix, depth):
+ """Generate a breadth-first tree"""
+ for count, function in [[n_files, self.make_file],
+ [n_children, self.make_child_recurse],
+ [n_symlinks, self.make_symlink]]:
+ for i in range(count):
+ if not self.can_continue():
+ return
+ name = os.path.join(prefix, self.name_gen.next())
+ function(name, depth)
+
+
+ def print_summary(self):
+ print "total bytes: %d" % self.actual_size
+
+
+ def make_child_recurse(self, dname, depth):
+ if depth > 1:
+ self.make_dir(dname)
+ self.build_tree(dname, depth-1)
+
+
+ def make_dir(self, dname, depth='ignore'):
+ print "%s/" % (dname)
+ os.mkdir(dname)
+ self.all_dirs.append(dname)
+
+
+ def make_symlink(self, lname, depth='ignore'):
+ print "%s -> %s" % (lname, self.random_symlink_target())
+
+
+ def make_file(self, fname, depth='ignore'):
+ size = long(self.random_size())
+ print "%-70s %d" % (fname, size)
+ f = open(fname, 'w')
+ f.truncate(size)
+ self.fill_file(f, size)
+ self.all_files.append(fname)
+ self.actual_size += size
+
+ def fill_file(self, f, size):
+ while size > 0:
+ f.write(abuffer[:size])
+ size -= len(abuffer)
+
+
+tb = TreeBuilder()
+tb.build_tree('/tmp/foo', 3)
+tb.print_summary()
diff --git a/testrun.c b/testrun.c
new file mode 100644
index 0000000..049e3eb
--- /dev/null
+++ b/testrun.c
@@ -0,0 +1,61 @@
+/* Run a testsuite script with a timeout. */
+
+#include "rsync.h"
+
+#define DEFAULT_TIMEOUT_SECS (5*60)
+#define TIMEOUT_ENV "TESTRUN_TIMEOUT"
+
+ int main(int argc, char *argv[])
+{
+ pid_t pid;
+ char *timeout_env;
+ int status, timeout_secs, slept = 0;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: testrun [SHELL_OPTIONS] TESTSUITE_SCRIPT [ARGS]\n");
+ exit(1);
+ }
+
+ if ((timeout_env = getenv(TIMEOUT_ENV)) != NULL)
+ timeout_secs = atoi(timeout_env);
+ else
+ timeout_secs = DEFAULT_TIMEOUT_SECS;
+
+ if ((pid = fork()) < 0) {
+ fprintf(stderr, "TESTRUN ERROR: fork failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (pid == 0) {
+ argv[0] = "sh";
+ execvp(argv[0], argv);
+ fprintf(stderr, "TESTRUN ERROR: failed to exec %s: %s\n", argv[0], strerror(errno));
+ _exit(1);
+ }
+
+ while (1) {
+ int ret = waitpid(pid, &status, WNOHANG);
+ if (ret > 0)
+ break;
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "TESTRUN ERROR: waitpid failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (slept++ > timeout_secs) {
+ fprintf(stderr, "TESTRUN TIMEOUT: test took over %d seconds.\n", timeout_secs);
+ if (kill(pid, SIGTERM) < 0)
+ fprintf(stderr, "TESTRUN ERROR: failed to kill pid %d: %s\n", (int)pid, strerror(errno));
+ else
+ fprintf(stderr, "TESTRUN INFO: killed pid %d\n", (int)pid);
+ exit(1);
+ }
+ sleep(1);
+ }
+
+ if (!WIFEXITED(status))
+ exit(255);
+
+ return WEXITSTATUS(status);
+}
diff --git a/testsuite/00-hello.test b/testsuite/00-hello.test
new file mode 100644
index 0000000..ebd0683
--- /dev/null
+++ b/testsuite/00-hello.test
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+# Test some foundational things.
+
+. "$suitedir/rsync.fns"
+
+RSYNC_RSH="$scratchdir/src/support/lsh.sh"
+export RSYNC_RSH
+
+echo $0 running
+
+$RSYNC --version || test_fail '--version output failed'
+
+$RSYNC --info=help || test_fail '--info=help output failed'
+
+$RSYNC --debug=help || test_fail '--debug=help output failed'
+
+weird_name="A weird)name"
+
+mkdir "$fromdir"
+mkdir "$fromdir/$weird_name"
+
+append_line() {
+ echo "$1"
+ echo "$1" >>"$fromdir/$weird_name/file"
+}
+
+append_line test1
+checkit "$RSYNC -ai '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+copy_weird() {
+ checkit "$RSYNC $1 --rsync-path='$RSYNC' '$2$fromdir/$weird_name/' '$3$todir/$weird_name'" "$fromdir" "$todir"
+}
+
+append_line test2
+copy_weird '-ai' 'lh:' ''
+
+append_line test3
+copy_weird '-ai' '' 'lh:'
+
+append_line test4
+copy_weird '-ais' 'lh:' ''
+
+append_line test5
+copy_weird '-ais' '' 'lh:'
+
+echo test6
+
+touch "$fromdir/one" "$fromdir/two"
+(cd "$fromdir" && $RSYNC -ai --old-args --rsync-path="$RSYNC" lh:'one two' "$todir/")
+if [ ! -f "$todir/one" ] || [ ! -f "$todir/two" ]; then
+ test_fail "old-args copy of 'one two' failed"
+fi
+
+echo test7
+
+rm "$todir/one" "$todir/two"
+(cd "$fromdir" && RSYNC_OLD_ARGS=1 $RSYNC -ai --rsync-path="$RSYNC" lh:'one two' "$todir/")
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/README.testsuite b/testsuite/README.testsuite
new file mode 100644
index 0000000..782cb1c
--- /dev/null
+++ b/testsuite/README.testsuite
@@ -0,0 +1,28 @@
+automatic testsuite for rsync -*- text -*-
+
+We're trying to develop some more substantial tests to prevent rsync
+regressions. Ideally, all code changes or bug reports would come with
+an appropriate test suite.
+
+You can run these tests by typing "make check" in the build directory.
+The tests will run using the rsync binary in the build directory, so
+you do not need to do "make install" first. Indeed, you probably
+should not install rsync before running the tests.
+
+If you instead type "make installcheck" then the suite will test the
+rsync binary from its installed location (e.g. /usr/local/bin/rsync).
+You can use this to test a distribution build, or perhaps to run a new
+test suite against an old version of rsync. Note that in accordance
+with the GNU Standards, installcheck does not look for rsync on the
+path.
+
+If the tests pass, you should see a report to that effect. Some tests
+require being root or some other precondition, and so will normally not
+be checked -- look at the test scripts for more information.
+
+If the tests fail, you will see rather more output. The scratch
+directory will remain in the build directory. It would be useful if
+you could include the log messages when reporting a failure.
+
+These tests also run automatically on the build farm, and you can see
+the results on http://build.samba.org/.
diff --git a/testsuite/acls-default.test b/testsuite/acls-default.test
new file mode 100644
index 0000000..d8fba7f
--- /dev/null
+++ b/testsuite/acls-default.test
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that rsync obeys default ACLs. -- Matt McCutchen
+
+. $suitedir/rsync.fns
+
+$RSYNC -VV | grep '"ACLs": true' >/dev/null || test_skipped "Rsync is configured without ACL support"
+
+case "$setfacl_nodef" in
+true) test_skipped "I don't know how to use your setfacl command" ;;
+*-k*) opts='-dm u::7,g::5,o:5' ;;
+*) opts='-m d:u::7,d:g::5,d:o:5' ;;
+esac
+setfacl $opts "$scratchdir" || test_skipped "Your filesystem has ACLs disabled"
+
+# Call as: testit <dirname> <default-acl> <file-expected> <program-expected>
+testit() {
+ todir="$scratchdir/$1"
+ mkdir "$todir"
+ $setfacl_nodef "$todir"
+ if [ -n "$2" ]; then
+ case "$setfacl_nodef" in
+ *-k*) opts="-dm $2" ;;
+ *) opts="-m `echo $2 | sed 's/\([ugom]:\)/d:\1/g'`"
+ esac
+ setfacl $opts "$todir"
+ fi
+ # Make sure we obey ACLs when creating a directory to hold multiple transferred files,
+ # even though the directory itself is outside the transfer
+ $RSYNC -rvv "$scratchdir/dir" "$scratchdir/file" "$scratchdir/program" "$todir/to/"
+ check_perms "$todir/to" $4 "Target $1"
+ check_perms "$todir/to/dir" $4 "Target $1"
+ check_perms "$todir/to/file" $3 "Target $1"
+ check_perms "$todir/to/program" $4 "Target $1"
+ # Make sure get_local_name doesn't mess us up when transferring only one file
+ $RSYNC -rvv "$scratchdir/file" "$todir/to/anotherfile"
+ check_perms "$todir/to/anotherfile" $3 "Target $1"
+ # Make sure we obey default ACLs when not transferring a regular file
+ $RSYNC -rvv "$scratchdir/dir/" "$todir/to/anotherdir/"
+ check_perms "$todir/to/anotherdir" $4 "Target $1"
+}
+
+mkdir "$scratchdir/dir"
+echo "File!" >"$scratchdir/file"
+echo "#!/bin/sh" >"$scratchdir/program"
+chmod 777 "$scratchdir/dir"
+chmod 666 "$scratchdir/file"
+chmod 777 "$scratchdir/program"
+
+# Test some target directories
+umask 0077
+testit da777 u::7,g::7,o:7 rw-rw-rw- rwxrwxrwx
+testit da775 u::7,g::7,o:5 rw-rw-r-- rwxrwxr-x
+testit da750 u::7,g::5,o:0 rw-r----- rwxr-x---
+testit da750mask u::7,u:0:7,g::7,m:5,o:0 rw-r----- rwxr-x---
+testit noda1 '' rw------- rwx------
+umask 0000
+testit noda2 '' rw-rw-rw- rwxrwxrwx
+umask 0022
+testit noda3 '' rw-r--r-- rwxr-xr-x
+
+# Hooray
+exit 0
diff --git a/testsuite/acls.test b/testsuite/acls.test
new file mode 100644
index 0000000..693da66
--- /dev/null
+++ b/testsuite/acls.test
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that rsync handles basic ACL preservation.
+
+. $suitedir/rsync.fns
+
+$RSYNC -VV | grep '"ACLs": true' >/dev/null || test_skipped "Rsync is configured without ACL support"
+
+makepath "$fromdir/foo"
+echo something >"$fromdir/file1"
+echo else >"$fromdir/file2"
+
+files='foo file1 file2'
+
+case "$setfacl_nodef" in
+true)
+ if ! chmod --help 2>&1 | grep -F +a >/dev/null; then
+ test_skipped "I don't know how to use setfacl or chmod for ACLs"
+ fi
+ chmod +a "root allow read,write,execute" "$fromdir/foo" || test_skipped "Your filesystem has ACLs disabled"
+ chmod +a "root allow read,execute" "$fromdir/file1"
+ chmod +a "admin allow read" "$fromdir/file1"
+ chmod +a "daemon allow read,write" "$fromdir/file1"
+ chmod +a "root allow read,execute" "$fromdir/file2"
+
+ see_acls() {
+ ls -le "${@}"
+ }
+ ;;
+*)
+ setfacl -m u:0:7 "$fromdir/foo" || test_skipped "Your filesystem has ACLs disabled"
+ setfacl -m g:1:5 "$fromdir/foo"
+ setfacl -m g:2:1 "$fromdir/foo"
+ setfacl -m g:0:7 "$fromdir/foo"
+ setfacl -m u:2:1 "$fromdir/foo"
+ setfacl -m u:1:5 "$fromdir/foo"
+
+ setfacl -m u:0:5 "$fromdir/file1"
+ setfacl -m g:0:4 "$fromdir/file1"
+ setfacl -m u:1:6 "$fromdir/file1"
+
+ setfacl -m u:0:5 "$fromdir/file2"
+
+ see_acls() {
+ getfacl "${@}"
+ }
+ ;;
+esac
+
+cd "$fromdir"
+$RSYNC -avvA $files "$todir/"
+
+see_acls $files >"$scratchdir/acls.txt"
+
+cd "$todir"
+see_acls $files | diff $diffopt "$scratchdir/acls.txt" -
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/alt-dest.test b/testsuite/alt-dest.test
new file mode 100644
index 0000000..d2fb5a1
--- /dev/null
+++ b/testsuite/alt-dest.test
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+# Copyright (C) 2004-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of --compare-dest and similar options.
+
+. "$suitedir/rsync.fns"
+
+alt1dir="$tmpdir/alt1"
+alt2dir="$tmpdir/alt2"
+alt3dir="$tmpdir/alt3"
+
+SSH="$scratchdir/src/support/lsh.sh"
+
+# Build some files/dirs/links to copy
+
+hands_setup
+
+# Setup the alt and chk dirs
+$RSYNC -av --include=text --include='*/' --exclude='*' "$fromdir/" "$alt1dir/"
+$RSYNC -av --include=etc-ltr-list --include='*/' --exclude='*' "$fromdir/" "$alt2dir/"
+
+# Create a side dir where there is a candidate destfile of the same name as a sourcefile
+echo "This is a test file" >"$fromdir/likely"
+
+mkdir "$alt3dir"
+echo "This is a test file" >"$alt3dir/likely"
+
+sleep 1
+touch "$fromdir/dir/text" "$fromdir/likely"
+
+$RSYNC -av --exclude=/text --exclude=etc-ltr-list "$fromdir/" "$chkdir/"
+
+# Let's do it!
+checkit "$RSYNC -avv --no-whole-file \
+ --compare-dest='$alt1dir' --compare-dest='$alt2dir' \
+ '$fromdir/' '$todir/'" "$chkdir" "$todir"
+
+rm -rf "$todir"
+checkit "$RSYNC -avv --no-whole-file \
+ --copy-dest='$alt1dir' --copy-dest='$alt2dir' \
+ '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+# Test that copy_file() works correctly with tmpfiles
+for maybe_inplace in '' --inplace; do
+ rm -rf "$todir"
+ checkit "$RSYNC -av $maybe_inplace --copy-dest='$alt3dir' \
+ '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+ for srchost in '' 'localhost:'; do
+ if [ -z "$srchost" ]; then
+ desthost='localhost:'
+ else
+ desthost=''
+ fi
+
+ rm -rf "$todir"
+ checkit "$RSYNC -ave '$SSH' --rsync-path='$RSYNC' $maybe_inplace \
+ --copy-dest='$alt3dir' '$srchost$fromdir/' '$desthost$todir/'" \
+ "$fromdir" "$todir"
+ done
+done
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/atimes.test b/testsuite/atimes.test
new file mode 100644
index 0000000..4d46eb0
--- /dev/null
+++ b/testsuite/atimes.test
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# Test rsync copying atimes
+
+. "$suitedir/rsync.fns"
+
+$RSYNC -VV | grep '"atimes": true' >/dev/null || test_skipped "Rsync is configured without atimes support"
+
+mkdir "$fromdir"
+
+touch "$fromdir/foo"
+touch -a -t 200102031717.42 "$fromdir/foo"
+
+TLS_ARGS=--atimes
+
+checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/backup.test b/testsuite/backup.test
new file mode 100644
index 0000000..4de3867
--- /dev/null
+++ b/testsuite/backup.test
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+# Copyright (C) 2004-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that the --backup option works right.
+
+. "$suitedir/rsync.fns"
+
+bakdir="$tmpdir/bak"
+
+makepath "$fromdir/deep" "$bakdir/dname"
+name1="$fromdir/deep/name1"
+name2="$fromdir/deep/name2"
+
+cat "$srcdir"/[gr]*.[ch] > "$name1"
+cat "$srcdir"/[et]*.[ch] > "$name2"
+
+checkit "$RSYNC -ai --info=backup '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+checkit "$RSYNC -ai --info=backup '$fromdir/' '$chkdir/'" "$fromdir" "$chkdir"
+cat "$srcdir"/[fgpr]*.[ch] > "$name1"
+cat "$srcdir"/[etw]*.[ch] > "$name2"
+
+checktee "$RSYNC -ai --info=backup --no-whole-file --backup '$fromdir/' '$todir/'"
+for fn in deep/name1 deep/name2; do
+ grep "backed up $fn to $fn~" "$outfile" >/dev/null || test_fail "no backup message output for $fn"
+ diff $diffopt "$fromdir/$fn" "$todir/$fn" || test_fail "copy of $fn failed"
+ diff $diffopt "$chkdir/$fn" "$todir/$fn~" || test_fail "backup of $fn to $fn~ failed"
+ mv "$todir/$fn~" "$todir/$fn"
+done
+
+echo deleted-file >"$todir/dname"
+cp_touch "$todir/dname" "$chkdir"
+
+checkit "$RSYNC -ai --info=backup --no-whole-file --delete-delay \
+ --backup --backup-dir='$bakdir' '$fromdir/' '$todir/'" "$fromdir" "$todir" \
+ | tee "$outfile"
+
+for fn in deep/name1 deep/name2; do
+ grep "backed up $fn to .*/$fn$" "$outfile" >/dev/null || test_fail "no backup message output for $fn"
+done
+diff -r $diffopt "$chkdir" "$bakdir" || test_fail "backup dir contents are bogus"
+rm "$bakdir/dname"
+
+checkit "$RSYNC -ai --info=backup --del '$fromdir/' '$chkdir/'" "$fromdir" "$chkdir"
+cat "$srcdir"/[efgr]*.[ch] > "$name1"
+cat "$srcdir"/[ew]*.[ch] > "$name2"
+
+checkit "$RSYNC -ai --info=backup --inplace --no-whole-file --backup --backup-dir='$bakdir' '$fromdir/' '$todir/'" "$fromdir" "$todir" \
+ | tee "$outfile"
+
+for fn in deep/name1 deep/name2; do
+ grep "backed up $fn to .*/$fn$" "$outfile" >/dev/null || test_fail "no backup message output for $fn"
+done
+diff -r $diffopt "$chkdir" "$bakdir" || test_fail "backup dir contents are bogus"
+
+checkit "$RSYNC -ai --info=backup --inplace --no-whole-file '$fromdir/' '$bakdir/'" "$fromdir" "$bakdir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/batch-mode.test b/testsuite/batch-mode.test
new file mode 100644
index 0000000..cf4e94d
--- /dev/null
+++ b/testsuite/batch-mode.test
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+# Copyright (C) 2004 by Chris Shoemaker <c.shoemaker@cox.net>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync's --write-batch and --read-batch options
+
+. "$suitedir/rsync.fns"
+
+hands_setup
+
+cd "$tmpdir"
+
+# Build chkdir for the daemon tests using a normal rsync and an --exclude.
+$RSYNC -av --exclude=foobar.baz "$fromdir/" "$chkdir/"
+
+$RSYNC -av --only-write-batch=BATCH --exclude=foobar.baz "$fromdir/" "$todir/missing/"
+test -d "$todir/missing" && test_fail "--only-write-batch should not have created destination dir"
+
+runtest "--read-batch (only)" 'checkit "$RSYNC -av --read-batch=BATCH \"$todir\"" "$chkdir" "$todir"'
+
+rm -rf "$todir" BATCH*
+runtest "local --write-batch" 'checkit "$RSYNC -av --write-batch=BATCH \"$fromdir/\" \"$todir\"" "$fromdir" "$todir"'
+
+rm -rf "$todir"
+runtest "--read-batch" 'checkit "$RSYNC -av --read-batch=BATCH \"$todir\"" "$fromdir" "$todir"'
+
+build_rsyncd_conf
+
+RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon"
+export RSYNC_CONNECT_PROG
+
+rm -rf "$todir"
+runtest "daemon sender --write-batch" 'checkit "$RSYNC -av --write-batch=BATCH rsync://localhost/test-from/ \"$todir\"" "$chkdir" "$todir"'
+
+rm -rf "$todir"
+runtest "--read-batch from daemon" 'checkit "$RSYNC -av --read-batch=BATCH \"$todir\"" "$chkdir" "$todir"'
+
+rm -rf "$todir"
+runtest "BATCH.sh use of --read-batch" 'checkit "./BATCH.sh" "$chkdir" "$todir"'
+
+runtest "do-nothing re-run of batch" 'checkit "./BATCH.sh" "$chkdir" "$todir"'
+
+rm -rf "$todir"
+mkdir "$todir" || test_fail "failed to restore empty destination directory"
+runtest "daemon recv --write-batch" 'checkit "\"$ignore23\" $RSYNC -av --write-batch=BATCH \"$fromdir/\" rsync://localhost/test-to" "$chkdir" "$todir"'
+
+# The script would have aborted on error, so getting here means we pass.
+exit 0
diff --git a/testsuite/chgrp.test b/testsuite/chgrp.test
new file mode 100644
index 0000000..467d402
--- /dev/null
+++ b/testsuite/chgrp.test
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that rsync with -gr will preserve groups when the user running
+# the test is a member of them. Hopefully they're in at least one
+# test.
+
+. "$suitedir/rsync.fns"
+
+# Build some hardlinks
+
+mygrps="`rsync_getgroups`" || test_fail "Can't get groups"
+mkdir "$fromdir"
+
+for g in $mygrps; do
+ name="$fromdir/foo-$g"
+ date > "$name"
+ chgrp "$g" "$name" || test_fail "Can't chgrp"
+done
+sleep 2
+
+checkit "$RSYNC -rtgpvvv '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/chmod-option.test b/testsuite/chmod-option.test
new file mode 100644
index 0000000..ddf764c
--- /dev/null
+++ b/testsuite/chmod-option.test
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that the --chmod option functions correctly.
+
+. $suitedir/rsync.fns
+
+# Build some files
+
+fromdir="$scratchdir/from"
+todir="$scratchdir/to"
+checkdir="$scratchdir/check"
+
+mkdir "$fromdir"
+name1="$fromdir/name1"
+name2="$fromdir/name2"
+dir1="$fromdir/dir1"
+dir2="$fromdir/dir2"
+echo "This is the file" > "$name1"
+echo "This is the other file" > "$name2"
+mkdir "$dir1" "$dir2"
+
+chmod 4700 "$name1" || test_skipped "Can't chmod"
+chmod 700 "$dir1"
+chmod 770 "$dir2"
+
+# Copy the files we've created over to another directory
+checkit "$RSYNC -avv '$fromdir/' '$checkdir/'" "$fromdir" "$checkdir"
+
+# And then manually make the changes which should occur
+umask 002
+chmod ug-s,a+rX "$checkdir"/*
+chmod +w "$checkdir" "$checkdir"/dir*
+
+checkit "$RSYNC -avv --chmod ug-s,a+rX,D+w '$fromdir/' '$todir/'" "$checkdir" "$todir"
+
+rm -r "$fromdir" "$checkdir" "$todir"
+makepath "$todir" "$fromdir/foo"
+touch "$fromdir/bar"
+
+checkit "$RSYNC -avv '$fromdir/' '$checkdir/'" "$fromdir" "$checkdir"
+chmod o+x "$fromdir"/bar
+
+checkit "$RSYNC -avv --chmod=Fo-x '$fromdir/' '$todir/'" "$checkdir" "$todir"
+
+# Tickle a bug in rsync 2.6.8: if you push a new directory with --perms off to
+# a daemon with an incoming chmod, the daemon pretends the directory is a file
+# for the purposes of the second application of the incoming chmod.
+
+build_rsyncd_conf
+cat >>"$scratchdir/test-rsyncd.conf" <<EOF
+[test-incoming-chmod]
+ path = $todir
+ read only = no
+ incoming chmod = Fo-x
+EOF
+
+RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon"
+export RSYNC_CONNECT_PROG
+
+rm -r "$todir"
+makepath "$todir"
+
+checkit "$RSYNC -avv --no-perms '$fromdir/' localhost::test-incoming-chmod/" "$checkdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/chmod-temp-dir.test b/testsuite/chmod-temp-dir.test
new file mode 100644
index 0000000..362d9d9
--- /dev/null
+++ b/testsuite/chmod-temp-dir.test
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Copyright (C) 2004-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that various read-only and set[ug]id permissions work properly,
+# even when using a --temp-dir option (which we try to point at a
+# different filesystem than the destination dir).
+
+. "$suitedir/rsync.fns"
+
+hands_setup
+
+sdev=`$TOOLDIR/getfsdev $scratchdir`
+tdev=$sdev
+
+for tmpdir2 in "${RSYNC_TEST_TMP:-/override-tmp-not-specified}" /run/shm /var/tmp /tmp; do
+ [ -d "$tmpdir2" ] && [ -w "$tmpdir2" ] || continue
+ tdev=`$TOOLDIR/getfsdev "$tmpdir2"`
+ [ x$sdev != x$tdev ] && break
+done
+
+[ x$sdev = x$tdev ] && test_skipped "Can't find a tmp dir on a different file system"
+
+chmod 440 "$fromdir/text"
+chmod 500 "$fromdir/dir/text"
+e="$fromdir/dir/subdir/foobar.baz"
+chmod 6450 "$e" || chmod 2450 "$e" || chmod 1450 "$e" || chmod 450 "$e"
+e="$fromdir/dir/subdir/subsubdir/etc-ltr-list"
+chmod 2670 "$e" || chmod 1670 "$e" || chmod 670 "$e"
+
+# First a normal copy.
+runtest "normal copy" 'checkit "$RSYNC -avv --temp-dir=\"$tmpdir2\" \"$fromdir/\" \"$todir\"" "$fromdir" "$todir"'
+
+# Then we update all the files.
+runtest "update copy" 'checkit "$RSYNC -avvI --no-whole-file --temp-dir=\"$tmpdir2\" \"$fromdir/\" \"$todir\"" "$fromdir" "$todir"'
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/chmod.test b/testsuite/chmod.test
new file mode 100644
index 0000000..1646a9c
--- /dev/null
+++ b/testsuite/chmod.test
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# Copyright (C) 2004-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that various read-only and set[ug]id permissions work properly,
+# even when using a --temp-dir option (which we try to point at a
+# different filesystem than the destination dir).
+
+. "$suitedir/rsync.fns"
+
+hands_setup
+
+chmod 440 "$fromdir/text"
+chmod 500 "$fromdir/dir/text"
+e="$fromdir/dir/subdir/foobar.baz"
+chmod 6450 "$e" || chmod 2450 "$e" || chmod 1450 "$e" || chmod 450 "$e"
+e="$fromdir/dir/subdir/subsubdir/etc-ltr-list"
+chmod 2670 "$e" || chmod 1670 "$e" || chmod 670 "$e"
+
+# First a normal copy.
+runtest "normal copy" 'checkit "$RSYNC -avv \"$fromdir/\" \"$todir\"" "$fromdir" "$todir"'
+
+# Then we update all the files.
+runtest "update copy" 'checkit "$RSYNC -avvI --no-whole-file \"$fromdir/\" \"$todir\"" "$fromdir" "$todir"'
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/chown.test b/testsuite/chown.test
new file mode 100644
index 0000000..b53413e
--- /dev/null
+++ b/testsuite/chown.test
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that when rsync is running as root and has -a it correctly sets
+# the ownership of the destination.
+
+# We don't know what users will be present on this system, so we just
+# use random numeric uids and gids.
+
+. "$suitedir/rsync.fns"
+
+case $0 in
+*fake*)
+ $RSYNC -VV | grep '"xattrs": true' >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
+ RSYNC="$RSYNC --fake-super"
+ TLS_ARGS="$TLS_ARGS --fake-super"
+ case "$HOST_OS" in
+ darwin*)
+ chown() {
+ own=$1
+ shift
+ xattr -s 'rsync.%stat' "100644 0,0 $own" "${@}"
+ }
+ ;;
+ solaris*)
+ chown() {
+ own=$1
+ shift
+ for fn in "${@}"; do
+ runat "$fn" "$SHELL_PATH" <<EOF
+echo "100644 0,0 $own" > rsync.%stat
+EOF
+ done
+ }
+ ;;
+ freebsd*)
+ chown() {
+ own=$1
+ shift
+ setextattr -h user "rsync.%stat" "100644 0,0 $own" "${@}"
+ }
+ ;;
+ *)
+ chown() {
+ own=$1
+ shift
+ setfattr -n 'user.rsync.%stat' -v "100644 0,0 $own" "${@}"
+ }
+ ;;
+ esac
+ ;;
+*)
+ RSYNC="$RSYNC --super"
+ my_uid=`get_testuid`
+ root_uid=`get_rootuid`
+ if test x"$my_uid" = x; then
+ : # If "id" failed, try to continue...
+ elif test x"$my_uid" != x"$root_uid"; then
+ if [ -e "$FAKEROOT_PATH" ]; then
+ echo "Let's try re-running the script under fakeroot..."
+ exec "$FAKEROOT_PATH" "$SHELL_PATH" "$0"
+ fi
+ fi
+ ;;
+esac
+
+# Build some hardlinks
+
+mkdir "$fromdir"
+name1="$fromdir/name1"
+name2="$fromdir/name2"
+echo "This is the file" > "$name1"
+echo "This is the other file" > "$name2"
+
+chown 5000:5002 "$name1" || test_skipped "Can't chown (probably need root)"
+chown 5001:5003 "$name2" || test_skipped "Can't chown (probably need root)"
+
+cd "$fromdir/.."
+checkit "$RSYNC -aHvv from/ to/" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
new file mode 100644
index 0000000..456f0a5
--- /dev/null
+++ b/testsuite/crtimes.test
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Test rsync copying create times
+
+. "$suitedir/rsync.fns"
+
+$RSYNC -VV | grep '"crtimes": true' >/dev/null || test_skipped "Rsync is configured without crtimes support"
+
+# Setting an older time via touch sets the create time to the mtime.
+# Setting it to a newer time affects just the mtime.
+
+mkdir "$fromdir"
+echo hiho >"$fromdir/foo"
+
+touch -t 200101011111.11 "$fromdir"
+touch -t 200202022222.22 "$fromdir"
+
+touch -t 200111111111.11 "$fromdir/foo"
+touch -t 200212122222.22 "$fromdir/foo"
+
+TLS_ARGS=--crtimes
+
+checkit "$RSYNC -rtgvvv --crtimes \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/daemon-gzip-download.test b/testsuite/daemon-gzip-download.test
new file mode 100644
index 0000000..57dd820
--- /dev/null
+++ b/testsuite/daemon-gzip-download.test
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
+
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This test tries to download a tree over a compressed connection from
+# the server. This ought to exercise (exorcise?) a bug in 2.5.3.
+
+. "$suitedir/rsync.fns"
+
+build_rsyncd_conf
+
+RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon"
+export RSYNC_CONNECT_PROG
+
+hands_setup
+
+# Build chkdir with a normal rsync and an --exclude.
+$RSYNC -av --exclude=foobar.baz "$fromdir/" "$chkdir/"
+
+checkit "$RSYNC -avvvvzz localhost::test-from/ '$todir/'" "$chkdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/daemon-gzip-upload.test b/testsuite/daemon-gzip-upload.test
new file mode 100644
index 0000000..b2110ea
--- /dev/null
+++ b/testsuite/daemon-gzip-upload.test
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING)
+
+# We don't really want to start the server listening, because that
+# might interfere with the security or operation of the test machine.
+# Instead we use the fake-connect feature to dynamically assign a pair
+# of ports.
+
+# This test tries to upload a file over a compressed connection to the
+# server. This ought to exercise (exorcise?) a bug in 2.5.3.
+
+. "$suitedir/rsync.fns"
+
+build_rsyncd_conf
+
+RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon"
+export RSYNC_CONNECT_PROG
+
+hands_setup
+
+# Build chkdir with a normal rsync and an --exclude.
+$RSYNC -av --exclude=foobar.baz "$fromdir/" "$chkdir/"
+
+checkit "'$ignore23' $RSYNC -avvvvzz '$fromdir/' localhost::test-to/" "$chkdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/daemon.test b/testsuite/daemon.test
new file mode 100644
index 0000000..60aa334
--- /dev/null
+++ b/testsuite/daemon.test
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING)
+
+# We don't really want to start the server listening, because that
+# might interfere with the security or operation of the test machine.
+# Instead we use the fake-connect feature to dynamically assign a pair
+# of ports.
+
+# Having started the server we try some basic operations against it:
+
+# getting a list of module
+# listing files in a module
+# retrieving a module
+# uploading to a module
+# checking the log file
+# password authentication
+
+. "$suitedir/rsync.fns"
+
+SSH="src/support/lsh.sh --no-cd"
+FILE_REPL='s/^\([^d][^ ]*\) *\(..........[0-9]\) /\1 \2 /'
+DIR_REPL='s/^\(d[^ ]*\) *[0-9][.,0-9]* /\1 DIR /'
+LS_REPL='s;[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ;####/##/## ##:##:## ;g'
+
+build_rsyncd_conf
+
+makepath "$fromdir/foo" "$fromdir/bar/baz"
+makepath "$todir"
+echo one >"$fromdir/foo/one"
+echo two >"$fromdir/bar/two"
+echo three >"$fromdir/bar/baz/three"
+
+cd "$scratchdir"
+
+ln -s test-rsyncd.conf rsyncd.conf
+
+my_uid=`get_testuid`
+root_uid=`get_rootuid`
+confopt=''
+if test x"$my_uid" = x"$root_uid"; then
+ # Root needs to specify the config file, or it uses /etc/rsyncd.conf.
+ echo "Forcing --config=$conf"
+ confopt=" --config=$conf"
+fi
+
+# These have a space-padded 15-char name, then a tab, then a comment.
+sed 's/NOCOMMENT//' <<EOT >"$chkfile"
+test-from r/o
+test-to r/w
+test-scratch NOCOMMENT
+EOT
+
+checkdiff2 "$RSYNC -ve '$SSH' --rsync-path='$RSYNC$confopt' localhost::"
+echo '===='
+
+RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon"
+export RSYNC_CONNECT_PROG
+
+checkdiff2 "$RSYNC -v localhost::"
+echo '===='
+
+checkdiff "$RSYNC -r localhost::test-hidden" \
+ "sed -e '$FILE_REPL' -e '$DIR_REPL' -e '$LS_REPL'" <<EOT
+drwxr-xr-x DIR ####/##/## ##:##:## .
+drwxr-xr-x DIR ####/##/## ##:##:## bar
+-rw-r--r-- 4 ####/##/## ##:##:## bar/two
+drwxr-xr-x DIR ####/##/## ##:##:## bar/baz
+-rw-r--r-- 6 ####/##/## ##:##:## bar/baz/three
+drwxr-xr-x DIR ####/##/## ##:##:## foo
+-rw-r--r-- 4 ####/##/## ##:##:## foo/one
+EOT
+
+checkdiff "$RSYNC -r localhost::test-from/f*" \
+ "sed -e '$FILE_REPL' -e '$DIR_REPL' -e '$LS_REPL'" <<EOT
+drwxr-xr-x DIR ####/##/## ##:##:## foo
+-rw-r--r-- 4 ####/##/## ##:##:## foo/one
+EOT
+diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
+
+if $RSYNC -VV | grep '"atimes": true' >/dev/null; then
+ checkdiff "$RSYNC -rU localhost::test-from/f*" \
+ "sed -e '$FILE_REPL' -e '$DIR_REPL' -e '$LS_REPL'" <<EOT
+drwxr-xr-x DIR ####/##/## ##:##:## foo
+-rw-r--r-- 4 ####/##/## ##:##:## ####/##/## ##:##:## foo/one
+EOT
+fi
diff --git a/testsuite/delay-updates.test b/testsuite/delay-updates.test
new file mode 100644
index 0000000..3b6226b
--- /dev/null
+++ b/testsuite/delay-updates.test
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# Test rsync --delay-updates
+
+. "$suitedir/rsync.fns"
+
+mkdir "$fromdir"
+
+echo 1 > "$fromdir/foo"
+
+checkit "$RSYNC -aiv --delay-updates \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
+
+mkdir "$todir/.~tmp~"
+echo 2 > "$todir/.~tmp~/foo"
+touch -r .. "$todir/.~tmp~/foo" "$todir/foo"
+echo 3 > "$fromdir/foo"
+
+checkit "$RSYNC -aiv --delay-updates \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/delete.test b/testsuite/delete.test
new file mode 100644
index 0000000..2a9df7c
--- /dev/null
+++ b/testsuite/delete.test
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# Copyright (C) 2005-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of various delete directives.
+
+. "$suitedir/rsync.fns"
+
+hands_setup
+
+makepath "$chkdir" "$todir/extradir" "$todir/emptydir/subdir"
+
+echo extra >"$todir"/remove1
+echo extra >"$todir"/remove2
+echo extra >"$todir"/extradir/remove3
+echo extra >"$todir"/emptydir/subdir/remove4
+
+# Create two chk dirs, one with a copy of the source files, and one with
+# what we expect to be left behind by the copy using --remove-source-files.
+# Also, make sure that --dry-run --del doesn't output anything extraneous.
+$RSYNC -av "$fromdir/" "$chkdir/copy/" >"$tmpdir/copy.out" 2>&1
+cat "$tmpdir/copy.out"
+grep -E -v '^(created directory|sent|total size) ' "$tmpdir/copy.out" >"$tmpdir/copy.new"
+mv "$tmpdir/copy.new" "$tmpdir/copy.out"
+
+$RSYNC -avn --del "$fromdir/" "$chkdir/copy2/" >"$tmpdir/copy2.out" 2>&1 || true
+cat "$tmpdir/copy2.out"
+grep -E -v '^(created directory|sent|total size) ' "$tmpdir/copy2.out" >"$tmpdir/copy2.new"
+mv "$tmpdir/copy2.new" "$tmpdir/copy2.out"
+
+diff $diffopt "$tmpdir/copy.out" "$tmpdir/copy2.out"
+
+$RSYNC -av -f 'exclude,! */' "$fromdir/" "$chkdir/empty/"
+
+checkit "$RSYNC -avv --del --remove-source-files '$fromdir/' '$todir/'" "$chkdir/copy" "$todir"
+
+diff -r "$chkdir/empty" "$fromdir"
+
+# Make sure that "P" but not "-" per-dir merge-file filters take effect with
+# --delete-excluded.
+cat >"$todir/filters" <<EOF
+P foo
+- bar
+EOF
+touch "$todir/foo" "$todir/bar" "$todir/baz"
+
+$RSYNC -r --exclude=baz --filter=': filters' --delete-excluded "$fromdir/" "$todir/"
+
+test -f "$todir/foo" || test_fail "rsync should NOT have deleted $todir/foo"
+test -f "$todir/bar" && test_fail "rsync SHOULD have deleted $todir/bar"
+test -f "$todir/baz" && test_fail "rsync SHOULD have deleted $todir/baz"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/devices.test b/testsuite/devices.test
new file mode 100644
index 0000000..ad5f936
--- /dev/null
+++ b/testsuite/devices.test
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of devices. This can only run if you're root.
+
+. "$suitedir/rsync.fns"
+
+# Build some hardlinks
+
+case $0 in
+*fake*)
+ $RSYNC -VV | grep '"xattrs": true' >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
+ RSYNC="$RSYNC --fake-super"
+ TLS_ARGS="$TLS_ARGS --fake-super"
+ case "$HOST_OS" in
+ darwin*)
+ mknod() {
+ fn="$1"
+ case "$2" in
+ p) mode=10644 ;;
+ c) mode=20644 ;;
+ b) mode=60644 ;;
+ esac
+ maj="${3:-0}"
+ min="${4:-0}"
+ touch "$fn"
+ xattr -s 'rsync.%stat' "$mode $maj,$min 0:0" "$fn"
+ }
+ ;;
+ solaris*)
+ mknod() {
+ fn="$1"
+ case "$2" in
+ p) mode=10644 ;;
+ c) mode=20644 ;;
+ b) mode=60644 ;;
+ esac
+ maj="${3:-0}"
+ min="${4:-0}"
+ touch "$fn"
+ runat "$fn" "$SHELL_PATH" <<EOF
+echo "$mode $maj,$min 0:0" > rsync.%stat
+EOF
+ }
+ ;;
+ freebsd*)
+ mknod() {
+ fn="$1"
+ case "$2" in
+ p) mode=10644 ;;
+ c) mode=20644 ;;
+ b) mode=60644 ;;
+ esac
+ maj="${3:-0}"
+ min="${4:-0}"
+ touch "$fn"
+ setextattr -h user "rsync.%stat" "$mode $maj,$min 0:0" "$fn"
+ }
+ ;;
+ *)
+ mknod() {
+ fn="$1"
+ case "$2" in
+ p) mode=10644 ;;
+ c) mode=20644 ;;
+ b) mode=60644 ;;
+ esac
+ maj="${3:-0}"
+ min="${4:-0}"
+ touch "$fn"
+ setfattr -n 'user.rsync.%stat' -v "$mode $maj,$min 0:0" "$fn"
+ }
+ ;;
+ esac
+ ;;
+*)
+ my_uid=`get_testuid`
+ root_uid=`get_rootuid`
+ if test x"$my_uid" = x; then
+ : # If "id" failed, try to continue...
+ elif test x"$my_uid" != x"$root_uid"; then
+ if [ -e "$FAKEROOT_PATH" ]; then
+ echo "Let's try re-running the script under fakeroot..."
+ exec "$FAKEROOT_PATH" "$SHELL_PATH" $RUNSHFLAGS "$0"
+ fi
+ test_skipped "Rsync needs root/fakeroot for device tests"
+ fi
+ ;;
+esac
+
+# TODO: Need to test whether hardlinks are possible on this OS/filesystem
+
+$RSYNC -VV | grep '"hardlink_specials": true' >/dev/null && CAN_HLINK_SPECIAL=yes || CAN_HLINK_SPECIAL=no
+
+mkdir "$fromdir"
+mkdir "$todir"
+mknod "$fromdir/char" c 41 67 || test_skipped "Can't create char device node"
+mknod "$fromdir/char2" c 42 68 || test_skipped "Can't create char device node"
+mknod "$fromdir/char3" c 42 69 || test_skipped "Can't create char device node"
+mknod "$fromdir/block" b 42 69 || test_skipped "Can't create block device node"
+mknod "$fromdir/block2" b 42 73 || test_skipped "Can't create block device node"
+mknod "$fromdir/block3" b 105 73 || test_skipped "Can't create block device node"
+if test "$CAN_HLINK_SPECIAL" = yes; then
+ ln "$fromdir/block3" "$fromdir/block3.5"
+else
+ echo "Skipping hard-linked device test..."
+fi
+mkfifo "$fromdir/fifo" || mknod "$fromdir/fifo" p || test_skipped "Can't run mkfifo"
+# Work around time rounding/truncating issue by touching both files.
+touch -r "$fromdir/block" "$fromdir/block" "$fromdir/block2"
+
+checkdiff "$RSYNC -ai '$fromdir/block' '$todir/block2'" <<EOT
+cD$all_plus block
+EOT
+
+checkdiff "$RSYNC -ai '$fromdir/block2' '$todir/block'" <<EOT
+cD$all_plus block2
+EOT
+
+sleep 1
+
+checkdiff "$RSYNC -Di '$fromdir/block3' '$todir/block'" <<EOT
+cDc.T.$dots block3
+EOT
+
+cat >"$chkfile" <<EOT
+.d..t.$dots ./
+cDc.t.$dots block
+cDc...$dots block2
+cD$all_plus block3
+hD$all_plus block3.5 => block3
+cD$all_plus char
+cD$all_plus char2
+cD$all_plus char3
+cS$all_plus fifo
+EOT
+if test "$CAN_HLINK_SPECIAL" = no; then
+ grep -v block3.5 <"$chkfile" >"$chkfile.new"
+ mv "$chkfile.new" "$chkfile"
+fi
+
+checkdiff2 "$RSYNC -aiHvv '$fromdir/' '$todir/'" v_filt
+
+echo "check how the directory listings compare with diff:"
+echo ""
+( cd "$fromdir" && rsync_ls_lR . ) > "$tmpdir/ls-from"
+( cd "$todir" && rsync_ls_lR . ) > "$tmpdir/ls-to"
+diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to"
+
+if test "$CAN_HLINK_SPECIAL" = yes; then
+ set -x
+ checkdiff "$RSYNC -aii --link-dest='$todir' '$fromdir/' '$chkdir/'" <<EOT
+created directory $chkdir
+cd$allspace ./
+hD$allspace block
+hD$allspace block2
+hD$allspace block3
+hD$allspace block3.5
+hD$allspace char
+hD$allspace char2
+hD$allspace char3
+hS$allspace fifo
+EOT
+fi
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/dir-sgid.test b/testsuite/dir-sgid.test
new file mode 100644
index 0000000..d6b9a3c
--- /dev/null
+++ b/testsuite/dir-sgid.test
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that rsync obeys directory setgid. -- Matt McCutchen
+
+. $suitedir/rsync.fns
+
+umask 077
+
+# Call as: testit <dirname> <dirperms> <file-expected> <program-expected> <dir-expected>
+testit() {
+ todir="$scratchdir/$1"
+ mkdir "$todir"
+ chmod $2 "$todir"
+ # Make sure we obey directory setgid when creating a directory to hold multiple transferred files,
+ # even though the directory itself is outside the transfer
+ $RSYNC -rvv "$scratchdir/dir" "$scratchdir/file" "$scratchdir/program" "$todir/to/"
+ check_perms "$todir/to" $5 "Target $1"
+ check_perms "$todir/to/dir" $5 "Target $1"
+ check_perms "$todir/to/file" $3 "Target $1"
+ check_perms "$todir/to/program" $4 "Target $1"
+}
+
+mkdir "$scratchdir/dir"
+# Cygwin has a persistent default dir ACL that ruins this test.
+case `getfacl "$scratchdir/dir" 2>/dev/null || true` in
+*default:user::*) test_skipped "The default ACL mode interferes with this test" ;;
+esac
+
+echo "File!" >"$scratchdir/file"
+echo "#!/bin/sh" >"$scratchdir/program"
+
+chmod u=rwx,g=rw,g+s,o=r "$scratchdir/dir" || test_skipped "Can't chmod"
+chmod 664 "$scratchdir/file"
+chmod 775 "$scratchdir/program"
+
+[ -g "$scratchdir/dir" ] || test_skipped "The directory setgid bit vanished!"
+mkdir "$scratchdir/dir/blah"
+[ -g "$scratchdir/dir/blah" ] || test_skipped "Your filesystem doesn't use directory setgid; maybe it's BSD."
+
+# Test some target directories
+testit setgid-off 700 rw------- rwx------ rwx------
+testit setgid-on u=rwx,g=rw,g+s,o-rwx rw------- rwx------ rwx--S---
+
+# Hooray
+exit 0
diff --git a/testsuite/duplicates.test b/testsuite/duplicates.test
new file mode 100644
index 0000000..3317e72
--- /dev/null
+++ b/testsuite/duplicates.test
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of duplicate filenames.
+
+# It's quite possible that the user might specify the same source file
+# more than once on the command line, perhaps through shell variables
+# or wildcard expansions. It might cause problems for rsync if the
+# same name occurred more than once in the file list, because we might
+# be trying to update the first copy and generate checksums for the
+# second copy at the same time. See clean_flist() for the implementation.
+
+# We don't need to worry about hardlinks or symlinks. Because we
+# always rename-and-replace the new copy, they can't affect us.
+
+# This test is not great, because it is a timing-dependent bug.
+
+. "$suitedir/rsync.fns"
+
+# Build some hardlinks
+
+mkdir "$fromdir"
+name1="$fromdir/name1"
+name2="$fromdir/name2"
+echo "This is the file" > "$name1"
+ln -s "$name1" "$name2" || test_fail "can't create symlink"
+
+checkit "$RSYNC -avv '$fromdir/' '$fromdir/' '$fromdir/' '$fromdir/' '$fromdir/' '$fromdir/' '$fromdir/' '$fromdir/' '$fromdir/' '$fromdir/' '$todir/'" "$fromdir" "$todir" \
+ | tee "$outfile"
+
+# Make sure each file was only copied once...
+if [ `grep -c '^name1$' "$outfile"` != 1 ]; then
+ test_fail "name1 was not copied exactly once"
+fi
+if [ `grep -c '^name2 -> ' "$outfile"` != 1 ]; then
+ test_fail "name2 was not copied exactly once"
+fi
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/exclude-lsh.test b/testsuite/exclude-lsh.test
new file mode 120000
index 0000000..84bc98a
--- /dev/null
+++ b/testsuite/exclude-lsh.test
@@ -0,0 +1 @@
+exclude.test \ No newline at end of file
diff --git a/testsuite/exclude.test b/testsuite/exclude.test
new file mode 100644
index 0000000..56b68b8
--- /dev/null
+++ b/testsuite/exclude.test
@@ -0,0 +1,252 @@
+#!/bin/sh
+
+# Copyright (C) 2003-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of exclude/include directives.
+
+# Test some of the more obscure wildcard handling of exclude/include
+# processing.
+
+. "$suitedir/rsync.fns"
+
+CVSIGNORE='*.junk'
+export CVSIGNORE
+
+case $0 in
+*-lsh.*)
+ RSYNC_RSH="$scratchdir/src/support/lsh.sh"
+ export RSYNC_RSH
+ rpath=" --rsync-path='$RSYNC'"
+ host='lh:'
+ ;;
+*)
+ rpath=''
+ host=''
+ ;;
+esac
+
+# Build some files/dirs/links to copy
+
+makepath "$fromdir/foo/down/to/you"
+makepath "$fromdir/foo/sub"
+makepath "$fromdir/bar/down/to/foo/too"
+makepath "$fromdir/bar/down/to/bar/baz"
+makepath "$fromdir/mid/for/foo/and/that/is/who"
+makepath "$fromdir/new/keep/this"
+makepath "$fromdir/new/lose/this"
+cat >"$fromdir/.filt" <<EOF
+exclude down
+: .filt-temp
+clear
+- .filt
+- *.bak
+- *.old
+EOF
+echo filtered-1 >"$fromdir/foo/file1"
+echo removed >"$fromdir/foo/file2"
+echo cvsout >"$fromdir/foo/file2.old"
+cat >"$fromdir/foo/.filt" <<EOF
+include .filt
+- /file1
+EOF
+echo not-filtered-1 >"$fromdir/foo/sub/file1"
+cat >"$fromdir/bar/.filt" <<EOF
+- home-cvs-exclude
+dir-merge .filt2
++ to
+EOF
+echo cvsout >"$fromdir/bar/down/to/home-cvs-exclude"
+cat >"$fromdir/bar/down/to/.filt2" <<EOF
+- .filt2
+EOF
+cat >"$fromdir/bar/down/to/foo/.filt2" <<EOF
++ *.junk
+EOF
+echo keeper >"$fromdir/bar/down/to/foo/file1"
+echo cvsout >"$fromdir/bar/down/to/foo/file1.bak"
+echo gone >"$fromdir/bar/down/to/foo/file3"
+echo lost >"$fromdir/bar/down/to/foo/file4"
+echo weird >"$fromdir/bar/down/to/foo/+ file3"
+echo cvsout-but-filtin >"$fromdir/bar/down/to/foo/file4.junk"
+echo smashed >"$fromdir/bar/down/to/foo/to"
+cat >"$fromdir/bar/down/to/bar/.filt2" <<EOF
+- *.deep
+EOF
+echo filtout >"$fromdir/bar/down/to/bar/baz/file5.deep"
+# This one should be ineffectual
+cat >"$fromdir/mid/.filt2" <<EOF
+- extra
+EOF
+echo cvsout >"$fromdir/mid/one-in-one-out"
+echo one-in-one-out >"$fromdir/mid/.cvsignore"
+echo cvsin >"$fromdir/mid/one-for-all"
+cat >"$fromdir/mid/.filt" <<EOF
+:C
+EOF
+echo cvsin >"$fromdir/mid/for/one-in-one-out"
+echo expunged >"$fromdir/mid/for/foo/extra"
+echo retained >"$fromdir/mid/for/foo/keep"
+
+# Setup our test exclude/include files.
+
+excl="$scratchdir/exclude-from"
+cat >"$excl" <<EOF
+!
+# If the second line of these two lines does anything, it's a bug.
++ **/bar
+- /bar
+# This should match against the whole path, not just the name.
++ foo**too
+# These should float at the end of the path.
++ foo/s?b/
+- foo/*/
+# Test how /** differs from /***
+- new/keep/**
+- new/lose/***
+# Test some normal excludes. Competing lines are paired.
++ t[o]/
+- to
++ file4
+- file[2-9]
+- /mid/for/foo/extra
+EOF
+
+cat >"$scratchdir/.cvsignore" <<EOF
+home-cvs-exclude
+EOF
+
+# Start with a check of --prune-empty-dirs:
+$RSYNC -av --rsync-path="$RSYNC" -f -_foo/too/ -f -_foo/down/ -f -_foo/and/ -f -_new/ "$host$fromdir/" "$chkdir/"
+checkit "$RSYNC -av$rpath --prune-empty-dirs '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
+rm -rf "$todir"
+
+# Add a directory symlink.
+ln -s too "$fromdir/bar/down/to/foo/sym"
+
+# Start to prep an --update test dir
+mkdir "$scratchdir/up1" "$scratchdir/up2"
+touch "$scratchdir/up1/dst-newness" "$scratchdir/up2/src-newness"
+touch "$scratchdir/up1/same-newness" "$scratchdir/up2/same-newness"
+touch "$scratchdir/up1/extra-src" "$scratchdir/up2/extra-dest"
+
+# Create chkdir with what we expect to be excluded.
+checkit "$RSYNC -avv$rpath '$host$fromdir/' '$chkdir/'" "$fromdir" "$chkdir"
+sleep 1 # Ensures that the rm commands will tweak the directory times.
+rm -r "$chkdir"/foo/down
+rm -r "$chkdir"/mid/for/foo/and
+rm -r "$chkdir"/new/keep/this
+rm -r "$chkdir"/new/lose
+rm "$chkdir"/foo/file[235-9]
+rm "$chkdir"/bar/down/to/foo/to "$chkdir"/bar/down/to/foo/file[235-9]
+rm "$chkdir"/mid/for/foo/extra
+
+# Finish prep for the --update test (run last)
+touch "$scratchdir/up1/src-newness" "$scratchdir/up2/dst-newness"
+
+# Un-tweak the directory times in our first (weak) exclude test (though
+# it's a good test of the --existing option).
+$RSYNC -av --rsync-path="$RSYNC" --existing --include='*/' --exclude='*' "$host$fromdir/" "$chkdir/"
+
+# Now, test if rsync excludes the same files.
+
+checkit "$RSYNC -avv$rpath --exclude-from='$excl' \
+ --delete-during '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
+
+# Modify the chk dir by removing cvs-ignored files and then tweaking the dir times.
+
+rm "$chkdir"/foo/*.old
+rm "$chkdir"/bar/down/to/foo/*.bak
+rm "$chkdir"/bar/down/to/foo/*.junk
+rm "$chkdir"/bar/down/to/home-cvs-exclude
+rm "$chkdir"/mid/one-in-one-out
+
+$RSYNC -av --rsync-path="$RSYNC" --existing --filter='exclude,! */' "$host$fromdir/" "$chkdir/"
+
+# Now, test if rsync excludes the same files, this time with --cvs-exclude
+# and --delete-excluded.
+
+# The -C option gets applied in a different order when pushing & pulling, so we instead
+# add the 2 --cvs-exclude filter rules (":C" & "-C") via -f to keep the order the same.
+checkit "$RSYNC -avv$rpath --filter='merge $excl' -f:C -f-C --delete-excluded \
+ --delete-during '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
+
+# Modify the chk dir for our merge-exclude test and then tweak the dir times.
+
+rm "$chkdir"/foo/file1
+rm "$chkdir"/bar/down/to/bar/baz/*.deep
+cp_touch "$fromdir"/bar/down/to/foo/*.junk "$chkdir"/bar/down/to/foo
+cp_touch "$fromdir"/bar/down/to/foo/to "$chkdir"/bar/down/to/foo
+
+$RSYNC -av --rsync-path="$RSYNC" --existing -f 'show .filt*' -f 'hide,! */' --del "$host$fromdir/" "$todir/"
+
+echo retained >"$todir"/bar/down/to/bar/baz/nodel.deep
+cp_touch "$todir"/bar/down/to/bar/baz/nodel.deep "$chkdir"/bar/down/to/bar/baz
+
+$RSYNC -av --rsync-path="$RSYNC" --existing --filter='-! */' "$host$fromdir/" "$chkdir/"
+
+# Now, test if rsync excludes the same files, this time with a merge-exclude
+# file.
+
+checkit "sed '/!/d' '$excl' |
+ $RSYNC -avv$rpath -f dir-merge_.filt -f merge_- \
+ --delete-during '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
+
+# Remove the files that will be deleted.
+
+rm "$chkdir"/.filt
+rm "$chkdir"/bar/.filt
+rm "$chkdir"/bar/down/to/.filt2
+rm "$chkdir"/bar/down/to/foo/.filt2
+rm "$chkdir"/bar/down/to/bar/.filt2
+rm "$chkdir"/mid/.filt
+
+$RSYNC -av --rsync-path="$RSYNC" --existing --include='*/' --exclude='*' "$host$fromdir/" "$chkdir/"
+
+# Now, try the prior command with --delete-before and some side-specific
+# rules.
+
+checkit "sed '/!/d' '$excl' |
+ $RSYNC -avv$rpath -f :s_.filt -f .s_- -f P_nodel.deep \
+ --delete-before '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
+
+# Next, we'll test some rule-restricted filter files.
+
+cat >"$fromdir/bar/down/.excl" <<EOF
+file3
+EOF
+cat >"$fromdir/bar/down/to/foo/.excl" <<EOF
++ file3
+*.bak
+EOF
+$RSYNC -av --rsync-path="$RSYNC" --del "$host$fromdir/" "$chkdir/"
+rm "$chkdir/bar/down/to/foo/file1.bak"
+rm "$chkdir/bar/down/to/foo/file3"
+rm "$chkdir/bar/down/to/foo/+ file3"
+$RSYNC -av --rsync-path="$RSYNC" --existing --filter='-! */' "$host$fromdir/" "$chkdir/"
+$RSYNC -av --rsync-path="$RSYNC" --delete-excluded --exclude='*' "$host$fromdir/" "$todir/"
+
+checkit "$RSYNC -avv$rpath -f dir-merge,-_.excl \
+ '$host$fromdir/' '$todir/'" "$chkdir" "$todir"
+
+relative_opts='--relative --chmod=Du+w --copy-unsafe-links'
+$RSYNC -av --rsync-path="$RSYNC" $relative_opts "$host$fromdir/foo" "$chkdir/"
+rm -rf "$chkdir$fromdir/foo/down"
+$RSYNC -av $relative_opts --existing --filter='-! */' "$fromdir/foo" "$chkdir/"
+
+checkit "$RSYNC -avv$rpath $relative_opts --exclude='$fromdir/foo/down' \
+ '$host$fromdir/foo' '$todir'" "$chkdir$fromdir/foo" "$todir$fromdir/foo"
+
+# Now we'll test the --update option.
+checkdiff "$RSYNC -aiiO$rpath --update --info=skip '$host$scratchdir/up1/' '$scratchdir/up2/'" \
+ "grep -v '^\.d$allspace'" <<EOT
+dst-newness is newer
+>f$all_plus extra-src
+.f$allspace same-newness
+>f..t.$dots src-newness
+EOT
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/executability.test b/testsuite/executability.test
new file mode 100644
index 0000000..8f09d8f
--- /dev/null
+++ b/testsuite/executability.test
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test the --executability or -E option. -- Matt McCutchen
+
+. $suitedir/rsync.fns
+
+# Put some files in the From directory
+mkdir "$fromdir"
+cat <<EOF >"$fromdir/1"
+#!/bin/sh
+echo 'Program One!'
+EOF
+cat <<EOF >"$fromdir/2"
+#!/bin/sh
+echo 'Program Two!'
+EOF
+
+chmod 1700 "$fromdir/1" || test_skipped "Can't chmod"
+chmod 600 "$fromdir/2"
+
+$RSYNC -rvv "$fromdir/" "$todir/"
+
+check_perms "$todir/1" rwx------ 1
+check_perms "$todir/2" rw------- 1
+
+# Mix up the permissions a bit
+chmod 600 "$fromdir/1"
+chmod 601 "$fromdir/2"
+chmod 604 "$todir/2"
+
+$RSYNC -rvv "$fromdir/" "$todir/"
+
+# No -E, so nothing should have changed
+check_perms "$todir/1" rwx------ 2
+check_perms "$todir/2" rw----r-- 2
+
+$RSYNC -rvvE "$fromdir/" "$todir/"
+
+# Now things should have happened!
+check_perms "$todir/1" rw------- 3
+check_perms "$todir/2" rwx---r-x 3
+
+# Hooray
+exit 0
diff --git a/testsuite/files-from.test b/testsuite/files-from.test
new file mode 100644
index 0000000..207eab5
--- /dev/null
+++ b/testsuite/files-from.test
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# Copyright (C) 2008-2020 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that --files-from=FILE works right.
+
+. "$suitedir/rsync.fns"
+
+SSH="$scratchdir/src/support/lsh.sh"
+
+hands_setup
+
+# This list of files skips the contents of "subsubdir" but includes
+# the contents of "subsubdir2" due to its trailing slash.
+cat >"$scratchdir/filelist" <<EOT
+from/./
+from/./dir/subdir
+from/./dir/subdir/subsubdir
+from/./dir/subdir/subsubdir2/
+from/./dir/subdir/foobar.baz
+EOT
+
+# Create a chkdir without the content that we expect to be omitted.
+$RSYNC -a --exclude=dir/text --exclude='subsubdir/**' "$fromdir/" "$chkdir/"
+
+checkit "$RSYNC -av --files-from='$scratchdir/filelist' '$scratchdir' '$todir/'" "$chkdir" "$todir"
+
+for filehost in '' 'localhost:'; do
+ for srchost in '' 'localhost:'; do
+ if [ -z "$srchost" ]; then
+ desthost='localhost:'
+ else
+ desthost=''
+ fi
+
+ rm -rf "$todir"
+ checkit "$RSYNC -avse '$SSH' --rsync-path='$RSYNC' --files-from='$filehost$scratchdir/filelist' '$srchost$scratchdir' '$desthost$todir/'" "$chkdir" "$todir"
+ done
+done
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/fuzzy.test b/testsuite/fuzzy.test
new file mode 100644
index 0000000..101ffd3
--- /dev/null
+++ b/testsuite/fuzzy.test
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# Copyright (C) 2005-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of the --fuzzy option.
+
+. "$suitedir/rsync.fns"
+
+mkdir "$fromdir"
+mkdir "$todir"
+
+cp_p "$srcdir"/rsync.c "$fromdir"/rsync.c
+cp_touch "$fromdir"/rsync.c "$todir"/rsync2.c
+sleep 1
+
+# Let's do it!
+checkit "$RSYNC -avvi --no-whole-file --fuzzy --delete-delay \
+ '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/hands.test b/testsuite/hands.test
new file mode 100644
index 0000000..8e265b7
--- /dev/null
+++ b/testsuite/hands.test
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Copyright (C) 1998, 1999 by Philip Hands <phil@hands.com>
+# Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
+#
+# This program is distributable under the terms of the GNU GPL (see COPYING)
+
+. "$suitedir/rsync.fns"
+
+hands_setup
+
+DEBUG_OPTS="--debug=all0,deltasum0"
+
+# Main script starts here
+
+runtest "basic operation" 'checkit "$RSYNC -av \"$fromdir/\" \"$todir\"" "$fromdir/" "$todir"'
+
+ln "$fromdir/filelist" "$fromdir/dir"
+runtest "hard links" 'checkit "$RSYNC -avH --bwlimit=0 $DEBUG_OPTS \"$fromdir/\" \"$todir\"" "$fromdir/" "$todir"'
+
+rm "$todir/text"
+runtest "one file" 'checkit "$RSYNC -avH $DEBUG_OPTS \"$fromdir/\" \"$todir\"" "$fromdir/" "$todir"'
+
+echo "extra line" >> "$todir/text"
+runtest "extra data" 'checkit "$RSYNC -avH $DEBUG_OPTS --no-whole-file \"$fromdir/\" \"$todir\"" "$fromdir/" "$todir"'
+
+cp "$fromdir/text" "$todir/ThisShouldGo"
+runtest " --delete" 'checkit "$RSYNC --delete -avH $DEBUG_OPTS \"$fromdir/\" \"$todir\"" "$fromdir/" "$todir"'
+
+cd "$tmpdir"
+rm -rf to from/*dir
+
+# Do the real copy, touch up the parent-dir's time, and then check the copy.
+$RSYNC -av from/* to/
+checkit "$RSYNC -av --exclude='*' from/ to/" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/hardlinks.test b/testsuite/hardlinks.test
new file mode 100644
index 0000000..af2ea40
--- /dev/null
+++ b/testsuite/hardlinks.test
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync handling of hardlinks. By default, rsync does not detect
+# hard links and they get sent as separate files. If you specify -H,
+# then hard links are detected and linked together on the receiver.
+
+. "$suitedir/rsync.fns"
+
+SSH="$scratchdir/src/support/lsh.sh"
+
+# Build some hardlinks
+
+fromdir="$scratchdir/from"
+todir="$scratchdir/to"
+
+# TODO: Need to test whether hardlinks are possible on this OS/filesystem
+
+mkdir "$fromdir"
+name1="$fromdir/name1"
+name2="$fromdir/name2"
+name3="$fromdir/name3"
+name4="$fromdir/name4"
+echo "This is the file" > "$name1"
+ln "$name1" "$name2" || test_skipped "Can't create hardlink"
+ln "$name2" "$name3" || test_fail "Can't create hardlink"
+cp "$name2" "$name4" || test_fail "Can't copy file"
+cat $srcdir/*.c >"$fromdir/text"
+
+checkit "$RSYNC -aHivv --debug=HLINK5 '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+echo "extra extra" >>"$todir/name1"
+
+checkit "$RSYNC -aHivv --debug=HLINK5 --no-whole-file '$fromdir/' '$todir/'" "$fromdir" "$todir"
+
+# Add a new link in a new subdirectory to test that we don't try to link
+# the files before the directory gets created. We also create a bunch of
+# extra files to ensure that an incremental-recursion transfer works across
+# distant files.
+makepath "$fromdir/subdir/down/deep"
+
+files=''
+for x in a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9; do
+ for y in a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9; do
+ files="$files $x$y"
+ done
+done
+(cd "$fromdir/subdir"; touch $files)
+
+ln "$name1" "$fromdir/subdir/down/deep/new-file"
+rm "$todir/text"
+
+checkit "$RSYNC -aHivve '$SSH' --debug=HLINK5 --rsync-path='$RSYNC' '$fromdir/' localhost:'$todir/'" "$fromdir" "$todir"
+
+# Do some duplicate copies using --link-dest and --copy-dest to test that
+# we hard-link all locally-inherited items.
+checkit "$RSYNC -aHivv --debug=HLINK5 --link-dest='$todir' '$fromdir/' '$chkdir/'" "$todir" "$chkdir"
+
+rm -rf "$chkdir"
+checkit "$RSYNC -aHivv --debug=HLINK5 --copy-dest='$todir' '$fromdir/' '$chkdir/'" "$fromdir" "$chkdir"
+
+# Create a hard link that has only one part in the hierarchy.
+echo "This is another file" >"$fromdir/solo"
+ln "$fromdir/solo" "$chkdir/solo" || test_fail "Can't create hardlink"
+
+# Make sure that the checksum data doesn't slide due to an HLINK_BUMP() change.
+checktee "$RSYNC -aHivc --debug=HLINK5 '$fromdir/' '$chkdir/'"
+grep solo "$outfile" && test_fail "Erroneous copy of solo file occurred!"
+
+# Make sure there's nothing wrong with sending a single file with -H
+# enabled (this has broken twice so far, so we need this test).
+rm -rf "$todir"
+$RSYNC -aHivv --debug=HLINK5 "$name1" "$todir/"
+diff $diffopt "$name1" "$todir" || test_fail "solo copy of name1 failed"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/itemize.test b/testsuite/itemize.test
new file mode 100644
index 0000000..c1c57c5
--- /dev/null
+++ b/testsuite/itemize.test
@@ -0,0 +1,246 @@
+#!/bin/sh
+
+# Copyright (C) 2005-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test the output of various copy commands to ensure itemized output
+# and double-verbose output is correct.
+
+. "$suitedir/rsync.fns"
+
+to2dir="$tmpdir/to2"
+
+makepath "$fromdir/foo"
+makepath "$fromdir/bar/baz"
+cp_p "$srcdir/configure.ac" "$fromdir/foo/config1"
+cp_p "$srcdir/config.sub" "$fromdir/foo/config2"
+cp_p "$srcdir/rsync.h" "$fromdir/bar/baz/rsync"
+chmod 600 "$fromdir"/foo/config? "$fromdir/bar/baz/rsync"
+umask 0
+ln -s ../bar/baz/rsync "$fromdir/foo/sym"
+umask 022
+ln "$fromdir/foo/config1" "$fromdir/foo/extra"
+rm -f "$to2dir"
+
+# Check if rsync is set to hard-link symlinks.
+if $RSYNC -VV | grep '"hardlink_symlinks": true' >/dev/null; then
+ L=hL
+ sym_dots="$allspace"
+ L_sym_dots=".L$allspace"
+ is_uptodate='is uptodate'
+ touch "$chkfile.extra"
+else
+ L=cL
+ sym_dots="c.t.$dots"
+ L_sym_dots="cL$sym_dots"
+ is_uptodate='-> ../bar/baz/rsync'
+ echo "cL$sym_dots foo/sym $is_uptodate" >"$chkfile.extra"
+fi
+
+# Check if rsync can preserve time on symlinks
+case "$RSYNC" in
+*protocol=2*)
+ T=.T
+ ;;
+*)
+ if $RSYNC -VV | grep '"symtimes": true' >/dev/null; then
+ T=.t
+ else
+ T=.T
+ fi
+ ;;
+esac
+
+checkdiff "$RSYNC -iplr '$fromdir/' '$todir/'" <<EOT
+created directory $todir
+cd$all_plus ./
+cd$all_plus bar/
+cd$all_plus bar/baz/
+>f$all_plus bar/baz/rsync
+cd$all_plus foo/
+>f$all_plus foo/config1
+>f$all_plus foo/config2
+>f$all_plus foo/extra
+cL$all_plus foo/sym -> ../bar/baz/rsync
+EOT
+
+# Ensure there are no accidental directory-time problems.
+$RSYNC -a -f '-! */' "$fromdir/" "$todir"
+
+cp_p "$srcdir/configure.ac" "$fromdir/foo/config2"
+chmod 601 "$fromdir/foo/config2"
+checkdiff "$RSYNC -iplrH '$fromdir/' '$todir/'" <<EOT
+>f..T.$dots bar/baz/rsync
+>f..T.$dots foo/config1
+>f.sTp$dots foo/config2
+hf..T.$dots foo/extra => foo/config1
+EOT
+
+$RSYNC -a -f '-! */' "$fromdir/" "$todir"
+cp_p "$srcdir/config.sub" "$fromdir/foo/config2"
+sleep 1 # For directory mod below to ensure time difference
+rm "$todir/foo/sym"
+umask 0
+ln -s ../bar/baz "$todir/foo/sym"
+umask 022
+chmod 600 "$fromdir/foo/config2"
+chmod 777 "$todir/bar/baz/rsync"
+
+checkdiff "$RSYNC -iplrtc '$fromdir/' '$todir/'" <<EOT
+.f..tp$dots bar/baz/rsync
+.d..t.$dots foo/
+.f..t.$dots foo/config1
+>fcstp$dots foo/config2
+cLc$T.$dots foo/sym -> ../bar/baz/rsync
+EOT
+
+cp_p "$srcdir/configure.ac" "$fromdir/foo/config2"
+chmod 600 "$fromdir/foo/config2"
+# Lack of -t is for unchanged hard-link stress-test!
+checkdiff "$RSYNC -vvplrH '$fromdir/' '$todir/'" \
+ v_filt <<EOT
+bar/baz/rsync is uptodate
+foo/config1 is uptodate
+foo/extra is uptodate
+foo/sym is uptodate
+foo/config2
+EOT
+
+chmod 747 "$todir/bar/baz/rsync"
+$RSYNC -a -f '-! */' "$fromdir/" "$todir"
+checkdiff "$RSYNC -ivvplrtH '$fromdir/' '$todir/'" \
+ v_filt <<EOT
+.d$allspace ./
+.d$allspace bar/
+.d$allspace bar/baz/
+.f...p$dots bar/baz/rsync
+.d$allspace foo/
+.f$allspace foo/config1
+>f..t.$dots foo/config2
+hf$allspace foo/extra
+.L$allspace foo/sym -> ../bar/baz/rsync
+EOT
+
+chmod 757 "$todir/foo/config1"
+touch "$todir/foo/config2"
+checkdiff "$RSYNC -vplrtH '$fromdir/' '$todir/'" \
+ v_filt <<EOT
+foo/config2
+EOT
+
+chmod 757 "$todir/foo/config1"
+touch "$todir/foo/config2"
+checkdiff "$RSYNC -iplrtH '$fromdir/' '$todir/'" <<EOT
+.f...p$dots foo/config1
+>f..t.$dots foo/config2
+EOT
+
+checkdiff "$RSYNC -ivvplrtH --copy-dest=../to '$fromdir/' '$to2dir/'" \
+ v_filt <<EOT
+cd$allspace ./
+cd$allspace bar/
+cd$allspace bar/baz/
+cf$allspace bar/baz/rsync
+cd$allspace foo/
+cf$allspace foo/config1
+cf$allspace foo/config2
+hf$allspace foo/extra => foo/config1
+cL$sym_dots foo/sym -> ../bar/baz/rsync
+EOT
+
+rm -rf "$to2dir"
+cat - "$chkfile.extra" <<EOT >"$chkfile"
+created directory $to2dir
+hf$allspace foo/extra => foo/config1
+EOT
+checkdiff2 "$RSYNC -iplrtH --copy-dest=../to '$fromdir/' '$to2dir/'"
+
+rm -rf "$to2dir"
+checkdiff "$RSYNC -vvplrtH --copy-dest='$todir' '$fromdir/' '$to2dir/'" \
+ v_filt <<EOT
+./ is uptodate
+bar/ is uptodate
+bar/baz/ is uptodate
+bar/baz/rsync is uptodate
+foo/ is uptodate
+foo/config1 is uptodate
+foo/config2 is uptodate
+foo/sym $is_uptodate
+foo/extra => foo/config1
+EOT
+
+rm -rf "$to2dir"
+checkdiff "$RSYNC -ivvplrtH --link-dest='$todir' '$fromdir/' '$to2dir/'" \
+ v_filt <<EOT
+cd$allspace ./
+cd$allspace bar/
+cd$allspace bar/baz/
+hf$allspace bar/baz/rsync
+cd$allspace foo/
+hf$allspace foo/config1
+hf$allspace foo/config2
+hf$allspace foo/extra => foo/config1
+$L$sym_dots foo/sym -> ../bar/baz/rsync
+EOT
+
+rm -rf "$to2dir"
+cat - "$chkfile.extra" <<EOT >"$chkfile"
+created directory $to2dir
+EOT
+checkdiff2 "$RSYNC -iplrtH --dry-run --link-dest=../to '$fromdir/' '$to2dir/'"
+
+rm -rf "$to2dir"
+checkdiff2 "$RSYNC -iplrtH --link-dest=../to '$fromdir/' '$to2dir/'"
+
+rm -rf "$to2dir"
+checkdiff "$RSYNC -vvplrtH --link-dest='$todir' '$fromdir/' '$to2dir/'" \
+ v_filt <<EOT
+./ is uptodate
+bar/ is uptodate
+bar/baz/ is uptodate
+bar/baz/rsync is uptodate
+foo/ is uptodate
+foo/config1 is uptodate
+foo/config2 is uptodate
+foo/extra is uptodate
+foo/sym $is_uptodate
+EOT
+
+rm -rf "$to2dir"
+checkdiff "$RSYNC -ivvplrtH --compare-dest='$todir' '$fromdir/' '$to2dir/'" \
+ v_filt <<EOT
+cd$allspace ./
+cd$allspace bar/
+cd$allspace bar/baz/
+.f$allspace bar/baz/rsync
+cd$allspace foo/
+.f$allspace foo/config1
+.f$allspace foo/config2
+.f$allspace foo/extra
+$L_sym_dots foo/sym -> ../bar/baz/rsync
+EOT
+
+rm -rf "$to2dir"
+cat - "$chkfile.extra" <<EOT >"$chkfile"
+created directory $to2dir
+EOT
+checkdiff2 "$RSYNC -iplrtH --compare-dest='$todir' '$fromdir/' '$to2dir/'"
+
+rm -rf "$to2dir"
+checkdiff "$RSYNC -vvplrtH --compare-dest='$todir' '$fromdir/' '$to2dir/'" \
+ v_filt <<EOT
+./ is uptodate
+bar/ is uptodate
+bar/baz/ is uptodate
+bar/baz/rsync is uptodate
+foo/ is uptodate
+foo/config1 is uptodate
+foo/config2 is uptodate
+foo/extra is uptodate
+foo/sym $is_uptodate
+EOT
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/longdir.test b/testsuite/longdir.test
new file mode 100644
index 0000000..8d66bb5
--- /dev/null
+++ b/testsuite/longdir.test
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Copyright (C) 1998,1999 Philip Hands <phil@hands.com>
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+#
+# This program is distributable under the terms of the GNU GPL (see COPYING)
+
+. "$suitedir/rsync.fns"
+
+hands_setup
+
+longname=This-is-a-directory-with-a-stupidly-long-name-created-in-an-attempt-to-provoke-an-error-found-in-2.0.11-that-should-hopefully-never-appear-again-if-this-test-does-its-job
+longdir="$fromdir/$longname/$longname/$longname"
+
+makepath "$longdir" || test_skipped "unable to create long directory"
+touch "$longdir/1" || test_skipped "unable to create files in long directory"
+date > "$longdir/1"
+if [ -r /etc ]; then
+ ls -la /etc >"$longdir/2"
+else
+ ls -la / >"$longdir/2"
+fi
+checkit "$RSYNC --delete -avH '$fromdir/' '$todir'" "$fromdir/" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/merge.test b/testsuite/merge.test
new file mode 100644
index 0000000..17050a1
--- /dev/null
+++ b/testsuite/merge.test
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# Copyright (C) 2004-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Make sure we can merge files from multiple directories into one.
+
+. "$suitedir/rsync.fns"
+
+# Build some files/dirs/links to copy
+
+# Use local dirnames to better exercise the arg-parsing code.
+cd "$tmpdir"
+
+mkdir from1 from2 from3 deep
+mkdir from2/sub1 from3/sub1
+mkdir from3/sub2 from1/dir-and-not-dir
+mkdir chk chk/sub1 chk/sub2 chk/dir-and-not-dir
+echo "one" >from1/one
+cp_touch from1/one from2/one
+cp_touch from1/one from3/one
+echo "two" >from1/two
+echo "three" >from2/three
+echo "four" >from3/four
+echo "five" >from1/five
+echo "six" >from3/six
+echo "sub1" >from2/sub1/uno
+cp_touch from2/sub1/uno from3/sub1/uno
+echo "sub2" >from3/sub1/dos
+echo "sub3" >from2/sub1/tres
+echo "subby" >from3/sub2/subby
+echo "extra" >from1/dir-and-not-dir/inside
+echo "not-dir" >from3/dir-and-not-dir
+echo "arg-test" >deep/arg-test
+echo "shallow" >shallow
+
+cp_touch from1/one from1/two from2/three from3/four from1/five from3/six chk
+cp_touch deep/arg-test shallow chk
+cp_touch from1/dir-and-not-dir/inside chk/dir-and-not-dir
+cp_touch from2/sub1/uno from3/sub1/dos from2/sub1/tres chk/sub1
+cp_touch from3/sub2/subby chk/sub2
+
+# Make sure that time has moved on.
+sleep 1
+
+# Get rid of any directory-time differences
+$RSYNC -av --existing -f 'exclude,! */' from1/ from2/
+$RSYNC -av --existing -f 'exclude,! */' from2/ from3/
+$RSYNC -av --existing -f 'exclude,! */' from1/ chk/
+$RSYNC -av --existing -f 'exclude,! */' from3/ chk/
+
+checkit "$RSYNC -avv deep/arg-test shallow from1/ from2/ from3/ to/" "$chkdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/missing.test b/testsuite/missing.test
new file mode 100644
index 0000000..2fbf461
--- /dev/null
+++ b/testsuite/missing.test
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test three bugs fixed by my redoing of the missing_below logic.
+
+. $suitedir/rsync.fns
+
+makepath "$fromdir/subdir" "$todir"
+echo data >"$fromdir/subdir/file"
+echo data >"$todir/other"
+
+# Test 1: Too much "not creating new..." output on a dry run
+$RSYNC -n -r --ignore-non-existing -vv "$fromdir/" "$todir/" | tee "$scratchdir/out"
+if grep 'not creating new.*subdir/file' "$scratchdir/out" >/dev/null; then
+ test_fail 'test 1 failed'
+fi
+
+case "$RSYNC" in
+*protocol=29*) # FIXME can we get past the new flist sanity check in protocol 29?
+ echo "Skipped test 2 for protocol 29."
+ ;;
+*)
+ # Test 2: Attempt to make a fuzzy dirlist for a dir not created on a dry run
+ $RSYNC -n -r -R --no-implied-dirs -y "$fromdir/./subdir/file" "$todir/" \
+ || test_fail 'test 2 failed'
+ ;;
+esac
+
+# Test 3: --delete-after pass skipped when last dir is dry-missing
+$RSYNC -n -r --delete-after -i "$fromdir/" "$todir/" | tee "$scratchdir/out"
+grep '^\*deleting * other' "$scratchdir/out" >/dev/null \
+ || test_fail 'test 3 failed'
diff --git a/testsuite/mkpath.test b/testsuite/mkpath.test
new file mode 100644
index 0000000..8046345
--- /dev/null
+++ b/testsuite/mkpath.test
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+. "$suitedir/rsync.fns"
+
+makepath "$fromdir"
+makepath "$todir"
+
+cp_p "$srcdir/rsync.h" "$fromdir/text"
+cp_p "$srcdir/configure.ac" "$fromdir/extra"
+
+cd "$tmpdir"
+
+deep_dir=to/foo/bar/baz/down/deep
+
+# Check that we can create several levels of dest dir
+$RSYNC -aiv --mkpath from/text $deep_dir/new
+test -f $deep_dir/new || test_fail "'new' file not found in $deep_dir dir"
+rm -rf to/foo
+
+$RSYNC -aiv --mkpath from/text $deep_dir/
+test -f $deep_dir/text || test_fail "'text' file not found in $deep_dir dir"
+rm $deep_dir/text
+
+# Make sure we can handle an existing path
+mkdir $deep_dir/new
+$RSYNC -aiv --mkpath from/text $deep_dir/new
+test -f $deep_dir/new/text || test_fail "'text' file not found in $deep_dir/new dir"
+
+# ... and an existing path when an alternate dest filename is specified
+$RSYNC -aiv --mkpath from/text $deep_dir/new/text2
+test -f $deep_dir/new/text2 || test_fail "'text2' file not found in $deep_dir/new dir"
+rm -rf to/foo
+
+# Try the tests again with multiple source args
+$RSYNC -aiv --mkpath from/ $deep_dir
+test -f $deep_dir/extra || test_fail "'extra' file not found in $deep_dir dir"
+rm -rf to/foo
+
+$RSYNC -aiv --mkpath from/ $deep_dir/
+test -f $deep_dir/text || test_fail "'text' file not found in $deep_dir dir"
+
+# Make sure that we can handle no path
+$RSYNC -aiv --mkpath from/text to_text
+test -f to_text || test_fail "'to_text' file not found in current dir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/protected-regular.test b/testsuite/protected-regular.test
new file mode 100644
index 0000000..40416b0
--- /dev/null
+++ b/testsuite/protected-regular.test
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Copyright (C) 2021 by Achim Leitner <aleitner@lis-engineering.de>
+# This program is distributable under the terms of the GNU GPL (see COPYING)
+#
+# Modern linux systems have the protected_regular feature set to 1 or 2
+# See https://www.kernel.org/doc/Documentation/sysctl/fs.txt
+# Make sure we can still write these files in --inplace mode
+
+. "$suitedir/rsync.fns"
+
+test -f /proc/sys/fs/protected_regular || test_skipped "Can't find protected_regular setting (only available on Linux)"
+pr_lvl=`cat /proc/sys/fs/protected_regular 2>/dev/null` || test_skipped "Can't check if fs.protected_regular is enabled (probably need root)"
+test "$pr_lvl" != 0 || test_skipped "fs.protected_regular is not enabled"
+
+workdir="$tmpdir/files"
+mkdir "$workdir"
+chmod 1777 "$workdir"
+
+echo "Source" > "$workdir/src"
+echo "" > "$workdir/dst"
+chown 5001 "$workdir/dst" || test_skipped "Can't chown (probably need root)"
+
+# Output is only shown in case of an error
+echo "Contents of $workdir:"
+ls -al "$workdir"
+
+$RSYNC --inplace "$workdir/src" "$workdir/dst" || test_fail
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/relative.test b/testsuite/relative.test
new file mode 100644
index 0000000..5546291
--- /dev/null
+++ b/testsuite/relative.test
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# Copyright (C) 2005-2020 Wayne Davison
+#
+# This program is distributable under the terms of the GNU GPL (see COPYING)
+
+. "$suitedir/rsync.fns"
+
+deepstr='down/3/deep'
+deepdir="$fromdir/$deepstr"
+extradir="$fromdir/extra"
+makepath "$deepdir" "$extradir/$deepstr" "$chkdir"
+
+fromdir="$deepdir"
+hands_setup
+fromdir="$tmpdir/from"
+
+extrafile="$extradir/./$deepstr/extra.added.value"
+echo wowza >"$extrafile"
+
+$RSYNC -av --existing --include='*/' --exclude='*' "$fromdir/" "$extradir/"
+
+cd "$fromdir"
+
+# Main script starts here
+
+$RSYNC -ai --include=/down/ --exclude='/*' "$fromdir/" "$chkdir/"
+
+sleep 1
+runtest "basic relative" 'checkit "$RSYNC -avR ./$deepstr \"$todir\"" "$chkdir" "$todir"'
+
+ln $deepstr/filelist $deepstr/dir
+ln ../chk/$deepstr/filelist ../chk/$deepstr/dir
+# Work around time rounding/truncating issue by touching both dirs.
+touch -r $deepstr/dir $deepstr/dir ../chk/$deepstr/dir
+runtest "hard links" 'checkit "$RSYNC -avHR ./$deepstr/ \"$todir\"" "$chkdir" "$todir"'
+
+cp "$deepdir/text" "$todir/$deepstr/ThisShouldGo"
+cp "$deepdir/text" "$todir/$deepstr/dir/ThisShouldGoToo"
+runtest "deletion" 'checkit "$RSYNC -avHR --del ./$deepstr/ \"$todir\"" "$chkdir" "$todir"'
+
+runtest "non-deletion" 'checkit "$RSYNC -aiHR --del ./$deepstr/ \"$todir\"" "$chkdir" "$todir"' \
+ | tee "$outfile"
+
+# Make sure no files were deleted
+grep 'deleting ' "$outfile" && test_fail "Erroneous deletions occurred!"
+
+# Relative with merging.
+$RSYNC -ai "$extradir/down" "$chkdir/"
+
+checkit "$RSYNC -aiR $deepstr '$extrafile' '$todir'" "$chkdir" "$todir"
+
+checkit "$RSYNC -aiR --del $deepstr '$extrafile' '$todir'" "$chkdir" "$todir" \
+ | tee "$outfile"
+
+# Make sure no files were deleted
+grep 'deleting ' "$outfile" && test_fail "Erroneous deletions occurred! (2)"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
new file mode 100644
index 0000000..2ab97b6
--- /dev/null
+++ b/testsuite/rsync.fns
@@ -0,0 +1,498 @@
+#!/bin/sh
+
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+# General-purpose test functions for rsync.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version
+# 2 as published by the Free Software Foundation.
+#
+# 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+tmpdir="$scratchdir"
+fromdir="$tmpdir/from"
+todir="$tmpdir/to"
+chkdir="$tmpdir/chk"
+
+chkfile="$scratchdir/rsync.chk"
+outfile="$scratchdir/rsync.out"
+
+# For itemized output:
+all_plus='+++++++++'
+allspace=' '
+dots='.....' # trailing dots after changes
+tab_ch=' ' # a single tab character
+
+# Berkley's nice.
+PATH="$PATH:/usr/ucb"
+
+if diff -u "$suitedir/rsync.fns" "$suitedir/rsync.fns" >/dev/null 2>&1; then
+ diffopt="-u"
+else
+ diffopt="-c"
+fi
+
+HOME="$scratchdir"
+export HOME
+
+runtest() {
+ echo $ECHO_N "Test $1: $ECHO_C"
+ if eval "$2"; then
+ echo "$ECHO_T done."
+ return 0
+ else
+ echo "$ECHO_T failed!"
+ return 1
+ fi
+}
+
+set_cp_destdir() {
+ while test $# -gt 1; do
+ shift
+ done
+ destdir="$1"
+}
+
+# Perform a "cp -p", making sure that timestamps are really the same,
+# even if the copy rounded microsecond times on the destination file.
+cp_touch() {
+ cp_p "${@}"
+ if test $# -gt 2 || test -d "$2"; then
+ set_cp_destdir "${@}" # sets destdir var
+ while test $# -gt 1; do
+ destname="$destdir/`basename $1`"
+ touch -r "$destname" "$1" "$destname"
+ shift
+ done
+ else
+ touch -r "$2" "$1" "$2"
+ fi
+}
+
+# Call this if you want to filter (stdin -> stdout) verbose messages (-v or
+# -vv) from an rsync run (whittling the output down to just the file messages).
+# This isn't needed if you use -i without -v.
+v_filt() {
+ sed -e '/^building file list /d' \
+ -e '/^sending incremental file list/d' \
+ -e '/^created directory /d' \
+ -e '/^done$/d' \
+ -e '/ --whole-file$/d' \
+ -e '/^total: /d' \
+ -e '/^client charset: /d' \
+ -e '/^server charset: /d' \
+ -e '/^$/,$d'
+}
+
+printmsg() {
+ echo "$1"
+}
+
+rsync_ls_lR() {
+ find "$@" -name .git -prune -o -name auto-build-save -prune -o -print | \
+ sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls" $TLS_ARGS
+}
+
+get_testuid() {
+ uid=`id -u 2>/dev/null || true`
+ case "$uid" in
+ [0-9]*) echo "$uid" ;;
+ *) id 2>/dev/null | sed 's/^[^0-9]*\([0-9][0-9]*\).*/\1/' ;;
+ esac
+}
+
+get_rootuid() {
+ uid=`id -u root 2>/dev/null || true`
+ case "$uid" in
+ [0-9]*) echo "$uid" ;;
+ *) echo 0 ;;
+ esac
+}
+
+get_rootgid() {
+ gid=`id -g root 2>/dev/null || true`
+ case "$gid" in
+ [0-9]*) echo "$gid" ;;
+ *) echo 0 ;;
+ esac
+}
+
+# When copying via "cp -p", we want to ensure that a non-root user does not
+# preserve ownership (we want our files to be created as the testing user).
+# For instance, a Cygwin CI run might have git files owned by a different
+# user than the (admin) user running the tests.
+cp_cmd="cp -p"
+if test x`get_testuid` != x0; then
+ case `cp --help 2>/dev/null` in
+ *--no-preserve=*) cp_cmd="cp -p --no-preserve=ownership" ;;
+ esac
+fi
+cp_p() {
+ $cp_cmd "${@}" || test_fail "$cp_cmd failed"
+}
+
+check_perms() {
+ perms=`"$TOOLDIR/tls" "$1" | sed 's/^[-d]\(.........\).*/\1/'`
+ if test $perms = $2; then
+ return 0
+ fi
+ echo "permissions: $perms on $1"
+ echo "should be: $2"
+ test_fail "failed test $3"
+}
+
+rsync_getgroups() {
+ "$TOOLDIR/getgroups"
+}
+
+
+####################
+# Build test directories $todir and $fromdir, with $fromdir full of files.
+
+hands_setup() {
+ # Clean before creation
+ rm -rf "$fromdir"
+ rm -rf "$todir"
+
+ [ -d "$tmpdir" ] || mkdir "$tmpdir"
+ [ -d "$fromdir" ] || mkdir "$fromdir"
+ [ -d "$todir" ] || mkdir "$todir"
+
+ # On some BSD systems, the umask affects the mode of created
+ # symlinks, even though the mode apparently has no effect on how
+ # the links behave in the future, and it cannot be changed using
+ # chmod! rsync always sets its umask to 000 so that it can
+ # accurately recreate permissions, but this script is probably run
+ # with a different umask.
+
+ # This causes a little problem that "ls -l" of the two will not be
+ # the same. So, we need to set our umask before doing any creations.
+
+ # set up test data
+ touch "$fromdir/empty"
+ mkdir "$fromdir/emptydir"
+
+ # a hundred lines of text or so
+ rsync_ls_lR "$srcdir" > "$fromdir/filelist"
+
+ echo $ECHO_N "This file has no trailing lf$ECHO_C" > "$fromdir/nolf"
+ umask 0
+ ln -s nolf "$fromdir/nolf-symlink"
+ umask 022
+
+ cat "$srcdir"/*.c > "$fromdir/text"
+ mkdir "$fromdir/dir"
+ cp "$fromdir/text" "$fromdir/dir"
+ mkdir "$fromdir/dir/subdir"
+ echo some data > "$fromdir/dir/subdir/foobar.baz"
+ mkdir "$fromdir/dir/subdir/subsubdir"
+ if [ -r /etc ]; then
+ ls -ltr /etc > "$fromdir/dir/subdir/subsubdir/etc-ltr-list"
+ else
+ ls -ltr / > "$fromdir/dir/subdir/subsubdir/etc-ltr-list"
+ fi
+ mkdir "$fromdir/dir/subdir/subsubdir2"
+ if [ -r /bin ]; then
+ ls -lt /bin > "$fromdir/dir/subdir/subsubdir2/bin-lt-list"
+ else
+ ls -lt / > "$fromdir/dir/subdir/subsubdir2/bin-lt-list"
+ fi
+
+# echo testing head:
+# ls -lR "$srcdir" | head -10 || echo failed
+}
+
+
+####################
+# Many machines do not have "mkdir -p", so we have to build up long paths.
+# How boring.
+makepath() {
+ for p in "${@}"; do
+ (echo " makepath $p"
+
+ # Absolute Unix path.
+ if echo $p | grep '^/' >/dev/null; then
+ cd /
+ fi
+
+ # This will break if $p contains a space.
+ for c in `echo $p | tr '/' ' '`; do
+ if [ -d "$c" ] || mkdir "$c"; then
+ cd "$c" || return $?
+ else
+ echo "failed to create $c" >&2; return $?
+ fi
+ done)
+ done
+}
+
+
+###########################
+# Run a test (in '$1') then compare directories $2 and $3 to see if
+# there are any difference. If there are, explain them.
+
+# So normally basically $1 should be an rsync command, and $2 and $3
+# the source and destination directories. This is only good when you
+# expect to transfer the whole directory exactly as is. If some files
+# should be excluded, you might need to use something else.
+
+checkit() {
+ failed=
+
+ # We can just write everything to stdout/stderr, because the
+ # wrapper hides it unless there is a problem.
+
+ case "x$TLS_ARGS" in
+ *--atimes*)
+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
+ ;;
+ *)
+ ;;
+ esac
+
+ echo "Running: \"$1\""
+ eval "$1"
+ status=$?
+ if [ $status != 0 ]; then
+ failed="$failed status=$status"
+ fi
+
+ case "x$TLS_ARGS" in
+ *--atimes*)
+ ;;
+ *)
+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
+ ;;
+ esac
+
+ echo "-------------"
+ echo "check how the directory listings compare with diff:"
+ echo ""
+ ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
+ diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed="$failed dir-diff"
+
+ echo "-------------"
+ echo "check how the files compare with diff:"
+ echo ""
+ if [ "x$4" != x ]; then
+ echo " === Skipping (as directed) ==="
+ else
+ diff -r $diffopt "$2" "$3" || failed="$failed file-diff"
+ fi
+
+ echo "-------------"
+ if [ -z "$failed" ]; then
+ return 0
+ fi
+
+ echo "Failed: $failed"
+ return 1
+}
+
+
+# Run a test in $1 and make sure it has a zero exit status. Capture the
+# output into $outfile and echo it to stdout.
+checktee() {
+ echo "Running: \"$1\""
+ eval "$1" >"$outfile"
+ status=$?
+ cat "$outfile"
+ if [ $status != 0 ]; then
+ echo "Failed: status=$status"
+ return 1
+ fi
+ return 0
+}
+
+
+# Slurp stdin into $chkfile and then call checkdiff2().
+checkdiff() {
+ cat >"$chkfile" # Save off stdin
+ checkdiff2 "${@}"
+}
+
+
+# Run a test in $1 and make sure it has a zero exit status. Capture the output
+# into $outfile. If $2 is set, use it to filter the outfile. If resulting
+# outfile differs from the chkfile data, fail with an error.
+checkdiff2() {
+ failed=
+
+ echo "Running: \"$1\""
+ eval "$1" >"$outfile"
+ status=$?
+ cat "$outfile"
+ if [ $status != 0 ]; then
+ failed="$failed status=$status"
+ fi
+
+ if [ -n "$2" ]; then
+ eval "cat '$outfile' | $2 >'$outfile.new'"
+ mv "$outfile.new" "$outfile"
+ fi
+
+ diff $diffopt "$chkfile" "$outfile" || failed="$failed output differs"
+
+ if [ -n "$failed" ]; then
+ echo "Failed:$failed"
+ return 1
+ fi
+ return 0
+}
+
+
+build_rsyncd_conf() {
+ # Build an appropriate configuration file
+ conf="$scratchdir/test-rsyncd.conf"
+ echo "building configuration $conf"
+
+ port=2612
+ pidfile="$scratchdir/rsyncd.pid"
+ logfile="$scratchdir/rsyncd.log"
+ hostname=`uname -n`
+
+ my_uid=`get_testuid`
+ root_uid=`get_rootuid`
+ root_gid=`get_rootgid`
+
+ uid_setting="uid = $root_uid"
+ gid_setting="gid = $root_gid"
+
+ if test x"$my_uid" != x"$root_uid"; then
+ # Non-root cannot specify uid & gid settings
+ uid_setting="#$uid_setting"
+ gid_setting="#$gid_setting"
+ fi
+
+ cat >"$conf" <<EOF
+# rsyncd configuration file autogenerated by $0
+
+pid file = $pidfile
+use chroot = no
+munge symlinks = no
+hosts allow = localhost 127.0.0.0/24 192.168.0.0/16 10.0.0.0/8 $hostname
+log file = $logfile
+transfer logging = yes
+# We don't define log format here so that the test-hidden module will default
+# to the internal static string (since we had a crash trying to tweak it).
+exclude = ? foobar.baz
+max verbosity = 4
+$uid_setting
+$gid_setting
+
+[test-from]
+ path = $fromdir
+ log format = %i %h [%a] %m (%u) %l %f%L
+ read only = yes
+ comment = r/o
+
+[test-to]
+ path = $todir
+ log format = %i %h [%a] %m (%u) %l %f%L
+ read only = no
+ comment = r/w
+
+[test-scratch]
+ path = $scratchdir
+ log format = %i %h [%a] %m (%u) %l %f%L
+ read only = no
+
+[test-hidden]
+ path = $fromdir
+ list = no
+EOF
+
+ # Build a helper script to ignore exit code 23
+ ignore23="$scratchdir/ignore23"
+ echo "building help script $ignore23"
+
+ cat >"$ignore23" <<'EOT'
+if "${@}"; then
+ exit
+fi
+
+ret=$?
+
+if test $ret = 23; then
+ exit
+fi
+
+exit $ret
+EOT
+chmod +x "$ignore23"
+}
+
+
+build_symlinks() {
+ mkdir "$fromdir"
+ date >"$fromdir/referent"
+ ln -s referent "$fromdir/relative"
+ ln -s "$fromdir/referent" "$fromdir/absolute"
+ ln -s nonexistent "$fromdir/dangling"
+ ln -s "$srcdir/rsync.c" "$fromdir/unsafe"
+}
+
+test_fail() {
+ echo "$@" >&2
+ exit 1
+}
+
+test_skipped() {
+ echo "$@" >&2
+ echo "$@" > "$tmpdir/whyskipped"
+ exit 77
+}
+
+# It failed, but we expected that. Don't dump out error logs,
+# because most users won't want to see them. But do leave
+# the working directory around.
+test_xfail() {
+ echo "$@" >&2
+ exit 78
+}
+
+# Determine what shell command will appropriately test for links.
+ln -s foo "$scratchdir/testlink"
+for cmd in test /bin/test /usr/bin/test /usr/ucb/bin/test /usr/ucb/test; do
+ for switch in -h -L; do
+ if $cmd $switch "$scratchdir/testlink" 2>/dev/null; then
+ # how nice
+ TEST_SYMLINK_CMD="$cmd $switch"
+ # i wonder if break 2 is portable?
+ break 2
+ fi
+ done
+done
+# ok, now get rid of it
+rm "$scratchdir/testlink"
+
+
+if [ "x$TEST_SYMLINK_CMD" = 'x' ]; then
+ test_fail "Couldn't determine how to test for symlinks"
+else
+ echo "Testing for symlinks using '$TEST_SYMLINK_CMD'"
+fi
+
+
+# Test whether something is a link, allowing for shell peculiarities
+is_a_link() {
+ # note the variable contains the first option and therefore is not quoted
+ $TEST_SYMLINK_CMD "$1"
+}
+
+
+# We need to set the umask to be reproducible. Note also that when we
+# do some daemon tests as root, we will setuid() and therefore the
+# directory has to be writable by the nobody user in some cases. The
+# best thing is probably to explicitly chmod those directories after
+# creation.
+
+umask 022
diff --git a/testsuite/ssh-basic.test b/testsuite/ssh-basic.test
new file mode 100644
index 0000000..1559ca2
--- /dev/null
+++ b/testsuite/ssh-basic.test
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Copyright (C) 1998,1999 Philip Hands <phil@hands.com>
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING)
+
+# This script tests ssh, if possible. It's called by runtests.sh
+
+. "$suitedir/rsync.fns"
+
+SSH="$scratchdir/src/support/lsh.sh"
+
+if test x"$rsync_enable_ssh_tests" = xyes; then
+ if type ssh >/dev/null; then
+ SSH=ssh
+ fi
+fi
+
+if [ "`$SSH -o'BatchMode yes' localhost echo yes`" != "yes" ]; then
+ test_skipped "Skipping SSH tests because ssh connection to localhost not authorised"
+fi
+
+echo "Using remote shell: $SSH"
+
+# Create some files for rsync to copy
+hands_setup
+
+runtest "ssh: basic test" 'checkit "$RSYNC -avH -e \"$SSH\" --rsync-path=\"$RSYNC\" \"$fromdir/\" \"localhost:$todir\"" "$fromdir/" "$todir"'
+
+mv "$todir/text" "$todir/ThisShouldGo"
+
+runtest "ssh: renamed file" 'checkit "$RSYNC --delete -avH -e \"$SSH\" --rsync-path=\"$RSYNC\" \"$fromdir/\" \"localhost:$todir\"" "$fromdir/" "$todir"'
diff --git a/testsuite/symlink-ignore.test b/testsuite/symlink-ignore.test
new file mode 100644
index 0000000..7055a92
--- /dev/null
+++ b/testsuite/symlink-ignore.test
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test rsync's somewhat over-featured symlink control: the default
+# behaviour is that symlinks should not be copied at all.
+
+. "$suitedir/rsync.fns"
+
+build_symlinks || test_fail "failed to build symlinks"
+
+# Copy recursively, but without -l or -L or -a, and all the symlinks
+# should be missing.
+$RSYNC -r "$fromdir/" "$todir" || test_fail "$RSYNC returned $?"
+
+[ -f "$todir/referent" ] || test_fail "referent was not copied"
+[ -d "$todir/from" ] && test_fail "extra level of directories"
+if is_a_link "$todir/dangling"; then
+ test_fail "dangling symlink was copied"
+fi
+
+if is_a_link "$todir/relative"; then
+ test_fail "relative symlink was copied"
+fi
+
+if is_a_link "$todir/absolute"; then
+ test_fail "absolute symlink was copied"
+fi
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/trimslash.test b/testsuite/trimslash.test
new file mode 100644
index 0000000..2efaa07
--- /dev/null
+++ b/testsuite/trimslash.test
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test tiny function to trim trailing slashes.
+
+. "$suitedir/rsync.fns"
+
+"$TOOLDIR/trimslash" "/usr/local/bin" "/usr/local/bin/" "/usr/local/bin///" \
+ "//a//" "////" \
+ "/Users/Weird Macintosh Name/// Ooh, translucent plastic/" \
+ > "$scratchdir/slash.out"
+diff $diffopt "$scratchdir/slash.out" - <<EOF
+/usr/local/bin
+/usr/local/bin
+/usr/local/bin
+//a
+/
+/Users/Weird Macintosh Name/// Ooh, translucent plastic
+EOF
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/unsafe-byname.test b/testsuite/unsafe-byname.test
new file mode 100644
index 0000000..75e7201
--- /dev/null
+++ b/testsuite/unsafe-byname.test
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+# Copyright (C) 2002 by Martin Pool
+
+# Call directly into unsafe_symlink and test its handling of various filenames
+
+. "$suitedir/rsync.fns"
+
+test_unsafe() {
+ # $1 is the target of a symlink
+ # $2 is the directory we're copying
+ # $3 is the expected outcome: "safe" if the link lies within $2,
+ # or "unsafe" otherwise
+
+ result=`"$TOOLDIR/t_unsafe" "$1" "$2"` || test_fail "Failed to check $1 $2"
+ if [ "$result" != "$3" ]; then
+ test_fail "t_unsafe $1 $2 returned \"$result\", expected \"$3\""
+ fi
+}
+
+test_unsafe file from safe
+test_unsafe dir/file from safe
+test_unsafe dir/./file from safe
+test_unsafe dir/. from safe
+test_unsafe dir/ from safe
+
+test_unsafe /etc/passwd from unsafe
+test_unsafe //../etc/passwd from unsafe
+test_unsafe //./etc/passwd from unsafe
+
+test_unsafe ./foo from safe
+test_unsafe ../foo from unsafe
+test_unsafe ./../foo from unsafe
+test_unsafe .//../foo from unsafe
+test_unsafe ./../foo from/.. unsafe
+test_unsafe ../dest from/dir safe
+test_unsafe ../../dest from//dir unsafe
+test_unsafe ..//../dest from/dir unsafe
+
+test_unsafe .. from/file safe
+test_unsafe ../.. from/file unsafe
+test_unsafe ..//.. from//file unsafe
+test_unsafe dir/.. from safe
+test_unsafe dir/../.. from unsafe
+test_unsafe dir/..//.. from unsafe
+
+test_unsafe '' from unsafe
+
+# Based on tests from unsafe-links by Vladimír Michl
+test_unsafe ../../unsafe/unsafefile from/safe unsafe
+test_unsafe ..//../unsafe/unsafefile from/safe unsafe
+test_unsafe ../files/file1 from/safe safe
+
+test_unsafe ../../unsafe/unsafefile safe unsafe
+test_unsafe ../files/file1 safe unsafe
+
+test_unsafe ../../unsafe/unsafefile `pwd`/from/safe safe
+test_unsafe ../files/file1 `pwd`/from/safe safe
diff --git a/testsuite/unsafe-links.test b/testsuite/unsafe-links.test
new file mode 100644
index 0000000..2d209eb
--- /dev/null
+++ b/testsuite/unsafe-links.test
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# Originally by Vladimír Michl <Vladimir.Michl@hlubocky.del.cz>
+
+. "$suitedir/rsync.fns"
+
+test_symlink() {
+ is_a_link "$1" || test_fail "File $1 is not a symlink"
+}
+
+test_regular() {
+ if [ ! -f "$1" ]; then
+ test_fail "File $1 is not regular file or not exists"
+ fi
+}
+
+cd "$tmpdir"
+
+mkdir from
+
+mkdir "from/safe"
+mkdir "from/unsafe"
+
+mkdir "from/safe/files"
+mkdir "from/safe/links"
+
+touch "from/safe/files/file1"
+touch "from/safe/files/file2"
+touch "from/unsafe/unsafefile"
+
+ln -s ../files/file1 "from/safe/links/"
+ln -s ../files/file2 "from/safe/links/"
+ln -s ../../unsafe/unsafefile "from/safe/links/"
+
+echo "rsync with relative path and just -a"
+$RSYNC -avv from/safe/ to
+test_symlink to/links/file1
+test_symlink to/links/file2
+test_symlink to/links/unsafefile
+
+echo "rsync with relative path and -a --copy-links"
+$RSYNC -avv --copy-links from/safe/ to
+test_regular to/links/file1
+test_regular to/links/file2
+test_regular to/links/unsafefile
+
+echo "rsync with relative path and --copy-unsafe-links"
+$RSYNC -avv --copy-unsafe-links from/safe/ to
+test_symlink to/links/file1
+test_symlink to/links/file2
+test_regular to/links/unsafefile
+
+rm -rf to
+echo "rsync with relative2 path"
+(cd from; $RSYNC -avv --copy-unsafe-links safe/ ../to)
+test_symlink to/links/file1
+test_symlink to/links/file2
+test_regular to/links/unsafefile
+
+rm -rf to
+echo "rsync with absolute path"
+$RSYNC -avv --copy-unsafe-links `pwd`/from/safe/ to
+test_symlink to/links/file1
+test_symlink to/links/file2
+test_regular to/links/unsafefile
diff --git a/testsuite/wildmatch.test b/testsuite/wildmatch.test
new file mode 100644
index 0000000..cfe7584
--- /dev/null
+++ b/testsuite/wildmatch.test
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# Copyright (C) 2003-2022 Wayne Davison
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test the wildmatch functionality
+
+. "$suitedir/rsync.fns"
+
+# This test exercises the wildmatch() function (with no options) and the
+# wildmatch_join() function (using -x and/or -e).
+for opts in "" -x1 "-x1 -e1" "-x1 -e1se" -x2 "-x2 -ese" -x3 "-x3 -e1" -x4 "-x4 -e2e" -x5 "-x5 -es"; do
+ echo Running wildtest with "$opts"
+ "$TOOLDIR/wildtest" $opts "$srcdir/wildtest.txt" >"$scratchdir/wild.out"
+ diff $diffopt "$scratchdir/wild.out" - <<EOF
+No wildmatch errors found.
+EOF
+done
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/testsuite/xattrs.test b/testsuite/xattrs.test
new file mode 100644
index 0000000..d94d5f9
--- /dev/null
+++ b/testsuite/xattrs.test
@@ -0,0 +1,239 @@
+#!/bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test that rsync handles basic xattr preservation.
+
+. $suitedir/rsync.fns
+lnkdir="$tmpdir/lnk"
+
+$RSYNC -VV | grep '"xattrs": true' >/dev/null || test_skipped "Rsync is configured without xattr support"
+
+case "$HOST_OS" in
+darwin*)
+ xset() {
+ xnam="$1"
+ xval="$2"
+ shift 2
+ xattr -s "$xnam" "$xval" "${@}"
+ }
+ xls() {
+ xattr -l "${@}" | sed "s/^[ $tab_ch]*//"
+ }
+ RSYNC_PREFIX='rsync'
+ RUSR='rsync.nonuser'
+ ;;
+solaris*)
+ xset() {
+ xnam="$1"
+ xval="$2"
+ shift 2
+ for fn in "${@}"; do
+ runat "$fn" "$SHELL_PATH" <<EOF
+echo "${xval}" > "${xnam}"
+EOF
+ done
+ }
+ xls() {
+ for fn in "${@}"; do
+ runat "$fn" "$SHELL_PATH" <<EOF
+for x in *; do echo "\$x=\`cat \$x\`"; done
+EOF
+ done
+ }
+ RSYNC_PREFIX='rsync'
+ RUSR='rsync.nonuser'
+ ;;
+freebsd*)
+ xset() {
+ xnam="$1"
+ xval="$2"
+ shift 2
+ setextattr -h user "$xnam" "$xval" "${@}"
+ }
+ xls() {
+ for f in "${@}"; do lsextattr -q -h user "$f" | tr '[[:space:]]' '\n' | sort | xargs -I % getextattr -h user % "$f"; done
+ }
+ RSYNC_PREFIX='rsync'
+ RUSR='rsync'
+ ;;
+*)
+ xset() {
+ xnam="$1"
+ xval="$2"
+ shift 2
+ setfattr -n "$xnam" -v "$xval" "${@}"
+ }
+ xls() {
+ getfattr -d "${@}"
+ }
+ RSYNC_PREFIX='user.rsync'
+ RUSR='user.rsync'
+ ;;
+esac
+
+makepath "$lnkdir" "$fromdir/foo/bar"
+echo now >"$fromdir/file0"
+echo something >"$fromdir/file1"
+echo else >"$fromdir/file2"
+echo deep >"$fromdir/foo/file3"
+echo normal >"$fromdir/file4"
+echo deeper >"$fromdir/foo/bar/file5"
+
+makepath "$chkdir/foo"
+echo wow >"$chkdir/file1"
+cp_touch "$fromdir/foo/file3" "$chkdir/foo"
+
+dirs='foo foo/bar'
+files='file0 file1 file2 foo/file3 file4 foo/bar/file5'
+
+uid_gid=`"$TOOLDIR/tls" "$fromdir/foo" | sed 's/^.* \([0-9][0-9]*\)\.\([0-9][0-9]*\) .*/\1:\2/'`
+
+cd "$fromdir"
+
+xset user.foo foo file0 2>/dev/null || test_skipped "Unable to set an xattr"
+xset user.bar bar file0
+
+xset user.short 'this is short' file1
+xset user.long 'this is a long attribute that will be truncated in the initial data send' file1
+xset user.good 'this is good' file1
+xset user.nice 'this is nice' file1
+
+xset user.foo foo file2
+xset user.bar bar file2
+xset user.long 'a long attribute for our new file that tests to ensure that this works' file2
+
+xset user.dir1 'need to test directory xattrs too' foo
+xset user.dir2 'another xattr' foo
+xset user.dir3 'this is one last one for the moment' foo
+
+xset user.dir4 'another dir test' foo/bar
+xset user.dir5 'one last one' foo/bar
+
+xset user.foo 'new foo' foo/file3 foo/bar/file5
+xset user.bar 'new bar' foo/file3 foo/bar/file5
+xset user.long 'this is also a long attribute that will be truncated in the initial data send' foo/file3 foo/bar/file5
+xset $RUSR.equal 'this long attribute should remain the same and not need to be transferred' foo/file3 foo/bar/file5
+
+xset user.dir0 'old extra value' "$chkdir/foo"
+xset user.dir1 'old dir value' "$chkdir/foo"
+
+xset user.short 'old short' "$chkdir/file1"
+xset user.extra 'remove me' "$chkdir/file1"
+
+xset user.foo 'old foo' "$chkdir/foo/file3"
+xset $RUSR.equal 'this long attribute should remain the same and not need to be transferred' "$chkdir/foo/file3"
+
+case $0 in
+*hlink*)
+ ln foo/bar/file5 foo/bar/file6 || test_skipped "Can't create hardlink"
+ files="$files foo/bar/file6"
+ dashH='-H'
+ altDest='--link-dest'
+ ;;
+*)
+ dashH=''
+ altDest='--copy-dest'
+ ;;
+esac
+
+xls $dirs $files >"$scratchdir/xattrs.txt"
+
+XFILT='-f-x_system.* -f-x_security.*'
+
+# OK, let's try a simple xattr copy.
+checkit "$RSYNC -avX $XFILT $dashH --super . '$chkdir/'" "$fromdir" "$chkdir"
+
+cd "$chkdir"
+xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" -
+
+cd "$fromdir"
+
+if [ -n "$dashH" ]; then
+ for fn in $files; do
+ name=`basename $fn`
+ ln $fn ../lnk/$name
+ done
+fi
+
+checkit "$RSYNC -aiX $XFILT $dashH --super $altDest=../chk . ../to" "$fromdir" "$todir"
+
+cd "$todir"
+xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" -
+
+[ -n "$dashH" ] && rm -rf "$lnkdir"
+
+cd "$fromdir"
+rm -rf "$todir"
+
+xset user.nice 'this is nice, but different' file1
+
+xls $dirs $files >"$scratchdir/xattrs.txt"
+
+checkit "$RSYNC -aiX $XFILT $dashH --fake-super --link-dest=../chk . ../to" "$chkdir" "$todir"
+
+cd "$todir"
+xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" -
+
+sed -n -e '/^[^d ][^ ]* *[^ ][^ ]* *[^ ][^ ]* *1 /p' "$scratchdir/ls-to" >"$scratchdir/ls-diff-all"
+grep -F -v './file1' "$scratchdir/ls-diff-all" >"$scratchdir/ls-diff" || :
+if [ -s "$scratchdir/ls-diff" ]; then
+ echo "Missing hard links on:"
+ cat "$scratchdir/ls-diff"
+ exit 1
+fi
+if [ ! -s "$scratchdir/ls-diff-all" ]; then
+ echo "Too many hard links on file1!"
+ exit 1
+fi
+
+cd "$chkdir"
+chmod go-rwx . $dirs $files
+
+xset user.nice 'this is nice, but different' file1
+xset $RSYNC_PREFIX.%stat "40000 0,0 $uid_gid" $dirs
+xset $RSYNC_PREFIX.%stat "100000 0,0 $uid_gid" $files
+
+xls $dirs $files >"$scratchdir/xattrs.txt"
+
+cd "$fromdir"
+rm -rf "$todir"
+
+# When run by a non-root tester, this checks if no-user-perm files/dirs can be copied.
+checkit "$RSYNC -aiX $XFILT $dashH --fake-super --chmod=a= . ../to" "$chkdir" "$todir" # 2>"$scratchdir/errors.txt"
+
+cd "$todir"
+xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" -
+
+cd "$fromdir"
+rm -rf "$todir" "$chkdir"
+
+$RSYNC -aX file1 file2
+$RSYNC -aX file1 file2 ../chk/
+$RSYNC -aX --del ../chk/ .
+$RSYNC -aX file1 ../lnk/
+[ -n "$dashH" ] && ln "$chkdir/file1" ../lnk/extra-link
+
+xls file1 file2 >"$scratchdir/xattrs.txt"
+
+checkit "$RSYNC -aiiX $XFILT $dashH $altDest=../lnk . ../to" "$chkdir" "$todir"
+
+[ -n "$dashH" ] && rm ../lnk/extra-link
+
+cd "$todir"
+xls file1 file2 | diff $diffopt "$scratchdir/xattrs.txt" -
+
+cd "$fromdir"
+rm "$todir/file2"
+
+echo extra >file1
+$RSYNC -aX . ../chk/
+
+checkit "$RSYNC -aiiX $XFILT . ../to" "$chkdir" "$todir"
+
+cd "$todir"
+xls file1 file2 | diff $diffopt "$scratchdir/xattrs.txt" -
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
diff --git a/tls.c b/tls.c
new file mode 100644
index 0000000..e6b0708
--- /dev/null
+++ b/tls.c
@@ -0,0 +1,296 @@
+/*
+ * Trivial ls for comparing two directories after running an rsync.
+ *
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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 Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* The problem with using the system's own ls is that some features
+ * have little quirks that make directories look different when for
+ * our purposes they're the same -- for example, the BSD braindamage
+ * about setting the mode on symlinks based on your current umask.
+ *
+ * All the filenames must be given on the command line -- tls does not
+ * even read directories, let alone recurse. The typical usage is
+ * "find|sort|xargs tls".
+ *
+ * The format is not exactly the same as any particular Unix ls(1).
+ *
+ * A key requirement for this program is that the output be "very
+ * reproducible." So we mask away information that can accidentally
+ * change. */
+
+#include "rsync.h"
+#include <popt.h>
+#include "lib/sysxattrs.h"
+
+#define PROGRAM "tls"
+
+/* These are to make syscall.o shut up. */
+int dry_run = 0;
+int am_root = 0;
+int am_sender = 1;
+int read_only = 1;
+int list_only = 0;
+int link_times = 0;
+int link_owner = 0;
+int nsec_times = 0;
+
+#ifdef SUPPORT_XATTRS
+
+#ifdef HAVE_LINUX_XATTRS
+#define XSTAT_ATTR "user.rsync.%stat"
+#else
+#define XSTAT_ATTR "rsync.%stat"
+#endif
+
+static int stat_xattr(const char *fname, STRUCT_STAT *fst)
+{
+ unsigned int mode;
+ int rdev_major, rdev_minor, uid, gid, len;
+ char buf[256];
+
+ if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
+ return -1;
+
+ len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
+ if (len >= (int)sizeof buf) {
+ len = -1;
+ errno = ERANGE;
+ }
+ if (len < 0) {
+ if (errno == ENOTSUP || errno == ENOATTR)
+ return -1;
+ if (errno == EPERM && S_ISLNK(fst->st_mode)) {
+ fst->st_uid = 0;
+ fst->st_gid = 0;
+ return 0;
+ }
+ fprintf(stderr, "failed to read xattr %s for %s: %s\n",
+ XSTAT_ATTR, fname, strerror(errno));
+ return -1;
+ }
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%o %d,%d %d:%d",
+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
+ fprintf(stderr, "Corrupt %s xattr attached to %s: \"%s\"\n",
+ XSTAT_ATTR, fname, buf);
+ exit(1);
+ }
+
+#if _S_IFLNK != 0120000
+ if ((mode & (_S_IFMT)) == 0120000)
+ mode = (mode & ~(_S_IFMT)) | _S_IFLNK;
+#endif
+ fst->st_mode = mode;
+
+ fst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
+ fst->st_uid = uid;
+ fst->st_gid = gid;
+
+ return 0;
+}
+
+#endif
+
+static int display_atimes = 0;
+#ifdef SUPPORT_CRTIMES
+static int display_crtimes = 0;
+#endif
+
+static void failed(char const *what, char const *where)
+{
+ fprintf(stderr, PROGRAM ": %s %s: %s\n",
+ what, where, strerror(errno));
+ exit(1);
+}
+
+static void storetime(char *dest, size_t destsize, time_t t, int nsecs)
+{
+ if (t) {
+ int len;
+ struct tm *mt = gmtime(&t);
+
+ len = snprintf(dest, destsize,
+ " %04d-%02d-%02d %02d:%02d:%02d",
+ (int)mt->tm_year + 1900,
+ (int)mt->tm_mon + 1,
+ (int)mt->tm_mday,
+ (int)mt->tm_hour,
+ (int)mt->tm_min,
+ (int)mt->tm_sec);
+ if (nsecs >= 0 && len >= 0)
+ snprintf(dest + len, destsize - len, ".%09d", nsecs);
+ } else {
+ int has_nsecs = nsecs >= 0 ? 1 : 0;
+ int len = MIN(20 + 10*has_nsecs, (int)destsize - 1);
+ memset(dest, ' ', len);
+ dest[len] = '\0';
+ }
+}
+
+static void list_file(const char *fname)
+{
+ STRUCT_STAT buf;
+#ifdef SUPPORT_CRTIMES
+ time_t crtime = 0;
+#endif
+ char permbuf[PERMSTRING_SIZE];
+ char mtimebuf[50];
+ char atimebuf[50];
+ char crtimebuf[50];
+ char linkbuf[4096];
+ int nsecs;
+
+ if (do_lstat(fname, &buf) < 0)
+ failed("stat", fname);
+#ifdef SUPPORT_CRTIMES
+ if (display_crtimes && (crtime = get_create_time(fname, &buf)) == 0)
+ failed("get_create_time", fname);
+#endif
+#ifdef SUPPORT_XATTRS
+ if (am_root < 0)
+ stat_xattr(fname, &buf);
+#endif
+
+ /* The size of anything but a regular file is probably not
+ * worth thinking about. */
+ if (!S_ISREG(buf.st_mode))
+ buf.st_size = 0;
+
+ /* On some BSD platforms the mode bits of a symlink are
+ * undefined. Also it tends not to be possible to reset a
+ * symlink's mtime, so we default to ignoring it too. */
+ if (S_ISLNK(buf.st_mode)) {
+ int len;
+ buf.st_mode &= ~0777;
+ if (!link_times)
+ buf.st_mtime = (time_t)0;
+ if (!link_owner)
+ buf.st_uid = buf.st_gid = 0;
+ strlcpy(linkbuf, " -> ", sizeof linkbuf);
+ /* const-cast required for silly UNICOS headers */
+ len = do_readlink((char*)fname, linkbuf+4, sizeof linkbuf - 4);
+ if (len == -1)
+ failed("do_readlink", fname);
+ else
+ /* it's not nul-terminated */
+ linkbuf[4+len] = 0;
+ } else {
+ linkbuf[0] = '\0';
+ }
+
+ permstring(permbuf, buf.st_mode);
+#ifdef ST_MTIME_NSEC
+ if (nsec_times)
+ nsecs = (int)buf.ST_MTIME_NSEC;
+ else
+#endif
+ nsecs = -1;
+ storetime(mtimebuf, sizeof mtimebuf, buf.st_mtime, nsecs);
+ if (display_atimes)
+ storetime(atimebuf, sizeof atimebuf, S_ISDIR(buf.st_mode) ? 0 : buf.st_atime, -1);
+ else
+ atimebuf[0] = '\0';
+#ifdef SUPPORT_CRTIMES
+ if (display_crtimes)
+ storetime(crtimebuf, sizeof crtimebuf, crtime, -1);
+ else
+#endif
+ crtimebuf[0] = '\0';
+
+ /* TODO: Perhaps escape special characters in fname? */
+ printf("%s ", permbuf);
+
+ if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) {
+ printf("%5ld,%6ld", (long)major(buf.st_rdev), (long)minor(buf.st_rdev));
+ } else
+ printf("%15s", do_big_num(buf.st_size, 1, NULL));
+
+ printf(" %6ld.%-6ld %6ld%s%s%s %s%s\n",
+ (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
+ mtimebuf, atimebuf, crtimebuf, fname, linkbuf);
+}
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"atimes", 'U', POPT_ARG_NONE, &display_atimes, 0, 0, 0},
+#ifdef SUPPORT_CRTIMES
+ {"crtimes", 'N', POPT_ARG_NONE, &display_crtimes, 0, 0, 0},
+#endif
+ {"link-times", 'l', POPT_ARG_NONE, &link_times, 0, 0, 0 },
+ {"link-owner", 'L', POPT_ARG_NONE, &link_owner, 0, 0, 0 },
+#ifdef SUPPORT_XATTRS
+ {"fake-super", 'f', POPT_ARG_VAL, &am_root, -1, 0, 0 },
+#endif
+#ifdef ST_MTIME_NSEC
+ {"nsec", 's', POPT_ARG_NONE, &nsec_times, 0, 0, 0 },
+#endif
+ {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 },
+ {0,0,0,0,0,0,0}
+};
+
+static void NORETURN tls_usage(int ret)
+{
+ FILE *F = ret ? stderr : stdout;
+ fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
+ fprintf(F,"Trivial file listing program for portably checking rsync\n");
+ fprintf(F,"\nOptions:\n");
+ fprintf(F," -U, --atimes display access (last-used) times\n");
+#ifdef SUPPORT_CRTIMES
+ fprintf(F," -N, --crtimes display create times (newness)\n");
+#endif
+ fprintf(F," -l, --link-times display the time on a symlink\n");
+ fprintf(F," -L, --link-owner display the owner+group on a symlink\n");
+#ifdef SUPPORT_XATTRS
+ fprintf(F," -f, --fake-super display attributes including fake-super xattrs\n");
+#endif
+ fprintf(F," -h, --help show this help\n");
+ exit(ret);
+}
+
+int
+main(int argc, char *argv[])
+{
+ poptContext pc;
+ const char **extra_args;
+ int opt;
+
+ pc = poptGetContext(PROGRAM, argc, (const char **)argv, long_options, 0);
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'h':
+ tls_usage(0);
+ default:
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ tls_usage(1);
+ }
+ }
+
+ extra_args = poptGetArgs(pc);
+ if (!extra_args || *extra_args == NULL)
+ tls_usage(1);
+
+ for (; *extra_args; extra_args++)
+ list_file(*extra_args);
+ poptFreeContext(pc);
+
+ return 0;
+}
diff --git a/token.c b/token.c
new file mode 100644
index 0000000..c108b3a
--- /dev/null
+++ b/token.c
@@ -0,0 +1,1113 @@
+/*
+ * Routines used by the file-transfer code.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include <zlib.h>
+#ifdef SUPPORT_ZSTD
+#include <zstd.h>
+#endif
+#ifdef SUPPORT_LZ4
+#include <lz4.h>
+#endif
+
+extern int do_compression;
+extern int protocol_version;
+extern int module_id;
+extern int do_compression_level;
+extern char *skip_compress;
+
+#ifndef Z_INSERT_ONLY
+#define Z_INSERT_ONLY Z_SYNC_FLUSH
+#endif
+
+static int skip_compression_level; /* The least possible compressing for handling skip-compress files. */
+static int per_file_default_level; /* The default level that each new file gets prior to checking its suffix. */
+
+struct suffix_tree {
+ struct suffix_tree *sibling;
+ struct suffix_tree *child;
+ char letter, word_end;
+};
+
+static char *match_list;
+static struct suffix_tree *suftree;
+
+void init_compression_level(void)
+{
+ int min_level, max_level, def_level, off_level;
+
+ switch (do_compression) {
+ case CPRES_NONE:
+ return;
+ case CPRES_ZLIB:
+ case CPRES_ZLIBX:
+ min_level = 1;
+ max_level = Z_BEST_COMPRESSION;
+ def_level = 6; /* Z_DEFAULT_COMPRESSION is -1, so set it to the real default */
+ off_level = skip_compression_level = Z_NO_COMPRESSION;
+ if (do_compression_level == Z_DEFAULT_COMPRESSION)
+ do_compression_level = def_level;
+ break;
+#ifdef SUPPORT_ZSTD
+ case CPRES_ZSTD:
+ min_level = skip_compression_level = ZSTD_minCLevel();
+ max_level = ZSTD_maxCLevel();
+ def_level = ZSTD_CLEVEL_DEFAULT;
+ off_level = CLVL_NOT_SPECIFIED;
+ if (do_compression_level == 0)
+ do_compression_level = def_level;
+ break;
+#endif
+#ifdef SUPPORT_LZ4
+ case CPRES_LZ4:
+ min_level = skip_compression_level = 0;
+ max_level = 0;
+ def_level = 0;
+ off_level = CLVL_NOT_SPECIFIED;
+ break;
+#endif
+ default: /* paranoia to prevent missing case values */
+ NOISY_DEATH("Unknown do_compression value");
+ }
+
+ if (do_compression_level == CLVL_NOT_SPECIFIED)
+ do_compression_level = def_level;
+ else if (do_compression_level == off_level) {
+ do_compression = CPRES_NONE;
+ return;
+ }
+
+ /* We don't bother with any errors or warnings -- just make sure that the values are valid. */
+ if (do_compression_level < min_level)
+ do_compression_level = min_level;
+ else if (do_compression_level > max_level)
+ do_compression_level = max_level;
+}
+
+static void add_suffix(struct suffix_tree **prior, char ltr, const char *str)
+{
+ struct suffix_tree *node, *newnode;
+
+ if (ltr == '[') {
+ const char *after = strchr(str, ']');
+ /* Treat "[foo" and "[]" as having a literal '['. */
+ if (after && after++ != str+1) {
+ while ((ltr = *str++) != ']')
+ add_suffix(prior, ltr, after);
+ return;
+ }
+ }
+
+ for (node = *prior; node; prior = &node->sibling, node = node->sibling) {
+ if (node->letter == ltr) {
+ if (*str)
+ add_suffix(&node->child, *str, str+1);
+ else
+ node->word_end = 1;
+ return;
+ }
+ if (node->letter > ltr)
+ break;
+ }
+ newnode = new(struct suffix_tree);
+ newnode->sibling = node;
+ newnode->child = NULL;
+ newnode->letter = ltr;
+ *prior = newnode;
+ if (*str) {
+ add_suffix(&newnode->child, *str, str+1);
+ newnode->word_end = 0;
+ } else
+ newnode->word_end = 1;
+}
+
+static void add_nocompress_suffixes(const char *str)
+{
+ char *buf, *t;
+ const char *f = str;
+
+ buf = new_array(char, strlen(f) + 1);
+
+ while (*f) {
+ if (*f == '/') {
+ f++;
+ continue;
+ }
+
+ t = buf;
+ do {
+ if (isUpper(f))
+ *t++ = toLower(f);
+ else
+ *t++ = *f;
+ } while (*++f != '/' && *f);
+ *t++ = '\0';
+
+ add_suffix(&suftree, *buf, buf+1);
+ }
+
+ free(buf);
+}
+
+static void init_set_compression(void)
+{
+ const char *f;
+ char *t, *start;
+
+ if (skip_compress)
+ add_nocompress_suffixes(skip_compress);
+
+ /* A non-daemon transfer skips the default suffix list if the
+ * user specified --skip-compress. */
+ if (skip_compress && module_id < 0)
+ f = "";
+ else
+ f = lp_dont_compress(module_id);
+
+ match_list = t = new_array(char, strlen(f) + 2);
+
+ per_file_default_level = do_compression_level;
+
+ while (*f) {
+ if (*f == ' ') {
+ f++;
+ continue;
+ }
+
+ start = t;
+ do {
+ if (isUpper(f))
+ *t++ = toLower(f);
+ else
+ *t++ = *f;
+ } while (*++f != ' ' && *f);
+ *t++ = '\0';
+
+ if (t - start == 1+1 && *start == '*') {
+ /* Optimize a match-string of "*". */
+ *match_list = '\0';
+ suftree = NULL;
+ per_file_default_level = skip_compression_level;
+ break;
+ }
+
+ /* Move *.foo items into the stuffix tree. */
+ if (*start == '*' && start[1] == '.' && start[2]
+ && !strpbrk(start+2, ".?*")) {
+ add_suffix(&suftree, start[2], start+3);
+ t = start;
+ }
+ }
+ *t++ = '\0';
+}
+
+/* determine the compression level based on a wildcard filename list */
+void set_compression(const char *fname)
+{
+#if 0 /* No compression algorithms currently allow mid-stream changing of the level. */
+ const struct suffix_tree *node;
+ const char *s;
+ char ltr;
+#endif
+
+ if (!do_compression)
+ return;
+
+ if (!match_list)
+ init_set_compression();
+
+#if 0
+ compression_level = per_file_default_level;
+
+ if (!*match_list && !suftree)
+ return;
+
+ if ((s = strrchr(fname, '/')) != NULL)
+ fname = s + 1;
+
+ for (s = match_list; *s; s += strlen(s) + 1) {
+ if (iwildmatch(s, fname)) {
+ compression_level = skip_compression_level;
+ return;
+ }
+ }
+
+ if (!(node = suftree) || !(s = strrchr(fname, '.'))
+ || s == fname || !(ltr = *++s))
+ return;
+
+ while (1) {
+ if (isUpper(&ltr))
+ ltr = toLower(&ltr);
+ while (node->letter != ltr) {
+ if (node->letter > ltr)
+ return;
+ if (!(node = node->sibling))
+ return;
+ }
+ if ((ltr = *++s) == '\0') {
+ if (node->word_end)
+ compression_level = skip_compression_level;
+ return;
+ }
+ if (!(node = node->child))
+ return;
+ }
+#else
+ (void)fname;
+#endif
+}
+
+/* non-compressing recv token */
+static int32 simple_recv_token(int f, char **data)
+{
+ static int32 residue;
+ static char *buf;
+ int32 n;
+
+ if (!buf)
+ buf = new_array(char, CHUNK_SIZE);
+
+ if (residue == 0) {
+ int32 i = read_int(f);
+ if (i <= 0)
+ return i;
+ residue = i;
+ }
+
+ *data = buf;
+ n = MIN(CHUNK_SIZE,residue);
+ residue -= n;
+ read_buf(f,buf,n);
+ return n;
+}
+
+/* non-compressing send token */
+static void simple_send_token(int f, int32 token, struct map_struct *buf, OFF_T offset, int32 n)
+{
+ if (n > 0) {
+ int32 len = 0;
+ while (len < n) {
+ int32 n1 = MIN(CHUNK_SIZE, n-len);
+ write_int(f, n1);
+ write_buf(f, map_ptr(buf, offset+len, n1), n1);
+ len += n1;
+ }
+ }
+ /* a -2 token means to send data only and no token */
+ if (token != -2)
+ write_int(f, -(token+1));
+}
+
+/* Flag bytes in compressed stream are encoded as follows: */
+#define END_FLAG 0 /* that's all folks */
+#define TOKEN_LONG 0x20 /* followed by 32-bit token number */
+#define TOKENRUN_LONG 0x21 /* ditto with 16-bit run count */
+#define DEFLATED_DATA 0x40 /* + 6-bit high len, then low len byte */
+#define TOKEN_REL 0x80 /* + 6-bit relative token number */
+#define TOKENRUN_REL 0xc0 /* ditto with 16-bit run count */
+
+#define MAX_DATA_COUNT 16383 /* fit 14 bit count into 2 bytes with flags */
+
+/* zlib.h says that if we want to be able to compress something in a single
+ * call, avail_out must be at least 0.1% larger than avail_in plus 12 bytes.
+ * We'll add in 0.1%+16, just to be safe (and we'll avoid floating point,
+ * to ensure that this is a compile-time value). */
+#define AVAIL_OUT_SIZE(avail_in_size) ((avail_in_size)*1001/1000+16)
+
+/* For coding runs of tokens */
+static int32 last_token = -1;
+static int32 run_start;
+static int32 last_run_end;
+
+/* Deflation state */
+static z_stream tx_strm;
+
+/* Output buffer */
+static char *obuf;
+
+/* We want obuf to be able to hold both MAX_DATA_COUNT+2 bytes as well as
+ * AVAIL_OUT_SIZE(CHUNK_SIZE) bytes, so make sure that it's large enough. */
+#if MAX_DATA_COUNT+2 > AVAIL_OUT_SIZE(CHUNK_SIZE)
+#define OBUF_SIZE (MAX_DATA_COUNT+2)
+#else
+#define OBUF_SIZE AVAIL_OUT_SIZE(CHUNK_SIZE)
+#endif
+
+/* Send a deflated token */
+static void
+send_deflated_token(int f, int32 token, struct map_struct *buf, OFF_T offset, int32 nb, int32 toklen)
+{
+ static int init_done, flush_pending;
+ int32 n, r;
+
+ if (last_token == -1) {
+ /* initialization */
+ if (!init_done) {
+ tx_strm.next_in = NULL;
+ tx_strm.zalloc = NULL;
+ tx_strm.zfree = NULL;
+ if (deflateInit2(&tx_strm, per_file_default_level,
+ Z_DEFLATED, -15, 8,
+ Z_DEFAULT_STRATEGY) != Z_OK) {
+ rprintf(FERROR, "compression init failed\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ obuf = new_array(char, OBUF_SIZE);
+ init_done = 1;
+ } else
+ deflateReset(&tx_strm);
+ last_run_end = 0;
+ run_start = token;
+ flush_pending = 0;
+ } else if (last_token == -2) {
+ run_start = token;
+ } else if (nb != 0 || token != last_token + 1 || token >= run_start + 65536) {
+ /* output previous run */
+ r = run_start - last_run_end;
+ n = last_token - run_start;
+ if (r >= 0 && r <= 63) {
+ write_byte(f, (n==0? TOKEN_REL: TOKENRUN_REL) + r);
+ } else {
+ write_byte(f, (n==0? TOKEN_LONG: TOKENRUN_LONG));
+ write_int(f, run_start);
+ }
+ if (n != 0) {
+ write_byte(f, n);
+ write_byte(f, n >> 8);
+ }
+ last_run_end = last_token;
+ run_start = token;
+ }
+
+ last_token = token;
+
+ if (nb != 0 || flush_pending) {
+ /* deflate the data starting at offset */
+ int flush = Z_NO_FLUSH;
+ tx_strm.avail_in = 0;
+ tx_strm.avail_out = 0;
+ do {
+ if (tx_strm.avail_in == 0 && nb != 0) {
+ /* give it some more input */
+ n = MIN(nb, CHUNK_SIZE);
+ tx_strm.next_in = (Bytef *)
+ map_ptr(buf, offset, n);
+ tx_strm.avail_in = n;
+ nb -= n;
+ offset += n;
+ }
+ if (tx_strm.avail_out == 0) {
+ tx_strm.next_out = (Bytef *)(obuf + 2);
+ tx_strm.avail_out = MAX_DATA_COUNT;
+ if (flush != Z_NO_FLUSH) {
+ /*
+ * We left the last 4 bytes in the
+ * buffer, in case they are the
+ * last 4. Move them to the front.
+ */
+ memcpy(tx_strm.next_out, obuf+MAX_DATA_COUNT-2, 4);
+ tx_strm.next_out += 4;
+ tx_strm.avail_out -= 4;
+ }
+ }
+ if (nb == 0 && token != -2)
+ flush = Z_SYNC_FLUSH;
+ r = deflate(&tx_strm, flush);
+ if (r != Z_OK) {
+ rprintf(FERROR, "deflate returned %d\n", r);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ if (nb == 0 || tx_strm.avail_out == 0) {
+ n = MAX_DATA_COUNT - tx_strm.avail_out;
+ if (flush != Z_NO_FLUSH) {
+ /*
+ * We have to trim off the last 4
+ * bytes of output when flushing
+ * (they are just 0, 0, ff, ff).
+ */
+ n -= 4;
+ }
+ if (n > 0) {
+ obuf[0] = DEFLATED_DATA + (n >> 8);
+ obuf[1] = n;
+ write_buf(f, obuf, n+2);
+ }
+ }
+ } while (nb != 0 || tx_strm.avail_out == 0);
+ flush_pending = token == -2;
+ }
+
+ if (token == -1) {
+ /* end of file - clean up */
+ write_byte(f, END_FLAG);
+ } else if (token != -2 && do_compression == CPRES_ZLIB) {
+ /* Add the data in the current block to the compressor's
+ * history and hash table. */
+ do {
+ /* Break up long sections in the same way that
+ * see_deflate_token() does. */
+ int32 n1 = toklen > 0xffff ? 0xffff : toklen;
+ toklen -= n1;
+ tx_strm.next_in = (Bytef *)map_ptr(buf, offset, n1);
+ tx_strm.avail_in = n1;
+ if (protocol_version >= 31) /* Newer protocols avoid a data-duplicating bug */
+ offset += n1;
+ tx_strm.next_out = (Bytef *) obuf;
+ tx_strm.avail_out = AVAIL_OUT_SIZE(CHUNK_SIZE);
+ r = deflate(&tx_strm, Z_INSERT_ONLY);
+ if (r != Z_OK || tx_strm.avail_in != 0) {
+ rprintf(FERROR, "deflate on token returned %d (%d bytes left)\n",
+ r, tx_strm.avail_in);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ } while (toklen > 0);
+ }
+}
+
+/* tells us what the receiver is in the middle of doing */
+static enum { r_init, r_idle, r_running, r_inflating, r_inflated } recv_state;
+
+/* for inflating stuff */
+static z_stream rx_strm;
+static char *cbuf;
+static char *dbuf;
+
+/* for decoding runs of tokens */
+static int32 rx_token;
+static int32 rx_run;
+
+/* Receive a deflated token and inflate it */
+static int32 recv_deflated_token(int f, char **data)
+{
+ static int init_done;
+ static int32 saved_flag;
+ int32 n, flag;
+ int r;
+
+ for (;;) {
+ switch (recv_state) {
+ case r_init:
+ if (!init_done) {
+ rx_strm.next_out = NULL;
+ rx_strm.zalloc = NULL;
+ rx_strm.zfree = NULL;
+ if (inflateInit2(&rx_strm, -15) != Z_OK) {
+ rprintf(FERROR, "inflate init failed\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ cbuf = new_array(char, MAX_DATA_COUNT);
+ dbuf = new_array(char, AVAIL_OUT_SIZE(CHUNK_SIZE));
+ init_done = 1;
+ } else {
+ inflateReset(&rx_strm);
+ }
+ recv_state = r_idle;
+ rx_token = 0;
+ break;
+
+ case r_idle:
+ case r_inflated:
+ if (saved_flag) {
+ flag = saved_flag & 0xff;
+ saved_flag = 0;
+ } else
+ flag = read_byte(f);
+ if ((flag & 0xC0) == DEFLATED_DATA) {
+ n = ((flag & 0x3f) << 8) + read_byte(f);
+ read_buf(f, cbuf, n);
+ rx_strm.next_in = (Bytef *)cbuf;
+ rx_strm.avail_in = n;
+ recv_state = r_inflating;
+ break;
+ }
+ if (recv_state == r_inflated) {
+ /* check previous inflated stuff ended correctly */
+ rx_strm.avail_in = 0;
+ rx_strm.next_out = (Bytef *)dbuf;
+ rx_strm.avail_out = AVAIL_OUT_SIZE(CHUNK_SIZE);
+ r = inflate(&rx_strm, Z_SYNC_FLUSH);
+ n = AVAIL_OUT_SIZE(CHUNK_SIZE) - rx_strm.avail_out;
+ /*
+ * Z_BUF_ERROR just means no progress was
+ * made, i.e. the decompressor didn't have
+ * any pending output for us.
+ */
+ if (r != Z_OK && r != Z_BUF_ERROR) {
+ rprintf(FERROR, "inflate flush returned %d (%d bytes)\n",
+ r, n);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ if (n != 0 && r != Z_BUF_ERROR) {
+ /* have to return some more data and
+ save the flag for later. */
+ saved_flag = flag + 0x10000;
+ *data = dbuf;
+ return n;
+ }
+ /*
+ * At this point the decompressor should
+ * be expecting to see the 0, 0, ff, ff bytes.
+ */
+ if (!inflateSyncPoint(&rx_strm)) {
+ rprintf(FERROR, "decompressor lost sync!\n");
+ exit_cleanup(RERR_STREAMIO);
+ }
+ rx_strm.avail_in = 4;
+ rx_strm.next_in = (Bytef *)cbuf;
+ cbuf[0] = cbuf[1] = 0;
+ cbuf[2] = cbuf[3] = (char)0xff;
+ inflate(&rx_strm, Z_SYNC_FLUSH);
+ recv_state = r_idle;
+ }
+ if (flag == END_FLAG) {
+ /* that's all folks */
+ recv_state = r_init;
+ return 0;
+ }
+
+ /* here we have a token of some kind */
+ if (flag & TOKEN_REL) {
+ rx_token += flag & 0x3f;
+ flag >>= 6;
+ } else
+ rx_token = read_int(f);
+ if (flag & 1) {
+ rx_run = read_byte(f);
+ rx_run += read_byte(f) << 8;
+ recv_state = r_running;
+ }
+ return -1 - rx_token;
+
+ case r_inflating:
+ rx_strm.next_out = (Bytef *)dbuf;
+ rx_strm.avail_out = AVAIL_OUT_SIZE(CHUNK_SIZE);
+ r = inflate(&rx_strm, Z_NO_FLUSH);
+ n = AVAIL_OUT_SIZE(CHUNK_SIZE) - rx_strm.avail_out;
+ if (r != Z_OK) {
+ rprintf(FERROR, "inflate returned %d (%d bytes)\n", r, n);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ if (rx_strm.avail_in == 0)
+ recv_state = r_inflated;
+ if (n != 0) {
+ *data = dbuf;
+ return n;
+ }
+ break;
+
+ case r_running:
+ ++rx_token;
+ if (--rx_run == 0)
+ recv_state = r_idle;
+ return -1 - rx_token;
+ }
+ }
+}
+
+/*
+ * put the data corresponding to a token that we've just returned
+ * from recv_deflated_token into the decompressor's history buffer.
+ */
+static void see_deflate_token(char *buf, int32 len)
+{
+ int r;
+ int32 blklen;
+ unsigned char hdr[5];
+
+ rx_strm.avail_in = 0;
+ blklen = 0;
+ hdr[0] = 0;
+ do {
+ if (rx_strm.avail_in == 0 && len != 0) {
+ if (blklen == 0) {
+ /* Give it a fake stored-block header. */
+ rx_strm.next_in = (Bytef *)hdr;
+ rx_strm.avail_in = 5;
+ blklen = len;
+ if (blklen > 0xffff)
+ blklen = 0xffff;
+ hdr[1] = blklen;
+ hdr[2] = blklen >> 8;
+ hdr[3] = ~hdr[1];
+ hdr[4] = ~hdr[2];
+ } else {
+ rx_strm.next_in = (Bytef *)buf;
+ rx_strm.avail_in = blklen;
+ if (protocol_version >= 31) /* Newer protocols avoid a data-duplicating bug */
+ buf += blklen;
+ len -= blklen;
+ blklen = 0;
+ }
+ }
+ rx_strm.next_out = (Bytef *)dbuf;
+ rx_strm.avail_out = AVAIL_OUT_SIZE(CHUNK_SIZE);
+ r = inflate(&rx_strm, Z_SYNC_FLUSH);
+ if (r != Z_OK && r != Z_BUF_ERROR) {
+ rprintf(FERROR, "inflate (token) returned %d\n", r);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ } while (len || rx_strm.avail_out == 0);
+}
+
+#ifdef SUPPORT_ZSTD
+
+static ZSTD_inBuffer zstd_in_buff;
+static ZSTD_outBuffer zstd_out_buff;
+static ZSTD_CCtx *zstd_cctx;
+
+static void send_zstd_token(int f, int32 token, struct map_struct *buf, OFF_T offset, int32 nb)
+{
+ static int comp_init_done, flush_pending;
+ ZSTD_EndDirective flush = ZSTD_e_continue;
+ int32 n, r;
+
+ /* initialization */
+ if (!comp_init_done) {
+ zstd_cctx = ZSTD_createCCtx();
+ if (!zstd_cctx) {
+ rprintf(FERROR, "compression init failed\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ obuf = new_array(char, OBUF_SIZE);
+
+ ZSTD_CCtx_setParameter(zstd_cctx, ZSTD_c_compressionLevel, do_compression_level);
+ zstd_out_buff.dst = obuf + 2;
+
+ comp_init_done = 1;
+ }
+
+ if (last_token == -1) {
+ last_run_end = 0;
+ run_start = token;
+ flush_pending = 0;
+ } else if (last_token == -2) {
+ run_start = token;
+ } else if (nb != 0 || token != last_token + 1 || token >= run_start + 65536) {
+ /* output previous run */
+ r = run_start - last_run_end;
+ n = last_token - run_start;
+
+ if (r >= 0 && r <= 63) {
+ write_byte(f, (n==0? TOKEN_REL: TOKENRUN_REL) + r);
+ } else {
+ write_byte(f, (n==0? TOKEN_LONG: TOKENRUN_LONG));
+ write_int(f, run_start);
+ }
+ if (n != 0) {
+ write_byte(f, n);
+ write_byte(f, n >> 8);
+ }
+ last_run_end = last_token;
+ run_start = token;
+ }
+
+ last_token = token;
+
+ if (nb || flush_pending) {
+
+ zstd_in_buff.src = map_ptr(buf, offset, nb);
+ zstd_in_buff.size = nb;
+ zstd_in_buff.pos = 0;
+
+ do {
+ if (zstd_out_buff.size == 0) {
+ zstd_out_buff.size = MAX_DATA_COUNT;
+ zstd_out_buff.pos = 0;
+ }
+
+ /* File ended, flush */
+ if (token != -2)
+ flush = ZSTD_e_flush;
+
+ r = ZSTD_compressStream2(zstd_cctx, &zstd_out_buff, &zstd_in_buff, flush);
+ if (ZSTD_isError(r)) {
+ rprintf(FERROR, "ZSTD_compressStream returned %d\n", r);
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ /*
+ * Nothing is sent if the buffer isn't full so avoid smaller
+ * transfers. If a file is finished then we flush the internal
+ * state and send a smaller buffer so that the remote side can
+ * finish the file.
+ */
+ if (zstd_out_buff.pos == zstd_out_buff.size || flush == ZSTD_e_flush) {
+ n = zstd_out_buff.pos;
+
+ obuf[0] = DEFLATED_DATA + (n >> 8);
+ obuf[1] = n;
+ write_buf(f, obuf, n+2);
+
+ zstd_out_buff.size = 0;
+ }
+ /*
+ * Loop while the input buffer isn't full consumed or the
+ * internal state isn't fully flushed.
+ */
+ } while (zstd_in_buff.pos < zstd_in_buff.size || r > 0);
+ flush_pending = token == -2;
+ }
+
+ if (token == -1) {
+ /* end of file - clean up */
+ write_byte(f, END_FLAG);
+ }
+}
+
+static ZSTD_DCtx *zstd_dctx;
+
+static int32 recv_zstd_token(int f, char **data)
+{
+ static int decomp_init_done;
+ static int out_buffer_size;
+ int32 n, flag;
+ int r;
+
+ if (!decomp_init_done) {
+ zstd_dctx = ZSTD_createDCtx();
+ if (!zstd_dctx) {
+ rprintf(FERROR, "ZSTD_createDStream failed\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ /* Output buffer fits two decompressed blocks */
+ out_buffer_size = ZSTD_DStreamOutSize() * 2;
+ cbuf = new_array(char, MAX_DATA_COUNT);
+ dbuf = new_array(char, out_buffer_size);
+
+ zstd_in_buff.src = cbuf;
+ zstd_out_buff.dst = dbuf;
+
+ decomp_init_done = 1;
+ }
+
+ for (;;) {
+ switch (recv_state) {
+ case r_init:
+ recv_state = r_idle;
+ rx_token = 0;
+ break;
+
+ case r_idle:
+ flag = read_byte(f);
+ if ((flag & 0xC0) == DEFLATED_DATA) {
+ n = ((flag & 0x3f) << 8) + read_byte(f);
+ read_buf(f, cbuf, n);
+
+ zstd_in_buff.size = n;
+ zstd_in_buff.pos = 0;
+
+ recv_state = r_inflating;
+ break;
+ }
+
+ if (flag == END_FLAG) {
+ /* that's all folks */
+ recv_state = r_init;
+ return 0;
+ }
+ /* here we have a token of some kind */
+ if (flag & TOKEN_REL) {
+ rx_token += flag & 0x3f;
+ flag >>= 6;
+ } else
+ rx_token = read_int(f);
+ if (flag & 1) {
+ rx_run = read_byte(f);
+ rx_run += read_byte(f) << 8;
+ recv_state = r_running;
+ }
+ return -1 - rx_token;
+
+ case r_inflated: /* zstd doesn't get into this state */
+ break;
+
+ case r_inflating:
+ zstd_out_buff.size = out_buffer_size;
+ zstd_out_buff.pos = 0;
+
+ r = ZSTD_decompressStream(zstd_dctx, &zstd_out_buff, &zstd_in_buff);
+ n = zstd_out_buff.pos;
+ if (ZSTD_isError(r)) {
+ rprintf(FERROR, "ZSTD decomp returned %d (%d bytes)\n", r, n);
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ /*
+ * If the input buffer is fully consumed and the output
+ * buffer is not full then next step is to read more
+ * data.
+ */
+ if (zstd_in_buff.size == zstd_in_buff.pos && n < out_buffer_size)
+ recv_state = r_idle;
+
+ if (n != 0) {
+ *data = dbuf;
+ return n;
+ }
+ break;
+
+ case r_running:
+ ++rx_token;
+ if (--rx_run == 0)
+ recv_state = r_idle;
+ return -1 - rx_token;
+ }
+ }
+}
+#endif /* SUPPORT_ZSTD */
+
+#ifdef SUPPORT_LZ4
+static void
+send_compressed_token(int f, int32 token, struct map_struct *buf, OFF_T offset, int32 nb)
+{
+ static int init_done, flush_pending;
+ int size = MAX(LZ4_compressBound(CHUNK_SIZE), MAX_DATA_COUNT+2);
+ int32 n, r;
+
+ if (last_token == -1) {
+ if (!init_done) {
+ obuf = new_array(char, size);
+ init_done = 1;
+ }
+ last_run_end = 0;
+ run_start = token;
+ flush_pending = 0;
+ } else if (last_token == -2) {
+ run_start = token;
+ } else if (nb != 0 || token != last_token + 1 || token >= run_start + 65536) {
+ /* output previous run */
+ r = run_start - last_run_end;
+ n = last_token - run_start;
+ if (r >= 0 && r <= 63) {
+ write_byte(f, (n==0? TOKEN_REL: TOKENRUN_REL) + r);
+ } else {
+ write_byte(f, (n==0? TOKEN_LONG: TOKENRUN_LONG));
+ write_int(f, run_start);
+ }
+ if (n != 0) {
+ write_byte(f, n);
+ write_byte(f, n >> 8);
+ }
+ last_run_end = last_token;
+ run_start = token;
+ }
+
+ last_token = token;
+
+ if (nb != 0 || flush_pending) {
+ int available_in, available_out = 0;
+ const char *next_in;
+
+ do {
+ char *next_out = obuf + 2;
+
+ if (available_out == 0) {
+ available_in = MIN(nb, MAX_DATA_COUNT);
+ next_in = map_ptr(buf, offset, available_in);
+ } else
+ available_in /= 2;
+
+ available_out = LZ4_compress_default(next_in, next_out, available_in, size - 2);
+ if (!available_out) {
+ rprintf(FERROR, "compress returned %d\n", available_out);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ if (available_out <= MAX_DATA_COUNT) {
+ obuf[0] = DEFLATED_DATA + (available_out >> 8);
+ obuf[1] = available_out;
+
+ write_buf(f, obuf, available_out + 2);
+
+ available_out = 0;
+ nb -= available_in;
+ offset += available_in;
+ }
+ } while (nb != 0);
+ flush_pending = token == -2;
+ }
+ if (token == -1) {
+ /* end of file - clean up */
+ write_byte(f, END_FLAG);
+ }
+}
+
+static int32 recv_compressed_token(int f, char **data)
+{
+ static int init_done;
+ int32 n, flag;
+ int size = MAX(LZ4_compressBound(CHUNK_SIZE), MAX_DATA_COUNT+2);
+ static const char *next_in;
+ static int avail_in;
+ int avail_out;
+
+ for (;;) {
+ switch (recv_state) {
+ case r_init:
+ if (!init_done) {
+ cbuf = new_array(char, MAX_DATA_COUNT);
+ dbuf = new_array(char, size);
+ init_done = 1;
+ }
+ recv_state = r_idle;
+ rx_token = 0;
+ break;
+
+ case r_idle:
+ flag = read_byte(f);
+ if ((flag & 0xC0) == DEFLATED_DATA) {
+ n = ((flag & 0x3f) << 8) + read_byte(f);
+ read_buf(f, cbuf, n);
+ next_in = (char *)cbuf;
+ avail_in = n;
+ recv_state = r_inflating;
+ break;
+ }
+
+ if (flag == END_FLAG) {
+ /* that's all folks */
+ recv_state = r_init;
+ return 0;
+ }
+
+ /* here we have a token of some kind */
+ if (flag & TOKEN_REL) {
+ rx_token += flag & 0x3f;
+ flag >>= 6;
+ } else
+ rx_token = read_int(f);
+ if (flag & 1) {
+ rx_run = read_byte(f);
+ rx_run += read_byte(f) << 8;
+ recv_state = r_running;
+ }
+ return -1 - rx_token;
+
+ case r_inflating:
+ avail_out = LZ4_decompress_safe(next_in, dbuf, avail_in, size);
+ if (avail_out < 0) {
+ rprintf(FERROR, "uncompress failed: %d\n", avail_out);
+ exit_cleanup(RERR_STREAMIO);
+ }
+ recv_state = r_idle;
+ *data = dbuf;
+ return avail_out;
+
+ case r_inflated: /* lz4 doesn't get into this state */
+ break;
+
+ case r_running:
+ ++rx_token;
+ if (--rx_run == 0)
+ recv_state = r_idle;
+ return -1 - rx_token;
+ }
+ }
+}
+#endif /* SUPPORT_LZ4 */
+
+/**
+ * Transmit a verbatim buffer of length @p n followed by a token.
+ * If token == -1 then we have reached EOF
+ * If n == 0 then don't send a buffer
+ */
+void send_token(int f, int32 token, struct map_struct *buf, OFF_T offset,
+ int32 n, int32 toklen)
+{
+ switch (do_compression) {
+ case CPRES_NONE:
+ simple_send_token(f, token, buf, offset, n);
+ break;
+ case CPRES_ZLIB:
+ case CPRES_ZLIBX:
+ send_deflated_token(f, token, buf, offset, n, toklen);
+ break;
+#ifdef SUPPORT_ZSTD
+ case CPRES_ZSTD:
+ send_zstd_token(f, token, buf, offset, n);
+ break;
+#endif
+#ifdef SUPPORT_LZ4
+ case CPRES_LZ4:
+ send_compressed_token(f, token, buf, offset, n);
+ break;
+#endif
+ default:
+ NOISY_DEATH("Unknown do_compression value");
+ }
+}
+
+/*
+ * receive a token or buffer from the other end. If the return value is >0 then
+ * it is a data buffer of that length, and *data will point at the data.
+ * if the return value is -i then it represents token i-1
+ * if the return value is 0 then the end has been reached
+ */
+int32 recv_token(int f, char **data)
+{
+ switch (do_compression) {
+ case CPRES_NONE:
+ return simple_recv_token(f,data);
+ case CPRES_ZLIB:
+ case CPRES_ZLIBX:
+ return recv_deflated_token(f, data);
+#ifdef SUPPORT_ZSTD
+ case CPRES_ZSTD:
+ return recv_zstd_token(f, data);
+#endif
+#ifdef SUPPORT_LZ4
+ case CPRES_LZ4:
+ return recv_compressed_token(f, data);
+#endif
+ default:
+ NOISY_DEATH("Unknown do_compression value");
+ }
+}
+
+/*
+ * look at the data corresponding to a token, if necessary
+ */
+void see_token(char *data, int32 toklen)
+{
+ switch (do_compression) {
+ case CPRES_NONE:
+ break;
+ case CPRES_ZLIB:
+ see_deflate_token(data, toklen);
+ break;
+ case CPRES_ZLIBX:
+ break;
+#ifdef SUPPORT_ZSTD
+ case CPRES_ZSTD:
+ break;
+#endif
+#ifdef SUPPORT_LZ4
+ case CPRES_LZ4:
+ /*see_uncompressed_token(data, toklen);*/
+ break;
+#endif
+ default:
+ NOISY_DEATH("Unknown do_compression value");
+ }
+}
diff --git a/trimslash.c b/trimslash.c
new file mode 100644
index 0000000..1ec928c
--- /dev/null
+++ b/trimslash.c
@@ -0,0 +1,45 @@
+/*
+ * Simple utility used only by the test harness.
+ *
+ * Copyright (C) 2002 Martin Pool
+ * Copyright (C) 2003-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+
+/* These are to make syscall.o shut up. */
+int dry_run = 0;
+int am_root = 0;
+int am_sender = 1;
+int read_only = 1;
+int list_only = 0;
+
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ if (argc <= 1) {
+ fprintf(stderr, "trimslash: needs at least one argument\n");
+ return 1;
+ }
+
+ for (i = 1; i < argc; i++) {
+ trim_trailing_slashes(argv[i]); /* modify in place */
+ printf("%s\n", argv[i]);
+ }
+ return 0;
+}
diff --git a/uidlist.c b/uidlist.c
new file mode 100644
index 0000000..99a3467
--- /dev/null
+++ b/uidlist.c
@@ -0,0 +1,621 @@
+/*
+ * Handle the mapping of uid/gid and user/group names between systems.
+ *
+ * Copyright (C) 1996 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2004-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/* If the source username/group does not exist on the target then use
+ * the numeric IDs. Never do any mapping for uid=0 or gid=0 as these
+ * are special. */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "itypes.h"
+#include "io.h"
+
+extern int am_root;
+extern int preserve_uid;
+extern int preserve_gid;
+extern int preserve_acls;
+extern int numeric_ids;
+extern int xmit_id0_names;
+extern pid_t namecvt_pid;
+extern gid_t our_gid;
+extern char *usermap;
+extern char *groupmap;
+
+#ifdef HAVE_GETGROUPS
+# ifndef GETGROUPS_T
+# define GETGROUPS_T gid_t
+# endif
+#endif
+
+#define NFLAGS_WILD_NAME_MATCH (1<<0)
+#define NFLAGS_NAME_MATCH (1<<1)
+
+union name_or_id {
+ const char *name;
+ id_t max_id;
+};
+
+struct idlist {
+ struct idlist *next;
+ union name_or_id u;
+ id_t id, id2;
+ uint16 flags;
+};
+
+static struct idlist *uidlist, *uidmap;
+static struct idlist *gidlist, *gidmap;
+
+static inline int id_eq_uid(id_t id, uid_t uid)
+{
+ return id == (id_t)uid;
+}
+
+static inline int id_eq_gid(id_t id, gid_t gid)
+{
+ return id == (id_t)gid;
+}
+
+static id_t id_parse(const char *num_str)
+{
+ id_t tmp, num = 0;
+ const char *cp = num_str;
+
+ while (*cp) {
+ if (!isDigit(cp)) {
+ invalid_num:
+ rprintf(FERROR, "Invalid ID number: %s\n", num_str);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ tmp = num * 10 + *cp++ - '0';
+ if (tmp < num)
+ goto invalid_num;
+ num = tmp;
+ }
+
+ return num;
+}
+
+static struct idlist *add_to_list(struct idlist **root, id_t id, union name_or_id noiu,
+ id_t id2, uint16 flags)
+{
+ struct idlist *node = new(struct idlist);
+ node->next = *root;
+ node->u = noiu;
+ node->id = id;
+ node->id2 = id2;
+ node->flags = flags;
+ *root = node;
+ return node;
+}
+
+/* turn a uid into a user name */
+const char *uid_to_user(uid_t uid)
+{
+ const char *name = NULL;
+
+ if (namecvt_pid) {
+ id_t id = uid;
+ namecvt_call("uid", &name, &id);
+ } else {
+ struct passwd *pass = getpwuid(uid);
+ if (pass)
+ name = strdup(pass->pw_name);
+ }
+
+ return name;
+}
+
+/* turn a gid into a group name */
+const char *gid_to_group(gid_t gid)
+{
+ const char *name = NULL;
+
+ if (namecvt_pid) {
+ id_t id = gid;
+ namecvt_call("gid", &name, &id);
+ } else {
+ struct group *grp = getgrgid(gid);
+ if (grp)
+ name = strdup(grp->gr_name);
+ }
+
+ return name;
+}
+
+/* Parse a user name or (optionally) a number into a uid */
+int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok)
+{
+ if (!name || !*name)
+ return 0;
+
+ if (num_ok && name[strspn(name, "0123456789")] == '\0') {
+ *uid_p = id_parse(name);
+ return 1;
+ }
+
+ if (namecvt_pid) {
+ id_t id;
+ if (!namecvt_call("usr", &name, &id))
+ return 0;
+ *uid_p = id;
+ } else {
+ struct passwd *pass = getpwnam(name);
+ if (!pass)
+ return 0;
+ *uid_p = pass->pw_uid;
+ }
+
+ return 1;
+}
+
+/* Parse a group name or (optionally) a number into a gid */
+int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok)
+{
+ if (!name || !*name)
+ return 0;
+
+ if (num_ok && name[strspn(name, "0123456789")] == '\0') {
+ *gid_p = id_parse(name);
+ return 1;
+ }
+
+ if (namecvt_pid) {
+ id_t id;
+ if (!namecvt_call("grp", &name, &id))
+ return 0;
+ *gid_p = id;
+ } else {
+ struct group *grp = getgrnam(name);
+ if (!grp)
+ return 0;
+ *gid_p = grp->gr_gid;
+ }
+
+ return 1;
+}
+
+static int is_in_group(gid_t gid)
+{
+#ifdef HAVE_GETGROUPS
+ static gid_t last_in;
+ static int ngroups = -2, last_out = -1;
+ static GETGROUPS_T *gidset;
+ int n;
+
+ if (gid == last_in && last_out >= 0)
+ return last_out;
+ if (ngroups < -1) {
+ if ((ngroups = getgroups(0, NULL)) < 0)
+ ngroups = 0;
+ gidset = new_array(GETGROUPS_T, ngroups+1);
+ if (ngroups > 0)
+ ngroups = getgroups(ngroups, gidset);
+ /* The default gid might not be in the list on some systems. */
+ for (n = 0; n < ngroups; n++) {
+ if ((gid_t)gidset[n] == our_gid)
+ break;
+ }
+ if (n == ngroups)
+ gidset[ngroups++] = our_gid;
+ if (DEBUG_GTE(OWN, 2)) {
+ int pos;
+ char *gidbuf = new_array(char, ngroups*21+32);
+ pos = snprintf(gidbuf, 32, "process has %d gid%s: ", ngroups, ngroups == 1? "" : "s");
+ for (n = 0; n < ngroups; n++) {
+ pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]);
+ }
+ rprintf(FINFO, "%s\n", gidbuf);
+ free(gidbuf);
+ }
+ }
+
+ last_in = gid;
+ for (n = 0; n < ngroups; n++) {
+ if ((gid_t)gidset[n] == gid)
+ return last_out = 1;
+ }
+ return last_out = 0;
+
+#else
+ return gid == our_gid;
+#endif
+}
+
+/* Add a uid/gid to its list of ids. Only called on receiving side. */
+static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap,
+ id_t id, const char *name)
+{
+ struct idlist *node;
+ union name_or_id noiu;
+ int flag;
+ id_t id2;
+
+ noiu.name = name; /* ensure that add_to_list() gets the raw value. */
+ if (!name)
+ name = "";
+
+ for (node = idmap; node; node = node->next) {
+ if (node->flags & NFLAGS_WILD_NAME_MATCH) {
+ if (!wildmatch(node->u.name, name))
+ continue;
+ } else if (node->flags & NFLAGS_NAME_MATCH) {
+ if (strcmp(node->u.name, name) != 0)
+ continue;
+ } else if (node->u.max_id) {
+ if (id < node->id || id > node->u.max_id)
+ continue;
+ } else {
+ if (node->id != id)
+ continue;
+ }
+ break;
+ }
+ if (node)
+ id2 = node->id2;
+ else if (*name && id) {
+ if (idlist_ptr == &uidlist) {
+ uid_t uid;
+ id2 = user_to_uid(name, &uid, False) ? (id_t)uid : id;
+ } else {
+ gid_t gid;
+ id2 = group_to_gid(name, &gid, False) ? (id_t)gid : id;
+ }
+ } else
+ id2 = id;
+
+ flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0;
+ node = add_to_list(idlist_ptr, id, noiu, id2, flag);
+
+ if (DEBUG_GTE(OWN, 2)) {
+ rprintf(FINFO, "%sid %u(%s) maps to %u\n",
+ idlist_ptr == &uidlist ? "u" : "g",
+ (unsigned)id, name, (unsigned)id2);
+ }
+
+ return node;
+}
+
+/* this function is a definite candidate for a faster algorithm */
+uid_t match_uid(uid_t uid)
+{
+ static struct idlist *last = NULL;
+ struct idlist *list;
+
+ if (last && id_eq_uid(last->id, uid))
+ return last->id2;
+
+ for (list = uidlist; list; list = list->next) {
+ if (id_eq_uid(list->id, uid))
+ break;
+ }
+
+ if (!list)
+ list = recv_add_id(&uidlist, uidmap, uid, NULL);
+ last = list;
+
+ return list->id2;
+}
+
+gid_t match_gid(gid_t gid, uint16 *flags_ptr)
+{
+ static struct idlist *last = NULL;
+ struct idlist *list;
+
+ if (last && id_eq_gid(last->id, gid))
+ list = last;
+ else {
+ for (list = gidlist; list; list = list->next) {
+ if (id_eq_gid(list->id, gid))
+ break;
+ }
+ if (!list)
+ list = recv_add_id(&gidlist, gidmap, gid, NULL);
+ last = list;
+ }
+
+ if (flags_ptr && list->flags & FLAG_SKIP_GROUP)
+ *flags_ptr |= FLAG_SKIP_GROUP;
+ return list->id2;
+}
+
+/* Add a uid to the list of uids. Only called on sending side. */
+const char *add_uid(uid_t uid)
+{
+ struct idlist *list;
+ struct idlist *node;
+ union name_or_id noiu;
+
+ for (list = uidlist; list; list = list->next) {
+ if (id_eq_uid(list->id, uid))
+ return NULL;
+ }
+
+ noiu.name = uid_to_user(uid);
+ node = add_to_list(&uidlist, uid, noiu, 0, 0);
+ return node->u.name;
+}
+
+/* Add a gid to the list of gids. Only called on sending side. */
+const char *add_gid(gid_t gid)
+{
+ struct idlist *list;
+ struct idlist *node;
+ union name_or_id noiu;
+
+ for (list = gidlist; list; list = list->next) {
+ if (id_eq_gid(list->id, gid))
+ return NULL;
+ }
+
+ noiu.name = gid_to_group(gid);
+ node = add_to_list(&gidlist, gid, noiu, 0, 0);
+ return node->u.name;
+}
+
+static void send_one_name(int f, id_t id, const char *name)
+{
+ int len;
+
+ if (!name)
+ name = "";
+ if ((len = strlen(name)) > 255) /* Impossible? */
+ len = 255;
+
+ write_varint30(f, id);
+ write_byte(f, len);
+ if (len)
+ write_buf(f, name, len);
+}
+
+static void send_one_list(int f, struct idlist *idlist, int usernames)
+{
+ struct idlist *list;
+
+ /* we send sequences of id/byte-len/name */
+ for (list = idlist; list; list = list->next) {
+ if (list->id && list->u.name)
+ send_one_name(f, list->id, list->u.name);
+ }
+
+ /* Terminate the uid list with 0 (which was excluded above).
+ * A modern rsync also sends the name of id 0. */
+ if (xmit_id0_names)
+ send_one_name(f, 0, usernames ? uid_to_user(0) : gid_to_group(0));
+ else
+ write_varint30(f, 0);
+}
+
+/* send a complete uid/gid mapping to the peer */
+void send_id_lists(int f)
+{
+ if (preserve_uid || preserve_acls)
+ send_one_list(f, uidlist, 1);
+
+ if (preserve_gid || preserve_acls)
+ send_one_list(f, gidlist, 0);
+}
+
+uid_t recv_user_name(int f, uid_t uid)
+{
+ struct idlist *node;
+ int len = read_byte(f);
+ char *name;
+
+ if (len) {
+ name = new_array(char, len+1);
+ read_sbuf(f, name, len);
+ if (numeric_ids < 0) {
+ free(name);
+ name = NULL;
+ }
+ } else
+ name = NULL;
+
+ node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
+ return node->id2;
+}
+
+gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
+{
+ struct idlist *node;
+ int len = read_byte(f);
+ char *name;
+
+ if (len) {
+ name = new_array(char, len+1);
+ read_sbuf(f, name, len);
+ if (numeric_ids < 0) {
+ free(name);
+ name = NULL;
+ }
+ } else
+ name = NULL;
+
+ node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
+ if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
+ *flags_ptr |= FLAG_SKIP_GROUP;
+ return node->id2;
+}
+
+/* recv a complete uid/gid mapping from the peer and map the uid/gid
+ * in the file list to local names */
+void recv_id_list(int f, struct file_list *flist)
+{
+ id_t id;
+ int i;
+
+ if ((preserve_uid || preserve_acls) && numeric_ids <= 0) {
+ /* read the uid list */
+ while ((id = read_varint30(f)) != 0)
+ recv_user_name(f, id);
+ if (xmit_id0_names)
+ recv_user_name(f, 0);
+ }
+
+ if ((preserve_gid || preserve_acls) && numeric_ids <= 0) {
+ /* read the gid list */
+ while ((id = read_varint30(f)) != 0)
+ recv_group_name(f, id, NULL);
+ if (xmit_id0_names)
+ recv_group_name(f, 0, NULL);
+ }
+
+ /* Now convert all the uids/gids from sender values to our values. */
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && (!numeric_ids || usermap || groupmap))
+ match_acl_ids();
+#endif
+ if (am_root && preserve_uid && (!numeric_ids || usermap)) {
+ for (i = 0; i < flist->used; i++)
+ F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
+ }
+ if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
+ for (i = 0; i < flist->used; i++) {
+ F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]), &flist->files[i]->flags);
+ }
+ }
+}
+
+void parse_name_map(char *map, BOOL usernames)
+{
+ struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap;
+ struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist;
+ char *colon, *cp = map + strlen(map);
+ union name_or_id noiu;
+ id_t id1;
+ uint16 flags;
+
+ /* Parse the list in reverse, so the order in the struct is right. */
+ while (1) {
+ while (cp > map && cp[-1] != ',') cp--;
+ if (!(colon = strchr(cp, ':'))) {
+ rprintf(FERROR, "No colon found in --%smap: %s\n",
+ usernames ? "user" : "group", cp);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (!colon[1]) {
+ rprintf(FERROR, "No name found after colon --%smap: %s\n",
+ usernames ? "user" : "group", cp);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ *colon = '\0';
+
+ if (isDigit(cp)) {
+ char *dash = strchr(cp, '-');
+ if (strspn(cp, "0123456789-") != (size_t)(colon - cp)
+ || (dash && (!dash[1] || strchr(dash+1, '-')))) {
+ rprintf(FERROR, "Invalid number in --%smap: %s\n",
+ usernames ? "user" : "group", cp);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (dash) {
+ *dash = '\0';
+ noiu.max_id = id_parse(dash+1);
+ } else
+ noiu.max_id = 0;
+ flags = 0;
+ id1 = id_parse(cp);
+ if (dash)
+ *dash = '-';
+ } else if (strpbrk(cp, "*[?")) {
+ flags = NFLAGS_WILD_NAME_MATCH;
+ noiu.name = cp;
+ id1 = 0;
+ } else {
+ flags = NFLAGS_NAME_MATCH;
+ noiu.name = cp;
+ id1 = 0;
+ }
+
+ if (usernames) {
+ uid_t uid;
+ if (user_to_uid(colon+1, &uid, True))
+ add_to_list(idmap_ptr, id1, noiu, uid, flags);
+ else {
+ rprintf(FERROR, "Unknown --usermap name on receiver: %s\n", colon+1);
+ }
+ } else {
+ gid_t gid;
+ if (group_to_gid(colon+1, &gid, True))
+ add_to_list(idmap_ptr, id1, noiu, gid, flags);
+ else {
+ rprintf(FERROR, "Unknown --groupmap name on receiver: %s\n", colon+1);
+ }
+ }
+
+ if (cp == map)
+ break;
+
+ *--cp = '\0'; /* replace comma */
+ }
+
+ /* If the sender isn't going to xmit the id0 name, we assume it's "root". */
+ if (!xmit_id0_names)
+ recv_add_id(idlist_ptr, *idmap_ptr, 0, numeric_ids ? NULL : "root");
+}
+
+#ifdef HAVE_GETGROUPLIST
+const char *getallgroups(uid_t uid, item_list *gid_list)
+{
+ struct passwd *pw;
+ gid_t *gid_array;
+ int size;
+
+ if ((pw = getpwuid(uid)) == NULL)
+ return "getpwuid failed";
+
+ gid_list->count = 0; /* We're overwriting any items in the list */
+ (void)EXPAND_ITEM_LIST(gid_list, gid_t, 32);
+ size = gid_list->malloced;
+
+ /* Get all the process's groups, with the pw_gid group first. */
+ if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list->items, &size) < 0) {
+ if (size > (int)gid_list->malloced) {
+ gid_list->count = gid_list->malloced;
+ (void)EXPAND_ITEM_LIST(gid_list, gid_t, size);
+ if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list->items, &size) < 0)
+ size = -1;
+ } else
+ size = -1;
+ if (size < 0)
+ return "getgrouplist failed";
+ }
+ gid_list->count = size;
+ gid_array = gid_list->items;
+
+ /* Paranoia: is the default group not first in the list? */
+ if (gid_array[0] != pw->pw_gid) {
+ int j;
+ for (j = 1; j < size; j++) {
+ if (gid_array[j] == pw->pw_gid)
+ break;
+ }
+ if (j == size) { /* The default group wasn't found! */
+ (void)EXPAND_ITEM_LIST(gid_list, gid_t, size+1);
+ gid_array = gid_list->items;
+ }
+ gid_array[j] = gid_array[0];
+ gid_array[0] = pw->pw_gid;
+ }
+
+ return NULL;
+}
+#endif
diff --git a/usage.c b/usage.c
new file mode 100644
index 0000000..a5b59ad
--- /dev/null
+++ b/usage.c
@@ -0,0 +1,371 @@
+/*
+ * Some usage & version related functions.
+ *
+ * Copyright (C) 2002-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "version.h"
+#include "latest-year.h"
+#include "git-version.h"
+#include "default-cvsignore.h"
+#include "itypes.h"
+
+extern struct name_num_obj valid_checksums, valid_compressions, valid_auth_checksums;
+
+static char *istring(const char *fmt, int val)
+{
+ char *str;
+ if (asprintf(&str, fmt, val) < 0)
+ out_of_memory("istring");
+ return str;
+}
+
+static void print_info_flags(enum logcode f)
+{
+ STRUCT_STAT *dumstat;
+ BOOL as_json = f == FNONE ? 1 : 0; /* We use 1 == first attribute, 2 == need closing array */
+ char line_buf[75], item_buf[32];
+ int line_len, j;
+ char *info_flags[] = {
+
+ "*Capabilities",
+
+ istring("%d-bit files", (int)(sizeof (OFF_T) * 8)),
+ istring("%d-bit inums", (int)(sizeof dumstat->st_ino * 8)), /* Don't check ino_t! */
+ istring("%d-bit timestamps", (int)(sizeof (time_t) * 8)),
+ istring("%d-bit long ints", (int)(sizeof (int64) * 8)),
+
+#ifndef HAVE_SOCKETPAIR
+ "no "
+#endif
+ "socketpairs",
+
+#ifndef SUPPORT_LINKS
+ "no "
+#endif
+ "symlinks",
+
+#ifndef CAN_SET_SYMLINK_TIMES
+ "no "
+#endif
+ "symtimes",
+
+#ifndef SUPPORT_HARD_LINKS
+ "no "
+#endif
+ "hardlinks",
+
+#ifndef CAN_HARDLINK_SPECIAL
+ "no "
+#endif
+ "hardlink-specials",
+
+#ifndef CAN_HARDLINK_SYMLINK
+ "no "
+#endif
+ "hardlink-symlinks",
+
+#ifndef INET6
+ "no "
+#endif
+ "IPv6",
+
+#ifndef SUPPORT_ATIMES
+ "no "
+#endif
+ "atimes",
+
+ "batchfiles",
+
+#ifndef HAVE_FTRUNCATE
+ "no "
+#endif
+ "inplace",
+
+#ifndef HAVE_FTRUNCATE
+ "no "
+#endif
+ "append",
+
+#ifndef SUPPORT_ACLS
+ "no "
+#endif
+ "ACLs",
+
+#ifndef SUPPORT_XATTRS
+ "no "
+#endif
+ "xattrs",
+
+#ifdef RSYNC_USE_SECLUDED_ARGS
+ "default "
+#else
+ "optional "
+#endif
+ "secluded-args",
+
+#ifndef ICONV_OPTION
+ "no "
+#endif
+ "iconv",
+
+#ifndef SUPPORT_PREALLOCATION
+ "no "
+#endif
+ "prealloc",
+
+#ifndef HAVE_MKTIME
+ "no "
+#endif
+ "stop-at",
+
+#ifndef SUPPORT_CRTIMES
+ "no "
+#endif
+ "crtimes",
+
+ "*Optimizations",
+
+#ifndef USE_ROLL_SIMD
+ "no "
+#endif
+ "SIMD-roll",
+
+#ifndef USE_ROLL_ASM
+ "no "
+#endif
+ "asm-roll",
+
+#ifndef USE_OPENSSL
+ "no "
+#endif
+ "openssl-crypto",
+
+#ifndef USE_MD5_ASM
+ "no "
+#endif
+ "asm-MD5",
+
+ NULL
+ };
+
+ for (line_len = 0, j = 0; ; j++) {
+ char *str = info_flags[j], *next_nfo = str ? info_flags[j+1] : NULL;
+ int need_comma = next_nfo && *next_nfo != '*' ? 1 : 0;
+ int item_len;
+ if (!str || *str == '*')
+ item_len = 1000;
+ else if (as_json) {
+ char *space = strchr(str, ' ');
+ int is_no = space && strncmp(str, "no ", 3) == 0;
+ int is_bits = space && isDigit(str);
+ char *quot = space && !is_no && !is_bits ? "\"" : "";
+ char *item = space ? space + 1 : str;
+ char *val = !space ? "true" : is_no ? "false" : str;
+ int val_len = !space ? 4 : is_no ? 5 : space - str;
+ if (is_bits && (space = strchr(val, '-')) != NULL)
+ val_len = space - str;
+ item_len = snprintf(item_buf, sizeof item_buf,
+ " \"%s%s\": %s%.*s%s%s", item, is_bits ? "bits" : "",
+ quot, val_len, val, quot, need_comma ? "," : "");
+ if (is_bits)
+ item_buf[strlen(item)+2-1] = '_'; /* Turn the 's' into a '_' */
+ for (space = item; (space = strpbrk(space, " -")) != NULL; space++)
+ item_buf[space - item + 2] = '_';
+ } else
+ item_len = snprintf(item_buf, sizeof item_buf, " %s%s", str, need_comma ? "," : "");
+ if (line_len && line_len + item_len >= (int)sizeof line_buf) {
+ if (as_json)
+ printf(" %s\n", line_buf);
+ else
+ rprintf(f, " %s\n", line_buf);
+ line_len = 0;
+ }
+ if (!str)
+ break;
+ if (*str == '*') {
+ if (as_json) {
+ if (as_json == 2)
+ printf(" }");
+ else
+ as_json = 2;
+ printf(",\n \"%c%s\": {\n", toLower(str+1), str+2);
+ } else
+ rprintf(f, "%s:\n", str+1);
+ } else {
+ strlcpy(line_buf + line_len, item_buf, sizeof line_buf - line_len);
+ line_len += item_len;
+ }
+ }
+ if (as_json == 2)
+ printf(" }");
+}
+
+static void output_nno_list(enum logcode f, const char *name, struct name_num_obj *nno)
+{
+ char namebuf[64], tmpbuf[256];
+ char *tok, *next_tok, *comma = ",";
+ char *cp;
+
+ /* Using '(' ensures that we get a trailing "none" but also includes aliases. */
+ get_default_nno_list(nno, tmpbuf, sizeof tmpbuf - 1, '(');
+ if (f != FNONE) {
+ rprintf(f, "%s:\n", name);
+ rprintf(f, " %s\n", tmpbuf);
+ return;
+ }
+
+ strlcpy(namebuf, name, sizeof namebuf);
+ for (cp = namebuf; *cp; cp++) {
+ if (*cp == ' ')
+ *cp = '_';
+ else if (isUpper(cp))
+ *cp = toLower(cp);
+ }
+
+ printf(",\n \"%s\": [\n ", namebuf);
+
+ for (tok = strtok(tmpbuf, " "); tok; tok = next_tok) {
+ next_tok = strtok(NULL, " ");
+ if (*tok != '(') /* Ignore the alises in the JSON output */
+ printf(" \"%s\"%s", tok, comma + (next_tok ? 0 : 1));
+ }
+
+ printf("\n ]");
+}
+
+/* A request of f == FNONE wants json on stdout. */
+void print_rsync_version(enum logcode f)
+{
+ char copyright[] = "(C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.";
+ char url[] = "https://rsync.samba.org/";
+ BOOL first_line = 1;
+
+#define json_line(name, value) \
+ do { \
+ printf("%c\n \"%s\": \"%s\"", first_line ? '{' : ',', name, value); \
+ first_line = 0; \
+ } while (0)
+
+ if (f == FNONE) {
+ char verbuf[32];
+ json_line("program", RSYNC_NAME);
+ json_line("version", rsync_version());
+ (void)snprintf(verbuf, sizeof verbuf, "%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
+ json_line("protocol", verbuf);
+ json_line("copyright", copyright);
+ json_line("url", url);
+ } else {
+#if SUBPROTOCOL_VERSION != 0
+ char *subprotocol = istring(".PR%d", SUBPROTOCOL_VERSION);
+#else
+ char *subprotocol = "";
+#endif
+ rprintf(f, "%s version %s protocol version %d%s\n",
+ RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
+ rprintf(f, "Copyright %s\n", copyright);
+ rprintf(f, "Web site: %s\n", url);
+ }
+
+ print_info_flags(f);
+
+ init_checksum_choices();
+
+ output_nno_list(f, "Checksum list", &valid_checksums);
+ output_nno_list(f, "Compress list", &valid_compressions);
+ output_nno_list(f, "Daemon auth list", &valid_auth_checksums);
+
+ if (f == FNONE) {
+ json_line("license", "GPLv3");
+ json_line("caveat", "rsync comes with ABSOLUTELY NO WARRANTY");
+ printf("\n}\n");
+ return;
+ }
+
+#ifdef MAINTAINER_MODE
+ rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
+#endif
+
+#if SIZEOF_INT64 < 8
+ rprintf(f, "WARNING: no 64-bit integers on this platform!\n");
+#endif
+ if (sizeof (int64) != SIZEOF_INT64) {
+ rprintf(f,
+ "WARNING: size mismatch in SIZEOF_INT64 define (%d != %d)\n",
+ (int) SIZEOF_INT64, (int) sizeof (int64));
+ }
+
+ rprintf(f,"\n");
+ rprintf(f,"rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n");
+ rprintf(f,"are welcome to redistribute it under certain conditions. See the GNU\n");
+ rprintf(f,"General Public Licence for details.\n");
+}
+
+void usage(enum logcode F)
+{
+ print_rsync_version(F);
+
+ rprintf(F,"\n");
+ rprintf(F,"rsync is a file transfer program capable of efficient remote update\n");
+ rprintf(F,"via a fast differencing algorithm.\n");
+
+ rprintf(F,"\n");
+ rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... DEST\n");
+ rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n");
+ rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n");
+ rprintf(F," or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n");
+ rprintf(F," or rsync [OPTION]... [USER@]HOST:SRC [DEST]\n");
+ rprintf(F," or rsync [OPTION]... [USER@]HOST::SRC [DEST]\n");
+ rprintf(F," or rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]\n");
+ rprintf(F,"The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect\n");
+ rprintf(F,"to an rsync daemon, and require SRC or DEST to start with a module name.\n");
+ rprintf(F,"\n");
+ rprintf(F,"Options\n");
+#include "help-rsync.h"
+ rprintf(F,"\n");
+ rprintf(F,"Use \"rsync --daemon --help\" to see the daemon-mode command-line options.\n");
+ rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) manpages for full documentation.\n");
+ rprintf(F,"See https://rsync.samba.org/ for updates, bug reports, and answers\n");
+}
+
+void daemon_usage(enum logcode F)
+{
+ print_rsync_version(F);
+
+ rprintf(F,"\n");
+ rprintf(F,"Usage: rsync --daemon [OPTION]...\n");
+#include "help-rsyncd.h"
+ rprintf(F,"\n");
+ rprintf(F,"If you were not trying to invoke rsync as a daemon, avoid using any of the\n");
+ rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) manpage.\n");
+}
+
+const char *rsync_version(void)
+{
+ char *ver;
+#ifdef RSYNC_GITVER
+ ver = RSYNC_GITVER;
+#else
+ ver = RSYNC_VERSION;
+#endif
+ return *ver == 'v' ? ver+1 : ver;
+}
+
+const char *default_cvsignore(void)
+{
+ return DEFAULT_CVSIGNORE;
+}
diff --git a/util1.c b/util1.c
new file mode 100644
index 0000000..da50ff1
--- /dev/null
+++ b/util1.c
@@ -0,0 +1,1705 @@
+/*
+ * Utility routines used in rsync.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "itypes.h"
+#include "inums.h"
+
+extern int dry_run;
+extern int module_id;
+extern int do_fsync;
+extern int protect_args;
+extern int modify_window;
+extern int relative_paths;
+extern int preserve_xattrs;
+extern int omit_link_times;
+extern int preallocate_files;
+extern char *module_dir;
+extern unsigned int module_dirlen;
+extern char *partial_dir;
+extern filter_rule_list daemon_filter_list;
+
+int sanitize_paths = 0;
+
+char curr_dir[MAXPATHLEN];
+unsigned int curr_dir_len;
+int curr_dir_depth; /* This is only set for a sanitizing daemon. */
+
+/* Set a fd into nonblocking mode. */
+void set_nonblocking(int fd)
+{
+ int val;
+
+ if ((val = fcntl(fd, F_GETFL)) == -1)
+ return;
+ if (!(val & NONBLOCK_FLAG)) {
+ val |= NONBLOCK_FLAG;
+ fcntl(fd, F_SETFL, val);
+ }
+}
+
+/* Set a fd into blocking mode. */
+void set_blocking(int fd)
+{
+ int val;
+
+ if ((val = fcntl(fd, F_GETFL)) == -1)
+ return;
+ if (val & NONBLOCK_FLAG) {
+ val &= ~NONBLOCK_FLAG;
+ fcntl(fd, F_SETFL, val);
+ }
+}
+
+/**
+ * Create a file descriptor pair - like pipe() but use socketpair if
+ * possible (because of blocking issues on pipes).
+ *
+ * Always set non-blocking.
+ */
+int fd_pair(int fd[2])
+{
+ int ret;
+
+#ifdef HAVE_SOCKETPAIR
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+#else
+ ret = pipe(fd);
+#endif
+
+ if (ret == 0) {
+ set_nonblocking(fd[0]);
+ set_nonblocking(fd[1]);
+ }
+
+ return ret;
+}
+
+void print_child_argv(const char *prefix, char **cmd)
+{
+ int cnt = 0;
+ rprintf(FCLIENT, "%s ", prefix);
+ for (; *cmd; cmd++) {
+ /* Look for characters that ought to be quoted. This
+ * is not a great quoting algorithm, but it's
+ * sufficient for a log message. */
+ if (strspn(*cmd, "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ ",.-_=+@/") != strlen(*cmd)) {
+ rprintf(FCLIENT, "\"%s\" ", *cmd);
+ } else {
+ rprintf(FCLIENT, "%s ", *cmd);
+ }
+ cnt++;
+ }
+ rprintf(FCLIENT, " (%d args)\n", cnt);
+}
+
+/* This returns 0 for success, 1 for a symlink if symlink time-setting
+ * is not possible, or -1 for any other error. */
+int set_times(const char *fname, STRUCT_STAT *stp)
+{
+ static int switch_step = 0;
+
+ if (DEBUG_GTE(TIME, 1)) {
+ rprintf(FINFO,
+ "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
+ fname, (long)stp->st_mtime,
+ timestring(stp->st_mtime), (long)stp->st_atime, timestring(stp->st_atime));
+ }
+
+ switch (switch_step) {
+#ifdef HAVE_SETATTRLIST
+#include "case_N.h"
+ if (do_setattrlist_times(fname, stp) == 0)
+ break;
+ if (errno != ENOSYS)
+ return -1;
+ switch_step++;
+#endif
+
+#ifdef HAVE_UTIMENSAT
+#include "case_N.h"
+ if (do_utimensat(fname, stp) == 0)
+ break;
+ if (errno != ENOSYS)
+ return -1;
+ switch_step++;
+#endif
+
+#ifdef HAVE_LUTIMES
+#include "case_N.h"
+ if (do_lutimes(fname, stp) == 0)
+ break;
+ if (errno != ENOSYS)
+ return -1;
+ switch_step++;
+#endif
+
+#include "case_N.h"
+ switch_step++;
+ if (!omit_link_times) {
+ omit_link_times = 1;
+ if (S_ISLNK(stp->st_mode))
+ return 1;
+ }
+
+#include "case_N.h"
+#ifdef HAVE_UTIMES
+ if (do_utimes(fname, stp) == 0)
+ break;
+#else
+ if (do_utime(fname, stp) == 0)
+ break;
+#endif
+
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Create any necessary directories in fname. Any missing directories are
+ * created with default permissions. Returns < 0 on error, or the number
+ * of directories created. */
+int make_path(char *fname, int flags)
+{
+ char *end, *p;
+ int ret = 0;
+
+ if (flags & MKP_SKIP_SLASH) {
+ while (*fname == '/')
+ fname++;
+ }
+
+ while (*fname == '.' && fname[1] == '/')
+ fname += 2;
+
+ if (flags & MKP_DROP_NAME) {
+ end = strrchr(fname, '/');
+ if (!end || end == fname)
+ return 0;
+ *end = '\0';
+ } else
+ end = fname + strlen(fname);
+
+ /* Try to find an existing dir, starting from the deepest dir. */
+ for (p = end; ; ) {
+ if (dry_run) {
+ STRUCT_STAT st;
+ if (do_stat(fname, &st) == 0) {
+ if (S_ISDIR(st.st_mode))
+ errno = EEXIST;
+ else
+ errno = ENOTDIR;
+ }
+ } else if (do_mkdir(fname, ACCESSPERMS) == 0) {
+ ret++;
+ break;
+ }
+
+ if (errno != ENOENT) {
+ STRUCT_STAT st;
+ if (errno != EEXIST || (do_stat(fname, &st) == 0 && !S_ISDIR(st.st_mode)))
+ ret = -ret - 1;
+ break;
+ }
+ while (1) {
+ if (p == fname) {
+ /* We got a relative path that doesn't exist, so assume that '.'
+ * is there and just break out and create the whole thing. */
+ p = NULL;
+ goto double_break;
+ }
+ if (*--p == '/') {
+ if (p == fname) {
+ /* We reached the "/" dir, which we assume is there. */
+ goto double_break;
+ }
+ *p = '\0';
+ break;
+ }
+ }
+ }
+ double_break:
+
+ /* Make all the dirs that we didn't find on the way here. */
+ while (p != end) {
+ if (p)
+ *p = '/';
+ else
+ p = fname;
+ p += strlen(p);
+ if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */
+ continue;
+ if (do_mkdir(fname, ACCESSPERMS) < 0)
+ ret = -ret - 1;
+ else
+ ret++;
+ }
+
+ if (flags & MKP_DROP_NAME)
+ *end = '/';
+
+ return ret;
+}
+
+/**
+ * Write @p len bytes at @p ptr to descriptor @p desc, retrying if
+ * interrupted.
+ *
+ * @retval len upon success
+ *
+ * @retval <0 write's (negative) error code
+ *
+ * Derived from GNU C's cccp.c.
+ */
+int full_write(int desc, const char *ptr, size_t len)
+{
+ int total_written;
+
+ total_written = 0;
+ while (len > 0) {
+ int written = write(desc, ptr, len);
+ if (written < 0) {
+ if (errno == EINTR)
+ continue;
+ return written;
+ }
+ total_written += written;
+ ptr += written;
+ len -= written;
+ }
+ return total_written;
+}
+
+/**
+ * Read @p len bytes at @p ptr from descriptor @p desc, retrying if
+ * interrupted.
+ *
+ * @retval >0 the actual number of bytes read
+ *
+ * @retval 0 for EOF
+ *
+ * @retval <0 for an error.
+ *
+ * Derived from GNU C's cccp.c. */
+static int safe_read(int desc, char *ptr, size_t len)
+{
+ int n_chars;
+
+ if (len == 0)
+ return len;
+
+ do {
+ n_chars = read(desc, ptr, len);
+ } while (n_chars < 0 && errno == EINTR);
+
+ return n_chars;
+}
+
+/* Remove existing file @dest and reopen, creating a new file with @mode */
+static int unlink_and_reopen(const char *dest, mode_t mode)
+{
+ int ofd;
+
+ if (robust_unlink(dest) && errno != ENOENT) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest));
+ errno = save_errno;
+ return -1;
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ mode |= S_IWUSR;
+#endif
+ mode &= INITACCESSPERMS;
+ if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest));
+ errno = save_errno;
+ return -1;
+ }
+ return ofd;
+}
+
+/* Copy contents of file @source to file @dest with mode @mode.
+ *
+ * If @tmpfilefd is < 0, copy_file unlinks @dest and then opens a new
+ * file with name @dest.
+ *
+ * Otherwise, copy_file writes to and closes the provided file
+ * descriptor.
+ *
+ * In either case, if --xattrs are being preserved, the dest file will
+ * have its xattrs set from the source file.
+ *
+ * This is used in conjunction with the --temp-dir, --backup, and
+ * --copy-dest options. */
+int copy_file(const char *source, const char *dest, int tmpfilefd, mode_t mode)
+{
+ int ifd, ofd;
+ char buf[1024 * 8];
+ int len; /* Number of bytes read into `buf'. */
+ OFF_T prealloc_len = 0, offset = 0;
+
+ if ((ifd = do_open(source, O_RDONLY, 0)) < 0) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "open %s", full_fname(source));
+ errno = save_errno;
+ return -1;
+ }
+
+ if (tmpfilefd >= 0) {
+ ofd = tmpfilefd;
+ } else {
+ ofd = unlink_and_reopen(dest, mode);
+ if (ofd < 0) {
+ int save_errno = errno;
+ close(ifd);
+ errno = save_errno;
+ return -1;
+ }
+ }
+
+#ifdef SUPPORT_PREALLOCATION
+ if (preallocate_files) {
+ STRUCT_STAT srcst;
+
+ /* Try to preallocate enough space for file's eventual length. Can
+ * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */
+ if (do_fstat(ifd, &srcst) < 0)
+ rsyserr(FWARNING, errno, "fstat %s", full_fname(source));
+ else if (srcst.st_size > 0) {
+ prealloc_len = do_fallocate(ofd, 0, srcst.st_size);
+ if (prealloc_len < 0)
+ rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(dest));
+ }
+ }
+#endif
+
+ while ((len = safe_read(ifd, buf, sizeof buf)) > 0) {
+ if (full_write(ofd, buf, len) < 0) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "write %s", full_fname(dest));
+ close(ifd);
+ close(ofd);
+ errno = save_errno;
+ return -1;
+ }
+ offset += len;
+ }
+
+ if (len < 0) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "read %s", full_fname(source));
+ close(ifd);
+ close(ofd);
+ errno = save_errno;
+ return -1;
+ }
+
+ if (close(ifd) < 0) {
+ rsyserr(FWARNING, errno, "close failed on %s",
+ full_fname(source));
+ }
+
+ /* Source file might have shrunk since we fstatted it.
+ * Cut off any extra preallocated zeros from dest file. */
+ if (offset < prealloc_len) {
+#ifdef HAVE_FTRUNCATE
+ /* If we fail to truncate, the dest file may be wrong, so we
+ * must trigger the "partial transfer" error. */
+ if (do_ftruncate(ofd, offset) < 0)
+ rsyserr(FERROR_XFER, errno, "ftruncate %s", full_fname(dest));
+#else
+ rprintf(FERROR_XFER, "no ftruncate for over-long pre-alloc: %s", full_fname(dest));
+#endif
+ }
+
+ if (do_fsync && fsync(ofd) < 0) {
+ int save_errno = errno;
+ rsyserr(FERROR, errno, "fsync failed on %s", full_fname(dest));
+ close(ofd);
+ errno = save_errno;
+ return -1;
+ }
+
+ if (close(ofd) < 0) {
+ int save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "close failed on %s", full_fname(dest));
+ errno = save_errno;
+ return -1;
+ }
+
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ copy_xattrs(source, dest);
+#endif
+
+ return 0;
+}
+
+/* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
+#define MAX_RENAMES_DIGITS 3
+#define MAX_RENAMES 1000
+
+/**
+ * Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
+ * rename to <path>/.rsyncNNN instead.
+ *
+ * Note that successive rsync runs will shuffle the filenames around a
+ * bit as long as the file is still busy; this is because this function
+ * does not know if the unlink call is due to a new file coming in, or
+ * --delete trying to remove old .rsyncNNN files, hence it renames it
+ * each time.
+ **/
+int robust_unlink(const char *fname)
+{
+#ifndef ETXTBSY
+ return do_unlink(fname);
+#else
+ static int counter = 1;
+ int rc, pos, start;
+ char path[MAXPATHLEN];
+
+ rc = do_unlink(fname);
+ if (rc == 0 || errno != ETXTBSY)
+ return rc;
+
+ if ((pos = strlcpy(path, fname, MAXPATHLEN)) >= MAXPATHLEN)
+ pos = MAXPATHLEN - 1;
+
+ while (pos > 0 && path[pos-1] != '/')
+ pos--;
+ pos += strlcpy(path+pos, ".rsync", MAXPATHLEN-pos);
+
+ if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
+ errno = ETXTBSY;
+ return -1;
+ }
+
+ /* start where the last one left off to reduce chance of clashes */
+ start = counter;
+ do {
+ snprintf(&path[pos], MAX_RENAMES_DIGITS+1, "%03d", counter);
+ if (++counter >= MAX_RENAMES)
+ counter = 1;
+ } while ((rc = access(path, 0)) == 0 && counter != start);
+
+ if (INFO_GTE(MISC, 1)) {
+ rprintf(FWARNING, "renaming %s to %s because of text busy\n",
+ fname, path);
+ }
+
+ /* maybe we should return rename()'s exit status? Nah. */
+ if (do_rename(fname, path) != 0) {
+ errno = ETXTBSY;
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+/* Returns 0 on successful rename, 1 if we successfully copied the file
+ * across filesystems, -2 if copy_file() failed, and -1 on other errors.
+ * If partialptr is not NULL and we need to do a copy, copy the file into
+ * the active partial-dir instead of over the destination file. */
+int robust_rename(const char *from, const char *to, const char *partialptr,
+ int mode)
+{
+ int tries = 4;
+
+ /* A resumed in-place partial-dir transfer might call us with from and
+ * to pointing to the same buf if the transfer failed yet again. */
+ if (from == to)
+ return 0;
+
+ while (tries--) {
+ if (do_rename(from, to) == 0)
+ return 0;
+
+ switch (errno) {
+#ifdef ETXTBSY
+ case ETXTBSY:
+ if (robust_unlink(to) != 0) {
+ errno = ETXTBSY;
+ return -1;
+ }
+ errno = ETXTBSY;
+ break;
+#endif
+ case EXDEV:
+ if (partialptr) {
+ if (!handle_partial_dir(partialptr,PDIR_CREATE))
+ return -2;
+ to = partialptr;
+ }
+ if (copy_file(from, to, -1, mode) != 0)
+ return -2;
+ do_unlink(from);
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+}
+
+static pid_t all_pids[10];
+static int num_pids;
+
+/** Fork and record the pid of the child. **/
+pid_t do_fork(void)
+{
+ pid_t newpid = fork();
+
+ if (newpid != 0 && newpid != -1) {
+ all_pids[num_pids++] = newpid;
+ }
+ return newpid;
+}
+
+/**
+ * Kill all children.
+ *
+ * @todo It would be kind of nice to make sure that they are actually
+ * all our children before we kill them, because their pids may have
+ * been recycled by some other process. Perhaps when we wait for a
+ * child, we should remove it from this array. Alternatively we could
+ * perhaps use process groups, but I think that would not work on
+ * ancient Unix versions that don't support them.
+ **/
+void kill_all(int sig)
+{
+ int i;
+
+ for (i = 0; i < num_pids; i++) {
+ /* Let's just be a little careful where we
+ * point that gun, hey? See kill(2) for the
+ * magic caused by negative values. */
+ pid_t p = all_pids[i];
+
+ if (p == getpid())
+ continue;
+ if (p <= 0)
+ continue;
+
+ kill(p, sig);
+ }
+}
+
+/** Lock a byte range in a open file */
+int lock_range(int fd, int offset, int len)
+{
+ struct flock lock;
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = offset;
+ lock.l_len = len;
+ lock.l_pid = 0;
+
+ return fcntl(fd,F_SETLK,&lock) == 0;
+}
+
+#define ENSURE_MEMSPACE(buf, type, sz, req) \
+ do { if ((req) > sz) buf = realloc_array(buf, type, sz = MAX(sz * 2, req)); } while(0)
+
+static inline void call_glob_match(const char *name, int len, int from_glob,
+ char *arg, int abpos, int fbpos);
+
+static struct glob_data {
+ char *arg_buf, *filt_buf, **argv;
+ int absize, fbsize, maxargs, argc;
+} glob;
+
+static void glob_match(char *arg, int abpos, int fbpos)
+{
+ int len;
+ char *slash;
+
+ while (*arg == '.' && arg[1] == '/') {
+ if (fbpos < 0) {
+ ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, glob.absize);
+ memcpy(glob.filt_buf, glob.arg_buf, abpos + 1);
+ fbpos = abpos;
+ }
+ ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + 3);
+ glob.arg_buf[abpos++] = *arg++;
+ glob.arg_buf[abpos++] = *arg++;
+ glob.arg_buf[abpos] = '\0';
+ }
+ if ((slash = strchr(arg, '/')) != NULL) {
+ *slash = '\0';
+ len = slash - arg;
+ } else
+ len = strlen(arg);
+ if (strpbrk(arg, "*?[")) {
+ struct dirent *di;
+ DIR *d;
+
+ if (!(d = opendir(abpos ? glob.arg_buf : ".")))
+ return;
+ while ((di = readdir(d)) != NULL) {
+ char *dname = d_name(di);
+ if (dname[0] == '.' && (dname[1] == '\0'
+ || (dname[1] == '.' && dname[2] == '\0')))
+ continue;
+ if (!wildmatch(arg, dname))
+ continue;
+ call_glob_match(dname, strlen(dname), 1,
+ slash ? arg + len + 1 : NULL,
+ abpos, fbpos);
+ }
+ closedir(d);
+ } else {
+ call_glob_match(arg, len, 0,
+ slash ? arg + len + 1 : NULL,
+ abpos, fbpos);
+ }
+ if (slash)
+ *slash = '/';
+}
+
+static inline void call_glob_match(const char *name, int len, int from_glob,
+ char *arg, int abpos, int fbpos)
+{
+ char *use_buf;
+
+ ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + len + 2);
+ memcpy(glob.arg_buf + abpos, name, len);
+ abpos += len;
+ glob.arg_buf[abpos] = '\0';
+
+ if (fbpos >= 0) {
+ ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, fbpos + len + 2);
+ memcpy(glob.filt_buf + fbpos, name, len);
+ fbpos += len;
+ glob.filt_buf[fbpos] = '\0';
+ use_buf = glob.filt_buf;
+ } else
+ use_buf = glob.arg_buf;
+
+ if (from_glob || (arg && len)) {
+ STRUCT_STAT st;
+ int is_dir;
+
+ if (do_stat(glob.arg_buf, &st) != 0)
+ return;
+ is_dir = S_ISDIR(st.st_mode) != 0;
+ if (arg && !is_dir)
+ return;
+
+ if (daemon_filter_list.head
+ && check_filter(&daemon_filter_list, FLOG, use_buf, is_dir) < 0)
+ return;
+ }
+
+ if (arg) {
+ glob.arg_buf[abpos++] = '/';
+ glob.arg_buf[abpos] = '\0';
+ if (fbpos >= 0) {
+ glob.filt_buf[fbpos++] = '/';
+ glob.filt_buf[fbpos] = '\0';
+ }
+ glob_match(arg, abpos, fbpos);
+ } else {
+ ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1);
+ glob.argv[glob.argc++] = strdup(glob.arg_buf);
+ }
+}
+
+/* This routine performs wild-card expansion of the pathname in "arg". Any
+ * daemon-excluded files/dirs will not be matched by the wildcards. Returns 0
+ * if a wild-card string is the only returned item (due to matching nothing). */
+int glob_expand(const char *arg, char ***argv_p, int *argc_p, int *maxargs_p)
+{
+ int ret, save_argc;
+ char *s;
+
+ if (!arg) {
+ if (glob.filt_buf)
+ free(glob.filt_buf);
+ free(glob.arg_buf);
+ memset(&glob, 0, sizeof glob);
+ return -1;
+ }
+
+ if (sanitize_paths)
+ s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS);
+ else {
+ s = strdup(arg);
+ clean_fname(s, CFN_KEEP_DOT_DIRS | CFN_KEEP_TRAILING_SLASH | CFN_COLLAPSE_DOT_DOT_DIRS);
+ }
+
+ ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN);
+ *glob.arg_buf = '\0';
+
+ glob.argc = save_argc = *argc_p;
+ glob.argv = *argv_p;
+ glob.maxargs = *maxargs_p;
+
+ ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, 100);
+
+ glob_match(s, 0, -1);
+
+ /* The arg didn't match anything, so add the failed arg to the list. */
+ if (glob.argc == save_argc) {
+ ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1);
+ glob.argv[glob.argc++] = s;
+ ret = 0;
+ } else {
+ free(s);
+ ret = 1;
+ }
+
+ *maxargs_p = glob.maxargs;
+ *argv_p = glob.argv;
+ *argc_p = glob.argc;
+
+ return ret;
+}
+
+/* This routine is only used in daemon mode. */
+void glob_expand_module(char *base1, char *arg, char ***argv_p, int *argc_p, int *maxargs_p)
+{
+ char *p, *s;
+ char *base = base1;
+ int base_len = strlen(base);
+
+ if (!arg || !*arg)
+ return;
+
+ if (strncmp(arg, base, base_len) == 0)
+ arg += base_len;
+
+ if (protect_args) {
+ glob_expand(arg, argv_p, argc_p, maxargs_p);
+ return;
+ }
+
+ arg = strdup(arg);
+
+ if (asprintf(&base," %s/", base1) < 0)
+ out_of_memory("glob_expand_module");
+ base_len++;
+
+ for (s = arg; *s; s = p + base_len) {
+ if ((p = strstr(s, base)) != NULL)
+ *p = '\0'; /* split it at this point */
+ glob_expand(s, argv_p, argc_p, maxargs_p);
+ if (!p)
+ break;
+ }
+
+ free(arg);
+ free(base);
+}
+
+/**
+ * Convert a string to lower case
+ **/
+void strlower(char *s)
+{
+ while (*s) {
+ if (isUpper(s))
+ *s = toLower(s);
+ s++;
+ }
+}
+
+/**
+ * Split a string into tokens based (usually) on whitespace & commas. If the
+ * string starts with a comma (after skipping any leading whitespace), then
+ * splitting is done only on commas. No empty tokens are ever returned. */
+char *conf_strtok(char *str)
+{
+ static int commas_only = 0;
+
+ if (str) {
+ while (isSpace(str)) str++;
+ if (*str == ',') {
+ commas_only = 1;
+ str++;
+ } else
+ commas_only = 0;
+ }
+
+ while (commas_only) {
+ char *end, *tok = strtok(str, ",");
+ if (!tok)
+ return NULL;
+ /* Trim just leading and trailing whitespace. */
+ while (isSpace(tok))
+ tok++;
+ end = tok + strlen(tok);
+ while (end > tok && isSpace(end-1))
+ *--end = '\0';
+ if (*tok)
+ return tok;
+ str = NULL;
+ }
+
+ return strtok(str, " ,\t\r\n");
+}
+
+/* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If
+ * p1 ends with a '/', no extra '/' is inserted.) Returns the length of both
+ * strings + 1 (if '/' was inserted), regardless of whether the null-terminated
+ * string fits into destsize. */
+size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2)
+{
+ size_t len = strlcpy(dest, p1, destsize);
+ if (len < destsize - 1) {
+ if (!len || dest[len-1] != '/')
+ dest[len++] = '/';
+ if (len < destsize - 1)
+ len += strlcpy(dest + len, p2, destsize - len);
+ else {
+ dest[len] = '\0';
+ len += strlen(p2);
+ }
+ }
+ else
+ len += strlen(p2) + 1; /* Assume we'd insert a '/'. */
+ return len;
+}
+
+/* Join any number of strings together, putting them in "dest". The return
+ * value is the length of all the strings, regardless of whether the null-
+ * terminated whole fits in destsize. Your list of string pointers must end
+ * with a NULL to indicate the end of the list. */
+size_t stringjoin(char *dest, size_t destsize, ...)
+{
+ va_list ap;
+ size_t len, ret = 0;
+ const char *src;
+
+ va_start(ap, destsize);
+ while (1) {
+ if (!(src = va_arg(ap, const char *)))
+ break;
+ len = strlen(src);
+ ret += len;
+ if (destsize > 1) {
+ if (len >= destsize)
+ len = destsize - 1;
+ memcpy(dest, src, len);
+ destsize -= len;
+ dest += len;
+ }
+ }
+ *dest = '\0';
+ va_end(ap);
+
+ return ret;
+}
+
+int count_dir_elements(const char *p)
+{
+ int cnt = 0, new_component = 1;
+ while (*p) {
+ if (*p++ == '/')
+ new_component = (*p != '.' || (p[1] != '/' && p[1] != '\0'));
+ else if (new_component) {
+ new_component = 0;
+ cnt++;
+ }
+ }
+ return cnt;
+}
+
+/* Turns multiple adjacent slashes into a single slash (possible exception:
+ * the preserving of two leading slashes at the start), drops all leading or
+ * interior "." elements unless CFN_KEEP_DOT_DIRS is flagged. Will also drop
+ * a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is flagged, removes
+ * a trailing slash (perhaps after removing the aforementioned dot) unless
+ * CFN_KEEP_TRAILING_SLASH is flagged, and will also collapse ".." elements
+ * (except at the start) if CFN_COLLAPSE_DOT_DOT_DIRS is flagged. If the
+ * resulting name would be empty, returns ".". */
+int clean_fname(char *name, int flags)
+{
+ char *limit = name - 1, *t = name, *f = name;
+ int anchored;
+
+ if (!name)
+ return 0;
+
+#define DOT_IS_DOT_DOT_DIR(bp) (bp[1] == '.' && (bp[2] == '/' || !bp[2]))
+
+ if ((anchored = *f == '/') != 0) {
+ *t++ = *f++;
+#ifdef __CYGWIN__
+ /* If there are exactly 2 slashes at the start, preserve
+ * them. Would break daemon excludes unless the paths are
+ * really treated differently, so used this sparingly. */
+ if (*f == '/' && f[1] != '/')
+ *t++ = *f++;
+#endif
+ } else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') {
+ *t++ = *f++;
+ *t++ = *f++;
+ } else if (flags & CFN_REFUSE_DOT_DOT_DIRS && *f == '.' && DOT_IS_DOT_DOT_DIR(f))
+ return -1;
+ while (*f) {
+ /* discard extra slashes */
+ if (*f == '/') {
+ f++;
+ continue;
+ }
+ if (*f == '.') {
+ /* discard interior "." dirs */
+ if (f[1] == '/' && !(flags & CFN_KEEP_DOT_DIRS)) {
+ f += 2;
+ continue;
+ }
+ if (f[1] == '\0' && flags & CFN_DROP_TRAILING_DOT_DIR)
+ break;
+ /* collapse ".." dirs */
+ if (flags & (CFN_COLLAPSE_DOT_DOT_DIRS|CFN_REFUSE_DOT_DOT_DIRS) && DOT_IS_DOT_DOT_DIR(f)) {
+ char *s = t - 1;
+ if (flags & CFN_REFUSE_DOT_DOT_DIRS)
+ return -1;
+ if (s == name && anchored) {
+ f += 2;
+ continue;
+ }
+ while (s > limit && *--s != '/') {}
+ if (s != t - 1 && (s < name || *s == '/')) {
+ t = s + 1;
+ f += 2;
+ continue;
+ }
+ limit = t + 2;
+ }
+ }
+ while (*f && (*t++ = *f++) != '/') {}
+ }
+
+ if (t > name+anchored && t[-1] == '/' && !(flags & CFN_KEEP_TRAILING_SLASH))
+ t--;
+ if (t == name)
+ *t++ = '.';
+ *t = '\0';
+
+#undef DOT_IS_DOT_DOT_DIR
+
+ return t - name;
+}
+
+/* Make path appear as if a chroot had occurred. This handles a leading
+ * "/" (either removing it or expanding it) and any leading or embedded
+ * ".." components that attempt to escape past the module's top dir.
+ *
+ * If dest is NULL, a buffer is allocated to hold the result. It is legal
+ * to call with the dest and the path (p) pointing to the same buffer, but
+ * rootdir will be ignored to avoid expansion of the string.
+ *
+ * The rootdir string contains a value to use in place of a leading slash.
+ * Specify NULL to get the default of "module_dir".
+ *
+ * The depth var is a count of how many '..'s to allow at the start of the
+ * path.
+ *
+ * We also clean the path in a manner similar to clean_fname() but with a
+ * few differences:
+ *
+ * Turns multiple adjacent slashes into a single slash, gets rid of "." dir
+ * elements (INCLUDING a trailing dot dir), PRESERVES a trailing slash, and
+ * ALWAYS collapses ".." elements (except for those at the start of the
+ * string up to "depth" deep). If the resulting name would be empty,
+ * change it into a ".". */
+char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, int flags)
+{
+ char *start, *sanp;
+ int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS);
+
+ if (dest != p) {
+ int plen = strlen(p); /* the path len INCLUDING any separating slash */
+ if (*p == '/') {
+ if (!rootdir)
+ rootdir = module_dir;
+ rlen = strlen(rootdir);
+ depth = 0;
+ p++;
+ }
+ if (!dest)
+ dest = new_array(char, MAX(rlen + plen + 1, 2));
+ else if (rlen + plen + 1 >= MAXPATHLEN)
+ return NULL;
+ if (rlen) { /* only true if p previously started with a slash */
+ memcpy(dest, rootdir, rlen);
+ if (rlen > 1) /* a rootdir of len 1 is "/", so this avoids a 2nd slash */
+ dest[rlen++] = '/';
+ }
+ }
+
+ if (drop_dot_dirs) {
+ while (*p == '.' && p[1] == '/')
+ p += 2;
+ }
+
+ start = sanp = dest + rlen;
+ /* This loop iterates once per filename component in p, pointing at
+ * the start of the name (past any prior slash) for each iteration. */
+ while (*p) {
+ /* discard leading or extra slashes */
+ if (*p == '/') {
+ p++;
+ continue;
+ }
+ if (drop_dot_dirs) {
+ if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
+ /* skip "." component */
+ p++;
+ continue;
+ }
+ }
+ if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
+ /* ".." component followed by slash or end */
+ if (depth <= 0 || sanp != start) {
+ p += 2;
+ if (sanp != start) {
+ /* back up sanp one level */
+ --sanp; /* now pointing at slash */
+ while (sanp > start && sanp[-1] != '/')
+ sanp--;
+ }
+ continue;
+ }
+ /* allow depth levels of .. at the beginning */
+ depth--;
+ /* move the virtual beginning to leave the .. alone */
+ start = sanp + 3;
+ }
+ /* copy one component through next slash */
+ while (*p && (*sanp++ = *p++) != '/') {}
+ }
+ if (sanp == dest) {
+ /* ended up with nothing, so put in "." component */
+ *sanp++ = '.';
+ }
+ *sanp = '\0';
+
+ return dest;
+}
+
+/* Like chdir(), but it keeps track of the current directory (in the
+ * global "curr_dir"), and ensures that the path size doesn't overflow.
+ * Also cleans the path using the clean_fname() function. */
+int change_dir(const char *dir, int set_path_only)
+{
+ static int initialised, skipped_chdir;
+ unsigned int len;
+
+ if (!initialised) {
+ initialised = 1;
+ if (getcwd(curr_dir, sizeof curr_dir - 1) == NULL) {
+ rsyserr(FERROR, errno, "getcwd()");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ curr_dir_len = strlen(curr_dir);
+ }
+
+ if (!dir) /* this call was probably just to initialize */
+ return 0;
+
+ len = strlen(dir);
+ if (len == 1 && *dir == '.' && (!skipped_chdir || set_path_only))
+ return 1;
+
+ if (*dir == '/') {
+ if (len >= sizeof curr_dir) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+ if (!set_path_only && chdir(dir))
+ return 0;
+ skipped_chdir = set_path_only;
+ memcpy(curr_dir, dir, len + 1);
+ } else {
+ unsigned int save_dir_len = curr_dir_len;
+ if (curr_dir_len + 1 + len >= sizeof curr_dir) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+ if (!(curr_dir_len && curr_dir[curr_dir_len-1] == '/'))
+ curr_dir[curr_dir_len++] = '/';
+ memcpy(curr_dir + curr_dir_len, dir, len + 1);
+
+ if (!set_path_only && chdir(curr_dir)) {
+ curr_dir_len = save_dir_len;
+ curr_dir[curr_dir_len] = '\0';
+ return 0;
+ }
+ skipped_chdir = set_path_only;
+ }
+
+ curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR);
+ if (sanitize_paths) {
+ if (module_dirlen > curr_dir_len)
+ module_dirlen = curr_dir_len;
+ curr_dir_depth = count_dir_elements(curr_dir + module_dirlen);
+ }
+
+ if (DEBUG_GTE(CHDIR, 1) && !set_path_only)
+ rprintf(FINFO, "[%s] change_dir(%s)\n", who_am_i(), curr_dir);
+
+ return 1;
+}
+
+/* This will make a relative path absolute and clean it up via clean_fname().
+ * Returns the string, which might be newly allocated, or NULL on error. */
+char *normalize_path(char *path, BOOL force_newbuf, unsigned int *len_ptr)
+{
+ unsigned int len;
+
+ if (*path != '/') { /* Make path absolute. */
+ int len = strlen(path);
+ if (curr_dir_len + 1 + len >= sizeof curr_dir)
+ return NULL;
+ curr_dir[curr_dir_len] = '/';
+ memcpy(curr_dir + curr_dir_len + 1, path, len + 1);
+ path = strdup(curr_dir);
+ curr_dir[curr_dir_len] = '\0';
+ } else if (force_newbuf)
+ path = strdup(path);
+
+ len = clean_fname(path, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR);
+
+ if (len_ptr)
+ *len_ptr = len;
+
+ return path;
+}
+
+/**
+ * Return a quoted string with the full pathname of the indicated filename.
+ * The string " (in MODNAME)" may also be appended. The returned pointer
+ * remains valid until the next time full_fname() is called.
+ **/
+char *full_fname(const char *fn)
+{
+ static char *result = NULL;
+ char *m1, *m2, *m3;
+ char *p1, *p2;
+
+ if (result)
+ free(result);
+
+ if (*fn == '/')
+ p1 = p2 = "";
+ else {
+ p1 = curr_dir + module_dirlen;
+ for (p2 = p1; *p2 == '/'; p2++) {}
+ if (*p2)
+ p2 = "/";
+ }
+ if (module_id >= 0) {
+ m1 = " (in ";
+ m2 = lp_name(module_id);
+ m3 = ")";
+ } else
+ m1 = m2 = m3 = "";
+
+ if (asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3) < 0)
+ out_of_memory("full_fname");
+
+ return result;
+}
+
+static char partial_fname[MAXPATHLEN];
+
+char *partial_dir_fname(const char *fname)
+{
+ char *t = partial_fname;
+ int sz = sizeof partial_fname;
+ const char *fn;
+
+ if ((fn = strrchr(fname, '/')) != NULL) {
+ fn++;
+ if (*partial_dir != '/') {
+ int len = fn - fname;
+ strncpy(t, fname, len); /* safe */
+ t += len;
+ sz -= len;
+ }
+ } else
+ fn = fname;
+ if ((int)pathjoin(t, sz, partial_dir, fn) >= sz)
+ return NULL;
+ if (daemon_filter_list.head) {
+ t = strrchr(partial_fname, '/');
+ *t = '\0';
+ if (check_filter(&daemon_filter_list, FLOG, partial_fname, 1) < 0)
+ return NULL;
+ *t = '/';
+ if (check_filter(&daemon_filter_list, FLOG, partial_fname, 0) < 0)
+ return NULL;
+ }
+
+ return partial_fname;
+}
+
+/* If no --partial-dir option was specified, we don't need to do anything
+ * (the partial-dir is essentially '.'), so just return success. */
+int handle_partial_dir(const char *fname, int create)
+{
+ char *fn, *dir;
+
+ if (fname != partial_fname)
+ return 1;
+ if (!create && *partial_dir == '/')
+ return 1;
+ if (!(fn = strrchr(partial_fname, '/')))
+ return 1;
+
+ *fn = '\0';
+ dir = partial_fname;
+ if (create) {
+ STRUCT_STAT st;
+ int statret = do_lstat(dir, &st);
+ if (statret == 0 && !S_ISDIR(st.st_mode)) {
+ if (do_unlink(dir) < 0) {
+ *fn = '/';
+ return 0;
+ }
+ statret = -1;
+ }
+ if (statret < 0 && do_mkdir(dir, 0700) < 0) {
+ *fn = '/';
+ return 0;
+ }
+ } else
+ do_rmdir(dir);
+ *fn = '/';
+
+ return 1;
+}
+
+/* Determine if a symlink points outside the current directory tree.
+ * This is considered "unsafe" because e.g. when mirroring somebody
+ * else's machine it might allow them to establish a symlink to
+ * /etc/passwd, and then read it through a web server.
+ *
+ * Returns 1 if unsafe, 0 if safe.
+ *
+ * Null symlinks and absolute symlinks are always unsafe.
+ *
+ * Basically here we are concerned with symlinks whose target contains
+ * "..", because this might cause us to walk back up out of the
+ * transferred directory. We are not allowed to go back up and
+ * reenter.
+ *
+ * "dest" is the target of the symlink in question.
+ *
+ * "src" is the top source directory currently applicable at the level
+ * of the referenced symlink. This is usually the symlink's full path
+ * (including its name), as referenced from the root of the transfer. */
+int unsafe_symlink(const char *dest, const char *src)
+{
+ const char *name, *slash;
+ int depth = 0;
+
+ /* all absolute and null symlinks are unsafe */
+ if (!dest || !*dest || *dest == '/')
+ return 1;
+
+ /* find out what our safety margin is */
+ for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
+ /* ".." segment starts the count over. "." segment is ignored. */
+ if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) {
+ if (name[1] == '.')
+ depth = 0;
+ } else
+ depth++;
+ while (slash[1] == '/') slash++; /* just in case src isn't clean */
+ }
+ if (*name == '.' && name[1] == '.' && name[2] == '\0')
+ depth = 0;
+
+ for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) {
+ if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) {
+ if (name[1] == '.') {
+ /* if at any point we go outside the current directory
+ then stop - it is unsafe */
+ if (--depth < 0)
+ return 1;
+ }
+ } else
+ depth++;
+ while (slash[1] == '/') slash++;
+ }
+ if (*name == '.' && name[1] == '.' && name[2] == '\0')
+ depth--;
+
+ return depth < 0;
+}
+
+/* Return the date and time as a string. Some callers tweak returned buf. */
+char *timestring(time_t t)
+{
+ static int ndx = 0;
+ static char buffers[4][20]; /* We support 4 simultaneous timestring results. */
+ char *TimeBuf = buffers[ndx = (ndx + 1) % 4];
+ struct tm *tm = localtime(&t);
+ int len = snprintf(TimeBuf, sizeof buffers[0], "%4d/%02d/%02d %02d:%02d:%02d",
+ (int)tm->tm_year + 1900, (int)tm->tm_mon + 1, (int)tm->tm_mday,
+ (int)tm->tm_hour, (int)tm->tm_min, (int)tm->tm_sec);
+ assert(len > 0); /* Silence gcc warning */
+
+ return TimeBuf;
+}
+
+/* Determine if two time_t values are equivalent (either exact, or in
+ * the modification timestamp window established by --modify-window).
+ * Returns 1 if the times the "same", or 0 if they are different. */
+int same_time(time_t f1_sec, unsigned long f1_nsec, time_t f2_sec, unsigned long f2_nsec)
+{
+ if (modify_window == 0)
+ return f1_sec == f2_sec;
+ if (modify_window < 0)
+ return f1_sec == f2_sec && f1_nsec == f2_nsec;
+ /* The nanoseconds do not figure into these checks -- time windows don't care about that. */
+ if (f2_sec > f1_sec)
+ return f2_sec - f1_sec <= modify_window;
+ return f1_sec - f2_sec <= modify_window;
+}
+
+#ifdef __INSURE__XX
+#include <dlfcn.h>
+
+/**
+ This routine is a trick to immediately catch errors when debugging
+ with insure. A xterm with a gdb is popped up when insure catches
+ a error. It is Linux specific.
+**/
+int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
+{
+ static int (*fn)();
+ int ret, pid_int = getpid();
+ char *cmd;
+
+ if (asprintf(&cmd,
+ "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; "
+ "gdb /proc/%d/exe %d'", pid_int, pid_int, pid_int) < 0)
+ return -1;
+
+ if (!fn) {
+ static void *h;
+ h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
+ fn = dlsym(h, "_Insure_trap_error");
+ }
+
+ ret = fn(a1, a2, a3, a4, a5, a6);
+
+ system(cmd);
+
+ free(cmd);
+
+ return ret;
+}
+#endif
+
+/* Take a filename and filename length and return the most significant
+ * filename suffix we can find. This ignores suffixes such as "~",
+ * ".bak", ".orig", ".~1~", etc. */
+const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr)
+{
+ const char *suf, *s;
+ BOOL had_tilde;
+ int s_len;
+
+ /* One or more dots at the start aren't a suffix. */
+ while (fn_len && *fn == '.') fn++, fn_len--;
+
+ /* Ignore the ~ in a "foo~" filename. */
+ if (fn_len > 1 && fn[fn_len-1] == '~')
+ fn_len--, had_tilde = True;
+ else
+ had_tilde = False;
+
+ /* Assume we don't find an suffix. */
+ suf = "";
+ *len_ptr = 0;
+
+ /* Find the last significant suffix. */
+ for (s = fn + fn_len; fn_len > 1; ) {
+ while (*--s != '.' && s != fn) {}
+ if (s == fn)
+ break;
+ s_len = fn_len - (s - fn);
+ fn_len = s - fn;
+ if (s_len == 4) {
+ if (strcmp(s+1, "bak") == 0
+ || strcmp(s+1, "old") == 0)
+ continue;
+ } else if (s_len == 5) {
+ if (strcmp(s+1, "orig") == 0)
+ continue;
+ } else if (s_len > 2 && had_tilde && s[1] == '~' && isDigit(s + 2))
+ continue;
+ *len_ptr = s_len;
+ suf = s;
+ if (s_len == 1)
+ break;
+ /* Determine if the suffix is all digits. */
+ for (s++, s_len--; s_len > 0; s++, s_len--) {
+ if (!isDigit(s))
+ return suf;
+ }
+ /* An all-digit suffix may not be that significant. */
+ s = suf;
+ }
+
+ return suf;
+}
+
+/* This is an implementation of the Levenshtein distance algorithm. It
+ * was implemented to avoid needing a two-dimensional matrix (to save
+ * memory). It was also tweaked to try to factor in the ASCII distance
+ * between changed characters as a minor distance quantity. The normal
+ * Levenshtein units of distance (each signifying a single change between
+ * the two strings) are defined as a "UNIT". */
+
+#define UNIT (1 << 16)
+
+uint32 fuzzy_distance(const char *s1, unsigned len1, const char *s2, unsigned len2, uint32 upperlimit)
+{
+ uint32 a[MAXPATHLEN], diag, above, left, diag_inc, above_inc, left_inc;
+ int32 cost;
+ unsigned i1, i2;
+
+ /* Check to see if the Levenshtein distance must be greater than the
+ * upper limit defined by the previously found lowest distance using
+ * the heuristic that the Levenshtein distance is greater than the
+ * difference in length of the two strings */
+ if ((len1 > len2 ? len1 - len2 : len2 - len1) * UNIT > upperlimit)
+ return 0xFFFFU * UNIT + 1;
+
+ if (!len1 || !len2) {
+ if (!len1) {
+ s1 = s2;
+ len1 = len2;
+ }
+ for (i1 = 0, cost = 0; i1 < len1; i1++)
+ cost += s1[i1];
+ return (int32)len1 * UNIT + cost;
+ }
+
+ for (i2 = 0; i2 < len2; i2++)
+ a[i2] = (i2+1) * UNIT;
+
+ for (i1 = 0; i1 < len1; i1++) {
+ diag = i1 * UNIT;
+ above = (i1+1) * UNIT;
+ for (i2 = 0; i2 < len2; i2++) {
+ left = a[i2];
+ if ((cost = *((uchar*)s1+i1) - *((uchar*)s2+i2)) != 0) {
+ if (cost < 0)
+ cost = UNIT - cost;
+ else
+ cost = UNIT + cost;
+ }
+ diag_inc = diag + cost;
+ left_inc = left + UNIT + *((uchar*)s1+i1);
+ above_inc = above + UNIT + *((uchar*)s2+i2);
+ a[i2] = above = left < above
+ ? (left_inc < diag_inc ? left_inc : diag_inc)
+ : (above_inc < diag_inc ? above_inc : diag_inc);
+ diag = left;
+ }
+ }
+
+ return a[len2-1];
+}
+
+#define BB_SLOT_SIZE (16*1024) /* Desired size in bytes */
+#define BB_PER_SLOT_BITS (BB_SLOT_SIZE * 8) /* Number of bits per slot */
+#define BB_PER_SLOT_INTS (BB_SLOT_SIZE / 4) /* Number of int32s per slot */
+
+struct bitbag {
+ uint32 **bits;
+ int slot_cnt;
+};
+
+struct bitbag *bitbag_create(int max_ndx)
+{
+ struct bitbag *bb = new(struct bitbag);
+ bb->slot_cnt = (max_ndx + BB_PER_SLOT_BITS - 1) / BB_PER_SLOT_BITS;
+
+ bb->bits = new_array0(uint32*, bb->slot_cnt);
+
+ return bb;
+}
+
+void bitbag_set_bit(struct bitbag *bb, int ndx)
+{
+ int slot = ndx / BB_PER_SLOT_BITS;
+ ndx %= BB_PER_SLOT_BITS;
+
+ if (!bb->bits[slot])
+ bb->bits[slot] = new_array0(uint32, BB_PER_SLOT_INTS);
+
+ bb->bits[slot][ndx/32] |= 1u << (ndx % 32);
+}
+
+#if 0 /* not needed yet */
+void bitbag_clear_bit(struct bitbag *bb, int ndx)
+{
+ int slot = ndx / BB_PER_SLOT_BITS;
+ ndx %= BB_PER_SLOT_BITS;
+
+ if (!bb->bits[slot])
+ return;
+
+ bb->bits[slot][ndx/32] &= ~(1u << (ndx % 32));
+}
+
+int bitbag_check_bit(struct bitbag *bb, int ndx)
+{
+ int slot = ndx / BB_PER_SLOT_BITS;
+ ndx %= BB_PER_SLOT_BITS;
+
+ if (!bb->bits[slot])
+ return 0;
+
+ return bb->bits[slot][ndx/32] & (1u << (ndx % 32)) ? 1 : 0;
+}
+#endif
+
+/* Call this with -1 to start checking from 0. Returns -1 at the end. */
+int bitbag_next_bit(struct bitbag *bb, int after)
+{
+ uint32 bits, mask;
+ int i, ndx = after + 1;
+ int slot = ndx / BB_PER_SLOT_BITS;
+ ndx %= BB_PER_SLOT_BITS;
+
+ mask = (1u << (ndx % 32)) - 1;
+ for (i = ndx / 32; slot < bb->slot_cnt; slot++, i = mask = 0) {
+ if (!bb->bits[slot])
+ continue;
+ for ( ; i < BB_PER_SLOT_INTS; i++, mask = 0) {
+ if (!(bits = bb->bits[slot][i] & ~mask))
+ continue;
+ /* The xor magic figures out the lowest enabled bit in
+ * bits, and the switch quickly computes log2(bit). */
+ switch (bits ^ (bits & (bits-1))) {
+#define LOG2(n) case 1u << n: return slot*BB_PER_SLOT_BITS + i*32 + n
+ LOG2(0); LOG2(1); LOG2(2); LOG2(3);
+ LOG2(4); LOG2(5); LOG2(6); LOG2(7);
+ LOG2(8); LOG2(9); LOG2(10); LOG2(11);
+ LOG2(12); LOG2(13); LOG2(14); LOG2(15);
+ LOG2(16); LOG2(17); LOG2(18); LOG2(19);
+ LOG2(20); LOG2(21); LOG2(22); LOG2(23);
+ LOG2(24); LOG2(25); LOG2(26); LOG2(27);
+ LOG2(28); LOG2(29); LOG2(30); LOG2(31);
+ }
+ return -1; /* impossible... */
+ }
+ }
+
+ return -1;
+}
+
+void flist_ndx_push(flist_ndx_list *lp, int ndx)
+{
+ struct flist_ndx_item *item;
+
+ item = new(struct flist_ndx_item);
+ item->next = NULL;
+ item->ndx = ndx;
+ if (lp->tail)
+ lp->tail->next = item;
+ else
+ lp->head = item;
+ lp->tail = item;
+}
+
+int flist_ndx_pop(flist_ndx_list *lp)
+{
+ struct flist_ndx_item *next;
+ int ndx;
+
+ if (!lp->head)
+ return -1;
+
+ ndx = lp->head->ndx;
+ next = lp->head->next;
+ free(lp->head);
+ lp->head = next;
+ if (!next)
+ lp->tail = NULL;
+
+ return ndx;
+}
+
+/* Make sure there is room for one more item in the item list. If there
+ * is not, expand the list as indicated by the value of "incr":
+ * - if incr < 0 then increase the malloced size by -1 * incr
+ * - if incr >= 0 then either make the malloced size equal to "incr"
+ * or (if that's not large enough) double the malloced size
+ * After the size check, the list's count is incremented by 1 and a pointer
+ * to the "new" list item is returned.
+ */
+void *expand_item_list(item_list *lp, size_t item_size, const char *desc, int incr)
+{
+ /* First time through, 0 <= 0, so list is expanded. */
+ if (lp->malloced <= lp->count) {
+ void *new_ptr;
+ size_t expand_size;
+ if (incr < 0)
+ expand_size = -incr; /* increase slowly */
+ else if (lp->malloced < (size_t)incr)
+ expand_size = incr - lp->malloced;
+ else if (lp->malloced)
+ expand_size = lp->malloced; /* double in size */
+ else
+ expand_size = 1;
+ if (SIZE_MAX/item_size - expand_size < lp->malloced)
+ overflow_exit("expand_item_list");
+ expand_size += lp->malloced;
+ new_ptr = realloc_buf(lp->items, expand_size * item_size);
+ if (DEBUG_GTE(FLIST, 3)) {
+ rprintf(FINFO, "[%s] expand %s to %s bytes, did%s move\n",
+ who_am_i(), desc, big_num(expand_size * item_size),
+ new_ptr == lp->items ? " not" : "");
+ }
+
+ lp->items = new_ptr;
+ lp->malloced = expand_size;
+ }
+ return (char*)lp->items + (lp->count++ * item_size);
+}
+
+/* This zeroing of memory won't be optimized away by the compiler. */
+void force_memzero(void *buf, size_t len)
+{
+ volatile uchar *z = buf;
+ while (len-- > 0)
+ *z++ = '\0';
+}
diff --git a/util2.c b/util2.c
new file mode 100644
index 0000000..a8609a5
--- /dev/null
+++ b/util2.c
@@ -0,0 +1,145 @@
+/*
+ * Utility routines used in rsync.
+ *
+ * Copyright (C) 1996-2000 Andrew Tridgell
+ * Copyright (C) 1996 Paul Mackerras
+ * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
+ * Copyright (C) 2003-2020 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "itypes.h"
+#include "inums.h"
+
+extern size_t max_alloc;
+
+char *do_calloc = "42";
+
+/**
+ * Sleep for a specified number of milliseconds.
+ *
+ * Always returns True.
+ **/
+int msleep(int t)
+{
+#ifdef HAVE_NANOSLEEP
+ struct timespec ts;
+
+ ts.tv_sec = t / 1000;
+ ts.tv_nsec = (t % 1000) * 1000000L;
+
+ while (nanosleep(&ts, &ts) < 0 && errno == EINTR) {}
+
+#elif defined HAVE_USLEEP
+ usleep(t*1000);
+
+#else
+ int tdiff = 0;
+ struct timeval tval, t1, t2;
+
+ gettimeofday(&t1, NULL);
+
+ while (tdiff < t) {
+ tval.tv_sec = (t-tdiff)/1000;
+ tval.tv_usec = 1000*((t-tdiff)%1000);
+
+ errno = 0;
+ select(0,NULL,NULL, NULL, &tval);
+
+ gettimeofday(&t2, NULL);
+ tdiff = (t2.tv_sec - t1.tv_sec)*1000 +
+ (t2.tv_usec - t1.tv_usec)/1000;
+ if (tdiff < 0)
+ t1 = t2; /* Time went backwards, so start over. */
+ }
+#endif
+
+ return True;
+}
+
+void *my_alloc(void *ptr, size_t num, size_t size, const char *file, int line)
+{
+ if (max_alloc && num >= max_alloc/size) {
+ if (!file)
+ return NULL;
+ rprintf(FERROR, "[%s] exceeded --max-alloc=%s setting (file=%s, line=%d)\n",
+ who_am_i(), do_big_num(max_alloc, 0, NULL), src_file(file), line);
+ exit_cleanup(RERR_MALLOC);
+ }
+ if (!ptr)
+ ptr = malloc(num * size);
+ else if (ptr == do_calloc)
+ ptr = calloc(num, size);
+ else
+ ptr = realloc(ptr, num * size);
+ if (!ptr && file)
+ _out_of_memory("my_alloc caller", file, line);
+ return ptr;
+}
+
+const char *sum_as_hex(int csum_type, const char *sum, int flist_csum)
+{
+ static char buf[MAX_DIGEST_LEN*2+1];
+ int i, x1, x2;
+ int canonical = canonical_checksum(csum_type);
+ int sum_len = csum_len_for_type(csum_type, flist_csum);
+ char *c;
+
+ if (!canonical)
+ return NULL;
+
+ assert(sum_len*2 < (int)sizeof buf);
+
+ for (i = sum_len, c = buf; --i >= 0; ) {
+ int ndx = canonical < 0 ? sum_len - i - 1 : i;
+ x2 = CVAL(sum, ndx);
+ x1 = x2 >> 4;
+ x2 &= 0xF;
+ *c++ = x1 <= 9 ? x1 + '0' : x1 + 'a' - 10;
+ *c++ = x2 <= 9 ? x2 + '0' : x2 + 'a' - 10;
+ }
+
+ *c = '\0';
+
+ return buf;
+}
+
+NORETURN void _out_of_memory(const char *msg, const char *file, int line)
+{
+ rprintf(FERROR, "[%s] out of memory: %s (file=%s, line=%d)\n", who_am_i(), msg, src_file(file), line);
+ exit_cleanup(RERR_MALLOC);
+}
+
+NORETURN void _overflow_exit(const char *msg, const char *file, int line)
+{
+ rprintf(FERROR, "[%s] buffer overflow: %s (file=%s, line=%d)\n", who_am_i(), msg, src_file(file), line);
+ exit_cleanup(RERR_MALLOC);
+}
+
+const char *src_file(const char *file)
+{
+ static const char *util2 = __FILE__;
+ static int prefix = -1;
+
+ if (prefix < 0) {
+ const char *cp = strrchr(util2, '/');
+ prefix = cp ? cp - util2 + 1 : 0;
+ }
+
+ if (prefix && strncmp(file, util2, prefix) == 0)
+ return file + prefix;
+ return file;
+}
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..fdfce4c
--- /dev/null
+++ b/version.h
@@ -0,0 +1,2 @@
+#define RSYNC_VERSION "3.2.7"
+#define MAINTAINER_TZ_OFFSET -7.0
diff --git a/wildtest.c b/wildtest.c
new file mode 100644
index 0000000..bea4ceb
--- /dev/null
+++ b/wildtest.c
@@ -0,0 +1,221 @@
+/*
+ * Test suite for the wildmatch code.
+ *
+ * Copyright (C) 2003-2019 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+/*#define COMPARE_WITH_FNMATCH*/
+
+#define WILD_TEST_ITERATIONS
+#include "lib/wildmatch.c"
+
+#include <popt.h>
+
+#ifdef COMPARE_WITH_FNMATCH
+#include <fnmatch.h>
+
+int fnmatch_errors = 0;
+#endif
+
+int wildmatch_errors = 0;
+
+typedef char bool;
+
+int output_iterations = 0;
+int explode_mod = 0;
+int empties_mod = 0;
+int empty_at_start = 0;
+int empty_at_end = 0;
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"iterations", 'i', POPT_ARG_NONE, &output_iterations, 0, 0, 0},
+ {"empties", 'e', POPT_ARG_STRING, 0, 'e', 0, 0},
+ {"explode", 'x', POPT_ARG_INT, &explode_mod, 0, 0, 0},
+ {0,0,0,0, 0, 0, 0}
+};
+
+/* match just at the start of string (anchored tests) */
+static void
+run_test(int line, bool matches,
+#ifdef COMPARE_WITH_FNMATCH
+ bool same_as_fnmatch,
+#endif
+ const char *text, const char *pattern)
+{
+ bool matched;
+#ifdef COMPARE_WITH_FNMATCH
+ bool fn_matched;
+ int flags = strstr(pattern, "**")? 0 : FNM_PATHNAME;
+#endif
+
+ if (explode_mod) {
+ char buf[MAXPATHLEN*2], *texts[MAXPATHLEN];
+ int pos = 0, cnt = 0, ndx = 0, len = strlen(text);
+
+ if (empty_at_start)
+ texts[ndx++] = "";
+ /* An empty string must turn into at least one empty array item. */
+ while (1) {
+ texts[ndx] = buf + ndx * (explode_mod + 1);
+ strlcpy(texts[ndx++], text + pos, explode_mod + 1);
+ if (pos + explode_mod >= len)
+ break;
+ pos += explode_mod;
+ if (!(++cnt % empties_mod))
+ texts[ndx++] = "";
+ }
+ if (empty_at_end)
+ texts[ndx++] = "";
+ texts[ndx] = NULL;
+ matched = wildmatch_array(pattern, (const char**)texts, 0);
+ } else
+ matched = wildmatch(pattern, text);
+#ifdef COMPARE_WITH_FNMATCH
+ fn_matched = !fnmatch(pattern, text, flags);
+#endif
+ if (matched != matches) {
+ printf("wildmatch failure on line %d:\n %s\n %s\n expected %s match\n",
+ line, text, pattern, matches? "a" : "NO");
+ wildmatch_errors++;
+ }
+#ifdef COMPARE_WITH_FNMATCH
+ if (fn_matched != (matches ^ !same_as_fnmatch)) {
+ printf("fnmatch disagreement on line %d:\n %s\n %s\n expected %s match\n",
+ line, text, pattern, matches ^ !same_as_fnmatch? "a" : "NO");
+ fnmatch_errors++;
+ }
+#endif
+ if (output_iterations) {
+ printf("%d: \"%s\" iterations = %d\n", line, pattern,
+ wildmatch_iteration_count);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ char buf[2048], *s, *string[2], *end[2];
+ const char *arg;
+ FILE *fp;
+ int opt, line, i, flag[2];
+ poptContext pc = poptGetContext("wildtest", argc, (const char**)argv,
+ long_options, 0);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'e':
+ arg = poptGetOptArg(pc);
+ empties_mod = atoi(arg);
+ if (strchr(arg, 's'))
+ empty_at_start = 1;
+ if (strchr(arg, 'e'))
+ empty_at_end = 1;
+ if (!explode_mod)
+ explode_mod = 1024;
+ break;
+ default:
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ exit(1);
+ }
+ }
+
+ if (explode_mod && !empties_mod)
+ empties_mod = 1024;
+
+ argv = (char**)poptGetArgs(pc);
+ if (!argv || argv[1]) {
+ fprintf(stderr, "Usage: wildtest [OPTIONS] TESTFILE\n");
+ exit(1);
+ }
+
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ fprintf(stderr, "Unable to open %s\n", *argv);
+ exit(1);
+ }
+
+ line = 0;
+ while (fgets(buf, sizeof buf, fp)) {
+ line++;
+ if (*buf == '#' || *buf == '\n')
+ continue;
+ for (s = buf, i = 0; i <= 1; i++) {
+ if (*s == '1')
+ flag[i] = 1;
+ else if (*s == '0')
+ flag[i] = 0;
+ else
+ flag[i] = -1;
+ if (*++s != ' ' && *s != '\t')
+ flag[i] = -1;
+ if (flag[i] < 0) {
+ fprintf(stderr, "Invalid flag syntax on line %d of %s:\n%s",
+ line, *argv, buf);
+ exit(1);
+ }
+ while (*++s == ' ' || *s == '\t') {}
+ }
+ for (i = 0; i <= 1; i++) {
+ if (*s == '\'' || *s == '"' || *s == '`') {
+ char quote = *s++;
+ string[i] = s;
+ while (*s && *s != quote) s++;
+ if (!*s) {
+ fprintf(stderr, "Unmatched quote on line %d of %s:\n%s",
+ line, *argv, buf);
+ exit(1);
+ }
+ end[i] = s;
+ }
+ else {
+ if (!*s || *s == '\n') {
+ fprintf(stderr, "Not enough strings on line %d of %s:\n%s",
+ line, *argv, buf);
+ exit(1);
+ }
+ string[i] = s;
+ while (*++s && *s != ' ' && *s != '\t' && *s != '\n') {}
+ end[i] = s;
+ }
+ while (*++s == ' ' || *s == '\t') {}
+ }
+ *end[0] = *end[1] = '\0';
+ run_test(line, flag[0],
+#ifdef COMPARE_WITH_FNMATCH
+ flag[1],
+#endif
+ string[0], string[1]);
+ }
+
+ if (!wildmatch_errors)
+ fputs("No", stdout);
+ else
+ printf("%d", wildmatch_errors);
+ printf(" wildmatch error%s found.\n", wildmatch_errors == 1? "" : "s");
+
+#ifdef COMPARE_WITH_FNMATCH
+ if (!fnmatch_errors)
+ fputs("No", stdout);
+ else
+ printf("%d", fnmatch_errors);
+ printf(" fnmatch error%s found.\n", fnmatch_errors == 1? "" : "s");
+
+#endif
+
+ return 0;
+}
diff --git a/wildtest.txt b/wildtest.txt
new file mode 100644
index 0000000..42c1678
--- /dev/null
+++ b/wildtest.txt
@@ -0,0 +1,165 @@
+# Input is in the following format (all items white-space separated):
+#
+# The first two items are 1 or 0 indicating if the wildmat call is expected to
+# succeed and if fnmatch works the same way as wildmat, respectively. After
+# that is a text string for the match, and a pattern string. Strings can be
+# quoted (if desired) in either double or single quotes, as well as backticks.
+#
+# MATCH FNMATCH_SAME "text to match" 'pattern to use'
+
+# Basic wildmat features
+1 1 foo foo
+0 1 foo bar
+1 1 '' ""
+1 1 foo ???
+0 1 foo ??
+1 1 foo *
+1 1 foo f*
+0 1 foo *f
+1 1 foo *foo*
+1 1 foobar *ob*a*r*
+1 1 aaaaaaabababab *ab
+1 1 foo* foo\*
+0 1 foobar foo\*bar
+1 1 f\oo f\\oo
+1 1 ball *[al]?
+0 1 ten [ten]
+1 1 ten **[!te]
+0 1 ten **[!ten]
+1 1 ten t[a-g]n
+0 1 ten t[!a-g]n
+1 1 ton t[!a-g]n
+1 1 ton t[^a-g]n
+1 1 a]b a[]]b
+1 1 a-b a[]-]b
+1 1 a]b a[]-]b
+0 1 aab a[]-]b
+1 1 aab a[]a-]b
+1 1 ] ]
+
+# Extended slash-matching features
+0 1 foo/baz/bar foo*bar
+1 1 foo/baz/bar foo**bar
+0 1 foo/bar foo?bar
+0 1 foo/bar foo[/]bar
+0 1 foo/bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+1 1 foo-bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+0 1 foo **/foo
+1 1 /foo **/foo
+1 1 bar/baz/foo **/foo
+0 1 bar/baz/foo */foo
+0 0 foo/bar/baz **/bar*
+1 1 deep/foo/bar/baz **/bar/*
+0 1 deep/foo/bar/baz/ **/bar/*
+1 1 deep/foo/bar/baz/ **/bar/**
+0 1 deep/foo/bar **/bar/*
+1 1 deep/foo/bar/ **/bar/**
+1 1 foo/bar/baz **/bar**
+1 1 foo/bar/baz/x */bar/**
+0 0 deep/foo/bar/baz/x */bar/**
+1 1 deep/foo/bar/baz/x **/bar/*/*
+
+# Various additional tests
+0 1 acrt a[c-c]st
+1 1 acrt a[c-c]rt
+0 1 ] [!]-]
+1 1 a [!]-]
+0 1 '' \
+0 1 \ \
+0 1 /\ */\
+1 1 /\ */\\
+1 1 foo foo
+1 1 @foo @foo
+0 1 foo @foo
+1 1 [ab] \[ab]
+1 1 [ab] [[]ab]
+1 1 [ab] [[:]ab]
+0 1 [ab] [[::]ab]
+1 1 [ab] [[:digit]ab]
+1 1 [ab] [\[:]ab]
+1 1 ?a?b \??\?b
+1 1 abc \a\b\c
+0 1 foo ''
+1 1 foo/bar/baz/to **/t[o]
+
+# Character class tests
+1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
+0 1 a [[:digit:][:upper:][:space:]]
+1 1 A [[:digit:][:upper:][:space:]]
+1 1 1 [[:digit:][:upper:][:space:]]
+0 1 1 [[:digit:][:upper:][:spaci:]]
+1 1 ' ' [[:digit:][:upper:][:space:]]
+0 1 . [[:digit:][:upper:][:space:]]
+1 1 . [[:digit:][:punct:][:space:]]
+1 1 5 [[:xdigit:]]
+1 1 f [[:xdigit:]]
+1 1 D [[:xdigit:]]
+1 1 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+#1 1 … [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1  [^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1 . [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]
+1 1 5 [a-c[:digit:]x-z]
+1 1 b [a-c[:digit:]x-z]
+1 1 y [a-c[:digit:]x-z]
+0 1 q [a-c[:digit:]x-z]
+
+# Additional tests, including some malformed wildmats
+1 1 ] [\\-^]
+0 1 [ [\\-^]
+1 1 - [\-_]
+1 1 ] [\]]
+0 1 \] [\]]
+0 1 \ [\]]
+0 1 ab a[]b
+0 1 a[]b a[]b
+0 1 ab[ ab[
+0 1 ab [!
+0 1 ab [-
+1 1 - [-]
+0 1 - [a-
+0 1 - [!a-
+1 1 - [--A]
+1 1 5 [--A]
+1 1 ' ' '[ --]'
+1 1 $ '[ --]'
+1 1 - '[ --]'
+0 1 0 '[ --]'
+1 1 - [---]
+1 1 - [------]
+0 1 j [a-e-n]
+1 1 - [a-e-n]
+1 1 a [!------]
+0 1 [ []-a]
+1 1 ^ []-a]
+0 1 ^ [!]-a]
+1 1 [ [!]-a]
+1 1 ^ [a^bc]
+1 1 -b] [a-]b]
+0 1 \ [\]
+1 1 \ [\\]
+0 1 \ [!\\]
+1 1 G [A-\\]
+0 1 aaabbb b*a
+0 1 aabcaa *ba*
+1 1 , [,]
+1 1 , [\\,]
+1 1 \ [\\,]
+1 1 - [,-.]
+0 1 + [,-.]
+0 1 -.] [,-.]
+1 1 2 [\1-\3]
+1 1 3 [\1-\3]
+0 1 4 [\1-\3]
+1 1 \ [[-\]]
+1 1 [ [[-\]]
+1 1 ] [[-\]]
+0 1 - [[-\]]
+
+# Test recursion and the abort code (use "wildtest -i" to see iteration counts)
+1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/*
+0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/*
+1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt **/*a*b*g*n*t
+0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz **/*a*b*g*n*t
diff --git a/xattrs.c b/xattrs.c
new file mode 100644
index 0000000..26e50a6
--- /dev/null
+++ b/xattrs.c
@@ -0,0 +1,1274 @@
+/*
+ * Extended Attribute support for rsync.
+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2006-2022 Wayne Davison
+ *
+ * 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 3 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, visit the http://fsf.org website.
+ */
+
+#include "rsync.h"
+#include "ifuncs.h"
+#include "inums.h"
+#include "lib/sysxattrs.h"
+
+#ifdef SUPPORT_XATTRS
+
+extern int dry_run;
+extern int am_root;
+extern int am_sender;
+extern int am_generator;
+extern int read_only;
+extern int list_only;
+extern int preserve_xattrs;
+extern int preserve_links;
+extern int preserve_devices;
+extern int preserve_specials;
+extern int checksum_seed;
+extern int saw_xattr_filter;
+
+extern struct name_num_item *xattr_sum_nni;
+extern int xattr_sum_len;
+
+#define RSYNC_XAL_INITIAL 5
+#define RSYNC_XAL_LIST_INITIAL 100
+
+#define MAX_XATTR_DIGEST_LEN MD5_DIGEST_LEN
+#define MAX_FULL_DATUM 32
+
+#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
+
+#define XATTR_ABBREV(x) ((size_t)((x).name - (x).datum) < (x).datum_len)
+
+#define XSTATE_ABBREV 1
+#define XSTATE_DONE 2
+#define XSTATE_TODO 3
+
+#define USER_PREFIX "user."
+#define UPRE_LEN ((int)sizeof USER_PREFIX - 1)
+#define SYSTEM_PREFIX "system."
+#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
+
+#ifdef HAVE_LINUX_XATTRS
+#define MIGHT_NEED_RPRE (am_root <= 0)
+#define RSYNC_PREFIX USER_PREFIX "rsync."
+#else
+#define MIGHT_NEED_RPRE am_root
+#define RSYNC_PREFIX "rsync."
+#endif
+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
+
+#define XSTAT_SUFFIX "stat"
+#define XSTAT_ATTR RSYNC_PREFIX "%" XSTAT_SUFFIX
+#define XACC_ACL_SUFFIX "aacl"
+#define XACC_ACL_ATTR RSYNC_PREFIX "%" XACC_ACL_SUFFIX
+#define XDEF_ACL_SUFFIX "dacl"
+#define XDEF_ACL_ATTR RSYNC_PREFIX "%" XDEF_ACL_SUFFIX
+
+typedef struct {
+ char *datum, *name;
+ size_t datum_len, name_len;
+ int num;
+} rsync_xa;
+
+struct _rsync_xa_list;
+
+typedef struct _rsync_xa_list_ref {
+ struct _rsync_xa_list_ref *next;
+ int ndx;
+} rsync_xa_list_ref;
+
+typedef struct _rsync_xa_list {
+ int ndx;
+ int64 key;
+ item_list xa_items;
+} rsync_xa_list;
+
+static size_t namebuf_len = 0;
+static char *namebuf = NULL;
+
+static const rsync_xa_list empty_xa_list = {
+ .xa_items = EMPTY_ITEM_LIST,
+};
+static const item_list empty_xattr = EMPTY_ITEM_LIST;
+static item_list rsync_xal_l = EMPTY_ITEM_LIST;
+static struct hashtable *rsync_xal_h = NULL;
+
+static size_t prior_xattr_count = (size_t)-1;
+
+/* ------------------------------------------------------------------------- */
+
+static void rsync_xal_free(item_list *xalp)
+{
+ size_t i;
+ rsync_xa *rxas = xalp->items;
+
+ if (!xalp->malloced)
+ return;
+
+ for (i = 0; i < xalp->count; i++) {
+ free(rxas[i].datum);
+ /*free(rxas[i].name);*/
+ }
+ free(xalp->items);
+}
+
+void free_xattr(stat_x *sxp)
+{
+ if (!sxp->xattr)
+ return;
+ rsync_xal_free(sxp->xattr);
+ free(sxp->xattr);
+ sxp->xattr = NULL;
+}
+
+static int rsync_xal_compare_names(const void *x1, const void *x2)
+{
+ const rsync_xa *xa1 = x1;
+ const rsync_xa *xa2 = x2;
+ return strcmp(xa1->name, xa2->name);
+}
+
+static ssize_t get_xattr_names(const char *fname)
+{
+ ssize_t list_len;
+ int64 arg;
+
+ if (!namebuf) {
+ namebuf_len = 1024;
+ namebuf = new_array(char, namebuf_len);
+ }
+
+ while (1) {
+ /* The length returned includes all the '\0' terminators. */
+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
+ if (list_len >= 0) {
+ if ((size_t)list_len <= namebuf_len)
+ break;
+ } else if (errno == ENOTSUP)
+ return 0;
+ else if (errno != ERANGE) {
+ arg = namebuf_len;
+ got_error:
+ rsyserr(FERROR_XFER, errno,
+ "get_xattr_names: llistxattr(%s,%s) failed",
+ full_fname(fname), big_num(arg));
+ return -1;
+ }
+ list_len = sys_llistxattr(fname, NULL, 0);
+ if (list_len < 0) {
+ arg = 0;
+ goto got_error;
+ }
+ if (namebuf_len)
+ free(namebuf);
+ namebuf_len = list_len + 1024;
+ namebuf = new_array(char, namebuf_len);
+ }
+
+ return list_len;
+}
+
+/* On entry, the *len_ptr parameter contains the size of the extra space we
+ * should allocate when we create a buffer for the data. On exit, it contains
+ * the length of the datum. */
+static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr, int no_missing_error)
+{
+ size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
+ size_t extra_len = *len_ptr;
+ char *ptr;
+
+ *len_ptr = datum_len;
+
+ if (datum_len == (size_t)-1) {
+ if (errno == ENOTSUP || no_missing_error)
+ return NULL;
+ rsyserr(FERROR_XFER, errno,
+ "get_xattr_data: lgetxattr(%s,\"%s\",0) failed",
+ full_fname(fname), name);
+ return NULL;
+ }
+
+ if (!datum_len && !extra_len)
+ extra_len = 1; /* request non-zero amount of memory */
+ if (SIZE_MAX - datum_len < extra_len)
+ overflow_exit("get_xattr_data");
+ ptr = new_array(char, datum_len + extra_len);
+
+ if (datum_len) {
+ size_t len = sys_lgetxattr(fname, name, ptr, datum_len);
+ if (len != datum_len) {
+ if (len == (size_t)-1) {
+ rsyserr(FERROR_XFER, errno,
+ "get_xattr_data: lgetxattr(%s,\"%s\",%ld) failed",
+ full_fname(fname), name, (long)datum_len);
+ } else {
+ rprintf(FERROR_XFER,
+ "get_xattr_data: lgetxattr(%s,\"%s\",%ld) returned %ld\n",
+ full_fname(fname), name,
+ (long)datum_len, (long)len);
+ }
+ free(ptr);
+ return NULL;
+ }
+ }
+
+ return ptr;
+}
+
+static int rsync_xal_get(const char *fname, item_list *xalp)
+{
+ ssize_t list_len, name_len;
+ size_t datum_len, name_offset;
+ char *name, *ptr;
+#ifdef HAVE_LINUX_XATTRS
+ int user_only = am_sender ? 0 : !am_root;
+#endif
+ rsync_xa *rxa;
+ int count;
+
+ /* This puts the name list into the "namebuf" buffer. */
+ if ((list_len = get_xattr_names(fname)) < 0)
+ return -1;
+
+ for (name = namebuf; list_len > 0; name += name_len) {
+ name_len = strlen(name) + 1;
+ list_len -= name_len;
+
+ if (saw_xattr_filter) {
+ if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS))
+ continue;
+ }
+#ifdef HAVE_LINUX_XATTRS
+ /* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */
+ else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
+ continue;
+#endif
+
+ /* No rsync.%FOO attributes are copied w/o 2 -X options. */
+ if (name_len > RPRE_LEN && name[RPRE_LEN] == '%' && HAS_PREFIX(name, RSYNC_PREFIX)) {
+ if ((am_sender && preserve_xattrs < 2)
+ || (am_root < 0
+ && (strcmp(name+RPRE_LEN+1, XSTAT_SUFFIX) == 0
+ || strcmp(name+RPRE_LEN+1, XACC_ACL_SUFFIX) == 0
+ || strcmp(name+RPRE_LEN+1, XDEF_ACL_SUFFIX) == 0)))
+ continue;
+ }
+
+ datum_len = name_len; /* Pass extra size to get_xattr_data() */
+ if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
+ return -1;
+
+ if (datum_len > MAX_FULL_DATUM) {
+ /* For large datums, we store a flag and a checksum. */
+ name_offset = 1 + MAX_XATTR_DIGEST_LEN;
+ sum_init(xattr_sum_nni, checksum_seed);
+ sum_update(ptr, datum_len);
+ free(ptr);
+
+ ptr = new_array(char, name_offset + name_len);
+ *ptr = XSTATE_ABBREV;
+ sum_end(ptr + 1);
+ } else
+ name_offset = datum_len;
+
+ rxa = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
+ rxa->name = ptr + name_offset;
+ memcpy(rxa->name, name, name_len);
+ rxa->datum = ptr;
+ rxa->name_len = name_len;
+ rxa->datum_len = datum_len;
+ }
+ count = xalp->count;
+ rxa = xalp->items;
+ if (count > 1)
+ qsort(rxa, count, sizeof (rsync_xa), rsync_xal_compare_names);
+ for (rxa += count-1; count; count--, rxa--)
+ rxa->num = count;
+ return 0;
+}
+
+/* Read the xattr(s) for this filename. */
+int get_xattr(const char *fname, stat_x *sxp)
+{
+ sxp->xattr = new(item_list);
+ *sxp->xattr = empty_xattr;
+
+ if (S_ISREG(sxp->st.st_mode) || S_ISDIR(sxp->st.st_mode)) {
+ /* Everyone supports this. */
+ } else if (S_ISLNK(sxp->st.st_mode)) {
+#ifndef NO_SYMLINK_XATTRS
+ if (!preserve_links)
+#endif
+ return 0;
+ } else if (IS_SPECIAL(sxp->st.st_mode)) {
+#ifndef NO_SPECIAL_XATTRS
+ if (!preserve_specials)
+#endif
+ return 0;
+ } else if (IS_DEVICE(sxp->st.st_mode)) {
+#ifndef NO_DEVICE_XATTRS
+ if (!preserve_devices)
+#endif
+ return 0;
+ } else if (IS_MISSING_FILE(sxp->st))
+ return 0;
+
+ if (rsync_xal_get(fname, sxp->xattr) < 0) {
+ free_xattr(sxp);
+ return -1;
+ }
+ return 0;
+}
+
+int copy_xattrs(const char *source, const char *dest)
+{
+ ssize_t list_len, name_len;
+ size_t datum_len;
+ char *name, *ptr;
+#ifdef HAVE_LINUX_XATTRS
+ int user_only = am_sender ? 0 : am_root <= 0;
+#endif
+
+ /* This puts the name list into the "namebuf" buffer. */
+ if ((list_len = get_xattr_names(source)) < 0)
+ return -1;
+
+ for (name = namebuf; list_len > 0; name += name_len) {
+ name_len = strlen(name) + 1;
+ list_len -= name_len;
+
+ if (saw_xattr_filter) {
+ if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS))
+ continue;
+ }
+#ifdef HAVE_LINUX_XATTRS
+ /* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */
+ else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
+ continue;
+#endif
+
+ datum_len = 0;
+ if (!(ptr = get_xattr_data(source, name, &datum_len, 0)))
+ return -1;
+ if (sys_lsetxattr(dest, name, ptr, datum_len) < 0) {
+ int save_errno = errno ? errno : EINVAL;
+ rsyserr(FERROR_XFER, errno,
+ "copy_xattrs: lsetxattr(%s,\"%s\") failed",
+ full_fname(dest), name);
+ errno = save_errno;
+ return -1;
+ }
+ free(ptr);
+ }
+
+ return 0;
+}
+
+static int64 xattr_lookup_hash(const item_list *xalp)
+{
+ const rsync_xa *rxas = xalp->items;
+ size_t i;
+ int64 key = hashlittle2(&xalp->count, sizeof xalp->count);
+
+ for (i = 0; i < xalp->count; i++) {
+ key += hashlittle2(rxas[i].name, rxas[i].name_len);
+ if (rxas[i].datum_len > MAX_FULL_DATUM)
+ key += hashlittle2(rxas[i].datum, xattr_sum_len);
+ else
+ key += hashlittle2(rxas[i].datum, rxas[i].datum_len);
+ }
+
+ return key;
+}
+
+static int find_matching_xattr(const item_list *xalp)
+{
+ const struct ht_int64_node *node;
+ const rsync_xa_list_ref *ref;
+ int64 key;
+
+ if (rsync_xal_h == NULL)
+ return -1;
+
+ key = xattr_lookup_hash(xalp);
+
+ node = hashtable_find(rsync_xal_h, key, NULL);
+ if (node == NULL)
+ return -1;
+
+ if (node->data == NULL)
+ return -1;
+
+ for (ref = node->data; ref != NULL; ref = ref->next) {
+ const rsync_xa_list *ptr = rsync_xal_l.items;
+ const rsync_xa *rxas1;
+ const rsync_xa *rxas2 = xalp->items;
+ size_t j;
+
+ ptr += ref->ndx;
+ rxas1 = ptr->xa_items.items;
+
+ /* Wrong number of elements? */
+ if (ptr->xa_items.count != xalp->count)
+ continue;
+ /* any elements different? */
+ for (j = 0; j < xalp->count; j++) {
+ if (rxas1[j].name_len != rxas2[j].name_len
+ || rxas1[j].datum_len != rxas2[j].datum_len
+ || strcmp(rxas1[j].name, rxas2[j].name))
+ break;
+ if (rxas1[j].datum_len > MAX_FULL_DATUM) {
+ if (memcmp(rxas1[j].datum + 1,
+ rxas2[j].datum + 1,
+ xattr_sum_len) != 0)
+ break;
+ } else {
+ if (memcmp(rxas1[j].datum, rxas2[j].datum,
+ rxas2[j].datum_len))
+ break;
+ }
+ }
+ /* no differences found. This is The One! */
+ if (j == xalp->count)
+ return ref->ndx;
+ }
+
+ return -1;
+}
+
+/* Store *xalp on the end of rsync_xal_l */
+static int rsync_xal_store(item_list *xalp)
+{
+ struct ht_int64_node *node;
+ int ndx = rsync_xal_l.count; /* pre-incremented count */
+ rsync_xa_list *new_list = EXPAND_ITEM_LIST(&rsync_xal_l, rsync_xa_list, RSYNC_XAL_LIST_INITIAL);
+ rsync_xa_list_ref *new_ref;
+ /* Since the following call starts a new list, we know it will hold the
+ * entire initial-count, not just enough space for one new item. */
+ *new_list = empty_xa_list;
+ (void)EXPAND_ITEM_LIST(&new_list->xa_items, rsync_xa, xalp->count);
+ memcpy(new_list->xa_items.items, xalp->items, xalp->count * sizeof (rsync_xa));
+ new_list->xa_items.count = xalp->count;
+ xalp->count = 0;
+
+ new_list->ndx = ndx;
+ new_list->key = xattr_lookup_hash(&new_list->xa_items);
+
+ if (rsync_xal_h == NULL)
+ rsync_xal_h = hashtable_create(512, HT_KEY64);
+
+ new_ref = new0(rsync_xa_list_ref);
+ new_ref->ndx = ndx;
+
+ node = hashtable_find(rsync_xal_h, new_list->key, new_ref);
+ if (node->data != (void*)new_ref) {
+ rsync_xa_list_ref *ref = node->data;
+
+ while (ref != NULL) {
+ if (ref->next != NULL) {
+ ref = ref->next;
+ continue;
+ }
+
+ ref->next = new_ref;
+ break;
+ }
+ }
+
+ return ndx;
+}
+
+/* Send the make_xattr()-generated xattr list for this flist entry. */
+int send_xattr(int f, stat_x *sxp)
+{
+ int ndx = find_matching_xattr(sxp->xattr);
+
+ /* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
+ write_varint(f, ndx + 1);
+
+ if (ndx < 0) {
+ rsync_xa *rxa;
+ int count = sxp->xattr->count;
+ write_varint(f, count);
+ for (rxa = sxp->xattr->items; count--; rxa++) {
+ size_t name_len = rxa->name_len;
+ const char *name = rxa->name;
+ /* Strip the rsync prefix from disguised namespaces. */
+ if (name_len > RPRE_LEN
+#ifdef HAVE_LINUX_XATTRS
+ && am_root < 0
+#endif
+ && name[RPRE_LEN] != '%' && HAS_PREFIX(name, RSYNC_PREFIX)) {
+ name += RPRE_LEN;
+ name_len -= RPRE_LEN;
+ }
+#ifndef HAVE_LINUX_XATTRS
+ else {
+ /* Put everything else in the user namespace. */
+ name_len += UPRE_LEN;
+ }
+#endif
+ write_varint(f, name_len);
+ write_varint(f, rxa->datum_len);
+#ifndef HAVE_LINUX_XATTRS
+ if (name_len > rxa->name_len) {
+ write_buf(f, USER_PREFIX, UPRE_LEN);
+ name_len -= UPRE_LEN;
+ }
+#endif
+ write_buf(f, name, name_len);
+ if (rxa->datum_len > MAX_FULL_DATUM)
+ write_buf(f, rxa->datum + 1, xattr_sum_len);
+ else
+ write_bigbuf(f, rxa->datum, rxa->datum_len);
+ }
+ ndx = rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
+ }
+
+ return ndx;
+}
+
+/* Return a flag indicating if we need to change a file's xattrs. If
+ * "find_all" is specified, also mark any abbreviated xattrs that we
+ * need so that send_xattr_request() can tell the sender about them. */
+int xattr_diff(struct file_struct *file, stat_x *sxp, int find_all)
+{
+ const rsync_xa_list *glst = rsync_xal_l.items;
+ const item_list *lst;
+ rsync_xa *snd_rxa, *rec_rxa;
+ int snd_cnt, rec_cnt;
+ int cmp, same, xattrs_equal = 1;
+
+ if (sxp && XATTR_READY(*sxp)) {
+ rec_rxa = sxp->xattr->items;
+ rec_cnt = sxp->xattr->count;
+ } else {
+ rec_rxa = NULL;
+ rec_cnt = 0;
+ }
+
+ if (F_XATTR(file) >= 0) {
+ glst += F_XATTR(file);
+ lst = &glst->xa_items;
+ } else
+ lst = &empty_xattr;
+
+ snd_rxa = lst->items;
+ snd_cnt = lst->count;
+
+ /* If the count of the sender's xattrs is different from our
+ * (receiver's) xattrs, the lists are not the same. */
+ if (snd_cnt != rec_cnt) {
+ if (!find_all)
+ return 1;
+ xattrs_equal = 0;
+ }
+
+ while (snd_cnt) {
+ cmp = rec_cnt ? strcmp(snd_rxa->name, rec_rxa->name) : -1;
+ if (cmp > 0)
+ same = 0;
+ else if (snd_rxa->datum_len > MAX_FULL_DATUM) {
+ same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
+ && memcmp(snd_rxa->datum + 1, rec_rxa->datum + 1,
+ xattr_sum_len) == 0;
+ /* Flag unrequested items that we need. */
+ if (!same && find_all && snd_rxa->datum[0] == XSTATE_ABBREV)
+ snd_rxa->datum[0] = XSTATE_TODO;
+ } else {
+ same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
+ && memcmp(snd_rxa->datum, rec_rxa->datum,
+ snd_rxa->datum_len) == 0;
+ }
+ if (!same) {
+ if (!find_all)
+ return 1;
+ xattrs_equal = 0;
+ }
+
+ if (cmp <= 0) {
+ snd_rxa++;
+ snd_cnt--;
+ }
+ if (cmp >= 0) {
+ rec_rxa++;
+ rec_cnt--;
+ }
+ }
+
+ if (rec_cnt)
+ xattrs_equal = 0;
+
+ return !xattrs_equal;
+}
+
+/* When called by the generator (with a NULL fname), this tells the sender
+ * all the abbreviated xattr values we need. When called by the sender
+ * (with a non-NULL fname), we send all the extra xattr data it needs.
+ * The generator may also call with f_out < 0 to just change all the
+ * XSTATE_ABBREV states into XSTATE_DONE. */
+void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
+{
+ const rsync_xa_list *glst = rsync_xal_l.items;
+ const item_list *lst;
+ int cnt, prior_req = 0;
+ rsync_xa *rxa;
+
+ glst += F_XATTR(file);
+ lst = &glst->xa_items;
+
+ for (rxa = lst->items, cnt = lst->count; cnt--; rxa++) {
+ if (rxa->datum_len <= MAX_FULL_DATUM)
+ continue;
+ switch (rxa->datum[0]) {
+ case XSTATE_ABBREV:
+ /* Items left abbreviated matched the sender's checksum, so
+ * the receiver will cache the local data for future use. */
+ if (am_generator)
+ rxa->datum[0] = XSTATE_DONE;
+ continue;
+ case XSTATE_TODO:
+ assert(f_out >= 0);
+ break;
+ default:
+ continue;
+ }
+
+ /* Flag that we handled this abbreviated item. */
+ rxa->datum[0] = XSTATE_DONE;
+
+ write_varint(f_out, rxa->num - prior_req);
+ prior_req = rxa->num;
+
+ if (fname) {
+ size_t len = 0;
+ char *ptr;
+
+ /* Re-read the long datum. */
+ if (!(ptr = get_xattr_data(fname, rxa->name, &len, 0))) {
+ rprintf(FERROR_XFER, "failed to re-read xattr %s for %s\n", rxa->name, fname);
+ write_varint(f_out, 0);
+ continue;
+ }
+
+ write_varint(f_out, len); /* length might have changed! */
+ write_bigbuf(f_out, ptr, len);
+ free(ptr);
+ }
+ }
+
+ if (f_out >= 0)
+ write_byte(f_out, 0); /* end the list */
+}
+
+/* When called by the sender, read the request from the generator and mark
+ * any needed xattrs with a flag that lets us know they need to be sent to
+ * the receiver. When called by the receiver, reads the sent data and
+ * stores it in place of its checksum. */
+int recv_xattr_request(struct file_struct *file, int f_in)
+{
+ const rsync_xa_list *glst = rsync_xal_l.items;
+ const item_list *lst;
+ char *old_datum, *name;
+ rsync_xa *rxa;
+ int rel_pos, cnt, num, got_xattr_data = 0;
+
+ if (F_XATTR(file) < 0) {
+ rprintf(FERROR, "recv_xattr_request: internal data error!\n");
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ glst += F_XATTR(file);
+ lst = &glst->xa_items;
+
+ cnt = lst->count;
+ rxa = lst->items;
+ num = 0;
+ while ((rel_pos = read_varint(f_in)) != 0) {
+ num += rel_pos;
+ if (am_sender) {
+ /* The sender-related num values are only in order on the sender.
+ * We use that order here to scan forward or backward as needed. */
+ if (rel_pos < 0) {
+ while (cnt < (int)lst->count && rxa->num > num) {
+ rxa--;
+ cnt++;
+ }
+ } else {
+ while (cnt > 1 && rxa->num < num) {
+ rxa++;
+ cnt--;
+ }
+ }
+ } else {
+ int j;
+ /* The receiving side has no known num order, so we just scan
+ * forward (w/wrap) and hope that the next value is near by. */
+ for (j = lst->count; j > 1 && rxa->num != num; j--) {
+ if (--cnt)
+ rxa++;
+ else {
+ cnt = lst->count;
+ rxa = lst->items;
+ }
+ }
+ }
+ if (!cnt || rxa->num != num) {
+ rprintf(FERROR, "[%s] could not find xattr #%d for %s\n",
+ who_am_i(), num, f_name(file, NULL));
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (!XATTR_ABBREV(*rxa) || rxa->datum[0] != XSTATE_ABBREV) {
+ rprintf(FERROR, "[%s] internal abbrev error on %s (%s, len=%ld)!\n",
+ who_am_i(), f_name(file, NULL), rxa->name, (long)rxa->datum_len);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (am_sender) {
+ rxa->datum[0] = XSTATE_TODO;
+ continue;
+ }
+
+ old_datum = rxa->datum;
+ rxa->datum_len = read_varint(f_in);
+
+ if (SIZE_MAX - rxa->name_len < rxa->datum_len)
+ overflow_exit("recv_xattr_request");
+ rxa->datum = new_array(char, rxa->datum_len + rxa->name_len);
+ name = rxa->datum + rxa->datum_len;
+ memcpy(name, rxa->name, rxa->name_len);
+ rxa->name = name;
+ free(old_datum);
+ read_buf(f_in, rxa->datum, rxa->datum_len);
+ got_xattr_data = 1;
+ }
+
+ return got_xattr_data;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* receive and build the rsync_xattr_lists */
+void receive_xattr(int f, struct file_struct *file)
+{
+ static item_list temp_xattr = EMPTY_ITEM_LIST;
+ int count, num;
+#ifdef HAVE_LINUX_XATTRS
+ int need_sort = 0;
+#else
+ int need_sort = 1;
+#endif
+ int ndx = read_varint(f);
+
+ if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) {
+ rprintf(FERROR, "receive_xattr: xa index %d out of"
+ " range for %s\n", ndx, f_name(file, NULL));
+ exit_cleanup(RERR_STREAMIO);
+ }
+
+ if (ndx != 0) {
+ F_XATTR(file) = ndx - 1;
+ return;
+ }
+
+ if ((count = read_varint(f)) != 0) {
+ (void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
+ temp_xattr.count = 0;
+ }
+
+ for (num = 1; num <= count; num++) {
+ char *ptr, *name;
+ rsync_xa *rxa;
+ size_t name_len = read_varint(f);
+ size_t datum_len = read_varint(f);
+ size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + (size_t)xattr_sum_len : datum_len;
+ size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
+ if (SIZE_MAX - dget_len < extra_len || SIZE_MAX - dget_len - extra_len < name_len)
+ overflow_exit("receive_xattr");
+ ptr = new_array(char, dget_len + extra_len + name_len);
+ name = ptr + dget_len + extra_len;
+ read_buf(f, name, name_len);
+ if (name_len < 1 || name[name_len-1] != '\0') {
+ rprintf(FERROR, "Invalid xattr name received (missing trailing \\0).\n");
+ exit_cleanup(RERR_FILEIO);
+ }
+ if (dget_len == datum_len)
+ read_buf(f, ptr, dget_len);
+ else {
+ *ptr = XSTATE_ABBREV;
+ read_buf(f, ptr + 1, xattr_sum_len);
+ }
+
+ if (saw_xattr_filter) {
+ if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS)) {
+ free(ptr);
+ continue;
+ }
+ }
+#ifdef HAVE_LINUX_XATTRS
+ /* Non-root can only save the user namespace. */
+ if (am_root <= 0 && !HAS_PREFIX(name, USER_PREFIX)) {
+ if (!am_root && !saw_xattr_filter) {
+ free(ptr);
+ continue;
+ }
+ name -= RPRE_LEN;
+ name_len += RPRE_LEN;
+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
+ need_sort = 1;
+ }
+#else
+ /* This OS only has a user namespace, so we either
+ * strip the user prefix, or we put a non-user
+ * namespace inside our rsync hierarchy. */
+ if (HAS_PREFIX(name, USER_PREFIX)) {
+ name += UPRE_LEN;
+ name_len -= UPRE_LEN;
+ } else if (am_root) {
+ name -= RPRE_LEN;
+ name_len += RPRE_LEN;
+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
+ } else {
+ free(ptr);
+ continue;
+ }
+#endif
+ /* No rsync.%FOO attributes are copied w/o 2 -X options. */
+ if (preserve_xattrs < 2 && name_len > RPRE_LEN
+ && name[RPRE_LEN] == '%' && HAS_PREFIX(name, RSYNC_PREFIX)) {
+ free(ptr);
+ continue;
+ }
+
+ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1);
+ rxa->name = name;
+ rxa->datum = ptr;
+ rxa->name_len = name_len;
+ rxa->datum_len = datum_len;
+ rxa->num = num;
+ }
+
+ if (need_sort && count > 1)
+ qsort(temp_xattr.items, count, sizeof (rsync_xa), rsync_xal_compare_names);
+
+ ndx = rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
+
+ F_XATTR(file) = ndx;
+}
+
+/* Turn the xattr data in stat_x into cached xattr data, setting the index
+ * values in the file struct. */
+void cache_tmp_xattr(struct file_struct *file, stat_x *sxp)
+{
+ int ndx;
+
+ if (!sxp->xattr)
+ return;
+
+ if (prior_xattr_count == (size_t)-1)
+ prior_xattr_count = rsync_xal_l.count;
+ ndx = find_matching_xattr(sxp->xattr);
+ if (ndx < 0)
+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
+
+ F_XATTR(file) = ndx;
+}
+
+void uncache_tmp_xattrs(void)
+{
+ if (prior_xattr_count != (size_t)-1) {
+ rsync_xa_list *xa_list_item = rsync_xal_l.items;
+ rsync_xa_list *xa_list_start = xa_list_item + prior_xattr_count;
+ xa_list_item += rsync_xal_l.count;
+ rsync_xal_l.count = prior_xattr_count;
+ while (xa_list_item-- > xa_list_start) {
+ struct ht_int64_node *node;
+ rsync_xa_list_ref *ref;
+
+ rsync_xal_free(&xa_list_item->xa_items);
+
+ if (rsync_xal_h == NULL)
+ continue;
+
+ node = hashtable_find(rsync_xal_h, xa_list_item->key, NULL);
+ if (node == NULL)
+ continue;
+
+ if (node->data == NULL)
+ continue;
+
+ ref = node->data;
+ if (xa_list_item->ndx == ref->ndx) {
+ /* xa_list_item is the first in the list. */
+ node->data = ref->next;
+ free(ref);
+ continue;
+ }
+
+ while (1) {
+ rsync_xa_list_ref *next = ref->next;
+ if (next == NULL)
+ break;
+ if (xa_list_item->ndx == next->ndx) {
+ ref->next = next->next;
+ free(next);
+ break;
+ }
+ ref = next;
+ }
+ }
+ prior_xattr_count = (size_t)-1;
+ }
+}
+
+static int rsync_xal_set(const char *fname, item_list *xalp,
+ const char *fnamecmp, stat_x *sxp)
+{
+ rsync_xa *rxas = xalp->items;
+ ssize_t list_len;
+ size_t i, len;
+ char *name, *ptr, sum[MAX_XATTR_DIGEST_LEN];
+#ifdef HAVE_LINUX_XATTRS
+ int user_only = am_root <= 0;
+#endif
+ size_t name_len;
+ int ret = 0;
+
+ /* This puts the current name list into the "namebuf" buffer. */
+ if ((list_len = get_xattr_names(fname)) < 0)
+ return -1;
+
+ for (i = 0; i < xalp->count; i++) {
+ name = rxas[i].name;
+
+ if (XATTR_ABBREV(rxas[i])) {
+ /* See if the fnamecmp version is identical. */
+ len = name_len = rxas[i].name_len;
+ if ((ptr = get_xattr_data(fnamecmp, name, &len, 1)) == NULL) {
+ still_abbrev:
+ if (am_generator)
+ continue;
+ rprintf(FERROR, "Missing abbreviated xattr value, %s, for %s\n",
+ rxas[i].name, full_fname(fname));
+ ret = -1;
+ continue;
+ }
+ if (len != rxas[i].datum_len) {
+ free(ptr);
+ goto still_abbrev;
+ }
+
+ sum_init(xattr_sum_nni, checksum_seed);
+ sum_update(ptr, len);
+ sum_end(sum);
+ if (memcmp(sum, rxas[i].datum + 1, xattr_sum_len) != 0) {
+ free(ptr);
+ goto still_abbrev;
+ }
+
+ if (fname == fnamecmp)
+ ; /* Value is already set when identical */
+ else if (sys_lsetxattr(fname, name, ptr, len) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "rsync_xal_set: lsetxattr(%s,\"%s\") failed",
+ full_fname(fname), name);
+ ret = -1;
+ } else /* make sure caller sets mtime */
+ sxp->st.st_mtime = (time_t)-1;
+
+ if (am_generator) { /* generator items stay abbreviated */
+ free(ptr);
+ continue;
+ }
+
+ memcpy(ptr + len, name, name_len);
+ free(rxas[i].datum);
+
+ rxas[i].name = name = ptr + len;
+ rxas[i].datum = ptr;
+ continue;
+ }
+
+ if (sys_lsetxattr(fname, name, rxas[i].datum, rxas[i].datum_len) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "rsync_xal_set: lsetxattr(%s,\"%s\") failed",
+ full_fname(fname), name);
+ ret = -1;
+ } else /* make sure caller sets mtime */
+ sxp->st.st_mtime = (time_t)-1;
+ }
+
+ /* Remove any extraneous names. */
+ for (name = namebuf; list_len > 0; name += name_len) {
+ name_len = strlen(name) + 1;
+ list_len -= name_len;
+
+ if (saw_xattr_filter) {
+ if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS))
+ continue;
+ }
+#ifdef HAVE_LINUX_XATTRS
+ /* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */
+ else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
+ continue;
+#endif
+ if (am_root < 0 && name_len > RPRE_LEN && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
+ continue;
+
+ for (i = 0; i < xalp->count; i++) {
+ if (strcmp(name, rxas[i].name) == 0)
+ break;
+ }
+ if (i == xalp->count) {
+ if (sys_lremovexattr(fname, name) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "rsync_xal_set: lremovexattr(%s,\"%s\") failed",
+ full_fname(fname), name);
+ ret = -1;
+ } else /* make sure caller sets mtime */
+ sxp->st.st_mtime = (time_t)-1;
+ }
+ }
+
+ return ret;
+}
+
+/* Set extended attributes on indicated filename. */
+int set_xattr(const char *fname, const struct file_struct *file, const char *fnamecmp, stat_x *sxp)
+{
+ rsync_xa_list *glst = rsync_xal_l.items;
+ item_list *lst;
+ int ndx, added_write_perm = 0;
+
+ if (dry_run)
+ return 1; /* FIXME: --dry-run needs to compute this value */
+
+ if (read_only || list_only) {
+ errno = EROFS;
+ return -1;
+ }
+
+#ifdef NO_SPECIAL_XATTRS
+ if (IS_SPECIAL(sxp->st.st_mode)) {
+ errno = ENOTSUP;
+ return -1;
+ }
+#endif
+#ifdef NO_DEVICE_XATTRS
+ if (IS_DEVICE(sxp->st.st_mode)) {
+ errno = ENOTSUP;
+ return -1;
+ }
+#endif
+#ifdef NO_SYMLINK_XATTRS
+ if (S_ISLNK(sxp->st.st_mode)) {
+ errno = ENOTSUP;
+ return -1;
+ }
+#endif
+
+ /* If the target file lacks write permission, we try to add it
+ * temporarily so we can change the extended attributes. */
+ if (!am_root
+#ifdef SUPPORT_LINKS
+ && !S_ISLNK(sxp->st.st_mode)
+#endif
+ && access(fname, W_OK) < 0
+ && do_chmod(fname, (sxp->st.st_mode & CHMOD_BITS) | S_IWUSR) == 0)
+ added_write_perm = 1;
+
+ ndx = F_XATTR(file);
+ glst += ndx;
+ lst = &glst->xa_items;
+ int return_value = rsync_xal_set(fname, lst, fnamecmp, sxp);
+ if (added_write_perm) /* remove the temporary write permission */
+ do_chmod(fname, sxp->st.st_mode);
+ return return_value;
+}
+
+#ifdef SUPPORT_ACLS
+char *get_xattr_acl(const char *fname, int is_access_acl, size_t *len_p)
+{
+ const char *name = is_access_acl ? XACC_ACL_ATTR : XDEF_ACL_ATTR;
+ *len_p = 0; /* no extra data alloc needed from get_xattr_data() */
+ return get_xattr_data(fname, name, len_p, 1);
+}
+
+int set_xattr_acl(const char *fname, int is_access_acl, const char *buf, size_t buf_len)
+{
+ const char *name = is_access_acl ? XACC_ACL_ATTR : XDEF_ACL_ATTR;
+ if (sys_lsetxattr(fname, name, buf, buf_len) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "set_xattr_acl: lsetxattr(%s,\"%s\") failed",
+ full_fname(fname), name);
+ return -1;
+ }
+ return 0;
+}
+
+int del_def_xattr_acl(const char *fname)
+{
+ return sys_lremovexattr(fname, XDEF_ACL_ATTR);
+}
+#endif
+
+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
+{
+ unsigned int mode;
+ int rdev_major, rdev_minor, uid, gid, len;
+ char buf[256];
+
+ if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
+ return -1;
+
+ if (xst)
+ *xst = *fst;
+ else
+ xst = fst;
+ if (fname) {
+ fd = -1;
+ len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
+ } else {
+ fname = "fd";
+ len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
+ }
+ if (len >= (int)sizeof buf) {
+ len = -1;
+ errno = ERANGE;
+ }
+ if (len < 0) {
+ if (errno == ENOTSUP || errno == ENOATTR)
+ return -1;
+ if (errno == EPERM && S_ISLNK(fst->st_mode)) {
+ xst->st_uid = 0;
+ xst->st_gid = 0;
+ return 0;
+ }
+ rsyserr(FERROR_XFER, errno, "failed to read xattr %s for %s",
+ XSTAT_ATTR, full_fname(fname));
+ return -1;
+ }
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%o %d,%d %d:%d",
+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
+ rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
+ XSTAT_ATTR, full_fname(fname), buf);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ xst->st_mode = from_wire_mode(mode);
+ xst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
+ xst->st_uid = uid;
+ xst->st_gid = gid;
+
+ return 0;
+}
+
+int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
+{
+ STRUCT_STAT fst, xst;
+ dev_t rdev;
+ mode_t mode, fmode;
+
+ if (dry_run)
+ return 0;
+
+ if (read_only || list_only) {
+ rsyserr(FERROR_XFER, EROFS, "failed to write xattr %s for %s",
+ XSTAT_ATTR, full_fname(fname));
+ return -1;
+ }
+
+ if (x_lstat(fname, &fst, &xst) < 0) {
+ rsyserr(FERROR_XFER, errno, "failed to re-stat %s",
+ full_fname(fname));
+ return -1;
+ }
+
+ fst.st_mode &= (_S_IFMT | CHMOD_BITS);
+ fmode = new_mode & (_S_IFMT | CHMOD_BITS);
+
+ if (IS_DEVICE(fmode)) {
+ uint32 *devp = F_RDEV_P(file);
+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ } else
+ rdev = 0;
+
+ /* Dump the special permissions and enable full owner access. */
+ mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
+ | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
+ if (fst.st_mode != mode)
+ do_chmod(fname, mode);
+ if (!IS_DEVICE(fst.st_mode))
+ fst.st_rdev = 0; /* just in case */
+
+ if (mode == fmode && fst.st_rdev == rdev
+ && fst.st_uid == F_OWNER(file) && fst.st_gid == F_GROUP(file)) {
+ /* xst.st_mode will be 0 if there's no current stat xattr */
+ if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "delete of stat xattr failed for %s",
+ full_fname(fname));
+ return -1;
+ }
+ return 0;
+ }
+
+ if (xst.st_mode != fmode || xst.st_rdev != rdev
+ || xst.st_uid != F_OWNER(file) || xst.st_gid != F_GROUP(file)) {
+ char buf[256];
+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
+ to_wire_mode(fmode),
+ (int)major(rdev), (int)minor(rdev),
+ F_OWNER(file), F_GROUP(file));
+ if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) {
+ if (errno == EPERM && S_ISLNK(fst.st_mode))
+ return 0;
+ rsyserr(FERROR_XFER, errno,
+ "failed to write xattr %s for %s",
+ XSTAT_ATTR, full_fname(fname));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
+{
+ int ret = do_stat(fname, fst);
+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
+ xst->st_mode = 0;
+ return ret;
+}
+
+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
+{
+ int ret = do_lstat(fname, fst);
+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
+ xst->st_mode = 0;
+ return ret;
+}
+
+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
+{
+ int ret = do_fstat(fd, fst);
+ if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
+ xst->st_mode = 0;
+ return ret;
+}
+
+#endif /* SUPPORT_XATTRS */
diff --git a/zlib/ChangeLog b/zlib/ChangeLog
new file mode 100644
index 0000000..f22aaba
--- /dev/null
+++ b/zlib/ChangeLog
@@ -0,0 +1,1472 @@
+
+ ChangeLog file for zlib
+
+Changes in 1.2.8 (28 Apr 2013)
+- Update contrib/minizip/iowin32.c for Windows RT [Vollant]
+- Do not force Z_CONST for C++
+- Clean up contrib/vstudio [Ro§]
+- Correct spelling error in zlib.h
+- Fix mixed line endings in contrib/vstudio
+
+Changes in 1.2.7.3 (13 Apr 2013)
+- Fix version numbers and DLL names in contrib/vstudio/*/zlib.rc
+
+Changes in 1.2.7.2 (13 Apr 2013)
+- Change check for a four-byte type back to hexadecimal
+- Fix typo in win32/Makefile.msc
+- Add casts in gzwrite.c for pointer differences
+
+Changes in 1.2.7.1 (24 Mar 2013)
+- Replace use of unsafe string functions with snprintf if available
+- Avoid including stddef.h on Windows for Z_SOLO compile [Niessink]
+- Fix gzgetc undefine when Z_PREFIX set [Turk]
+- Eliminate use of mktemp in Makefile (not always available)
+- Fix bug in 'F' mode for gzopen()
+- Add inflateGetDictionary() function
+- Correct comment in deflate.h
+- Use _snprintf for snprintf in Microsoft C
+- On Darwin, only use /usr/bin/libtool if libtool is not Apple
+- Delete "--version" file if created by "ar --version" [Richard G.]
+- Fix configure check for veracity of compiler error return codes
+- Fix CMake compilation of static lib for MSVC2010 x64
+- Remove unused variable in infback9.c
+- Fix argument checks in gzlog_compress() and gzlog_write()
+- Clean up the usage of z_const and respect const usage within zlib
+- Clean up examples/gzlog.[ch] comparisons of different types
+- Avoid shift equal to bits in type (caused endless loop)
+- Fix unintialized value bug in gzputc() introduced by const patches
+- Fix memory allocation error in examples/zran.c [Nor]
+- Fix bug where gzopen(), gzclose() would write an empty file
+- Fix bug in gzclose() when gzwrite() runs out of memory
+- Check for input buffer malloc failure in examples/gzappend.c
+- Add note to contrib/blast to use binary mode in stdio
+- Fix comparisons of differently signed integers in contrib/blast
+- Check for invalid code length codes in contrib/puff
+- Fix serious but very rare decompression bug in inftrees.c
+- Update inflateBack() comments, since inflate() can be faster
+- Use underscored I/O function names for WINAPI_FAMILY
+- Add _tr_flush_bits to the external symbols prefixed by --zprefix
+- Add contrib/vstudio/vc10 pre-build step for static only
+- Quote --version-script argument in CMakeLists.txt
+- Don't specify --version-script on Apple platforms in CMakeLists.txt
+- Fix casting error in contrib/testzlib/testzlib.c
+- Fix types in contrib/minizip to match result of get_crc_table()
+- Simplify contrib/vstudio/vc10 with 'd' suffix
+- Add TOP support to win32/Makefile.msc
+- Suport i686 and amd64 assembler builds in CMakeLists.txt
+- Fix typos in the use of _LARGEFILE64_SOURCE in zconf.h
+- Add vc11 and vc12 build files to contrib/vstudio
+- Add gzvprintf() as an undocumented function in zlib
+- Fix configure for Sun shell
+- Remove runtime check in configure for four-byte integer type
+- Add casts and consts to ease user conversion to C++
+- Add man pages for minizip and miniunzip
+- In Makefile uninstall, don't rm if preceding cd fails
+- Do not return Z_BUF_ERROR if deflateParam() has nothing to write
+
+Changes in 1.2.7 (2 May 2012)
+- Replace use of memmove() with a simple copy for portability
+- Test for existence of strerror
+- Restore gzgetc_ for backward compatibility with 1.2.6
+- Fix build with non-GNU make on Solaris
+- Require gcc 4.0 or later on Mac OS X to use the hidden attribute
+- Include unistd.h for Watcom C
+- Use __WATCOMC__ instead of __WATCOM__
+- Do not use the visibility attribute if NO_VIZ defined
+- Improve the detection of no hidden visibility attribute
+- Avoid using __int64 for gcc or solo compilation
+- Cast to char * in gzprintf to avoid warnings [Zinser]
+- Fix make_vms.com for VAX [Zinser]
+- Don't use library or built-in byte swaps
+- Simplify test and use of gcc hidden attribute
+- Fix bug in gzclose_w() when gzwrite() fails to allocate memory
+- Add "x" (O_EXCL) and "e" (O_CLOEXEC) modes support to gzopen()
+- Fix bug in test/minigzip.c for configure --solo
+- Fix contrib/vstudio project link errors [Mohanathas]
+- Add ability to choose the builder in make_vms.com [Schweda]
+- Add DESTDIR support to mingw32 win32/Makefile.gcc
+- Fix comments in win32/Makefile.gcc for proper usage
+- Allow overriding the default install locations for cmake
+- Generate and install the pkg-config file with cmake
+- Build both a static and a shared version of zlib with cmake
+- Include version symbols for cmake builds
+- If using cmake with MSVC, add the source directory to the includes
+- Remove unneeded EXTRA_CFLAGS from win32/Makefile.gcc [Truta]
+- Move obsolete emx makefile to old [Truta]
+- Allow the use of -Wundef when compiling or using zlib
+- Avoid the use of the -u option with mktemp
+- Improve inflate() documentation on the use of Z_FINISH
+- Recognize clang as gcc
+- Add gzopen_w() in Windows for wide character path names
+- Rename zconf.h in CMakeLists.txt to move it out of the way
+- Add source directory in CMakeLists.txt for building examples
+- Look in build directory for zlib.pc in CMakeLists.txt
+- Remove gzflags from zlibvc.def in vc9 and vc10
+- Fix contrib/minizip compilation in the MinGW environment
+- Update ./configure for Solaris, support --64 [Mooney]
+- Remove -R. from Solaris shared build (possible security issue)
+- Avoid race condition for parallel make (-j) running example
+- Fix type mismatch between get_crc_table() and crc_table
+- Fix parsing of version with "-" in CMakeLists.txt [Snider, Ziegler]
+- Fix the path to zlib.map in CMakeLists.txt
+- Force the native libtool in Mac OS X to avoid GNU libtool [Beebe]
+- Add instructions to win32/Makefile.gcc for shared install [Torri]
+
+Changes in 1.2.6.1 (12 Feb 2012)
+- Avoid the use of the Objective-C reserved name "id"
+- Include io.h in gzguts.h for Microsoft compilers
+- Fix problem with ./configure --prefix and gzgetc macro
+- Include gz_header definition when compiling zlib solo
+- Put gzflags() functionality back in zutil.c
+- Avoid library header include in crc32.c for Z_SOLO
+- Use name in GCC_CLASSIC as C compiler for coverage testing, if set
+- Minor cleanup in contrib/minizip/zip.c [Vollant]
+- Update make_vms.com [Zinser]
+- Remove unnecessary gzgetc_ function
+- Use optimized byte swap operations for Microsoft and GNU [Snyder]
+- Fix minor typo in zlib.h comments [Rzesniowiecki]
+
+Changes in 1.2.6 (29 Jan 2012)
+- Update the Pascal interface in contrib/pascal
+- Fix function numbers for gzgetc_ in zlibvc.def files
+- Fix configure.ac for contrib/minizip [Schiffer]
+- Fix large-entry detection in minizip on 64-bit systems [Schiffer]
+- Have ./configure use the compiler return code for error indication
+- Fix CMakeLists.txt for cross compilation [McClure]
+- Fix contrib/minizip/zip.c for 64-bit architectures [Dalsnes]
+- Fix compilation of contrib/minizip on FreeBSD [Marquez]
+- Correct suggested usages in win32/Makefile.msc [Shachar, Horvath]
+- Include io.h for Turbo C / Borland C on all platforms [Truta]
+- Make version explicit in contrib/minizip/configure.ac [Bosmans]
+- Avoid warning for no encryption in contrib/minizip/zip.c [Vollant]
+- Minor cleanup up contrib/minizip/unzip.c [Vollant]
+- Fix bug when compiling minizip with C++ [Vollant]
+- Protect for long name and extra fields in contrib/minizip [Vollant]
+- Avoid some warnings in contrib/minizip [Vollant]
+- Add -I../.. -L../.. to CFLAGS for minizip and miniunzip
+- Add missing libs to minizip linker command
+- Add support for VPATH builds in contrib/minizip
+- Add an --enable-demos option to contrib/minizip/configure
+- Add the generation of configure.log by ./configure
+- Exit when required parameters not provided to win32/Makefile.gcc
+- Have gzputc return the character written instead of the argument
+- Use the -m option on ldconfig for BSD systems [Tobias]
+- Correct in zlib.map when deflateResetKeep was added
+
+Changes in 1.2.5.3 (15 Jan 2012)
+- Restore gzgetc function for binary compatibility
+- Do not use _lseeki64 under Borland C++ [Truta]
+- Update win32/Makefile.msc to build test/*.c [Truta]
+- Remove old/visualc6 given CMakefile and other alternatives
+- Update AS400 build files and documentation [Monnerat]
+- Update win32/Makefile.gcc to build test/*.c [Truta]
+- Permit stronger flushes after Z_BLOCK flushes
+- Avoid extraneous empty blocks when doing empty flushes
+- Permit Z_NULL arguments to deflatePending
+- Allow deflatePrime() to insert bits in the middle of a stream
+- Remove second empty static block for Z_PARTIAL_FLUSH
+- Write out all of the available bits when using Z_BLOCK
+- Insert the first two strings in the hash table after a flush
+
+Changes in 1.2.5.2 (17 Dec 2011)
+- fix ld error: unable to find version dependency 'ZLIB_1.2.5'
+- use relative symlinks for shared libs
+- Avoid searching past window for Z_RLE strategy
+- Assure that high-water mark initialization is always applied in deflate
+- Add assertions to fill_window() in deflate.c to match comments
+- Update python link in README
+- Correct spelling error in gzread.c
+- Fix bug in gzgets() for a concatenated empty gzip stream
+- Correct error in comment for gz_make()
+- Change gzread() and related to ignore junk after gzip streams
+- Allow gzread() and related to continue after gzclearerr()
+- Allow gzrewind() and gzseek() after a premature end-of-file
+- Simplify gzseek() now that raw after gzip is ignored
+- Change gzgetc() to a macro for speed (~40% speedup in testing)
+- Fix gzclose() to return the actual error last encountered
+- Always add large file support for windows
+- Include zconf.h for windows large file support
+- Include zconf.h.cmakein for windows large file support
+- Update zconf.h.cmakein on make distclean
+- Merge vestigial vsnprintf determination from zutil.h to gzguts.h
+- Clarify how gzopen() appends in zlib.h comments
+- Correct documentation of gzdirect() since junk at end now ignored
+- Add a transparent write mode to gzopen() when 'T' is in the mode
+- Update python link in zlib man page
+- Get inffixed.h and MAKEFIXED result to match
+- Add a ./config --solo option to make zlib subset with no libary use
+- Add undocumented inflateResetKeep() function for CAB file decoding
+- Add --cover option to ./configure for gcc coverage testing
+- Add #define ZLIB_CONST option to use const in the z_stream interface
+- Add comment to gzdopen() in zlib.h to use dup() when using fileno()
+- Note behavior of uncompress() to provide as much data as it can
+- Add files in contrib/minizip to aid in building libminizip
+- Split off AR options in Makefile.in and configure
+- Change ON macro to Z_ARG to avoid application conflicts
+- Facilitate compilation with Borland C++ for pragmas and vsnprintf
+- Include io.h for Turbo C / Borland C++
+- Move example.c and minigzip.c to test/
+- Simplify incomplete code table filling in inflate_table()
+- Remove code from inflate.c and infback.c that is impossible to execute
+- Test the inflate code with full coverage
+- Allow deflateSetDictionary, inflateSetDictionary at any time (in raw)
+- Add deflateResetKeep and fix inflateResetKeep to retain dictionary
+- Fix gzwrite.c to accommodate reduced memory zlib compilation
+- Have inflate() with Z_FINISH avoid the allocation of a window
+- Do not set strm->adler when doing raw inflate
+- Fix gzeof() to behave just like feof() when read is not past end of file
+- Fix bug in gzread.c when end-of-file is reached
+- Avoid use of Z_BUF_ERROR in gz* functions except for premature EOF
+- Document gzread() capability to read concurrently written files
+- Remove hard-coding of resource compiler in CMakeLists.txt [Blammo]
+
+Changes in 1.2.5.1 (10 Sep 2011)
+- Update FAQ entry on shared builds (#13)
+- Avoid symbolic argument to chmod in Makefile.in
+- Fix bug and add consts in contrib/puff [Oberhumer]
+- Update contrib/puff/zeros.raw test file to have all block types
+- Add full coverage test for puff in contrib/puff/Makefile
+- Fix static-only-build install in Makefile.in
+- Fix bug in unzGetCurrentFileInfo() in contrib/minizip [Kuno]
+- Add libz.a dependency to shared in Makefile.in for parallel builds
+- Spell out "number" (instead of "nb") in zlib.h for total_in, total_out
+- Replace $(...) with `...` in configure for non-bash sh [Bowler]
+- Add darwin* to Darwin* and solaris* to SunOS\ 5* in configure [Groffen]
+- Add solaris* to Linux* in configure to allow gcc use [Groffen]
+- Add *bsd* to Linux* case in configure [Bar-Lev]
+- Add inffast.obj to dependencies in win32/Makefile.msc
+- Correct spelling error in deflate.h [Kohler]
+- Change libzdll.a again to libz.dll.a (!) in win32/Makefile.gcc
+- Add test to configure for GNU C looking for gcc in output of $cc -v
+- Add zlib.pc generation to win32/Makefile.gcc [Weigelt]
+- Fix bug in zlib.h for _FILE_OFFSET_BITS set and _LARGEFILE64_SOURCE not
+- Add comment in zlib.h that adler32_combine with len2 < 0 makes no sense
+- Make NO_DIVIDE option in adler32.c much faster (thanks to John Reiser)
+- Make stronger test in zconf.h to include unistd.h for LFS
+- Apply Darwin patches for 64-bit file offsets to contrib/minizip [Slack]
+- Fix zlib.h LFS support when Z_PREFIX used
+- Add updated as400 support (removed from old) [Monnerat]
+- Avoid deflate sensitivity to volatile input data
+- Avoid division in adler32_combine for NO_DIVIDE
+- Clarify the use of Z_FINISH with deflateBound() amount of space
+- Set binary for output file in puff.c
+- Use u4 type for crc_table to avoid conversion warnings
+- Apply casts in zlib.h to avoid conversion warnings
+- Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller]
+- Improve inflateSync() documentation to note indeterminancy
+- Add deflatePending() function to return the amount of pending output
+- Correct the spelling of "specification" in FAQ [Randers-Pehrson]
+- Add a check in configure for stdarg.h, use for gzprintf()
+- Check that pointers fit in ints when gzprint() compiled old style
+- Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler]
+- Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt]
+- Add debug records in assmebler code [Londer]
+- Update RFC references to use http://tools.ietf.org/html/... [Li]
+- Add --archs option, use of libtool to configure for Mac OS X [Borstel]
+
+Changes in 1.2.5 (19 Apr 2010)
+- Disable visibility attribute in win32/Makefile.gcc [Bar-Lev]
+- Default to libdir as sharedlibdir in configure [Nieder]
+- Update copyright dates on modified source files
+- Update trees.c to be able to generate modified trees.h
+- Exit configure for MinGW, suggesting win32/Makefile.gcc
+- Check for NULL path in gz_open [Homurlu]
+
+Changes in 1.2.4.5 (18 Apr 2010)
+- Set sharedlibdir in configure [Torok]
+- Set LDFLAGS in Makefile.in [Bar-Lev]
+- Avoid mkdir objs race condition in Makefile.in [Bowler]
+- Add ZLIB_INTERNAL in front of internal inter-module functions and arrays
+- Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C
+- Don't use hidden attribute when it is a warning generator (e.g. Solaris)
+
+Changes in 1.2.4.4 (18 Apr 2010)
+- Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok]
+- Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty
+- Try to use bash or ksh regardless of functionality of /bin/sh
+- Fix configure incompatibility with NetBSD sh
+- Remove attempt to run under bash or ksh since have better NetBSD fix
+- Fix win32/Makefile.gcc for MinGW [Bar-Lev]
+- Add diagnostic messages when using CROSS_PREFIX in configure
+- Added --sharedlibdir option to configure [Weigelt]
+- Use hidden visibility attribute when available [Frysinger]
+
+Changes in 1.2.4.3 (10 Apr 2010)
+- Only use CROSS_PREFIX in configure for ar and ranlib if they exist
+- Use CROSS_PREFIX for nm [Bar-Lev]
+- Assume _LARGEFILE64_SOURCE defined is equivalent to true
+- Avoid use of undefined symbols in #if with && and ||
+- Make *64 prototypes in gzguts.h consistent with functions
+- Add -shared load option for MinGW in configure [Bowler]
+- Move z_off64_t to public interface, use instead of off64_t
+- Remove ! from shell test in configure (not portable to Solaris)
+- Change +0 macro tests to -0 for possibly increased portability
+
+Changes in 1.2.4.2 (9 Apr 2010)
+- Add consistent carriage returns to readme.txt's in masmx86 and masmx64
+- Really provide prototypes for *64 functions when building without LFS
+- Only define unlink() in minigzip.c if unistd.h not included
+- Update README to point to contrib/vstudio project files
+- Move projects/vc6 to old/ and remove projects/
+- Include stdlib.h in minigzip.c for setmode() definition under WinCE
+- Clean up assembler builds in win32/Makefile.msc [Rowe]
+- Include sys/types.h for Microsoft for off_t definition
+- Fix memory leak on error in gz_open()
+- Symbolize nm as $NM in configure [Weigelt]
+- Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt]
+- Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined
+- Fix bug in gzeof() to take into account unused input data
+- Avoid initialization of structures with variables in puff.c
+- Updated win32/README-WIN32.txt [Rowe]
+
+Changes in 1.2.4.1 (28 Mar 2010)
+- Remove the use of [a-z] constructs for sed in configure [gentoo 310225]
+- Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech]
+- Restore "for debugging" comment on sprintf() in gzlib.c
+- Remove fdopen for MVS from gzguts.h
+- Put new README-WIN32.txt in win32 [Rowe]
+- Add check for shell to configure and invoke another shell if needed
+- Fix big fat stinking bug in gzseek() on uncompressed files
+- Remove vestigial F_OPEN64 define in zutil.h
+- Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE
+- Avoid errors on non-LFS systems when applications define LFS macros
+- Set EXE to ".exe" in configure for MINGW [Kahle]
+- Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill]
+- Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev]
+- Add DLL install in win32/makefile.gcc [Bar-Lev]
+- Allow Linux* or linux* from uname in configure [Bar-Lev]
+- Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev]
+- Add cross-compilation prefixes to configure [Bar-Lev]
+- Match type exactly in gz_load() invocation in gzread.c
+- Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func
+- Provide prototypes for *64 functions when building zlib without LFS
+- Don't use -lc when linking shared library on MinGW
+- Remove errno.h check in configure and vestigial errno code in zutil.h
+
+Changes in 1.2.4 (14 Mar 2010)
+- Fix VER3 extraction in configure for no fourth subversion
+- Update zlib.3, add docs to Makefile.in to make .pdf out of it
+- Add zlib.3.pdf to distribution
+- Don't set error code in gzerror() if passed pointer is NULL
+- Apply destination directory fixes to CMakeLists.txt [Lowman]
+- Move #cmakedefine's to a new zconf.in.cmakein
+- Restore zconf.h for builds that don't use configure or cmake
+- Add distclean to dummy Makefile for convenience
+- Update and improve INDEX, README, and FAQ
+- Update CMakeLists.txt for the return of zconf.h [Lowman]
+- Update contrib/vstudio/vc9 and vc10 [Vollant]
+- Change libz.dll.a back to libzdll.a in win32/Makefile.gcc
+- Apply license and readme changes to contrib/asm686 [Raiter]
+- Check file name lengths and add -c option in minigzip.c [Li]
+- Update contrib/amd64 and contrib/masmx86/ [Vollant]
+- Avoid use of "eof" parameter in trees.c to not shadow library variable
+- Update make_vms.com for removal of zlibdefs.h [Zinser]
+- Update assembler code and vstudio projects in contrib [Vollant]
+- Remove outdated assembler code contrib/masm686 and contrib/asm586
+- Remove old vc7 and vc8 from contrib/vstudio
+- Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe]
+- Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open()
+- Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant]
+- Remove *64 functions from win32/zlib.def (they're not 64-bit yet)
+- Fix bug in void-returning vsprintf() case in gzwrite.c
+- Fix name change from inflate.h in contrib/inflate86/inffas86.c
+- Check if temporary file exists before removing in make_vms.com [Zinser]
+- Fix make install and uninstall for --static option
+- Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta]
+- Update readme.txt in contrib/masmx64 and masmx86 to assemble
+
+Changes in 1.2.3.9 (21 Feb 2010)
+- Expunge gzio.c
+- Move as400 build information to old
+- Fix updates in contrib/minizip and contrib/vstudio
+- Add const to vsnprintf test in configure to avoid warnings [Weigelt]
+- Delete zconf.h (made by configure) [Weigelt]
+- Change zconf.in.h to zconf.h.in per convention [Weigelt]
+- Check for NULL buf in gzgets()
+- Return empty string for gzgets() with len == 1 (like fgets())
+- Fix description of gzgets() in zlib.h for end-of-file, NULL return
+- Update minizip to 1.1 [Vollant]
+- Avoid MSVC loss of data warnings in gzread.c, gzwrite.c
+- Note in zlib.h that gzerror() should be used to distinguish from EOF
+- Remove use of snprintf() from gzlib.c
+- Fix bug in gzseek()
+- Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant]
+- Fix zconf.h generation in CMakeLists.txt [Lowman]
+- Improve comments in zconf.h where modified by configure
+
+Changes in 1.2.3.8 (13 Feb 2010)
+- Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer]
+- Use z_off64_t in gz_zero() and gz_skip() to match state->skip
+- Avoid comparison problem when sizeof(int) == sizeof(z_off64_t)
+- Revert to Makefile.in from 1.2.3.6 (live with the clutter)
+- Fix missing error return in gzflush(), add zlib.h note
+- Add *64 functions to zlib.map [Levin]
+- Fix signed/unsigned comparison in gz_comp()
+- Use SFLAGS when testing shared linking in configure
+- Add --64 option to ./configure to use -m64 with gcc
+- Fix ./configure --help to correctly name options
+- Have make fail if a test fails [Levin]
+- Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson]
+- Remove assembler object files from contrib
+
+Changes in 1.2.3.7 (24 Jan 2010)
+- Always gzopen() with O_LARGEFILE if available
+- Fix gzdirect() to work immediately after gzopen() or gzdopen()
+- Make gzdirect() more precise when the state changes while reading
+- Improve zlib.h documentation in many places
+- Catch memory allocation failure in gz_open()
+- Complete close operation if seek forward in gzclose_w() fails
+- Return Z_ERRNO from gzclose_r() if close() fails
+- Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL
+- Return zero for gzwrite() errors to match zlib.h description
+- Return -1 on gzputs() error to match zlib.h description
+- Add zconf.in.h to allow recovery from configure modification [Weigelt]
+- Fix static library permissions in Makefile.in [Weigelt]
+- Avoid warnings in configure tests that hide functionality [Weigelt]
+- Add *BSD and DragonFly to Linux case in configure [gentoo 123571]
+- Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212]
+- Avoid access of uninitialized data for first inflateReset2 call [Gomes]
+- Keep object files in subdirectories to reduce the clutter somewhat
+- Remove default Makefile and zlibdefs.h, add dummy Makefile
+- Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_
+- Remove zlibdefs.h completely -- modify zconf.h instead
+
+Changes in 1.2.3.6 (17 Jan 2010)
+- Avoid void * arithmetic in gzread.c and gzwrite.c
+- Make compilers happier with const char * for gz_error message
+- Avoid unused parameter warning in inflate.c
+- Avoid signed-unsigned comparison warning in inflate.c
+- Indent #pragma's for traditional C
+- Fix usage of strwinerror() in glib.c, change to gz_strwinerror()
+- Correct email address in configure for system options
+- Update make_vms.com and add make_vms.com to contrib/minizip [Zinser]
+- Update zlib.map [Brown]
+- Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok]
+- Apply various fixes to CMakeLists.txt [Lowman]
+- Add checks on len in gzread() and gzwrite()
+- Add error message for no more room for gzungetc()
+- Remove zlib version check in gzwrite()
+- Defer compression of gzprintf() result until need to
+- Use snprintf() in gzdopen() if available
+- Remove USE_MMAP configuration determination (only used by minigzip)
+- Remove examples/pigz.c (available separately)
+- Update examples/gun.c to 1.6
+
+Changes in 1.2.3.5 (8 Jan 2010)
+- Add space after #if in zutil.h for some compilers
+- Fix relatively harmless bug in deflate_fast() [Exarevsky]
+- Fix same problem in deflate_slow()
+- Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown]
+- Add deflate_rle() for faster Z_RLE strategy run-length encoding
+- Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding
+- Change name of "write" variable in inffast.c to avoid library collisions
+- Fix premature EOF from gzread() in gzio.c [Brown]
+- Use zlib header window size if windowBits is 0 in inflateInit2()
+- Remove compressBound() call in deflate.c to avoid linking compress.o
+- Replace use of errno in gz* with functions, support WinCE [Alves]
+- Provide alternative to perror() in minigzip.c for WinCE [Alves]
+- Don't use _vsnprintf on later versions of MSVC [Lowman]
+- Add CMake build script and input file [Lowman]
+- Update contrib/minizip to 1.1 [Svensson, Vollant]
+- Moved nintendods directory from contrib to .
+- Replace gzio.c with a new set of routines with the same functionality
+- Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above
+- Update contrib/minizip to 1.1b
+- Change gzeof() to return 0 on error instead of -1 to agree with zlib.h
+
+Changes in 1.2.3.4 (21 Dec 2009)
+- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility
+- Update comments in configure and Makefile.in for default --shared
+- Fix test -z's in configure [Marquess]
+- Build examplesh and minigzipsh when not testing
+- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h
+- Import LDFLAGS from the environment in configure
+- Fix configure to populate SFLAGS with discovered CFLAGS options
+- Adapt make_vms.com to the new Makefile.in [Zinser]
+- Add zlib2ansi script for C++ compilation [Marquess]
+- Add _FILE_OFFSET_BITS=64 test to make test (when applicable)
+- Add AMD64 assembler code for longest match to contrib [Teterin]
+- Include options from $SFLAGS when doing $LDSHARED
+- Simplify 64-bit file support by introducing z_off64_t type
+- Make shared object files in objs directory to work around old Sun cc
+- Use only three-part version number for Darwin shared compiles
+- Add rc option to ar in Makefile.in for when ./configure not run
+- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4*
+- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile
+- Protect against _FILE_OFFSET_BITS being defined when compiling zlib
+- Rename Makefile.in targets allstatic to static and allshared to shared
+- Fix static and shared Makefile.in targets to be independent
+- Correct error return bug in gz_open() by setting state [Brown]
+- Put spaces before ;;'s in configure for better sh compatibility
+- Add pigz.c (parallel implementation of gzip) to examples/
+- Correct constant in crc32.c to UL [Leventhal]
+- Reject negative lengths in crc32_combine()
+- Add inflateReset2() function to work like inflateEnd()/inflateInit2()
+- Include sys/types.h for _LARGEFILE64_SOURCE [Brown]
+- Correct typo in doc/algorithm.txt [Janik]
+- Fix bug in adler32_combine() [Zhu]
+- Catch missing-end-of-block-code error in all inflates and in puff
+ Assures that random input to inflate eventually results in an error
+- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/
+- Update ENOUGH and its usage to reflect discovered bounds
+- Fix gzerror() error report on empty input file [Brown]
+- Add ush casts in trees.c to avoid pedantic runtime errors
+- Fix typo in zlib.h uncompress() description [Reiss]
+- Correct inflate() comments with regard to automatic header detection
+- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays)
+- Put new version of gzlog (2.0) in examples with interruption recovery
+- Add puff compile option to permit invalid distance-too-far streams
+- Add puff TEST command options, ability to read piped input
+- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but
+ _LARGEFILE64_SOURCE not defined
+- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart
+- Fix deflateSetDictionary() to use all 32K for output consistency
+- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h)
+- Clear bytes after deflate lookahead to avoid use of uninitialized data
+- Change a limit in inftrees.c to be more transparent to Coverity Prevent
+- Update win32/zlib.def with exported symbols from zlib.h
+- Correct spelling errors in zlib.h [Willem, Sobrado]
+- Allow Z_BLOCK for deflate() to force a new block
+- Allow negative bits in inflatePrime() to delete existing bit buffer
+- Add Z_TREES flush option to inflate() to return at end of trees
+- Add inflateMark() to return current state information for random access
+- Add Makefile for NintendoDS to contrib [Costa]
+- Add -w in configure compile tests to avoid spurious warnings [Beucler]
+- Fix typos in zlib.h comments for deflateSetDictionary()
+- Fix EOF detection in transparent gzread() [Maier]
+
+Changes in 1.2.3.3 (2 October 2006)
+- Make --shared the default for configure, add a --static option
+- Add compile option to permit invalid distance-too-far streams
+- Add inflateUndermine() function which is required to enable above
+- Remove use of "this" variable name for C++ compatibility [Marquess]
+- Add testing of shared library in make test, if shared library built
+- Use ftello() and fseeko() if available instead of ftell() and fseek()
+- Provide two versions of all functions that use the z_off_t type for
+ binary compatibility -- a normal version and a 64-bit offset version,
+ per the Large File Support Extension when _LARGEFILE64_SOURCE is
+ defined; use the 64-bit versions by default when _FILE_OFFSET_BITS
+ is defined to be 64
+- Add a --uname= option to configure to perhaps help with cross-compiling
+
+Changes in 1.2.3.2 (3 September 2006)
+- Turn off silly Borland warnings [Hay]
+- Use off64_t and define _LARGEFILE64_SOURCE when present
+- Fix missing dependency on inffixed.h in Makefile.in
+- Rig configure --shared to build both shared and static [Teredesai, Truta]
+- Remove zconf.in.h and instead create a new zlibdefs.h file
+- Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant]
+- Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt]
+
+Changes in 1.2.3.1 (16 August 2006)
+- Add watcom directory with OpenWatcom make files [Daniel]
+- Remove #undef of FAR in zconf.in.h for MVS [Fedtke]
+- Update make_vms.com [Zinser]
+- Use -fPIC for shared build in configure [Teredesai, Nicholson]
+- Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen]
+- Use fdopen() (not _fdopen()) for Interix in zutil.h [BŠck]
+- Add some FAQ entries about the contrib directory
+- Update the MVS question in the FAQ
+- Avoid extraneous reads after EOF in gzio.c [Brown]
+- Correct spelling of "successfully" in gzio.c [Randers-Pehrson]
+- Add comments to zlib.h about gzerror() usage [Brown]
+- Set extra flags in gzip header in gzopen() like deflate() does
+- Make configure options more compatible with double-dash conventions
+ [Weigelt]
+- Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen]
+- Fix uninstall target in Makefile.in [Truta]
+- Add pkgconfig support [Weigelt]
+- Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt]
+- Replace set_data_type() with a more accurate detect_data_type() in
+ trees.c, according to the txtvsbin.txt document [Truta]
+- Swap the order of #include <stdio.h> and #include "zlib.h" in
+ gzio.c, example.c and minigzip.c [Truta]
+- Shut up annoying VS2005 warnings about standard C deprecation [Rowe,
+ Truta] (where?)
+- Fix target "clean" from win32/Makefile.bor [Truta]
+- Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe]
+- Update zlib www home address in win32/DLL_FAQ.txt [Truta]
+- Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove]
+- Enable browse info in the "Debug" and "ASM Debug" configurations in
+ the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta]
+- Add pkgconfig support [Weigelt]
+- Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h,
+ for use in win32/zlib1.rc [Polushin, Rowe, Truta]
+- Add a document that explains the new text detection scheme to
+ doc/txtvsbin.txt [Truta]
+- Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta]
+- Move algorithm.txt into doc/ [Truta]
+- Synchronize FAQ with website
+- Fix compressBound(), was low for some pathological cases [Fearnley]
+- Take into account wrapper variations in deflateBound()
+- Set examples/zpipe.c input and output to binary mode for Windows
+- Update examples/zlib_how.html with new zpipe.c (also web site)
+- Fix some warnings in examples/gzlog.c and examples/zran.c (it seems
+ that gcc became pickier in 4.0)
+- Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain
+ un-versioned, the patch adds versioning only for symbols introduced in
+ zlib-1.2.0 or later. It also declares as local those symbols which are
+ not designed to be exported." [Levin]
+- Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure
+- Do not initialize global static by default in trees.c, add a response
+ NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess]
+- Don't use strerror() in gzio.c under WinCE [Yakimov]
+- Don't use errno.h in zutil.h under WinCE [Yakimov]
+- Move arguments for AR to its usage to allow replacing ar [Marot]
+- Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson]
+- Improve inflateInit() and inflateInit2() documentation
+- Fix structure size comment in inflate.h
+- Change configure help option from --h* to --help [Santos]
+
+Changes in 1.2.3 (18 July 2005)
+- Apply security vulnerability fixes to contrib/infback9 as well
+- Clean up some text files (carriage returns, trailing space)
+- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant]
+
+Changes in 1.2.2.4 (11 July 2005)
+- Add inflatePrime() function for starting inflation at bit boundary
+- Avoid some Visual C warnings in deflate.c
+- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit
+ compile
+- Fix some spelling errors in comments [Betts]
+- Correct inflateInit2() error return documentation in zlib.h
+- Add zran.c example of compressed data random access to examples
+ directory, shows use of inflatePrime()
+- Fix cast for assignments to strm->state in inflate.c and infback.c
+- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer]
+- Move declarations of gf2 functions to right place in crc32.c [Oberhumer]
+- Add cast in trees.c t avoid a warning [Oberhumer]
+- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer]
+- Update make_vms.com [Zinser]
+- Initialize state->write in inflateReset() since copied in inflate_fast()
+- Be more strict on incomplete code sets in inflate_table() and increase
+ ENOUGH and MAXD -- this repairs a possible security vulnerability for
+ invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for
+ discovering the vulnerability and providing test cases.
+- Add ia64 support to configure for HP-UX [Smith]
+- Add error return to gzread() for format or i/o error [Levin]
+- Use malloc.h for OS/2 [Necasek]
+
+Changes in 1.2.2.3 (27 May 2005)
+- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile
+- Typecast fread() return values in gzio.c [Vollant]
+- Remove trailing space in minigzip.c outmode (VC++ can't deal with it)
+- Fix crc check bug in gzread() after gzungetc() [Heiner]
+- Add the deflateTune() function to adjust internal compression parameters
+- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack)
+- Remove an incorrect assertion in examples/zpipe.c
+- Add C++ wrapper in infback9.h [Donais]
+- Fix bug in inflateCopy() when decoding fixed codes
+- Note in zlib.h how much deflateSetDictionary() actually uses
+- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used)
+- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer]
+- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer]
+- Add gzdirect() function to indicate transparent reads
+- Update contrib/minizip [Vollant]
+- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer]
+- Add casts in crc32.c to avoid warnings [Oberhumer]
+- Add contrib/masmx64 [Vollant]
+- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant]
+
+Changes in 1.2.2.2 (30 December 2004)
+- Replace structure assignments in deflate.c and inflate.c with zmemcpy to
+ avoid implicit memcpy calls (portability for no-library compilation)
+- Increase sprintf() buffer size in gzdopen() to allow for large numbers
+- Add INFLATE_STRICT to check distances against zlib header
+- Improve WinCE errno handling and comments [Chang]
+- Remove comment about no gzip header processing in FAQ
+- Add Z_FIXED strategy option to deflateInit2() to force fixed trees
+- Add updated make_vms.com [Coghlan], update README
+- Create a new "examples" directory, move gzappend.c there, add zpipe.c,
+ fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html.
+- Add FAQ entry and comments in deflate.c on uninitialized memory access
+- Add Solaris 9 make options in configure [Gilbert]
+- Allow strerror() usage in gzio.c for STDC
+- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer]
+- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant]
+- Use z_off_t for adler32_combine() and crc32_combine() lengths
+- Make adler32() much faster for small len
+- Use OS_CODE in deflate() default gzip header
+
+Changes in 1.2.2.1 (31 October 2004)
+- Allow inflateSetDictionary() call for raw inflate
+- Fix inflate header crc check bug for file names and comments
+- Add deflateSetHeader() and gz_header structure for custom gzip headers
+- Add inflateGetheader() to retrieve gzip headers
+- Add crc32_combine() and adler32_combine() functions
+- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list
+- Use zstreamp consistently in zlib.h (inflate_back functions)
+- Remove GUNZIP condition from definition of inflate_mode in inflate.h
+ and in contrib/inflate86/inffast.S [Truta, Anderson]
+- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson]
+- Update projects/README.projects and projects/visualc6 [Truta]
+- Update win32/DLL_FAQ.txt [Truta]
+- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta]
+- Deprecate Z_ASCII; use Z_TEXT instead [Truta]
+- Use a new algorithm for setting strm->data_type in trees.c [Truta]
+- Do not define an exit() prototype in zutil.c unless DEBUG defined
+- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta]
+- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate()
+- Fix Darwin build version identification [Peterson]
+
+Changes in 1.2.2 (3 October 2004)
+- Update zlib.h comments on gzip in-memory processing
+- Set adler to 1 in inflateReset() to support Java test suite [Walles]
+- Add contrib/dotzlib [Ravn]
+- Update win32/DLL_FAQ.txt [Truta]
+- Update contrib/minizip [Vollant]
+- Move contrib/visual-basic.txt to old/ [Truta]
+- Fix assembler builds in projects/visualc6/ [Truta]
+
+Changes in 1.2.1.2 (9 September 2004)
+- Update INDEX file
+- Fix trees.c to update strm->data_type (no one ever noticed!)
+- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown]
+- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE)
+- Add limited multitasking protection to DYNAMIC_CRC_TABLE
+- Add NO_vsnprintf for VMS in zutil.h [Mozilla]
+- Don't declare strerror() under VMS [Mozilla]
+- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize
+- Update contrib/ada [Anisimkov]
+- Update contrib/minizip [Vollant]
+- Fix configure to not hardcode directories for Darwin [Peterson]
+- Fix gzio.c to not return error on empty files [Brown]
+- Fix indentation; update version in contrib/delphi/ZLib.pas and
+ contrib/pascal/zlibpas.pas [Truta]
+- Update mkasm.bat in contrib/masmx86 [Truta]
+- Update contrib/untgz [Truta]
+- Add projects/README.projects [Truta]
+- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta]
+- Update win32/DLL_FAQ.txt [Truta]
+- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta]
+- Remove an unnecessary assignment to curr in inftrees.c [Truta]
+- Add OS/2 to exe builds in configure [Poltorak]
+- Remove err dummy parameter in zlib.h [Kientzle]
+
+Changes in 1.2.1.1 (9 January 2004)
+- Update email address in README
+- Several FAQ updates
+- Fix a big fat bug in inftrees.c that prevented decoding valid
+ dynamic blocks with only literals and no distance codes --
+ Thanks to "Hot Emu" for the bug report and sample file
+- Add a note to puff.c on no distance codes case.
+
+Changes in 1.2.1 (17 November 2003)
+- Remove a tab in contrib/gzappend/gzappend.c
+- Update some interfaces in contrib for new zlib functions
+- Update zlib version number in some contrib entries
+- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta]
+- Support shared libraries on Hurd and KFreeBSD [Brown]
+- Fix error in NO_DIVIDE option of adler32.c
+
+Changes in 1.2.0.8 (4 November 2003)
+- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas
+- Add experimental NO_DIVIDE #define in adler32.c
+ - Possibly faster on some processors (let me know if it is)
+- Correct Z_BLOCK to not return on first inflate call if no wrap
+- Fix strm->data_type on inflate() return to correctly indicate EOB
+- Add deflatePrime() function for appending in the middle of a byte
+- Add contrib/gzappend for an example of appending to a stream
+- Update win32/DLL_FAQ.txt [Truta]
+- Delete Turbo C comment in README [Truta]
+- Improve some indentation in zconf.h [Truta]
+- Fix infinite loop on bad input in configure script [Church]
+- Fix gzeof() for concatenated gzip files [Johnson]
+- Add example to contrib/visual-basic.txt [Michael B.]
+- Add -p to mkdir's in Makefile.in [vda]
+- Fix configure to properly detect presence or lack of printf functions
+- Add AS400 support [Monnerat]
+- Add a little Cygwin support [Wilson]
+
+Changes in 1.2.0.7 (21 September 2003)
+- Correct some debug formats in contrib/infback9
+- Cast a type in a debug statement in trees.c
+- Change search and replace delimiter in configure from % to # [Beebe]
+- Update contrib/untgz to 0.2 with various fixes [Truta]
+- Add build support for Amiga [Nikl]
+- Remove some directories in old that have been updated to 1.2
+- Add dylib building for Mac OS X in configure and Makefile.in
+- Remove old distribution stuff from Makefile
+- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X
+- Update links in README
+
+Changes in 1.2.0.6 (13 September 2003)
+- Minor FAQ updates
+- Update contrib/minizip to 1.00 [Vollant]
+- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta]
+- Update POSTINC comment for 68060 [Nikl]
+- Add contrib/infback9 with deflate64 decoding (unsupported)
+- For MVS define NO_vsnprintf and undefine FAR [van Burik]
+- Add pragma for fdopen on MVS [van Burik]
+
+Changes in 1.2.0.5 (8 September 2003)
+- Add OF to inflateBackEnd() declaration in zlib.h
+- Remember start when using gzdopen in the middle of a file
+- Use internal off_t counters in gz* functions to properly handle seeks
+- Perform more rigorous check for distance-too-far in inffast.c
+- Add Z_BLOCK flush option to return from inflate at block boundary
+- Set strm->data_type on return from inflate
+ - Indicate bits unused, if at block boundary, and if in last block
+- Replace size_t with ptrdiff_t in crc32.c, and check for correct size
+- Add condition so old NO_DEFLATE define still works for compatibility
+- FAQ update regarding the Windows DLL [Truta]
+- INDEX update: add qnx entry, remove aix entry [Truta]
+- Install zlib.3 into mandir [Wilson]
+- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta]
+- Adapt the zlib interface to the new DLL convention guidelines [Truta]
+- Introduce ZLIB_WINAPI macro to allow the export of functions using
+ the WINAPI calling convention, for Visual Basic [Vollant, Truta]
+- Update msdos and win32 scripts and makefiles [Truta]
+- Export symbols by name, not by ordinal, in win32/zlib.def [Truta]
+- Add contrib/ada [Anisimkov]
+- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta]
+- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant]
+- Add contrib/masm686 [Truta]
+- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm
+ [Truta, Vollant]
+- Update contrib/delphi; rename to contrib/pascal; add example [Truta]
+- Remove contrib/delphi2; add a new contrib/delphi [Truta]
+- Avoid inclusion of the nonstandard <memory.h> in contrib/iostream,
+ and fix some method prototypes [Truta]
+- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip
+ [Truta]
+- Avoid the use of backslash (\) in contrib/minizip [Vollant]
+- Fix file time handling in contrib/untgz; update makefiles [Truta]
+- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines
+ [Vollant]
+- Remove contrib/vstudio/vc15_16 [Vollant]
+- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta]
+- Update README.contrib [Truta]
+- Invert the assignment order of match_head and s->prev[...] in
+ INSERT_STRING [Truta]
+- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings
+ [Truta]
+- Compare function pointers with 0, not with NULL or Z_NULL [Truta]
+- Fix prototype of syncsearch in inflate.c [Truta]
+- Introduce ASMINF macro to be enabled when using an ASM implementation
+ of inflate_fast [Truta]
+- Change NO_DEFLATE to NO_GZCOMPRESS [Truta]
+- Modify test_gzio in example.c to take a single file name as a
+ parameter [Truta]
+- Exit the example.c program if gzopen fails [Truta]
+- Add type casts around strlen in example.c [Truta]
+- Remove casting to sizeof in minigzip.c; give a proper type
+ to the variable compared with SUFFIX_LEN [Truta]
+- Update definitions of STDC and STDC99 in zconf.h [Truta]
+- Synchronize zconf.h with the new Windows DLL interface [Truta]
+- Use SYS16BIT instead of __32BIT__ to distinguish between
+ 16- and 32-bit platforms [Truta]
+- Use far memory allocators in small 16-bit memory models for
+ Turbo C [Truta]
+- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in
+ zlibCompileFlags [Truta]
+- Cygwin has vsnprintf [Wilson]
+- In Windows16, OS_CODE is 0, as in MSDOS [Truta]
+- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson]
+
+Changes in 1.2.0.4 (10 August 2003)
+- Minor FAQ updates
+- Be more strict when checking inflateInit2's windowBits parameter
+- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well
+- Add gzip wrapper option to deflateInit2 using windowBits
+- Add updated QNX rule in configure and qnx directory [Bonnefoy]
+- Make inflate distance-too-far checks more rigorous
+- Clean up FAR usage in inflate
+- Add casting to sizeof() in gzio.c and minigzip.c
+
+Changes in 1.2.0.3 (19 July 2003)
+- Fix silly error in gzungetc() implementation [Vollant]
+- Update contrib/minizip and contrib/vstudio [Vollant]
+- Fix printf format in example.c
+- Correct cdecl support in zconf.in.h [Anisimkov]
+- Minor FAQ updates
+
+Changes in 1.2.0.2 (13 July 2003)
+- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons
+- Attempt to avoid warnings in crc32.c for pointer-int conversion
+- Add AIX to configure, remove aix directory [Bakker]
+- Add some casts to minigzip.c
+- Improve checking after insecure sprintf() or vsprintf() calls
+- Remove #elif's from crc32.c
+- Change leave label to inf_leave in inflate.c and infback.c to avoid
+ library conflicts
+- Remove inflate gzip decoding by default--only enable gzip decoding by
+ special request for stricter backward compatibility
+- Add zlibCompileFlags() function to return compilation information
+- More typecasting in deflate.c to avoid warnings
+- Remove leading underscore from _Capital #defines [Truta]
+- Fix configure to link shared library when testing
+- Add some Windows CE target adjustments [Mai]
+- Remove #define ZLIB_DLL in zconf.h [Vollant]
+- Add zlib.3 [Rodgers]
+- Update RFC URL in deflate.c and algorithm.txt [Mai]
+- Add zlib_dll_FAQ.txt to contrib [Truta]
+- Add UL to some constants [Truta]
+- Update minizip and vstudio [Vollant]
+- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h
+- Expand use of NO_DUMMY_DECL to avoid all dummy structures
+- Added iostream3 to contrib [Schwardt]
+- Replace rewind() with fseek() for WinCE [Truta]
+- Improve setting of zlib format compression level flags
+ - Report 0 for huffman and rle strategies and for level == 0 or 1
+ - Report 2 only for level == 6
+- Only deal with 64K limit when necessary at compile time [Truta]
+- Allow TOO_FAR check to be turned off at compile time [Truta]
+- Add gzclearerr() function [Souza]
+- Add gzungetc() function
+
+Changes in 1.2.0.1 (17 March 2003)
+- Add Z_RLE strategy for run-length encoding [Truta]
+ - When Z_RLE requested, restrict matches to distance one
+ - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE
+- Correct FASTEST compilation to allow level == 0
+- Clean up what gets compiled for FASTEST
+- Incorporate changes to zconf.in.h [Vollant]
+ - Refine detection of Turbo C need for dummy returns
+ - Refine ZLIB_DLL compilation
+ - Include additional header file on VMS for off_t typedef
+- Try to use _vsnprintf where it supplants vsprintf [Vollant]
+- Add some casts in inffast.c
+- Enchance comments in zlib.h on what happens if gzprintf() tries to
+ write more than 4095 bytes before compression
+- Remove unused state from inflateBackEnd()
+- Remove exit(0) from minigzip.c, example.c
+- Get rid of all those darn tabs
+- Add "check" target to Makefile.in that does the same thing as "test"
+- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in
+- Update contrib/inflate86 [Anderson]
+- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant]
+- Add msdos and win32 directories with makefiles [Truta]
+- More additions and improvements to the FAQ
+
+Changes in 1.2.0 (9 March 2003)
+- New and improved inflate code
+ - About 20% faster
+ - Does not allocate 32K window unless and until needed
+ - Automatically detects and decompresses gzip streams
+ - Raw inflate no longer needs an extra dummy byte at end
+ - Added inflateBack functions using a callback interface--even faster
+ than inflate, useful for file utilities (gzip, zip)
+ - Added inflateCopy() function to record state for random access on
+ externally generated deflate streams (e.g. in gzip files)
+ - More readable code (I hope)
+- New and improved crc32()
+ - About 50% faster, thanks to suggestions from Rodney Brown
+- Add deflateBound() and compressBound() functions
+- Fix memory leak in deflateInit2()
+- Permit setting dictionary for raw deflate (for parallel deflate)
+- Fix const declaration for gzwrite()
+- Check for some malloc() failures in gzio.c
+- Fix bug in gzopen() on single-byte file 0x1f
+- Fix bug in gzread() on concatenated file with 0x1f at end of buffer
+ and next buffer doesn't start with 0x8b
+- Fix uncompress() to return Z_DATA_ERROR on truncated input
+- Free memory at end of example.c
+- Remove MAX #define in trees.c (conflicted with some libraries)
+- Fix static const's in deflate.c, gzio.c, and zutil.[ch]
+- Declare malloc() and free() in gzio.c if STDC not defined
+- Use malloc() instead of calloc() in zutil.c if int big enough
+- Define STDC for AIX
+- Add aix/ with approach for compiling shared library on AIX
+- Add HP-UX support for shared libraries in configure
+- Add OpenUNIX support for shared libraries in configure
+- Use $cc instead of gcc to build shared library
+- Make prefix directory if needed when installing
+- Correct Macintosh avoidance of typedef Byte in zconf.h
+- Correct Turbo C memory allocation when under Linux
+- Use libz.a instead of -lz in Makefile (assure use of compiled library)
+- Update configure to check for snprintf or vsnprintf functions and their
+ return value, warn during make if using an insecure function
+- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that
+ is lost when library is used--resolution is to build new zconf.h
+- Documentation improvements (in zlib.h):
+ - Document raw deflate and inflate
+ - Update RFCs URL
+ - Point out that zlib and gzip formats are different
+ - Note that Z_BUF_ERROR is not fatal
+ - Document string limit for gzprintf() and possible buffer overflow
+ - Note requirement on avail_out when flushing
+ - Note permitted values of flush parameter of inflate()
+- Add some FAQs (and even answers) to the FAQ
+- Add contrib/inflate86/ for x86 faster inflate
+- Add contrib/blast/ for PKWare Data Compression Library decompression
+- Add contrib/puff/ simple inflate for deflate format description
+
+Changes in 1.1.4 (11 March 2002)
+- ZFREE was repeated on same allocation on some error conditions.
+ This creates a security problem described in
+ http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+ less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+ of 256 bytes. (A complete fix will be available in 1.1.5).
+
+Changes in 1.1.3 (9 July 1998)
+- fix "an inflate input buffer bug that shows up on rare but persistent
+ occasions" (Mark)
+- fix gzread and gztell for concatenated .gz files (Didier Le Botlan)
+- fix gzseek(..., SEEK_SET) in write mode
+- fix crc check after a gzeek (Frank Faubert)
+- fix miniunzip when the last entry in a zip file is itself a zip file
+ (J Lillge)
+- add contrib/asm586 and contrib/asm686 (Brian Raiter)
+ See http://www.muppetlabs.com/~breadbox/software/assembly.html
+- add support for Delphi 3 in contrib/delphi (Bob Dellaca)
+- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti)
+- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren)
+- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks)
+- added a FAQ file
+
+- Support gzdopen on Mac with Metrowerks (Jason Linhart)
+- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart)
+- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young)
+- avoid some warnings with Borland C (Tom Tanner)
+- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant)
+- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant)
+- allow several arguments to configure (Tim Mooney, Frodo Looijaard)
+- use libdir and includedir in Makefile.in (Tim Mooney)
+- support shared libraries on OSF1 V4 (Tim Mooney)
+- remove so_locations in "make clean" (Tim Mooney)
+- fix maketree.c compilation error (Glenn, Mark)
+- Python interface to zlib now in Python 1.5 (Jeremy Hylton)
+- new Makefile.riscos (Rich Walker)
+- initialize static descriptors in trees.c for embedded targets (Nick Smith)
+- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith)
+- add the OS/2 files in Makefile.in too (Andrew Zabolotny)
+- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane)
+- fix maketree.c to allow clean compilation of inffixed.h (Mark)
+- fix parameter check in deflateCopy (Gunther Nikl)
+- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler)
+- Many portability patches by Christian Spieler:
+ . zutil.c, zutil.h: added "const" for zmem*
+ . Make_vms.com: fixed some typos
+ . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists
+ . msdos/Makefile.msc: remove "default rtl link library" info from obj files
+ . msdos/Makefile.*: use model-dependent name for the built zlib library
+ . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc:
+ new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT)
+- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane)
+- replace __far with _far for better portability (Christian Spieler, Tom Lane)
+- fix test for errno.h in configure (Tim Newsham)
+
+Changes in 1.1.2 (19 March 98)
+- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant)
+ See http://www.winimage.com/zLibDll/unzip.html
+- preinitialize the inflate tables for fixed codes, to make the code
+ completely thread safe (Mark)
+- some simplifications and slight speed-up to the inflate code (Mark)
+- fix gzeof on non-compressed files (Allan Schrum)
+- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs)
+- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn)
+- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny)
+- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori)
+- do not wrap extern "C" around system includes (Tom Lane)
+- mention zlib binding for TCL in README (Andreas Kupries)
+- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert)
+- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson)
+- allow "configure --prefix $HOME" (Tim Mooney)
+- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson)
+- move Makefile.sas to amiga/Makefile.sas
+
+Changes in 1.1.1 (27 Feb 98)
+- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson)
+- remove block truncation heuristic which had very marginal effect for zlib
+ (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the
+ compression ratio on some files. This also allows inlining _tr_tally for
+ matches in deflate_slow.
+- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier)
+
+Changes in 1.1.0 (24 Feb 98)
+- do not return STREAM_END prematurely in inflate (John Bowler)
+- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler)
+- compile with -DFASTEST to get compression code optimized for speed only
+- in minigzip, try mmap'ing the input file first (Miguel Albrecht)
+- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain
+ on Sun but significant on HP)
+
+- add a pointer to experimental unzip library in README (Gilles Vollant)
+- initialize variable gcc in configure (Chris Herborth)
+
+Changes in 1.0.9 (17 Feb 1998)
+- added gzputs and gzgets functions
+- do not clear eof flag in gzseek (Mark Diekhans)
+- fix gzseek for files in transparent mode (Mark Diekhans)
+- do not assume that vsprintf returns the number of bytes written (Jens Krinke)
+- replace EXPORT with ZEXPORT to avoid conflict with other programs
+- added compress2 in zconf.h, zlib.def, zlib.dnt
+- new asm code from Gilles Vollant in contrib/asm386
+- simplify the inflate code (Mark):
+ . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new()
+ . ZALLOC the length list in inflate_trees_fixed() instead of using stack
+ . ZALLOC the value area for huft_build() instead of using stack
+ . Simplify Z_FINISH check in inflate()
+
+- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8
+- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi)
+- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with
+ the declaration of FAR (Gilles VOllant)
+- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann)
+- read_buf buf parameter of type Bytef* instead of charf*
+- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout)
+- do not redeclare unlink in minigzip.c for WIN32 (John Bowler)
+- fix check for presence of directories in "make install" (Ian Willis)
+
+Changes in 1.0.8 (27 Jan 1998)
+- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant)
+- fix gzgetc and gzputc for big endian systems (Markus Oberhumer)
+- added compress2() to allow setting the compression level
+- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong)
+- use constant arrays for the static trees in trees.c instead of computing
+ them at run time (thanks to Ken Raeburn for this suggestion). To create
+ trees.h, compile with GEN_TREES_H and run "make test".
+- check return code of example in "make test" and display result
+- pass minigzip command line options to file_compress
+- simplifying code of inflateSync to avoid gcc 2.8 bug
+
+- support CC="gcc -Wall" in configure -s (QingLong)
+- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn)
+- fix test for shared library support to avoid compiler warnings
+- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant)
+- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit)
+- do not use fdopen for Metrowerks on Mac (Brad Pettit))
+- add checks for gzputc and gzputc in example.c
+- avoid warnings in gzio.c and deflate.c (Andreas Kleinert)
+- use const for the CRC table (Ken Raeburn)
+- fixed "make uninstall" for shared libraries
+- use Tracev instead of Trace in infblock.c
+- in example.c use correct compressed length for test_sync
+- suppress +vnocompatwarnings in configure for HPUX (not always supported)
+
+Changes in 1.0.7 (20 Jan 1998)
+- fix gzseek which was broken in write mode
+- return error for gzseek to negative absolute position
+- fix configure for Linux (Chun-Chung Chen)
+- increase stack space for MSC (Tim Wegner)
+- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant)
+- define EXPORTVA for gzprintf (Gilles Vollant)
+- added man page zlib.3 (Rick Rodgers)
+- for contrib/untgz, fix makedir() and improve Makefile
+
+- check gzseek in write mode in example.c
+- allocate extra buffer for seeks only if gzseek is actually called
+- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant)
+- add inflateSyncPoint in zconf.h
+- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def
+
+Changes in 1.0.6 (19 Jan 1998)
+- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and
+ gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code)
+- Fix a deflate bug occurring only with compression level 0 (thanks to
+ Andy Buckler for finding this one).
+- In minigzip, pass transparently also the first byte for .Z files.
+- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress()
+- check Z_FINISH in inflate (thanks to Marc Schluper)
+- Implement deflateCopy (thanks to Adam Costello)
+- make static libraries by default in configure, add --shared option.
+- move MSDOS or Windows specific files to directory msdos
+- suppress the notion of partial flush to simplify the interface
+ (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4)
+- suppress history buffer provided by application to simplify the interface
+ (this feature was not implemented anyway in 1.0.4)
+- next_in and avail_in must be initialized before calling inflateInit or
+ inflateInit2
+- add EXPORT in all exported functions (for Windows DLL)
+- added Makefile.nt (thanks to Stephen Williams)
+- added the unsupported "contrib" directory:
+ contrib/asm386/ by Gilles Vollant <info@winimage.com>
+ 386 asm code replacing longest_match().
+ contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+ A C++ I/O streams interface to the zlib gz* functions
+ contrib/iostream2/ by Tyge Løvset <Tyge.Lovset@cmr.no>
+ Another C++ I/O streams interface
+ contrib/untgz/ by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+ A very simple tar.gz file extractor using zlib
+ contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+ How to use compress(), uncompress() and the gz* functions from VB.
+- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression
+ level) in minigzip (thanks to Tom Lane)
+
+- use const for rommable constants in deflate
+- added test for gzseek and gztell in example.c
+- add undocumented function inflateSyncPoint() (hack for Paul Mackerras)
+- add undocumented function zError to convert error code to string
+ (for Tim Smithers)
+- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code.
+- Use default memcpy for Symantec MSDOS compiler.
+- Add EXPORT keyword for check_func (needed for Windows DLL)
+- add current directory to LD_LIBRARY_PATH for "make test"
+- create also a link for libz.so.1
+- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura)
+- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX)
+- added -soname for Linux in configure (Chun-Chung Chen,
+- assign numbers to the exported functions in zlib.def (for Windows DLL)
+- add advice in zlib.h for best usage of deflateSetDictionary
+- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn)
+- allow compilation with ANSI keywords only enabled for TurboC in large model
+- avoid "versionString"[0] (Borland bug)
+- add NEED_DUMMY_RETURN for Borland
+- use variable z_verbose for tracing in debug mode (L. Peter Deutsch).
+- allow compilation with CC
+- defined STDC for OS/2 (David Charlap)
+- limit external names to 8 chars for MVS (Thomas Lund)
+- in minigzip.c, use static buffers only for 16-bit systems
+- fix suffix check for "minigzip -d foo.gz"
+- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee)
+- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau)
+- added makelcc.bat for lcc-win32 (Tom St Denis)
+- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe)
+- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion.
+- check for unistd.h in configure (for off_t)
+- remove useless check parameter in inflate_blocks_free
+- avoid useless assignment of s->check to itself in inflate_blocks_new
+- do not flush twice in gzclose (thanks to Ken Raeburn)
+- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h
+- use NO_ERRNO_H instead of enumeration of operating systems with errno.h
+- work around buggy fclose on pipes for HP/UX
+- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson)
+- fix configure if CC is already equal to gcc
+
+Changes in 1.0.5 (3 Jan 98)
+- Fix inflate to terminate gracefully when fed corrupted or invalid data
+- Use const for rommable constants in inflate
+- Eliminate memory leaks on error conditions in inflate
+- Removed some vestigial code in inflate
+- Update web address in README
+
+Changes in 1.0.4 (24 Jul 96)
+- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF
+ bit, so the decompressor could decompress all the correct data but went
+ on to attempt decompressing extra garbage data. This affected minigzip too.
+- zlibVersion and gzerror return const char* (needed for DLL)
+- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno)
+- use z_error only for DEBUG (avoid problem with DLLs)
+
+Changes in 1.0.3 (2 Jul 96)
+- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS
+ small and medium models; this makes the library incompatible with previous
+ versions for these models. (No effect in large model or on other systems.)
+- return OK instead of BUF_ERROR if previous deflate call returned with
+ avail_out as zero but there is nothing to do
+- added memcmp for non STDC compilers
+- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly)
+- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO)
+- better check for 16-bit mode MSC (avoids problem with Symantec)
+
+Changes in 1.0.2 (23 May 96)
+- added Windows DLL support
+- added a function zlibVersion (for the DLL support)
+- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model)
+- Bytef is define's instead of typedef'd only for Borland C
+- avoid reading uninitialized memory in example.c
+- mention in README that the zlib format is now RFC1950
+- updated Makefile.dj2
+- added algorithm.doc
+
+Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion]
+- fix array overlay in deflate.c which sometimes caused bad compressed data
+- fix inflate bug with empty stored block
+- fix MSDOS medium model which was broken in 0.99
+- fix deflateParams() which could generated bad compressed data.
+- Bytef is define'd instead of typedef'ed (work around Borland bug)
+- added an INDEX file
+- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32),
+ Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas)
+- speed up adler32 for modern machines without auto-increment
+- added -ansi for IRIX in configure
+- static_init_done in trees.c is an int
+- define unlink as delete for VMS
+- fix configure for QNX
+- add configure branch for SCO and HPUX
+- avoid many warnings (unused variables, dead assignments, etc...)
+- no fdopen for BeOS
+- fix the Watcom fix for 32 bit mode (define FAR as empty)
+- removed redefinition of Byte for MKWERKS
+- work around an MWKERKS bug (incorrect merge of all .h files)
+
+Changes in 0.99 (27 Jan 96)
+- allow preset dictionary shared between compressor and decompressor
+- allow compression level 0 (no compression)
+- add deflateParams in zlib.h: allow dynamic change of compression level
+ and compression strategy.
+- test large buffers and deflateParams in example.c
+- add optional "configure" to build zlib as a shared library
+- suppress Makefile.qnx, use configure instead
+- fixed deflate for 64-bit systems (detected on Cray)
+- fixed inflate_blocks for 64-bit systems (detected on Alpha)
+- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2)
+- always return Z_BUF_ERROR when deflate() has nothing to do
+- deflateInit and inflateInit are now macros to allow version checking
+- prefix all global functions and types with z_ with -DZ_PREFIX
+- make falloc completely reentrant (inftrees.c)
+- fixed very unlikely race condition in ct_static_init
+- free in reverse order of allocation to help memory manager
+- use zlib-1.0/* instead of zlib/* inside the tar.gz
+- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith
+ -Wconversion -Wstrict-prototypes -Wmissing-prototypes"
+- allow gzread on concatenated .gz files
+- deflateEnd now returns Z_DATA_ERROR if it was premature
+- deflate is finally (?) fully deterministic (no matches beyond end of input)
+- Document Z_SYNC_FLUSH
+- add uninstall in Makefile
+- Check for __cpluplus in zlib.h
+- Better test in ct_align for partial flush
+- avoid harmless warnings for Borland C++
+- initialize hash_head in deflate.c
+- avoid warning on fdopen (gzio.c) for HP cc -Aa
+- include stdlib.h for STDC compilers
+- include errno.h for Cray
+- ignore error if ranlib doesn't exist
+- call ranlib twice for NeXTSTEP
+- use exec_prefix instead of prefix for libz.a
+- renamed ct_* as _tr_* to avoid conflict with applications
+- clear z->msg in inflateInit2 before any error return
+- initialize opaque in example.c, gzio.c, deflate.c and inflate.c
+- fixed typo in zconf.h (_GNUC__ => __GNUC__)
+- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode)
+- fix typo in Make_vms.com (f$trnlnm -> f$getsyi)
+- in fcalloc, normalize pointer if size > 65520 bytes
+- don't use special fcalloc for 32 bit Borland C++
+- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc...
+- use Z_BINARY instead of BINARY
+- document that gzclose after gzdopen will close the file
+- allow "a" as mode in gzopen.
+- fix error checking in gzread
+- allow skipping .gz extra-field on pipes
+- added reference to Perl interface in README
+- put the crc table in FAR data (I dislike more and more the medium model :)
+- added get_crc_table
+- added a dimension to all arrays (Borland C can't count).
+- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast
+- guard against multiple inclusion of *.h (for precompiled header on Mac)
+- Watcom C pretends to be Microsoft C small model even in 32 bit mode.
+- don't use unsized arrays to avoid silly warnings by Visual C++:
+ warning C4746: 'inflate_mask' : unsized array treated as '__far'
+ (what's wrong with far data in far model?).
+- define enum out of inflate_blocks_state to allow compilation with C++
+
+Changes in 0.95 (16 Aug 95)
+- fix MSDOS small and medium model (now easier to adapt to any compiler)
+- inlined send_bits
+- fix the final (:-) bug for deflate with flush (output was correct but
+ not completely flushed in rare occasions).
+- default window size is same for compression and decompression
+ (it's now sufficient to set MAX_WBITS in zconf.h).
+- voidp -> voidpf and voidnp -> voidp (for consistency with other
+ typedefs and because voidnp was not near in large model).
+
+Changes in 0.94 (13 Aug 95)
+- support MSDOS medium model
+- fix deflate with flush (could sometimes generate bad output)
+- fix deflateReset (zlib header was incorrectly suppressed)
+- added support for VMS
+- allow a compression level in gzopen()
+- gzflush now calls fflush
+- For deflate with flush, flush even if no more input is provided.
+- rename libgz.a as libz.a
+- avoid complex expression in infcodes.c triggering Turbo C bug
+- work around a problem with gcc on Alpha (in INSERT_STRING)
+- don't use inline functions (problem with some gcc versions)
+- allow renaming of Byte, uInt, etc... with #define.
+- avoid warning about (unused) pointer before start of array in deflate.c
+- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c
+- avoid reserved word 'new' in trees.c
+
+Changes in 0.93 (25 June 95)
+- temporarily disable inline functions
+- make deflate deterministic
+- give enough lookahead for PARTIAL_FLUSH
+- Set binary mode for stdin/stdout in minigzip.c for OS/2
+- don't even use signed char in inflate (not portable enough)
+- fix inflate memory leak for segmented architectures
+
+Changes in 0.92 (3 May 95)
+- don't assume that char is signed (problem on SGI)
+- Clear bit buffer when starting a stored block
+- no memcpy on Pyramid
+- suppressed inftest.c
+- optimized fill_window, put longest_match inline for gcc
+- optimized inflate on stored blocks.
+- untabify all sources to simplify patches
+
+Changes in 0.91 (2 May 95)
+- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h
+- Document the memory requirements in zconf.h
+- added "make install"
+- fix sync search logic in inflateSync
+- deflate(Z_FULL_FLUSH) now works even if output buffer too short
+- after inflateSync, don't scare people with just "lo world"
+- added support for DJGPP
+
+Changes in 0.9 (1 May 95)
+- don't assume that zalloc clears the allocated memory (the TurboC bug
+ was Mark's bug after all :)
+- let again gzread copy uncompressed data unchanged (was working in 0.71)
+- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented
+- added a test of inflateSync in example.c
+- moved MAX_WBITS to zconf.h because users might want to change that.
+- document explicitly that zalloc(64K) on MSDOS must return a normalized
+ pointer (zero offset)
+- added Makefiles for Microsoft C, Turbo C, Borland C++
+- faster crc32()
+
+Changes in 0.8 (29 April 95)
+- added fast inflate (inffast.c)
+- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this
+ is incompatible with previous versions of zlib which returned Z_OK.
+- work around a TurboC compiler bug (bad code for b << 0, see infutil.h)
+ (actually that was not a compiler bug, see 0.81 above)
+- gzread no longer reads one extra byte in certain cases
+- In gzio destroy(), don't reference a freed structure
+- avoid many warnings for MSDOS
+- avoid the ERROR symbol which is used by MS Windows
+
+Changes in 0.71 (14 April 95)
+- Fixed more MSDOS compilation problems :( There is still a bug with
+ TurboC large model.
+
+Changes in 0.7 (14 April 95)
+- Added full inflate support.
+- Simplified the crc32() interface. The pre- and post-conditioning
+ (one's complement) is now done inside crc32(). WARNING: this is
+ incompatible with previous versions; see zlib.h for the new usage.
+
+Changes in 0.61 (12 April 95)
+- workaround for a bug in TurboC. example and minigzip now work on MSDOS.
+
+Changes in 0.6 (11 April 95)
+- added minigzip.c
+- added gzdopen to reopen a file descriptor as gzFile
+- added transparent reading of non-gziped files in gzread.
+- fixed bug in gzread (don't read crc as data)
+- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose).
+- don't allocate big arrays in the stack (for MSDOS)
+- fix some MSDOS compilation problems
+
+Changes in 0.5:
+- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but
+ not yet Z_FULL_FLUSH.
+- support decompression but only in a single step (forced Z_FINISH)
+- added opaque object for zalloc and zfree.
+- added deflateReset and inflateReset
+- added a variable zlib_version for consistency checking.
+- renamed the 'filter' parameter of deflateInit2 as 'strategy'.
+ Added Z_FILTERED and Z_HUFFMAN_ONLY constants.
+
+Changes in 0.4:
+- avoid "zip" everywhere, use zlib instead of ziplib.
+- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
+ if compression method == 8.
+- added adler32 and crc32
+- renamed deflateOptions as deflateInit2, call one or the other but not both
+- added the method parameter for deflateInit2.
+- added inflateInit2
+- simplied considerably deflateInit and inflateInit by not supporting
+ user-provided history buffer. This is supported only in deflateInit2
+ and inflateInit2.
+
+Changes in 0.3:
+- prefix all macro names with Z_
+- use Z_FINISH instead of deflateEnd to finish compression.
+- added Z_HUFFMAN_ONLY
+- added gzerror()
diff --git a/zlib/README b/zlib/README
new file mode 100644
index 0000000..5ca9d12
--- /dev/null
+++ b/zlib/README
@@ -0,0 +1,115 @@
+ZLIB DATA COMPRESSION LIBRARY
+
+zlib 1.2.8 is a general purpose data compression library. All the code is
+thread safe. The data format used by the zlib library is described by RFCs
+(Request for Comments) 1950 to 1952 in the files
+http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and
+rfc1952 (gzip format).
+
+All functions of the compression library are documented in the file zlib.h
+(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example
+of the library is given in the file test/example.c which also tests that
+the library is working correctly. Another example is given in the file
+test/minigzip.c. The compression library itself is composed of all source
+files in the root directory.
+
+To compile all files and run the test program, follow the instructions given at
+the top of Makefile.in. In short "./configure; make test", and if that goes
+well, "make install" should work for most flavors of Unix. For Windows, use
+one of the special makefiles in win32/ or contrib/vstudio/ . For VMS, use
+make_vms.com.
+
+Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant
+<info@winimage.com> for the Windows DLL version. The zlib home page is
+http://zlib.net/ . Before reporting a problem, please check this site to
+verify that you have the latest version of zlib; otherwise get the latest
+version and check whether the problem still exists or not.
+
+PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help.
+
+Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
+issue of Dr. Dobb's Journal; a copy of the article is available at
+http://marknelson.us/1997/01/01/zlib-engine/ .
+
+The changes made in version 1.2.8 are documented in the file ChangeLog.
+
+Unsupported third party contributions are provided in directory contrib/ .
+
+zlib is available in Java using the java.util.zip package, documented at
+http://java.sun.com/developer/technicalArticles/Programming/compression/ .
+
+A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is available
+at CPAN (Comprehensive Perl Archive Network) sites, including
+http://search.cpan.org/~pmqs/IO-Compress-Zlib/ .
+
+A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is
+available in Python 1.5 and later versions, see
+http://docs.python.org/library/zlib.html .
+
+zlib is built into tcl: http://wiki.tcl.tk/4610 .
+
+An experimental package to read and write files in .zip format, written on top
+of zlib by Gilles Vollant <info@winimage.com>, is available in the
+contrib/minizip directory of zlib.
+
+
+Notes for some targets:
+
+- For Windows DLL versions, please see win32/DLL_FAQ.txt
+
+- For 64-bit Irix, deflate.c must be compiled without any optimization. With
+ -O, one libpng test fails. The test works in 32 bit mode (with the -n32
+ compiler flag). The compiler bug has been reported to SGI.
+
+- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works
+ when compiled with cc.
+
+- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is
+ necessary to get gzprintf working correctly. This is done by configure.
+
+- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with
+ other compilers. Use "make test" to check your compiler.
+
+- gzdopen is not supported on RISCOS or BEOS.
+
+- For PalmOs, see http://palmzlib.sourceforge.net/
+
+
+Acknowledgments:
+
+ The deflate format used by zlib was defined by Phil Katz. The deflate and
+ zlib specifications were written by L. Peter Deutsch. Thanks to all the
+ people who reported problems and suggested various improvements in zlib; they
+ are too numerous to cite here.
+
+Copyright notice:
+
+ (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+If you use the zlib library in a product, we would appreciate *not* receiving
+lengthy legal documents to sign. The sources are provided for free but without
+warranty of any kind. The library has been entirely written by Jean-loup
+Gailly and Mark Adler; it does not include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include in
+the file ChangeLog history information documenting your changes. Please read
+the FAQ for more information on the distribution of modified source versions.
diff --git a/zlib/README.rsync b/zlib/README.rsync
new file mode 100644
index 0000000..5440bfd
--- /dev/null
+++ b/zlib/README.rsync
@@ -0,0 +1,31 @@
+READ THIS BEFORE TRYING TO DYNAMICALLY LINK RSYNC AND ZLIB!
+
+zlib has been adapted slightly for use in rsync. Please don't bother
+the zlib authors with problems related to the use of zlib in rsync as
+any bugs are likely to be our fault and not theirs.
+
+Specific changes that have been made to zlib for rsync include:
+
+- add Z_INSERT_ONLY to allow for efficient history updating without
+ actually emitting any data. This is used to compress the matched
+ blocks that don't cross the wire, which gives better compression
+ ratios on the literal data.
+
+- fixed a number of minor compilation issues. (redefinition of MAX and
+ other such trivial things)
+
+- include rsync.h to ensure that we get a consistent set of includes
+ for all C code in rsync and to take advantage of autoconf
+
+As a result of the first item, the streams from rsync's version of
+zlib are *not compatible* with those produced by the upstream version
+of rsync. In other words, if you link rsync against your system's
+copy, it will not be able to interoperate with any other version if
+the -z option is used. (Sorry. Sometimes standard is better than
+better.)
+
+The rsync maintainers hope to fix this problem in the future by either
+merging our changes into the upstream version, or backing them out of
+rsync in a way that preserves wire compatibility. But in the meantime
+this version must be maintained in parallel.
+
diff --git a/zlib/adler32.c b/zlib/adler32.c
new file mode 100644
index 0000000..a868f07
--- /dev/null
+++ b/zlib/adler32.c
@@ -0,0 +1,179 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2011 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+
+#define local static
+
+local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
+
+#define BASE 65521 /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* use NO_DIVIDE if your processor does not do division in hardware --
+ try it both ways to see which is faster */
+#ifdef NO_DIVIDE
+/* note that this assumes BASE is 65521, where 65536 % 65521 == 15
+ (thank you to John Reiser for pointing this out) */
+# define CHOP(a) \
+ do { \
+ unsigned long tmp = a >> 16; \
+ a &= 0xffffUL; \
+ a += (tmp << 4) - tmp; \
+ } while (0)
+# define MOD28(a) \
+ do { \
+ CHOP(a); \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+# define MOD(a) \
+ do { \
+ CHOP(a); \
+ MOD28(a); \
+ } while (0)
+# define MOD63(a) \
+ do { /* this assumes a is not negative */ \
+ z_off64_t tmp = a >> 32; \
+ a &= 0xffffffffL; \
+ a += (tmp << 8) - (tmp << 5) + tmp; \
+ tmp = a >> 16; \
+ a &= 0xffffL; \
+ a += (tmp << 4) - tmp; \
+ tmp = a >> 16; \
+ a &= 0xffffL; \
+ a += (tmp << 4) - tmp; \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+#else
+# define MOD(a) a %= BASE
+# define MOD28(a) a %= BASE
+# define MOD63(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long sum2;
+ unsigned n;
+
+ /* split Adler-32 into component sums */
+ sum2 = (adler >> 16) & 0xffff;
+ adler &= 0xffff;
+
+ /* in case user likes doing a byte at a time, keep it fast */
+ if (len == 1) {
+ adler += buf[0];
+ if (adler >= BASE)
+ adler -= BASE;
+ sum2 += adler;
+ if (sum2 >= BASE)
+ sum2 -= BASE;
+ return adler | (sum2 << 16);
+ }
+
+ /* initial Adler-32 value (deferred check for len == 1 speed) */
+ if (buf == Z_NULL)
+ return 1L;
+
+ /* in case short lengths are provided, keep it somewhat fast */
+ if (len < 16) {
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ if (adler >= BASE)
+ adler -= BASE;
+ MOD28(sum2); /* only added so many BASE's */
+ return adler | (sum2 << 16);
+ }
+
+ /* do length NMAX blocks -- requires just one modulo operation */
+ while (len >= NMAX) {
+ len -= NMAX;
+ n = NMAX / 16; /* NMAX is divisible by 16 */
+ do {
+ DO16(buf); /* 16 sums unrolled */
+ buf += 16;
+ } while (--n);
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* do remaining bytes (less than NMAX, still just one modulo) */
+ if (len) { /* avoid modulos if none remaining */
+ while (len >= 16) {
+ len -= 16;
+ DO16(buf);
+ buf += 16;
+ }
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* return recombined sums */
+ return adler | (sum2 << 16);
+}
+
+/* ========================================================================= */
+local uLong adler32_combine_(adler1, adler2, len2)
+ uLong adler1;
+ uLong adler2;
+ z_off64_t len2;
+{
+ unsigned long sum1;
+ unsigned long sum2;
+ unsigned rem;
+
+ /* for negative len, return invalid adler32 as a clue for debugging */
+ if (len2 < 0)
+ return 0xffffffffUL;
+
+ /* the derivation of this formula is left as an exercise for the reader */
+ MOD63(len2); /* assumes len2 >= 0 */
+ rem = (unsigned)len2;
+ sum1 = adler1 & 0xffff;
+ sum2 = rem * sum1;
+ MOD(sum2);
+ sum1 += (adler2 & 0xffff) + BASE - 1;
+ sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
+ if (sum1 >= BASE) sum1 -= BASE;
+ if (sum1 >= BASE) sum1 -= BASE;
+ if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1);
+ if (sum2 >= BASE) sum2 -= BASE;
+ return sum1 | (sum2 << 16);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT adler32_combine(adler1, adler2, len2)
+ uLong adler1;
+ uLong adler2;
+ z_off_t len2;
+{
+ return adler32_combine_(adler1, adler2, len2);
+}
+
+uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
+ uLong adler1;
+ uLong adler2;
+ z_off64_t len2;
+{
+ return adler32_combine_(adler1, adler2, len2);
+}
diff --git a/zlib/compress.c b/zlib/compress.c
new file mode 100644
index 0000000..6e97626
--- /dev/null
+++ b/zlib/compress.c
@@ -0,0 +1,80 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+ int level;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (z_const Bytef *)source;
+ stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit(&stream, level);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+ If the default memLevel or windowBits for deflateInit() is changed, then
+ this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+ uLong sourceLen;
+{
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+ (sourceLen >> 25) + 13;
+}
diff --git a/zlib/crc32.c b/zlib/crc32.c
new file mode 100644
index 0000000..05733f4
--- /dev/null
+++ b/zlib/crc32.c
@@ -0,0 +1,423 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors. This results in about a
+ * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $Id$ */
+
+/*
+ Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
+ protection on the static variables used to control the first-use generation
+ of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should
+ first call get_crc_table() to initialize the tables before allowing more than
+ one thread to use crc32().
+
+ DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h.
+ */
+
+#ifdef MAKECRCH
+# include <stdio.h>
+# ifndef DYNAMIC_CRC_TABLE
+# define DYNAMIC_CRC_TABLE
+# endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h" /* for STDC and FAR definitions */
+
+#define local static
+
+/* Definitions for doing the crc four data bytes at a time. */
+#if !defined(NOBYFOUR) && defined(Z_U4)
+# define BYFOUR
+#endif
+#ifdef BYFOUR
+ local unsigned long crc32_little OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+ local unsigned long crc32_big OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+# define TBLS 8
+#else
+# define TBLS 1
+#endif /* BYFOUR */
+
+/* Local functions for crc concatenation */
+local unsigned long gf2_matrix_times OF((unsigned long *mat,
+ unsigned long vec));
+local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
+local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2));
+
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local volatile int crc_table_empty = 1;
+local z_crc_t FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+ local void write_table OF((FILE *, const z_crc_t FAR *));
+#endif /* MAKECRCH */
+/*
+ Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+ Polynomials over GF(2) are represented in binary, one bit per coefficient,
+ with the lowest powers in the most significant bit. Then adding polynomials
+ is just exclusive-or, and multiplying a polynomial by x is a right shift by
+ one. If we call the above polynomial p, and represent a byte as the
+ polynomial q, also with the lowest power in the most significant bit (so the
+ byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+ where a mod b means the remainder after dividing a by b.
+
+ This calculation is done using the shift-register method of multiplying and
+ taking the remainder. The register is initialized to zero, and for each
+ incoming bit, x^32 is added mod p to the register if the bit is a one (where
+ x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+ x (which is shifting right by one and adding x^32 mod p if the bit shifted
+ out is a one). We start with the highest power (least significant bit) of
+ q and repeat for all eight bits of q.
+
+ The first table is simply the CRC of all possible eight bit values. This is
+ all the information needed to generate CRCs on data a byte at a time for all
+ combinations of CRC register values and incoming bytes. The remaining tables
+ allow for word-at-a-time CRC calculation for both big-endian and little-
+ endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+ z_crc_t c;
+ int n, k;
+ z_crc_t poly; /* polynomial exclusive-or pattern */
+ /* terms of polynomial defining this crc (except x^32): */
+ static volatile int first = 1; /* flag to limit concurrent making */
+ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+ /* See if another task is already doing this (not thread-safe, but better
+ than nothing -- significantly reduces duration of vulnerability in
+ case the advice about DYNAMIC_CRC_TABLE is ignored) */
+ if (first) {
+ first = 0;
+
+ /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+ poly = 0;
+ for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
+ poly |= (z_crc_t)1 << (31 - p[n]);
+
+ /* generate a crc for every 8-bit value */
+ for (n = 0; n < 256; n++) {
+ c = (z_crc_t)n;
+ for (k = 0; k < 8; k++)
+ c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+ crc_table[0][n] = c;
+ }
+
+#ifdef BYFOUR
+ /* generate crc for each value followed by one, two, and three zeros,
+ and then the byte reversal of those as well as the first table */
+ for (n = 0; n < 256; n++) {
+ c = crc_table[0][n];
+ crc_table[4][n] = ZSWAP32(c);
+ for (k = 1; k < 4; k++) {
+ c = crc_table[0][c & 0xff] ^ (c >> 8);
+ crc_table[k][n] = c;
+ crc_table[k + 4][n] = ZSWAP32(c);
+ }
+ }
+#endif /* BYFOUR */
+
+ crc_table_empty = 0;
+ }
+ else { /* not first */
+ /* wait for the other guy to finish (not efficient, but rare) */
+ while (crc_table_empty)
+ ;
+ }
+
+#ifdef MAKECRCH
+ /* write out CRC tables to crc32.h */
+ {
+ FILE *out;
+
+ out = fopen("crc32.h", "w");
+ if (out == NULL) return;
+ fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+ fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+ fprintf(out, "local const z_crc_t FAR ");
+ fprintf(out, "crc_table[TBLS][256] =\n{\n {\n");
+ write_table(out, crc_table[0]);
+# ifdef BYFOUR
+ fprintf(out, "#ifdef BYFOUR\n");
+ for (k = 1; k < 8; k++) {
+ fprintf(out, " },\n {\n");
+ write_table(out, crc_table[k]);
+ }
+ fprintf(out, "#endif\n");
+# endif /* BYFOUR */
+ fprintf(out, " }\n};\n");
+ fclose(out);
+ }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+ FILE *out;
+ const z_crc_t FAR *table;
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ",
+ (unsigned long)(table[n]),
+ n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const z_crc_t FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+ return (const z_crc_t FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ uInt len;
+{
+ if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+ if (sizeof(void *) == sizeof(ptrdiff_t)) {
+ z_crc_t endian;
+
+ endian = 1;
+ if (*((unsigned char *)(&endian)))
+ return crc32_little(crc, buf, len);
+ else
+ return crc32_big(crc, buf, len);
+ }
+#endif /* BYFOUR */
+ crc = crc ^ 0xffffffffUL;
+ while (len >= 8) {
+ DO8;
+ len -= 8;
+ }
+ if (len) do {
+ DO1;
+ } while (--len);
+ return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register z_crc_t c;
+ register const z_crc_t FAR *buf4;
+
+ c = (z_crc_t)crc;
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ len--;
+ }
+
+ buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
+ while (len >= 32) {
+ DOLIT32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOLIT4;
+ len -= 4;
+ }
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *buf4++; \
+ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register z_crc_t c;
+ register const z_crc_t FAR *buf4;
+
+ c = ZSWAP32((z_crc_t)crc);
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ len--;
+ }
+
+ buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
+ while (len >= 32) {
+ DOBIG32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOBIG4;
+ len -= 4;
+ }
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)(ZSWAP32(c));
+}
+
+#endif /* BYFOUR */
+
+#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */
+
+/* ========================================================================= */
+local unsigned long gf2_matrix_times(mat, vec)
+ unsigned long *mat;
+ unsigned long vec;
+{
+ unsigned long sum;
+
+ sum = 0;
+ while (vec) {
+ if (vec & 1)
+ sum ^= *mat;
+ vec >>= 1;
+ mat++;
+ }
+ return sum;
+}
+
+/* ========================================================================= */
+local void gf2_matrix_square(square, mat)
+ unsigned long *square;
+ unsigned long *mat;
+{
+ int n;
+
+ for (n = 0; n < GF2_DIM; n++)
+ square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+/* ========================================================================= */
+local uLong crc32_combine_(crc1, crc2, len2)
+ uLong crc1;
+ uLong crc2;
+ z_off64_t len2;
+{
+ int n;
+ unsigned long row;
+ unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */
+ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */
+
+ /* degenerate case (also disallow negative lengths) */
+ if (len2 <= 0)
+ return crc1;
+
+ /* put operator for one zero bit in odd */
+ odd[0] = 0xedb88320UL; /* CRC-32 polynomial */
+ row = 1;
+ for (n = 1; n < GF2_DIM; n++) {
+ odd[n] = row;
+ row <<= 1;
+ }
+
+ /* put operator for two zero bits in even */
+ gf2_matrix_square(even, odd);
+
+ /* put operator for four zero bits in odd */
+ gf2_matrix_square(odd, even);
+
+ /* apply len2 zeros to crc1 (first square will put the operator for one
+ zero byte, eight zero bits, in even) */
+ do {
+ /* apply zeros operator for this bit of len2 */
+ gf2_matrix_square(even, odd);
+ if (len2 & 1)
+ crc1 = gf2_matrix_times(even, crc1);
+ len2 >>= 1;
+
+ /* if no more bits set, then done */
+ if (len2 == 0)
+ break;
+
+ /* another iteration of the loop with odd and even swapped */
+ gf2_matrix_square(odd, even);
+ if (len2 & 1)
+ crc1 = gf2_matrix_times(odd, crc1);
+ len2 >>= 1;
+
+ /* if no more bits set, then done */
+ } while (len2 != 0);
+
+ /* return combined crc */
+ crc1 ^= crc2;
+ return crc1;
+}
+
+/* ========================================================================= */
+uLong ZEXPORT crc32_combine(crc1, crc2, len2)
+ uLong crc1;
+ uLong crc2;
+ z_off_t len2;
+{
+ return crc32_combine_(crc1, crc2, len2);
+}
+
+uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
+ uLong crc1;
+ uLong crc2;
+ z_off64_t len2;
+{
+ return crc32_combine_(crc1, crc2, len2);
+}
diff --git a/zlib/crc32.h b/zlib/crc32.h
new file mode 100644
index 0000000..9e0c778
--- /dev/null
+++ b/zlib/crc32.h
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const z_crc_t FAR crc_table[TBLS][256] =
+{
+ {
+ 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+ 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+ 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+ 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+ 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+ 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+ 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+ 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+ 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+ 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+ 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+ 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+ 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+ 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+ 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+ 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+ 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+ 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+ 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+ 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+ 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+ 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+ 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+ 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+ 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+ 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+ 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+ 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+ 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+ 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+ 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+ 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+ 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+ 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+ 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+ 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+ 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+ 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+ 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+ 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+ 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+ 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+ 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+ 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+ 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+ 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+ 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+ 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+ 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+ 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+ 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+ 0x2d02ef8dUL
+#ifdef BYFOUR
+ },
+ {
+ 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+ 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+ 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+ 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+ 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+ 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+ 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+ 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+ 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+ 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+ 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+ 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+ 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+ 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+ 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+ 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+ 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+ 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+ 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+ 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+ 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+ 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+ 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+ 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+ 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+ 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+ 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+ 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+ 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+ 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+ 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+ 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+ 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+ 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+ 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+ 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+ 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+ 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+ 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+ 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+ 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+ 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+ 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+ 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+ 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+ 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+ 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+ 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+ 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+ 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+ 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+ 0x9324fd72UL
+ },
+ {
+ 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+ 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+ 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+ 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+ 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+ 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+ 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+ 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+ 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+ 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+ 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+ 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+ 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+ 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+ 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+ 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+ 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+ 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+ 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+ 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+ 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+ 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+ 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+ 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+ 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+ 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+ 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+ 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+ 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+ 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+ 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+ 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+ 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+ 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+ 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+ 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+ 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+ 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+ 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+ 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+ 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+ 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+ 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+ 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+ 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+ 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+ 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+ 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+ 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+ 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+ 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+ 0xbe9834edUL
+ },
+ {
+ 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+ 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+ 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+ 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+ 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+ 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+ 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+ 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+ 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+ 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+ 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+ 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+ 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+ 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+ 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+ 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+ 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+ 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+ 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+ 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+ 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+ 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+ 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+ 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+ 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+ 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+ 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+ 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+ 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+ 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+ 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+ 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+ 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+ 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+ 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+ 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+ 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+ 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+ 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+ 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+ 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+ 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+ 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+ 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+ 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+ 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+ 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+ 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+ 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+ 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+ 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+ 0xde0506f1UL
+ },
+ {
+ 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+ 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+ 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+ 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+ 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+ 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+ 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+ 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+ 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+ 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+ 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+ 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+ 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+ 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+ 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+ 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+ 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+ 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+ 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+ 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+ 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+ 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+ 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+ 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+ 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+ 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+ 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+ 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+ 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+ 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+ 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+ 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+ 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+ 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+ 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+ 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+ 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+ 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+ 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+ 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+ 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+ 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+ 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+ 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+ 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+ 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+ 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+ 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+ 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+ 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+ 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+ 0x8def022dUL
+ },
+ {
+ 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+ 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+ 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+ 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+ 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+ 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+ 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+ 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+ 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+ 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+ 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+ 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+ 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+ 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+ 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+ 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+ 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+ 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+ 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+ 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+ 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+ 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+ 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+ 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+ 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+ 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+ 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+ 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+ 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+ 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+ 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+ 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+ 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+ 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+ 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+ 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+ 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+ 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+ 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+ 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+ 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+ 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+ 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+ 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+ 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+ 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+ 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+ 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+ 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+ 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+ 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+ 0x72fd2493UL
+ },
+ {
+ 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+ 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+ 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+ 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+ 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+ 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+ 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+ 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+ 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+ 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+ 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+ 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+ 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+ 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+ 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+ 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+ 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+ 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+ 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+ 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+ 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+ 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+ 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+ 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+ 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+ 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+ 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+ 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+ 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+ 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+ 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+ 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+ 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+ 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+ 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+ 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+ 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+ 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+ 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+ 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+ 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+ 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+ 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+ 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+ 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+ 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+ 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+ 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+ 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+ 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+ 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+ 0xed3498beUL
+ },
+ {
+ 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+ 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+ 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+ 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+ 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+ 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+ 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+ 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+ 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+ 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+ 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+ 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+ 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+ 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+ 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+ 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+ 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+ 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+ 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+ 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+ 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+ 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+ 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+ 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+ 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+ 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+ 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+ 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+ 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+ 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+ 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+ 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+ 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+ 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+ 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+ 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+ 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+ 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+ 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+ 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+ 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+ 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+ 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+ 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+ 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+ 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+ 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+ 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+ 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+ 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+ 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+ 0xf10605deUL
+#endif
+ }
+};
diff --git a/zlib/deflate.c b/zlib/deflate.c
new file mode 100644
index 0000000..2cbc4fc
--- /dev/null
+++ b/zlib/deflate.c
@@ -0,0 +1,2032 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in http://tools.ietf.org/html/rfc1951
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id$ */
+
+#include "deflate.h"
+
+#define read_buf dread_buf
+
+const char deflate_copyright[] =
+ " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow OF((deflate_state *s, int flush));
+#endif
+local block_state deflate_rle OF((deflate_state *s, int flush));
+local block_state deflate_huff OF((deflate_state *s, int flush));
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+
+#ifdef DEBUG
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */
+#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0))
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int wrap = 1;
+ static const char my_version[] = ZLIB_VERSION;
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+ if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+#endif
+ }
+ if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zfree = zcfree;
+#endif
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+ if (windowBits < 0) { /* suppress zlib wrapper */
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+#ifdef GZIP
+ else if (windowBits > 15) {
+ wrap = 2; /* write gzip wrapper instead */
+ windowBits -= 16;
+ }
+#endif
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_FIXED) {
+ return Z_STREAM_ERROR;
+ }
+ if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->wrap = wrap;
+ s->gzhead = Z_NULL;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->high_water = 0; /* nothing written to s->window yet */
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ /* We overlay pending_buf and sym_buf. This works since the average size
+ * for length/distance pairs over any compressed block is assured to be 31
+ * bits or less.
+ *
+ * Analysis: The longest fixed codes are a length code of 8 bits plus 5
+ * extra bits, for lengths 131 to 257. The longest fixed distance codes are
+ * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest
+ * possible fixed-codes length/distance pair is then 31 bits total.
+ *
+ * sym_buf starts one-fourth of the way into pending_buf. So there are
+ * three bytes in sym_buf for every four bytes in pending_buf. Each symbol
+ * in sym_buf is three bytes -- two for the distance and one for the
+ * literal/length. As each symbol is consumed, the pointer to the next
+ * sym_buf value to read moves forward three bytes. From that symbol, up to
+ * 31 bits are written to pending_buf. The closest the written pending_buf
+ * bits gets to the next sym_buf symbol to read is just before the last
+ * code is written. At that time, 31*(n-2) bits have been written, just
+ * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at
+ * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1
+ * symbols are written.) The closest the writing gets to what is unread is
+ * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and
+ * can range from 128 to 32768.
+ *
+ * Therefore, at a minimum, there are 142 bits of space between what is
+ * written and what is read in the overlain buffers, so the symbols cannot
+ * be overwritten by the compressed data. That space is actually 139 bits,
+ * due to the three-bit fixed-code block header.
+ *
+ * That covers the case where either Z_FIXED is specified, forcing fixed
+ * codes, or when the use of fixed codes is chosen, because that choice
+ * results in a smaller compressed block than dynamic codes. That latter
+ * condition then assures that the above analysis also covers all dynamic
+ * blocks. A dynamic-code block will only be chosen to be emitted if it has
+ * fewer bits than a fixed-code block would for the same set of symbols.
+ * Therefore its average symbol length is assured to be less than 31. So
+ * the compressed data for a dynamic block also cannot overwrite the
+ * symbols from which it is being constructed.
+ */
+
+ s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 4);
+ s->pending_buf_size = (ulg)s->lit_bufsize * 4;
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ s->status = FINISH_STATE;
+ strm->msg = ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->sym_buf = s->pending_buf + s->lit_bufsize;
+ s->sym_end = (s->lit_bufsize - 1) * 3;
+ /* We avoid equality with lit_bufsize*3 because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt str, n;
+ int wrap;
+ unsigned avail;
+ z_const unsigned char *next;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL)
+ return Z_STREAM_ERROR;
+ s = strm->state;
+ wrap = s->wrap;
+ if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead)
+ return Z_STREAM_ERROR;
+
+ /* when using zlib wrappers, compute Adler-32 for provided dictionary */
+ if (wrap == 1)
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+ s->wrap = 0; /* avoid computing Adler-32 in read_buf */
+
+ /* if dictionary would fill window, just replace the history */
+ if (dictLength >= s->w_size) {
+ if (wrap == 0) { /* already empty otherwise */
+ CLEAR_HASH(s);
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->insert = 0;
+ }
+ dictionary += dictLength - s->w_size; /* use the tail */
+ dictLength = s->w_size;
+ }
+
+ /* insert dictionary into window and hash */
+ avail = strm->avail_in;
+ next = strm->next_in;
+ strm->avail_in = dictLength;
+ strm->next_in = (z_const Bytef *)dictionary;
+ fill_window(s);
+ while (s->lookahead >= MIN_MATCH) {
+ str = s->strstart;
+ n = s->lookahead - (MIN_MATCH-1);
+ do {
+ UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
+#ifndef FASTEST
+ s->prev[str & s->w_mask] = s->head[s->ins_h];
+#endif
+ s->head[s->ins_h] = (Pos)str;
+ str++;
+ } while (--n);
+ s->strstart = str;
+ s->lookahead = MIN_MATCH-1;
+ fill_window(s);
+ }
+ s->strstart += s->lookahead;
+ s->block_start = (long)s->strstart;
+ s->insert = s->lookahead;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ strm->next_in = next;
+ strm->avail_in = avail;
+ s->wrap = wrap;
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateResetKeep (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
+ return Z_STREAM_ERROR;
+ }
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->wrap < 0) {
+ s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+ }
+ s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+ strm->adler =
+#ifdef GZIP
+ s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+ adler32(0L, Z_NULL, 0);
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+ z_streamp strm;
+{
+ int ret;
+
+ ret = deflateResetKeep(strm);
+ if (ret == Z_OK)
+ lm_init(strm->state);
+ return ret;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetHeader (strm, head)
+ z_streamp strm;
+ gz_headerp head;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ if (strm->state->wrap != 2) return Z_STREAM_ERROR;
+ strm->state->gzhead = head;
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePending (strm, pending, bits)
+ unsigned *pending;
+ int *bits;
+ z_streamp strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ if (pending != Z_NULL)
+ *pending = strm->state->pending;
+ if (bits != Z_NULL)
+ *bits = strm->state->bi_valid;
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+ z_streamp strm;
+ int bits;
+ int value;
+{
+ deflate_state *s;
+ int put;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+ if (s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
+ return Z_BUF_ERROR;
+ do {
+ put = Buf_size - s->bi_valid;
+ if (put > bits)
+ put = bits;
+ s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);
+ s->bi_valid += put;
+ _tr_flush_bits(s);
+ value >>= put;
+ bits -= put;
+ } while (bits);
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if ((strategy != s->strategy || func != configuration_table[level].func) &&
+ strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_BLOCK);
+ if (err == Z_BUF_ERROR && s->pending == 0)
+ err = Z_OK;
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
+ z_streamp strm;
+ int good_length;
+ int max_lazy;
+ int nice_length;
+ int max_chain;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+ s->good_match = good_length;
+ s->max_lazy_match = max_lazy;
+ s->nice_match = nice_length;
+ s->max_chain_length = max_chain;
+ return Z_OK;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well. The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds for
+ * every combination of windowBits and memLevel. But even the conservative
+ * upper bound of about 14% expansion does not seem onerous for output buffer
+ * allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+ z_streamp strm;
+ uLong sourceLen;
+{
+ deflate_state *s;
+ uLong complen, wraplen;
+ Bytef *str;
+
+ /* conservative upper bound for compressed data */
+ complen = sourceLen +
+ ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
+
+ /* if can't get parameters, return conservative bound plus zlib wrapper */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return complen + 6;
+
+ /* compute wrapper length */
+ s = strm->state;
+ switch (s->wrap) {
+ case 0: /* raw deflate */
+ wraplen = 0;
+ break;
+ case 1: /* zlib wrapper */
+ wraplen = 6 + (s->strstart ? 4 : 0);
+ break;
+ case 2: /* gzip wrapper */
+ wraplen = 18;
+ if (s->gzhead != Z_NULL) { /* user-supplied gzip header */
+ if (s->gzhead->extra != Z_NULL)
+ wraplen += 2 + s->gzhead->extra_len;
+ str = s->gzhead->name;
+ if (str != Z_NULL)
+ do {
+ wraplen++;
+ } while (*str++);
+ str = s->gzhead->comment;
+ if (str != Z_NULL)
+ do {
+ wraplen++;
+ } while (*str++);
+ if (s->gzhead->hcrc)
+ wraplen += 2;
+ }
+ break;
+ default: /* for compiler happiness */
+ wraplen = 6;
+ }
+
+ /* if not default parameters, return conservative bound */
+ if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+ return complen + wraplen;
+
+ /* default settings: return tight bound for that case */
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+ (sourceLen >> 25) + 13 - 6 + wraplen;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ unsigned len;
+ deflate_state *s = strm->state;
+
+ _tr_flush_bits(s);
+ len = s->pending;
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ zmemcpy(strm->next_out, s->pending_out, len);
+ strm->next_out += len;
+ s->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ s->pending -= len;
+ if (s->pending == 0) {
+ s->pending_out = s->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ (flush > Z_BLOCK && flush != Z_INSERT_ONLY) || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = strm->state;
+
+ if (strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the header */
+ if (s->status == INIT_STATE) {
+#ifdef GZIP
+ if (s->wrap == 2) {
+ strm->adler = crc32(0L, Z_NULL, 0);
+ put_byte(s, 31);
+ put_byte(s, 139);
+ put_byte(s, 8);
+ if (s->gzhead == Z_NULL) {
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, OS_CODE);
+ s->status = BUSY_STATE;
+ }
+ else {
+ put_byte(s, (s->gzhead->text ? 1 : 0) +
+ (s->gzhead->hcrc ? 2 : 0) +
+ (s->gzhead->extra == Z_NULL ? 0 : 4) +
+ (s->gzhead->name == Z_NULL ? 0 : 8) +
+ (s->gzhead->comment == Z_NULL ? 0 : 16)
+ );
+ put_byte(s, (Byte)(s->gzhead->time & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, s->gzhead->os & 0xff);
+ if (s->gzhead->extra != Z_NULL) {
+ put_byte(s, s->gzhead->extra_len & 0xff);
+ put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
+ }
+ if (s->gzhead->hcrc)
+ strm->adler = crc32(strm->adler, s->pending_buf,
+ s->pending);
+ s->gzindex = 0;
+ s->status = EXTRA_STATE;
+ }
+ }
+ else
+#endif
+ {
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags;
+
+ if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+ level_flags = 0;
+ else if (s->level < 6)
+ level_flags = 1;
+ else if (s->level == 6)
+ level_flags = 2;
+ else
+ level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = adler32(0L, Z_NULL, 0);
+ }
+ }
+#ifdef GZIP
+ if (s->status == EXTRA_STATE) {
+ if (s->gzhead->extra != Z_NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+
+ while (s->gzindex < (s->gzhead->extra_len & 0xffff)) {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size)
+ break;
+ }
+ put_byte(s, s->gzhead->extra[s->gzindex]);
+ s->gzindex++;
+ }
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (s->gzindex == s->gzhead->extra_len) {
+ s->gzindex = 0;
+ s->status = NAME_STATE;
+ }
+ }
+ else
+ s->status = NAME_STATE;
+ }
+ if (s->status == NAME_STATE) {
+ if (s->gzhead->name != Z_NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+ int val;
+
+ do {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ val = s->gzhead->name[s->gzindex++];
+ put_byte(s, val);
+ } while (val != 0);
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (val == 0) {
+ s->gzindex = 0;
+ s->status = COMMENT_STATE;
+ }
+ }
+ else
+ s->status = COMMENT_STATE;
+ }
+ if (s->status == COMMENT_STATE) {
+ if (s->gzhead->comment != Z_NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+ int val;
+
+ do {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ val = s->gzhead->comment[s->gzindex++];
+ put_byte(s, val);
+ } while (val != 0);
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (val == 0)
+ s->status = HCRC_STATE;
+ }
+ else
+ s->status = HCRC_STATE;
+ }
+ if (s->status == HCRC_STATE) {
+ if (s->gzhead->hcrc) {
+ if (s->pending + 2 > s->pending_buf_size)
+ flush_pending(strm);
+ if (s->pending + 2 <= s->pending_buf_size) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ strm->adler = crc32(0L, Z_NULL, 0);
+ s->status = BUSY_STATE;
+ }
+ }
+ else
+ s->status = BUSY_STATE;
+ }
+#endif
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) :
+ (s->strategy == Z_RLE ? deflate_rle(s, flush) :
+ (*(configuration_table[s->level].func))(s, flush));
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ if (s->lookahead == 0) {
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->insert = 0;
+ }
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->wrap <= 0) return Z_STREAM_END;
+
+ /* Write the trailer */
+#ifdef GZIP
+ if (s->wrap == 2) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+ put_byte(s, (Byte)(strm->total_in & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+ }
+ else
+#endif
+ {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+ status = strm->state->status;
+ if (status != INIT_STATE &&
+ status != EXTRA_STATE &&
+ status != NAME_STATE &&
+ status != COMMENT_STATE &&
+ status != HCRC_STATE &&
+ status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, strm->state->pending_buf);
+ TRY_FREE(strm, strm->state->head);
+ TRY_FREE(strm, strm->state->prev);
+ TRY_FREE(strm, strm->state->window);
+
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+#ifdef MAXSEG_64K
+ return Z_STREAM_ERROR;
+#else
+ deflate_state *ds;
+ deflate_state *ss;
+
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+ return Z_STREAM_ERROR;
+ }
+
+ ss = source->state;
+
+ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, 4);
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* following zmemcpy do not work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->sym_buf = ds->pending_buf + ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ Bytef *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ zmemcpy(buf, strm->next_in, len);
+ if (strm->state->wrap == 1) {
+ strm->adler = adler32(strm->adler, buf, len);
+ }
+#ifdef GZIP
+ else if (strm->state->wrap == 2) {
+ strm->adler = crc32(strm->adler, buf, len);
+ }
+#endif
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->insert = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifndef FASTEST
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2. Note that the checks below
+ * for insufficient lookahead only occur occasionally for performance
+ * reasons. Therefore uninitialized memory will be accessed, and
+ * conditional jumps will be made that depend on those values.
+ * However the length of the match is limited to the lookahead, so
+ * the output of deflate is not affected by the uninitialized values.
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+ return s->lookahead;
+}
+#endif /* ASMV */
+
+#else /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for FASTEST only
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ Assert(cur_match < s->strstart, "no future");
+
+ match = s->window + cur_match;
+
+ /* Return failure if the match length is less than 2:
+ */
+ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match += 2;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+
+ if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+ s->match_start = cur_match;
+ return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#endif /* FASTEST */
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp(s->window + match,
+ s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif /* DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (sizeof(int) <= 2) {
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if
+ * strstart == 0 && lookahead == 1 (input done a byte at time)
+ */
+ more--;
+ }
+ }
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+#ifndef FASTEST
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+#endif
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) break;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead + s->insert >= MIN_MATCH) {
+ uInt str = s->strstart - s->insert;
+ s->ins_h = s->window[str];
+ UPDATE_HASH(s, s->ins_h, s->window[str + 1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ while (s->insert) {
+ UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
+#ifndef FASTEST
+ s->prev[str & s->w_mask] = s->head[s->ins_h];
+#endif
+ s->head[s->ins_h] = (Pos)str;
+ str++;
+ s->insert--;
+ if (s->lookahead + s->insert < MIN_MATCH)
+ break;
+ }
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+
+ /* If the WIN_INIT bytes after the end of the current data have never been
+ * written, then zero those bytes in order to avoid memory check reports of
+ * the use of uninitialized (or uninitialised as Julian writes) bytes by
+ * the longest match routines. Update the high water mark for the next
+ * time through here. WIN_INIT is set to MAX_MATCH since the longest match
+ * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
+ */
+ if (s->high_water < s->window_size) {
+ ulg curr = s->strstart + (ulg)(s->lookahead);
+ ulg init;
+
+ if (s->high_water < curr) {
+ /* Previous high water mark below current data -- zero WIN_INIT
+ * bytes or up to end of window, whichever is less.
+ */
+ init = s->window_size - curr;
+ if (init > WIN_INIT)
+ init = WIN_INIT;
+ zmemzero(s->window + curr, (unsigned)init);
+ s->high_water = curr + init;
+ }
+ else if (s->high_water < (ulg)curr + WIN_INIT) {
+ /* High water mark at or above current data, but below current data
+ * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
+ * to end of window, whichever is less.
+ */
+ init = (ulg)curr + WIN_INIT - s->high_water;
+ if (init > s->window_size - s->high_water)
+ init = s->window_size - s->high_water;
+ zmemzero(s->window + s->high_water, (unsigned)init);
+ s->high_water += init;
+ }
+ }
+
+ Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
+ "not enough room for search");
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, last) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (last)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, last) { \
+ FLUSH_BLOCK_ONLY(s, last); \
+ if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ if (flush == Z_INSERT_ONLY) {
+ s->block_start = s->strstart;
+ continue;
+ }
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ s->insert = 0;
+ if (flush == Z_INSERT_ONLY) {
+ s->block_start = s->strstart;
+ return need_more;
+ }
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if ((long)s->strstart > s->block_start)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = NIL;
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ if (flush == Z_INSERT_ONLY) {
+ s->strstart++;
+ s->lookahead--;
+ continue;
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ s->match_length = longest_match (s, hash_head);
+ /* longest_match() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ _tr_tally_dist(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+#ifndef FASTEST
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else
+#endif
+ {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ if (flush == Z_INSERT_ONLY) {
+ s->block_start = s->strstart;
+ return need_more;
+ }
+ s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->sym_next)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = NIL;
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ if (flush == Z_INSERT_ONLY) {
+ s->strstart++;
+ s->lookahead--;
+ continue;
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ s->match_length = longest_match (s, hash_head);
+ /* longest_match() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+ || (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR)
+#endif
+ )) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH, bflush);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ if (bflush) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ if (flush == Z_INSERT_ONLY) {
+ s->block_start = s->strstart;
+ return need_more;
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ s->match_available = 0;
+ }
+ s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->sym_next)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
+}
+#endif /* FASTEST */
+
+/* ===========================================================================
+ * For Z_RLE, simply look for runs of bytes, generate matches only of distance
+ * one. Do not maintain a hash table. (It will be regenerated if this run of
+ * deflate switches away from Z_RLE.)
+ */
+local block_state deflate_rle(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ int bflush; /* set if current block must be flushed */
+ uInt prev; /* byte at distance one to match */
+ Bytef *scan, *strend; /* scan goes up to strend for length of run */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the longest run, plus one for the unrolled loop.
+ */
+ if (s->lookahead <= MAX_MATCH) {
+ fill_window(s);
+ if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* See how many times the previous byte repeats */
+ s->match_length = 0;
+ if (s->lookahead >= MIN_MATCH && s->strstart > 0) {
+ scan = s->window + s->strstart - 1;
+ prev = *scan;
+ if (prev == *++scan && prev == *++scan && prev == *++scan) {
+ strend = s->window + s->strstart + MAX_MATCH;
+ do {
+ } while (prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ scan < strend);
+ s->match_length = MAX_MATCH - (int)(strend - scan);
+ if (s->match_length > s->lookahead)
+ s->match_length = s->lookahead;
+ }
+ Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
+ }
+
+ /* Emit match if have run of MIN_MATCH or longer, else emit literal */
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->strstart - 1, s->match_length);
+
+ _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ s->insert = 0;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->sym_next)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
+}
+
+/* ===========================================================================
+ * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
+ * (It will be regenerated if this run of deflate switches away from Huffman.)
+ */
+local block_state deflate_huff(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we have a literal to write. */
+ if (s->lookahead == 0) {
+ fill_window(s);
+ if (s->lookahead == 0) {
+ if (flush == Z_NO_FLUSH)
+ return need_more;
+ break; /* flush the current block */
+ }
+ }
+
+ /* Output a literal byte */
+ s->match_length = 0;
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ s->insert = 0;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->sym_next)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
+}
diff --git a/zlib/deflate.h b/zlib/deflate.h
new file mode 100644
index 0000000..a300a28
--- /dev/null
+++ b/zlib/deflate.h
@@ -0,0 +1,343 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2012 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer creation by deflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip encoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define Buf_size 16
+/* size of bit buffer in bi_buf */
+
+#define INIT_STATE 42
+#define EXTRA_STATE 69
+#define NAME_STATE 73
+#define COMMENT_STATE 91
+#define HCRC_STATE 103
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ uInt pending; /* nb of bytes in the pending buffer */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ gz_headerp gzhead; /* gzip header information to write */
+ uInt gzindex; /* where in extra, name, or comment */
+ Byte method; /* can only be DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to suppress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *sym_buf; /* buffer for distances and literals/lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt sym_next; /* running index in sym_buf */
+ uInt sym_end; /* symbol table full when sym_next reaches this */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ uInt matches; /* number of string matches in current block */
+ uInt insert; /* bytes at end of window left to insert */
+
+#ifdef DEBUG
+ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+ ulg high_water;
+ /* High water mark offset in window for initialized bytes -- bytes above
+ * this are set to zero in order to avoid memory check warnings when
+ * longest match routines access bytes past the input. This is then
+ * updated to the new high water mark.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+#define WIN_INIT MAX_MATCH
+/* Number of bytes after end of data in window to initialize in order to avoid
+ memory checker errors from longest match routines */
+
+ /* in trees.c */
+void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
+int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
+ ulg stored_len, int last));
+void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
+void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
+void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
+ ulg stored_len, int last));
+
+#define d_code(dist) \
+ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+ extern uch ZLIB_INTERNAL _length_code[];
+ extern uch ZLIB_INTERNAL _dist_code[];
+#else
+ extern const uch ZLIB_INTERNAL _length_code[];
+ extern const uch ZLIB_INTERNAL _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+ { uch cc = (c); \
+ s->sym_buf[s->sym_next++] = 0; \
+ s->sym_buf[s->sym_next++] = 0; \
+ s->sym_buf[s->sym_next++] = cc; \
+ s->dyn_ltree[cc].Freq++; \
+ flush = (s->sym_next == s->sym_end); \
+ }
+# define _tr_tally_dist(s, distance, length, flush) \
+ { uch len = (length); \
+ ush dist = (distance); \
+ s->sym_buf[s->sym_next++] = dist; \
+ s->sym_buf[s->sym_next++] = dist >> 8; \
+ s->sym_buf[s->sym_next++] = len; \
+ dist--; \
+ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+ s->dyn_dtree[d_code(dist)].Freq++; \
+ flush = (s->sym_next == s->sym_end); \
+ }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+ flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
diff --git a/zlib/dummy.in b/zlib/dummy.in
new file mode 100644
index 0000000..3b26a20
--- /dev/null
+++ b/zlib/dummy.in
@@ -0,0 +1,2 @@
+This is a dummy file to ensure that the lib directory gets created
+by configure when a VPATH is used.
diff --git a/zlib/gzguts.h b/zlib/gzguts.h
new file mode 100644
index 0000000..d87659d
--- /dev/null
+++ b/zlib/gzguts.h
@@ -0,0 +1,209 @@
+/* gzguts.h -- zlib internal header definitions for gz* operations
+ * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#ifdef _LARGEFILE64_SOURCE
+# ifndef _LARGEFILE_SOURCE
+# define _LARGEFILE_SOURCE 1
+# endif
+# ifdef _FILE_OFFSET_BITS
+# undef _FILE_OFFSET_BITS
+# endif
+#endif
+
+#ifdef HAVE_HIDDEN
+# define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
+#else
+# define ZLIB_INTERNAL
+#endif
+
+#include <stdio.h>
+#include "zlib.h"
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+# include <limits.h>
+#endif
+#include <fcntl.h>
+
+#ifdef _WIN32
+# include <stddef.h>
+#endif
+
+#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32)
+# include <io.h>
+#endif
+
+#ifdef WINAPI_FAMILY
+# define open _open
+# define read _read
+# define write _write
+# define close _close
+#endif
+
+#ifdef NO_DEFLATE /* for compatibility with old definition */
+# define NO_GZCOMPRESS
+#endif
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+
+#if defined(__CYGWIN__)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+
+#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+
+#ifndef HAVE_VSNPRINTF
+# ifdef MSDOS
+/* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+ but for now we just assume it doesn't. */
+# define NO_vsnprintf
+# endif
+# ifdef __TURBOC__
+# define NO_vsnprintf
+# endif
+# ifdef WIN32
+/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+# if !defined(vsnprintf) && !defined(NO_vsnprintf)
+# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
+# define vsnprintf _vsnprintf
+# endif
+# endif
+# endif
+# ifdef __SASC
+# define NO_vsnprintf
+# endif
+# ifdef VMS
+# define NO_vsnprintf
+# endif
+# ifdef __OS400__
+# define NO_vsnprintf
+# endif
+# ifdef __MVS__
+# define NO_vsnprintf
+# endif
+#endif
+
+/* unlike snprintf (which is required in C99, yet still not supported by
+ Microsoft more than a decade later!), _snprintf does not guarantee null
+ termination of the result -- however this is only used in gzlib.c where
+ the result is assured to fit in the space provided */
+#ifdef _MSC_VER
+# define snprintf _snprintf
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+/* gz* functions always use library allocation functions */
+#ifndef STDC
+ extern voidp malloc OF((uInt size));
+ extern void free OF((voidpf ptr));
+#endif
+
+/* get errno and strerror definition */
+#if defined UNDER_CE
+# include <windows.h>
+# define zstrerror() gz_strwinerror((DWORD)GetLastError())
+#else
+# ifndef NO_STRERROR
+# include <errno.h>
+# define zstrerror() strerror(errno)
+# else
+# define zstrerror() "stdio error (consult errno)"
+# endif
+#endif
+
+/* provide prototypes for these when building zlib without LFS */
+#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
+ ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+ ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
+ ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
+ ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+#endif
+
+/* default memLevel */
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+
+/* default i/o buffer size -- double this for output when reading (this and
+ twice this must be able to fit in an unsigned type) */
+#define GZBUFSIZE 8192
+
+/* gzip modes, also provide a little integrity check on the passed structure */
+#define GZ_NONE 0
+#define GZ_READ 7247
+#define GZ_WRITE 31153
+#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */
+
+/* values for gz_state how */
+#define LOOK 0 /* look for a gzip header */
+#define COPY 1 /* copy input directly */
+#define GZIP 2 /* decompress a gzip stream */
+
+/* internal gzip file state data structure */
+typedef struct {
+ /* exposed contents for gzgetc() macro */
+ struct gzFile_s x; /* "x" for exposed */
+ /* x.have: number of bytes available at x.next */
+ /* x.next: next output data to deliver or write */
+ /* x.pos: current position in uncompressed data */
+ /* used for both reading and writing */
+ int mode; /* see gzip modes above */
+ int fd; /* file descriptor */
+ char *path; /* path or fd for error messages */
+ unsigned size; /* buffer size, zero if not allocated yet */
+ unsigned want; /* requested buffer size, default is GZBUFSIZE */
+ unsigned char *in; /* input buffer */
+ unsigned char *out; /* output buffer (double-sized when reading) */
+ int direct; /* 0 if processing gzip, 1 if transparent */
+ /* just for reading */
+ int how; /* 0: get header, 1: copy, 2: decompress */
+ z_off64_t start; /* where the gzip data started, for rewinding */
+ int eof; /* true if end of input file reached */
+ int past; /* true if read requested past end */
+ /* just for writing */
+ int level; /* compression level */
+ int strategy; /* compression strategy */
+ /* seek request */
+ z_off64_t skip; /* amount to skip (already rewound if backwards) */
+ int seek; /* true if seek request pending */
+ /* error information */
+ int err; /* error code */
+ char *msg; /* error message */
+ /* zlib inflate or deflate stream */
+ z_stream strm; /* stream structure in-place (not a pointer) */
+} gz_state;
+typedef gz_state FAR *gz_statep;
+
+/* shared functions */
+void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *));
+#if defined UNDER_CE
+char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
+#endif
+
+/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t
+ value -- needed when comparing unsigned to z_off64_t, which is signed
+ (possible z_off64_t types off_t, off64_t, and long are all signed) */
+#ifdef INT_MAX
+# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
+#else
+unsigned ZLIB_INTERNAL gz_intmax OF((void));
+# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
+#endif
diff --git a/zlib/inffast.c b/zlib/inffast.c
new file mode 100644
index 0000000..f0d163d
--- /dev/null
+++ b/zlib/inffast.c
@@ -0,0 +1,321 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2008, 2010, 2013 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifndef ASMINF
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state->mode == LEN
+ strm->avail_in >= 6
+ strm->avail_out >= 258
+ start >= strm->avail_out
+ state->bits < 8
+
+ On return, state->mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm->avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm->avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+void ZLIB_INTERNAL inflate_fast(strm, start)
+z_streamp strm;
+unsigned start; /* inflate()'s starting value for strm->avail_out */
+{
+ struct inflate_state FAR *state;
+ z_const unsigned char FAR *in; /* local strm->next_in */
+ z_const unsigned char FAR *last; /* have enough input while in < last */
+ unsigned char FAR *out; /* local strm->next_out */
+ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */
+ unsigned char FAR *end; /* while out < end, enough space available */
+#ifdef INFLATE_STRICT
+ unsigned dmax; /* maximum distance from zlib header */
+#endif
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned wnext; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */
+ unsigned long hold; /* local strm->hold */
+ unsigned bits; /* local strm->bits */
+ code const FAR *lcode; /* local strm->lencode */
+ code const FAR *dcode; /* local strm->distcode */
+ unsigned lmask; /* mask for first level of length codes */
+ unsigned dmask; /* mask for first level of distance codes */
+ code here; /* retrieved table entry */
+ unsigned op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ unsigned len; /* match length, unused bytes */
+ unsigned dist; /* match distance */
+ unsigned char FAR *from; /* where to copy match from */
+
+ /* copy state to local variables */
+ state = (struct inflate_state FAR *)strm->state;
+ in = strm->next_in;
+ last = in + (strm->avail_in - 5);
+ out = strm->next_out;
+ beg = out - (start - strm->avail_out);
+ end = out + (strm->avail_out - 257);
+#ifdef INFLATE_STRICT
+ dmax = state->dmax;
+#endif
+ wsize = state->wsize;
+ whave = state->whave;
+ wnext = state->wnext;
+ window = state->window;
+ hold = state->hold;
+ bits = state->bits;
+ lcode = state->lencode;
+ dcode = state->distcode;
+ lmask = (1U << state->lenbits) - 1;
+ dmask = (1U << state->distbits) - 1;
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+ do {
+ if (bits < 15) {
+ hold += (unsigned long)(*in++) << bits;
+ bits += 8;
+ hold += (unsigned long)(*in++) << bits;
+ bits += 8;
+ }
+ here = lcode[hold & lmask];
+ dolen:
+ op = (unsigned)(here.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(here.op);
+ if (op == 0) { /* literal */
+ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", here.val));
+ *out++ = (unsigned char)(here.val);
+ }
+ else if (op & 16) { /* length base */
+ len = (unsigned)(here.val);
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += (unsigned long)(*in++) << bits;
+ bits += 8;
+ }
+ len += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ }
+ Tracevv((stderr, "inflate: length %u\n", len));
+ if (bits < 15) {
+ hold += (unsigned long)(*in++) << bits;
+ bits += 8;
+ hold += (unsigned long)(*in++) << bits;
+ bits += 8;
+ }
+ here = dcode[hold & dmask];
+ dodist:
+ op = (unsigned)(here.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(here.op);
+ if (op & 16) { /* distance base */
+ dist = (unsigned)(here.val);
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += (unsigned long)(*in++) << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += (unsigned long)(*in++) << bits;
+ bits += 8;
+ }
+ }
+ dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+ if (dist > dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ hold >>= op;
+ bits -= op;
+ Tracevv((stderr, "inflate: distance %u\n", dist));
+ op = (unsigned)(out - beg); /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ if (state->sane) {
+ strm->msg =
+ (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ if (len <= op - whave) {
+ do {
+ *out++ = 0;
+ } while (--len);
+ continue;
+ }
+ len -= op - whave;
+ do {
+ *out++ = 0;
+ } while (--op > whave);
+ if (op == 0) {
+ from = out - dist;
+ do {
+ *out++ = *from++;
+ } while (--len);
+ continue;
+ }
+#endif
+ }
+ from = window;
+ if (wnext == 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ *out++ = *from++;
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ else if (wnext < op) { /* wrap around window */
+ from += wsize + wnext - op;
+ op -= wnext;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ *out++ = *from++;
+ } while (--op);
+ from = window;
+ if (wnext < len) { /* some from start of window */
+ op = wnext;
+ len -= op;
+ do {
+ *out++ = *from++;
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += wnext - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ *out++ = *from++;
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ while (len > 2) {
+ *out++ = *from++;
+ *out++ = *from++;
+ *out++ = *from++;
+ len -= 3;
+ }
+ if (len) {
+ *out++ = *from++;
+ if (len > 1)
+ *out++ = *from++;
+ }
+ }
+ else {
+ from = out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ *out++ = *from++;
+ *out++ = *from++;
+ *out++ = *from++;
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ *out++ = *from++;
+ if (len > 1)
+ *out++ = *from++;
+ }
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level distance code */
+ here = dcode[here.val + (hold & ((1U << op) - 1))];
+ goto dodist;
+ }
+ else {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level length code */
+ here = lcode[here.val + (hold & ((1U << op) - 1))];
+ goto dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ else {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ } while (in < last && out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ in -= len;
+ bits -= len << 3;
+ hold &= (1U << bits) - 1;
+
+ /* update state and return */
+ strm->next_in = in;
+ strm->next_out = out;
+ strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+ strm->avail_out = (unsigned)(out < end ?
+ 257 + (end - out) : 257 - (out - end));
+ state->hold = hold;
+ state->bits = bits;
+ return;
+}
+
+/*
+ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+ - Using bit fields for code structure
+ - Different op definition to avoid & for extra bits (do & for table bits)
+ - Three separate decoding do-loops for direct, window, and wnext == 0
+ - Special case for distance > 1 copies to do overlapped load and store copy
+ - Explicit branch predictions (based on measured branch probabilities)
+ - Deferring match copy and interspersed it with decoding subsequent codes
+ - Swapping literal/length else
+ - Swapping window/direct else
+ - Larger unrolled copy loops (three is about right)
+ - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
diff --git a/zlib/inffast.h b/zlib/inffast.h
new file mode 100644
index 0000000..e5c1aa4
--- /dev/null
+++ b/zlib/inffast.h
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));
diff --git a/zlib/inffixed.h b/zlib/inffixed.h
new file mode 100644
index 0000000..d628327
--- /dev/null
+++ b/zlib/inffixed.h
@@ -0,0 +1,94 @@
+ /* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by makefixed().
+ */
+
+ /* WARNING: this file should *not* be used by applications.
+ It is part of the implementation of this library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+ static const code lenfix[512] = {
+ {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+ {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+ {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+ {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+ {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+ {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+ {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+ {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+ {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+ {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+ {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+ {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+ {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+ {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+ {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+ {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+ {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+ {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+ {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+ {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+ {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+ {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+ {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+ {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+ {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+ {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+ {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+ {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+ {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+ {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+ {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+ {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+ {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+ {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+ {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+ {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+ {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+ {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+ {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+ {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+ {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+ {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+ {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+ {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+ {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+ {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+ {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+ {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+ {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+ {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+ {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+ {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+ {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+ {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+ {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+ {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+ {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+ {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+ {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+ {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+ {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+ {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+ {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+ {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+ {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+ {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+ {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+ {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+ {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+ {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+ {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+ {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+ {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+ {0,9,255}
+ };
+
+ static const code distfix[32] = {
+ {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+ {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+ {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+ {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+ {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+ {22,5,193},{64,5,0}
+ };
diff --git a/zlib/inflate.c b/zlib/inflate.c
new file mode 100644
index 0000000..e9840b6
--- /dev/null
+++ b/zlib/inflate.c
@@ -0,0 +1,1536 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2012 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0 24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ * creation of window when not needed, minimize use of window when it is
+ * needed, make inffast.c even faster, implement gzip decoding, and to
+ * improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1 25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2 4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ * to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3 22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ * buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4 1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common wnext == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ * source file infback.c to provide a call-back interface to inflate for
+ * programs like gzip and unzip -- uses window as output buffer to avoid
+ * window copying
+ *
+ * 1.2.beta5 1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ * input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6 4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ * make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7 27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0 9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ * for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ * and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+# ifndef BUILDFIXED
+# define BUILDFIXED
+# endif
+#endif
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
+ unsigned copy));
+#ifdef BUILDFIXED
+ void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
+ unsigned len));
+
+int ZEXPORT inflateResetKeep(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ strm->total_in = strm->total_out = state->total = 0;
+ strm->msg = Z_NULL;
+ if (state->wrap) /* to support ill-conceived Java test suite */
+ strm->adler = state->wrap & 1;
+ state->mode = HEAD;
+ state->last = 0;
+ state->havedict = 0;
+ state->dmax = 32768U;
+ state->head = Z_NULL;
+ state->hold = 0;
+ state->bits = 0;
+ state->lencode = state->distcode = state->next = state->codes;
+ state->sane = 1;
+ state->back = -1;
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ state->wsize = 0;
+ state->whave = 0;
+ state->wnext = 0;
+ return inflateResetKeep(strm);
+}
+
+int ZEXPORT inflateReset2(strm, windowBits)
+z_streamp strm;
+int windowBits;
+{
+ int wrap;
+ struct inflate_state FAR *state;
+
+ /* get the state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* extract wrap request from windowBits parameter */
+ if (windowBits < 0) {
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ wrap = (windowBits >> 4) + 1;
+#ifdef GUNZIP
+ if (windowBits < 48)
+ windowBits &= 15;
+#endif
+ }
+
+ /* set number of window bits, free window if different */
+ if (windowBits && (windowBits < 8 || windowBits > 15))
+ return Z_STREAM_ERROR;
+ if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {
+ ZFREE(strm, state->window);
+ state->window = Z_NULL;
+ }
+
+ /* update state and reset the rest of it */
+ state->wrap = wrap;
+ state->wbits = (unsigned)windowBits;
+ return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+ int ret;
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+#endif
+ }
+ if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zfree = zcfree;
+#endif
+ state = (struct inflate_state FAR *)
+ ZALLOC(strm, 1, sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (struct internal_state FAR *)state;
+ state->window = Z_NULL;
+ ret = inflateReset2(strm, windowBits);
+ if (ret != Z_OK) {
+ ZFREE(strm, state);
+ strm->state = Z_NULL;
+ }
+ return ret;
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+int ZEXPORT inflatePrime(strm, bits, value)
+z_streamp strm;
+int bits;
+int value;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (bits < 0) {
+ state->hold = 0;
+ state->bits = 0;
+ return Z_OK;
+ }
+ if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR;
+ value &= (1L << bits) - 1;
+ state->hold += value << state->bits;
+ state->bits += bits;
+ return Z_OK;
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+ Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also
+ defines BUILDFIXED, so the tables are built on the fly. makefixed() writes
+ those tables to stdout, which would be piped to inffixed.h. A small program
+ can simply call makefixed to do this:
+
+ void makefixed(void);
+
+ int main(void)
+ {
+ makefixed();
+ return 0;
+ }
+
+ Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+ a.out > inffixed.h
+ */
+void makefixed()
+{
+ unsigned low, size;
+ struct inflate_state state;
+
+ fixedtables(&state);
+ puts(" /* inffixed.h -- table for decoding fixed codes");
+ puts(" * Generated automatically by makefixed().");
+ puts(" */");
+ puts("");
+ puts(" /* WARNING: this file should *not* be used by applications.");
+ puts(" It is part of the implementation of this library and is");
+ puts(" subject to change. Applications should only use zlib.h.");
+ puts(" */");
+ puts("");
+ size = 1U << 9;
+ printf(" static const code lenfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 7) == 0) printf("\n ");
+ printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
+ state.lencode[low].bits, state.lencode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+ size = 1U << 5;
+ printf("\n static const code distfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 6) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+ state.distcode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+}
+#endif /* MAKEFIXED */
+
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. If window does not exist yet, create it. This is only called
+ when a window is already in use, or when output has been written during this
+ inflate call, but the end of the deflate stream has not been reached yet.
+ It is also called to create a window for dictionary data when a dictionary
+ is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, end, copy)
+z_streamp strm;
+const Bytef *end;
+unsigned copy;
+{
+ struct inflate_state FAR *state;
+ unsigned dist;
+
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* if it hasn't been done already, allocate space for the window */
+ if (state->window == Z_NULL) {
+ state->window = (unsigned char FAR *)
+ ZALLOC(strm, 1U << state->wbits,
+ sizeof(unsigned char));
+ if (state->window == Z_NULL) return 1;
+ }
+
+ /* if window not in use yet, initialize */
+ if (state->wsize == 0) {
+ state->wsize = 1U << state->wbits;
+ state->wnext = 0;
+ state->whave = 0;
+ }
+
+ /* copy state->wsize or less output bytes into the circular window */
+ if (copy >= state->wsize) {
+ zmemcpy(state->window, end - state->wsize, state->wsize);
+ state->wnext = 0;
+ state->whave = state->wsize;
+ }
+ else {
+ dist = state->wsize - state->wnext;
+ if (dist > copy) dist = copy;
+ zmemcpy(state->window + state->wnext, end - copy, dist);
+ copy -= dist;
+ if (copy) {
+ zmemcpy(state->window, end - copy, copy);
+ state->wnext = copy;
+ state->whave = state->wsize;
+ }
+ else {
+ state->wnext += dist;
+ if (state->wnext == state->wsize) state->wnext = 0;
+ if (state->whave < state->wsize) state->whave += dist;
+ }
+ }
+ return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+# define UPDATE(check, buf, len) \
+ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+# define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+# define CRC2(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ check = crc32(check, hbuf, 2); \
+ } while (0)
+
+# define CRC4(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ hbuf[2] = (unsigned char)((word) >> 16); \
+ hbuf[3] = (unsigned char)((word) >> 24); \
+ check = crc32(check, hbuf, 4); \
+ } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+ if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ if (have == 0) goto inf_leave; \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/*
+ inflate() uses a state machine to process as much input data and generate as
+ much output data as possible before returning. The state machine is
+ structured roughly as follows:
+
+ for (;;) switch (state) {
+ ...
+ case STATEn:
+ if (not enough input data or output space to make progress)
+ return;
+ ... make progress ...
+ state = STATEm;
+ break;
+ ...
+ }
+
+ so when inflate() is called again, the same case is attempted again, and
+ if the appropriate resources are provided, the machine proceeds to the
+ next state. The NEEDBITS() macro is usually the way the state evaluates
+ whether it can proceed or should return. NEEDBITS() does the return if
+ the requested bits are not available. The typical use of the BITS macros
+ is:
+
+ NEEDBITS(n);
+ ... do something with BITS(n) ...
+ DROPBITS(n);
+
+ where NEEDBITS(n) either returns from inflate() if there isn't enough
+ input left to load n bits into the accumulator, or it continues. BITS(n)
+ gives the low n bits in the accumulator. When done, DROPBITS(n) drops
+ the low n bits off the accumulator. INITBITS() clears the accumulator
+ and sets the number of available bits to zero. BYTEBITS() discards just
+ enough bits to put the accumulator on a byte boundary. After BYTEBITS()
+ and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+ NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+ if there is no input available. The decoding of variable length codes uses
+ PULLBYTE() directly in order to pull just enough bytes to decode the next
+ code, and no more.
+
+ Some states loop until they get enough input, making sure that enough
+ state information is maintained to continue the loop where it left off
+ if NEEDBITS() returns in the loop. For example, want, need, and keep
+ would all have to actually be part of the saved state in case NEEDBITS()
+ returns:
+
+ case STATEw:
+ while (want < need) {
+ NEEDBITS(n);
+ keep[want++] = BITS(n);
+ DROPBITS(n);
+ }
+ state = STATEx;
+ case STATEx:
+
+ As shown above, if the next state is also the next case, then the break
+ is omitted.
+
+ A state may also return if there is not enough output space available to
+ complete that state. Those states are copying stored data, writing a
+ literal byte, and copying a matching string.
+
+ When returning, a "goto inf_leave" is used to update the total counters,
+ update the check value, and determine whether any progress has been made
+ during that inflate() call in order to return the proper return code.
+ Progress is defined as a change in either strm->avail_in or strm->avail_out.
+ When there is a window, goto inf_leave will update the window with the last
+ output written. If a goto inf_leave occurs in the middle of decompression
+ and there is no window currently, goto inf_leave will create one and copy
+ output to the window for the next call of inflate().
+
+ In this implementation, the flush parameter of inflate() only affects the
+ return code (per zlib.h). inflate() always writes as much as possible to
+ strm->next_out, given the space available and the provided input--the effect
+ documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
+ the allocation of and copying into a sliding window until necessary, which
+ provides the effect documented in zlib.h for Z_FINISH when the entire input
+ stream available. So the only thing the flush parameter actually does is:
+ when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
+ will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+ struct inflate_state FAR *state;
+ z_const unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned in, out; /* save starting available input and output */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code here; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+#ifdef GUNZIP
+ unsigned char hbuf[4]; /* buffer for gzip header crc calculation */
+#endif
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0))
+ return Z_STREAM_ERROR;
+
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */
+ LOAD();
+ in = have;
+ out = left;
+ ret = Z_OK;
+ for (;;)
+ switch (state->mode) {
+ case HEAD:
+ if (state->wrap == 0) {
+ state->mode = TYPEDO;
+ break;
+ }
+ NEEDBITS(16);
+#ifdef GUNZIP
+ if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */
+ state->check = crc32(0L, Z_NULL, 0);
+ CRC2(state->check, hold);
+ INITBITS();
+ state->mode = FLAGS;
+ break;
+ }
+ state->flags = 0; /* expect zlib header */
+ if (state->head != Z_NULL)
+ state->head->done = -1;
+ if (!(state->wrap & 1) || /* check if zlib header allowed */
+#else
+ if (
+#endif
+ ((BITS(8) << 8) + (hold >> 8)) % 31) {
+ strm->msg = (char *)"incorrect header check";
+ state->mode = BAD;
+ break;
+ }
+ if (BITS(4) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ DROPBITS(4);
+ len = BITS(4) + 8;
+ if (state->wbits == 0)
+ state->wbits = len;
+ else if (len > state->wbits) {
+ strm->msg = (char *)"invalid window size";
+ state->mode = BAD;
+ break;
+ }
+ state->dmax = 1U << len;
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = hold & 0x200 ? DICTID : TYPE;
+ INITBITS();
+ break;
+#ifdef GUNZIP
+ case FLAGS:
+ NEEDBITS(16);
+ state->flags = (int)(hold);
+ if ((state->flags & 0xff) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ if (state->flags & 0xe000) {
+ strm->msg = (char *)"unknown header flags set";
+ state->mode = BAD;
+ break;
+ }
+ if (state->head != Z_NULL)
+ state->head->text = (int)((hold >> 8) & 1);
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = TIME;
+ /* FALL THROUGH */
+ case TIME:
+ NEEDBITS(32);
+ if (state->head != Z_NULL)
+ state->head->time = hold;
+ if (state->flags & 0x0200) CRC4(state->check, hold);
+ INITBITS();
+ state->mode = OS;
+ /* FALL THROUGH */
+ case OS:
+ NEEDBITS(16);
+ if (state->head != Z_NULL) {
+ state->head->xflags = (int)(hold & 0xff);
+ state->head->os = (int)(hold >> 8);
+ }
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = EXLEN;
+ /* FALL THROUGH */
+ case EXLEN:
+ if (state->flags & 0x0400) {
+ NEEDBITS(16);
+ state->length = (unsigned)(hold);
+ if (state->head != Z_NULL)
+ state->head->extra_len = (unsigned)hold;
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ }
+ else if (state->head != Z_NULL)
+ state->head->extra = Z_NULL;
+ state->mode = EXTRA;
+ /* FALL THROUGH */
+ case EXTRA:
+ if (state->flags & 0x0400) {
+ copy = state->length;
+ if (copy > have) copy = have;
+ if (copy) {
+ if (state->head != Z_NULL &&
+ state->head->extra != Z_NULL &&
+ (len = state->head->extra_len - state->length) <
+ state->head->extra_max) {
+ zmemcpy(state->head->extra + len, next,
+ len + copy > state->head->extra_max ?
+ state->head->extra_max - len : copy);
+ }
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ state->length -= copy;
+ }
+ if (state->length) goto inf_leave;
+ }
+ state->length = 0;
+ state->mode = NAME;
+ /* FALL THROUGH */
+ case NAME:
+ if (state->flags & 0x0800) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ if (state->head != Z_NULL &&
+ state->head->name != Z_NULL &&
+ state->length < state->head->name_max)
+ state->head->name[state->length++] = len;
+ } while (len && copy < have);
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ else if (state->head != Z_NULL)
+ state->head->name = Z_NULL;
+ state->length = 0;
+ state->mode = COMMENT;
+ /* FALL THROUGH */
+ case COMMENT:
+ if (state->flags & 0x1000) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ if (state->head != Z_NULL &&
+ state->head->comment != Z_NULL &&
+ state->length < state->head->comm_max)
+ state->head->comment[state->length++] = len;
+ } while (len && copy < have);
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ else if (state->head != Z_NULL)
+ state->head->comment = Z_NULL;
+ state->mode = HCRC;
+ /* FALL THROUGH */
+ case HCRC:
+ if (state->flags & 0x0200) {
+ NEEDBITS(16);
+ if (hold != (state->check & 0xffff)) {
+ strm->msg = (char *)"header crc mismatch";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ }
+ if (state->head != Z_NULL) {
+ state->head->hcrc = (int)((state->flags >> 9) & 1);
+ state->head->done = 1;
+ }
+ strm->adler = state->check = crc32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ break;
+#endif
+ case DICTID:
+ NEEDBITS(32);
+ strm->adler = state->check = ZSWAP32(hold);
+ INITBITS();
+ state->mode = DICT;
+ /* FALL THROUGH */
+ case DICT:
+ if (state->havedict == 0) {
+ RESTORE();
+ return Z_NEED_DICT;
+ }
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ /* FALL THROUGH */
+ case TYPE:
+ if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
+ /* FALL THROUGH */
+ case TYPEDO:
+ if (state->last) {
+ BYTEBITS();
+ state->mode = CHECK;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN_; /* decode codes */
+ if (flush == Z_TREES) {
+ DROPBITS(2);
+ goto inf_leave;
+ }
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+ case STORED:
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+ state->mode = COPY_;
+ if (flush == Z_TREES) goto inf_leave;
+ /* FALLTHROUGH */
+ case COPY_:
+ state->mode = COPY;
+ /* FALLTHROUGH */
+ case COPY:
+ copy = state->length;
+ if (copy) {
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ if (copy == 0) goto inf_leave;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ break;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ state->have = 0;
+ state->mode = LENLENS;
+ /* FALL THROUGH */
+ case LENLENS:
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (const code FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+ state->have = 0;
+ state->mode = CODELENS;
+ /* FALL THROUGH */
+ case CODELENS:
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ here = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (here.val < 16) {
+ DROPBITS(here.bits);
+ state->lens[state->have++] = here.val;
+ }
+ else {
+ if (here.val == 16) {
+ NEEDBITS(here.bits + 2);
+ DROPBITS(here.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = state->lens[state->have - 1];
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (here.val == 17) {
+ NEEDBITS(here.bits + 3);
+ DROPBITS(here.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(here.bits + 7);
+ DROPBITS(here.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* check for end-of-block code (better have one) */
+ if (state->lens[256] == 0) {
+ strm->msg = (char *)"invalid code -- missing end-of-block";
+ state->mode = BAD;
+ break;
+ }
+
+ /* build code tables -- note: do not change the lenbits or distbits
+ values here (9 and 6) without reading the comments in inftrees.h
+ concerning the ENOUGH constants, which depend on those values */
+ state->next = state->codes;
+ state->lencode = (const code FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (const code FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN_;
+ if (flush == Z_TREES) goto inf_leave;
+ /* FALL THROUGH */
+ case LEN_:
+ state->mode = LEN;
+ /* FALL THROUGH */
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ inflate_fast(strm, out);
+ LOAD();
+ if (state->mode == TYPE)
+ state->back = -1;
+ break;
+ }
+ state->back = 0;
+ for (;;) {
+ here = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (here.op && (here.op & 0xf0) == 0) {
+ last = here;
+ for (;;) {
+ here = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ state->back += last.bits;
+ }
+ DROPBITS(here.bits);
+ state->back += here.bits;
+ state->length = (unsigned)here.val;
+ if ((int)(here.op) == 0) {
+ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", here.val));
+ state->mode = LIT;
+ break;
+ }
+ if (here.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->back = -1;
+ state->mode = TYPE;
+ break;
+ }
+ if (here.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ state->extra = (unsigned)(here.op) & 15;
+ state->mode = LENEXT;
+ /* FALL THROUGH */
+ case LENEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ state->back += state->extra;
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+ state->was = state->length;
+ state->mode = DIST;
+ /* FALL THROUGH */
+ case DIST:
+ for (;;) {
+ here = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((here.op & 0xf0) == 0) {
+ last = here;
+ for (;;) {
+ here = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ state->back += last.bits;
+ }
+ DROPBITS(here.bits);
+ state->back += here.bits;
+ if (here.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)here.val;
+ state->extra = (unsigned)(here.op) & 15;
+ state->mode = DISTEXT;
+ /* FALL THROUGH */
+ case DISTEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ state->back += state->extra;
+ }
+#ifdef INFLATE_STRICT
+ if (state->offset > state->dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+ state->mode = MATCH;
+ /* FALL THROUGH */
+ case MATCH:
+ if (left == 0) goto inf_leave;
+ copy = out - left;
+ if (state->offset > copy) { /* copy from window */
+ copy = state->offset - copy;
+ if (copy > state->whave) {
+ if (state->sane) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ Trace((stderr, "inflate.c too far\n"));
+ copy -= state->whave;
+ if (copy > state->length) copy = state->length;
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = 0;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+#endif
+ }
+ if (copy > state->wnext) {
+ copy -= state->wnext;
+ from = state->window + (state->wsize - copy);
+ }
+ else
+ from = state->window + (state->wnext - copy);
+ if (copy > state->length) copy = state->length;
+ }
+ else { /* copy from output */
+ from = put - state->offset;
+ copy = state->length;
+ }
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+ case LIT:
+ if (left == 0) goto inf_leave;
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ case CHECK:
+ if (state->wrap) {
+ NEEDBITS(32);
+ out -= left;
+ strm->total_out += out;
+ state->total += out;
+ if (out)
+ strm->adler = state->check =
+ UPDATE(state->check, put - out, out);
+ out = left;
+ if ((
+#ifdef GUNZIP
+ state->flags ? hold :
+#endif
+ ZSWAP32(hold)) != state->check) {
+ strm->msg = (char *)"incorrect data check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: check matches trailer\n"));
+ }
+#ifdef GUNZIP
+ state->mode = LENGTH;
+ /* FALL THROUGH */
+ case LENGTH:
+ if (state->wrap && state->flags) {
+ NEEDBITS(32);
+ if (hold != (state->total & 0xffffffffUL)) {
+ strm->msg = (char *)"incorrect length check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: length matches trailer\n"));
+ }
+#endif
+ state->mode = DONE;
+ /* FALL THROUGH */
+ case DONE:
+ ret = Z_STREAM_END;
+ goto inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+ inf_leave:
+ RESTORE();
+ if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
+ (state->mode < CHECK || flush != Z_FINISH)))
+ if (updatewindow(strm, strm->next_out, out - strm->avail_out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ in -= strm->avail_in;
+ out -= strm->avail_out;
+ strm->total_in += in;
+ strm->total_out += out;
+ state->total += out;
+ if (state->wrap && out)
+ strm->adler = state->check =
+ UPDATE(state->check, strm->next_out - out, out);
+ strm->data_type = state->bits + (state->last ? 64 : 0) +
+ (state->mode == TYPE ? 128 : 0) +
+ (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
+ if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+ ret = Z_BUF_ERROR;
+ return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->window != Z_NULL) ZFREE(strm, state->window);
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+Bytef *dictionary;
+uInt *dictLength;
+{
+ struct inflate_state FAR *state;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* copy dictionary */
+ if (state->whave && dictionary != Z_NULL) {
+ zmemcpy(dictionary, state->window + state->wnext,
+ state->whave - state->wnext);
+ zmemcpy(dictionary + state->whave - state->wnext,
+ state->window, state->wnext);
+ }
+ if (dictLength != Z_NULL)
+ *dictLength = state->whave;
+ return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ struct inflate_state FAR *state;
+ unsigned long dictid;
+ int ret;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->wrap != 0 && state->mode != DICT)
+ return Z_STREAM_ERROR;
+
+ /* check for correct dictionary identifier */
+ if (state->mode == DICT) {
+ dictid = adler32(0L, Z_NULL, 0);
+ dictid = adler32(dictid, dictionary, dictLength);
+ if (dictid != state->check)
+ return Z_DATA_ERROR;
+ }
+
+ /* copy dictionary to window using updatewindow(), which will amend the
+ existing dictionary if appropriate */
+ ret = updatewindow(strm, dictionary + dictLength, dictLength);
+ if (ret) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ state->havedict = 1;
+ Tracev((stderr, "inflate: dictionary set\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateGetHeader(strm, head)
+z_streamp strm;
+gz_headerp head;
+{
+ struct inflate_state FAR *state;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
+
+ /* save header structure */
+ state->head = head;
+ head->done = 0;
+ return Z_OK;
+}
+
+/*
+ Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found
+ or when out of input. When called, *have is the number of pattern bytes
+ found in order so far, in 0..3. On return *have is updated to the new
+ state. If on return *have equals four, then the pattern was found and the
+ return value is how many bytes were read including the last byte of the
+ pattern. If *have is less than four, then the pattern has not been found
+ yet and the return value is len. In the latter case, syncsearch() can be
+ called again with more data and the *have state. *have is initialized to
+ zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+const unsigned char FAR *buf;
+unsigned len;
+{
+ unsigned got;
+ unsigned next;
+
+ got = *have;
+ next = 0;
+ while (next < len && got < 4) {
+ if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+ got++;
+ else if (buf[next])
+ got = 0;
+ else
+ got = 4 - got;
+ next++;
+ }
+ *have = got;
+ return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+ unsigned len; /* number of bytes to look at or looked at */
+ unsigned long in, out; /* temporary to save total_in and total_out */
+ unsigned char buf[4]; /* to restore bit buffer to byte string */
+ struct inflate_state FAR *state;
+
+ /* check parameters */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+ /* if first time, start search in bit buffer */
+ if (state->mode != SYNC) {
+ state->mode = SYNC;
+ state->hold <<= state->bits & 7;
+ state->bits -= state->bits & 7;
+ len = 0;
+ while (state->bits >= 8) {
+ buf[len++] = (unsigned char)(state->hold);
+ state->hold >>= 8;
+ state->bits -= 8;
+ }
+ state->have = 0;
+ syncsearch(&(state->have), buf, len);
+ }
+
+ /* search available input */
+ len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+ strm->avail_in -= len;
+ strm->next_in += len;
+ strm->total_in += len;
+
+ /* return no joy or set up to restart inflate() on a new block */
+ if (state->have != 4) return Z_DATA_ERROR;
+ in = strm->total_in; out = strm->total_out;
+ inflateReset(strm);
+ strm->total_in = in; strm->total_out = out;
+ state->mode = TYPE;
+ return Z_OK;
+}
+
+/*
+ Returns true if inflate is currently at the end of a block generated by
+ Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ implementation to provide an additional safety check. PPP uses
+ Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+ block. When decompressing, PPP checks that at the end of input packet,
+ inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+ struct inflate_state FAR *state;
+ struct inflate_state FAR *copy;
+ unsigned char FAR *window;
+ unsigned wsize;
+
+ /* check input */
+ if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
+ source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)source->state;
+
+ /* allocate space */
+ copy = (struct inflate_state FAR *)
+ ZALLOC(source, 1, sizeof(struct inflate_state));
+ if (copy == Z_NULL) return Z_MEM_ERROR;
+ window = Z_NULL;
+ if (state->window != Z_NULL) {
+ window = (unsigned char FAR *)
+ ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+ if (window == Z_NULL) {
+ ZFREE(source, copy);
+ return Z_MEM_ERROR;
+ }
+ }
+
+ /* copy state */
+ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
+ zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
+ if (state->lencode >= state->codes &&
+ state->lencode <= state->codes + ENOUGH - 1) {
+ copy->lencode = copy->codes + (state->lencode - state->codes);
+ copy->distcode = copy->codes + (state->distcode - state->codes);
+ }
+ copy->next = copy->codes + (state->next - state->codes);
+ if (window != Z_NULL) {
+ wsize = 1U << state->wbits;
+ zmemcpy(window, state->window, wsize);
+ }
+ copy->window = window;
+ dest->state = (struct internal_state FAR *)copy;
+ return Z_OK;
+}
+
+int ZEXPORT inflateUndermine(strm, subvert)
+z_streamp strm;
+int subvert;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ state->sane = !subvert;
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ return Z_OK;
+#else
+ state->sane = 1;
+ return Z_DATA_ERROR;
+#endif
+}
+
+long ZEXPORT inflateMark(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return (long)(((unsigned long)0 - 1) << 16);
+ state = (struct inflate_state FAR *)strm->state;
+ return (long)(((unsigned long)((long)state->back)) << 16) +
+ (state->mode == COPY ? state->length :
+ (state->mode == MATCH ? state->was - state->length : 0));
+}
diff --git a/zlib/inflate.h b/zlib/inflate.h
new file mode 100644
index 0000000..abe7968
--- /dev/null
+++ b/zlib/inflate.h
@@ -0,0 +1,126 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2009 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer decoding by inflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip decoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GUNZIP
+#endif
+
+#ifdef BAD /* For AIX */
+#undef BAD
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+ HEAD, /* i: waiting for magic header */
+ FLAGS, /* i: waiting for method and flags (gzip) */
+ TIME, /* i: waiting for modification time (gzip) */
+ OS, /* i: waiting for extra flags and operating system (gzip) */
+ EXLEN, /* i: waiting for extra length (gzip) */
+ EXTRA, /* i: waiting for extra bytes (gzip) */
+ NAME, /* i: waiting for end of file name (gzip) */
+ COMMENT, /* i: waiting for end of comment (gzip) */
+ HCRC, /* i: waiting for header crc (gzip) */
+ DICTID, /* i: waiting for dictionary check value */
+ DICT, /* waiting for inflateSetDictionary() call */
+ TYPE, /* i: waiting for type bits, including last-flag bit */
+ TYPEDO, /* i: same, but skip check to exit inflate on new block */
+ STORED, /* i: waiting for stored size (length and complement) */
+ COPY_, /* i/o: same as COPY below, but only first time in */
+ COPY, /* i/o: waiting for input or output to copy stored block */
+ TABLE, /* i: waiting for dynamic block table lengths */
+ LENLENS, /* i: waiting for code length code lengths */
+ CODELENS, /* i: waiting for length/lit and distance code lengths */
+ LEN_, /* i: same as LEN below, but only first time in */
+ LEN, /* i: waiting for length/lit/eob code */
+ LENEXT, /* i: waiting for length extra bits */
+ DIST, /* i: waiting for distance code */
+ DISTEXT, /* i: waiting for distance extra bits */
+ MATCH, /* o: waiting for output space to copy string */
+ LIT, /* o: waiting for output space to write literal */
+ CHECK, /* i: waiting for 32-bit check value */
+ LENGTH, /* i: waiting for 32-bit length (gzip) */
+ DONE, /* finished check, done -- remain here until reset */
+ BAD, /* got a data error -- remain here until reset */
+ MEM, /* got an inflate() memory error -- remain here until reset */
+ SYNC /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+ State transitions between above modes -
+
+ (most modes can go to BAD or MEM on error -- not shown for clarity)
+
+ Process header:
+ HEAD -> (gzip) or (zlib) or (raw)
+ (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->
+ HCRC -> TYPE
+ (zlib) -> DICTID or TYPE
+ DICTID -> DICT -> TYPE
+ (raw) -> TYPEDO
+ Read deflate blocks:
+ TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK
+ STORED -> COPY_ -> COPY -> TYPE
+ TABLE -> LENLENS -> CODELENS -> LEN_
+ LEN_ -> LEN
+ Read deflate codes in fixed or dynamic block:
+ LEN -> LENEXT or LIT or TYPE
+ LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+ LIT -> LEN
+ Process trailer:
+ CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls. Approximately 10K bytes. */
+struct inflate_state {
+ inflate_mode mode; /* current inflate mode */
+ int last; /* true if processing last block */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ int havedict; /* true if dictionary provided */
+ int flags; /* gzip header method and flags (0 if zlib) */
+ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */
+ unsigned long check; /* protected copy of check value */
+ unsigned long total; /* protected copy of output count */
+ gz_headerp head; /* where to save gzip header information */
+ /* sliding window */
+ unsigned wbits; /* log base 2 of requested window size */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned wnext; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if needed */
+ /* bit accumulator */
+ unsigned long hold; /* input bit accumulator */
+ unsigned bits; /* number of bits in "in" */
+ /* for string and stored block copying */
+ unsigned length; /* literal or length of data to copy */
+ unsigned offset; /* distance back to copy string from */
+ /* for table and code decoding */
+ unsigned extra; /* extra bits needed */
+ /* fixed and dynamic code tables */
+ code const FAR *lencode; /* starting table for length/literal codes */
+ code const FAR *distcode; /* starting table for distance codes */
+ unsigned lenbits; /* index bits for lencode */
+ unsigned distbits; /* index bits for distcode */
+ /* dynamic table building */
+ unsigned ncode; /* number of code length code lengths */
+ unsigned nlen; /* number of length code lengths */
+ unsigned ndist; /* number of distance code lengths */
+ unsigned have; /* number of code lengths in lens[] */
+ code FAR *next; /* next available space in codes[] */
+ unsigned short lens[320]; /* temporary storage for code lengths */
+ unsigned short work[288]; /* work area for code table building */
+ code codes[ENOUGH]; /* space for code tables */
+ int sane; /* if false, allow invalid distance too far */
+ int back; /* bits back of last unprocessed length/lit */
+ unsigned was; /* initial length of match */
+};
diff --git a/zlib/inftrees.c b/zlib/inftrees.c
new file mode 100644
index 0000000..571e810
--- /dev/null
+++ b/zlib/inftrees.c
@@ -0,0 +1,304 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2013 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+ " inflate 1.2.8 Copyright 1995-2013 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/*
+ Build a set of tables to decode the provided canonical Huffman code.
+ The code lengths are lens[0..codes-1]. The result starts at *table,
+ whose indices are 0..2^bits-1. work is a writable array of at least
+ lens shorts, which is used as a work area. type is the type of code
+ to be generated, CODES, LENS, or DISTS. On return, zero is success,
+ -1 is an invalid code, and +1 means that ENOUGH isn't enough. table
+ on return points to the next available entry's address. bits is the
+ requested root table index bits, and on return it is the actual root
+ table index bits. It will differ if the request is greater than the
+ longest code or if it is less than the shortest code.
+ */
+int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+ unsigned len; /* a code's length in bits */
+ unsigned sym; /* index of code symbols */
+ unsigned min, max; /* minimum and maximum code lengths */
+ unsigned root; /* number of index bits for root table */
+ unsigned curr; /* number of index bits for current table */
+ unsigned drop; /* code bits to drop for sub-table */
+ int left; /* number of prefix codes available */
+ unsigned used; /* code entries in table used */
+ unsigned huff; /* Huffman code */
+ unsigned incr; /* for incrementing code, index */
+ unsigned fill; /* index for replicating entries */
+ unsigned low; /* low bits for current root entry */
+ unsigned mask; /* mask for low root bits */
+ code here; /* table entry for duplication */
+ code FAR *next; /* next available space in table */
+ const unsigned short FAR *base; /* base value table to use */
+ const unsigned short FAR *extra; /* extra bits table to use */
+ unsigned match; /* use base and extra for symbol >= match */
+ unsigned short count[MAXBITS+1]; /* number of codes of each length */
+ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
+ static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78};
+ static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0};
+ static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64};
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++)
+ count[len] = 0;
+ for (sym = 0; sym < codes; sym++)
+ count[lens[sym]]++;
+
+ /* bound code lengths, force root to be within code lengths */
+ root = *bits;
+ for (max = MAXBITS; max >= 1; max--)
+ if (count[max] != 0) break;
+ if (root > max) root = max;
+ if (max == 0) { /* no symbols to code at all */
+ here.op = (unsigned char)64; /* invalid code marker */
+ here.bits = (unsigned char)1;
+ here.val = (unsigned short)0;
+ *(*table)++ = here; /* make a table to force an error */
+ *(*table)++ = here;
+ *bits = 1;
+ return 0; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min < max; min++)
+ if (count[min] != 0) break;
+ if (root < min) root = min;
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) return -1; /* over-subscribed */
+ }
+ if (left > 0 && (type == CODES || max != 1))
+ return -1; /* incomplete set */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + count[len];
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++)
+ if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked for LENS and DIST tables against
+ the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
+ the initial root table size constants. See the comments in inftrees.h
+ for more information.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ switch (type) {
+ case CODES:
+ base = extra = work; /* dummy value--not used */
+ match = 20;
+ break;
+ case LENS:
+ base = lbase;
+ extra = lext;
+ match = 257;
+ break;
+ default: /* DISTS */
+ base = dbase;
+ extra = dext;
+ match = 0;
+ }
+
+ /* initialize state for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = *table; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = (unsigned)(-1); /* trigger new sub-table when len > root */
+ used = 1U << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if ((type == LENS && used > ENOUGH_LENS) ||
+ (type == DISTS && used > ENOUGH_DISTS))
+ return 1;
+
+ /* process all codes and make table entries */
+ for (;;) {
+ /* create table entry */
+ here.bits = (unsigned char)(len - drop);
+ if (work[sym] + 1u < match) {
+ here.op = (unsigned char)0;
+ here.val = work[sym];
+ }
+ else if (work[sym] >= match) {
+ here.op = (unsigned char)(extra[work[sym] - match]);
+ here.val = base[work[sym] - match];
+ }
+ else {
+ here.op = (unsigned char)(32 + 64); /* end of block */
+ here.val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1U << (len - drop);
+ fill = 1U << curr;
+ min = fill; /* save offset to next table */
+ do {
+ fill -= incr;
+ next[(huff >> drop) + fill] = here;
+ } while (fill != 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--(count[len]) == 0) {
+ if (len == max) break;
+ len = lens[work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) != low) {
+ /* if first time, transition to sub-tables */
+ if (drop == 0)
+ drop = root;
+
+ /* increment past last table */
+ next += min; /* here min is 1 << curr */
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = (int)(1 << curr);
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) break;
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1U << curr;
+ if ((type == LENS && used > ENOUGH_LENS) ||
+ (type == DISTS && used > ENOUGH_DISTS))
+ return 1;
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ (*table)[low].op = (unsigned char)curr;
+ (*table)[low].bits = (unsigned char)root;
+ (*table)[low].val = (unsigned short)(next - *table);
+ }
+ }
+
+ /* fill in remaining table entry if code is incomplete (guaranteed to have
+ at most one remaining entry, since if the code is incomplete, the
+ maximum code length that was allowed to get this far is one bit) */
+ if (huff != 0) {
+ here.op = (unsigned char)64; /* invalid code marker */
+ here.bits = (unsigned char)(len - drop);
+ here.val = (unsigned short)0;
+ next[huff] = here;
+ }
+
+ /* set return parameters */
+ *table += used;
+ *bits = root;
+ return 0;
+}
diff --git a/zlib/inftrees.h b/zlib/inftrees.h
new file mode 100644
index 0000000..baa53a0
--- /dev/null
+++ b/zlib/inftrees.h
@@ -0,0 +1,62 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables. Each entry provides either the
+ information needed to do the operation requested by the code that
+ indexed that table entry, or it provides a pointer to another
+ table that indexes more bits of the code. op indicates whether
+ the entry is a pointer to another table, a literal, a length or
+ distance, an end-of-block, or an invalid code. For a table
+ pointer, the low four bits of op is the number of index bits of
+ that table. For a length or distance, the low four bits of op
+ is the number of extra bits to get after the code. bits is
+ the number of bits in this code or part of the code to drop off
+ of the bit buffer. val is the actual byte to output in the case
+ of a literal, the base length or distance, or the offset from
+ the current table to the next table. Each entry is four bytes. */
+typedef struct {
+ unsigned char op; /* operation, extra bits, table bits */
+ unsigned char bits; /* bits in this part of the code */
+ unsigned short val; /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+ 00000000 - literal
+ 0000tttt - table link, tttt != 0 is the number of table index bits
+ 0001eeee - length or distance, eeee is the number of extra bits
+ 01100000 - end of block
+ 01000000 - invalid code
+ */
+
+/* Maximum size of the dynamic table. The maximum number of code structures is
+ 1444, which is the sum of 852 for literal/length codes and 592 for distance
+ codes. These values were found by exhaustive searches using the program
+ examples/enough.c found in the zlib distribtution. The arguments to that
+ program are the number of symbols, the initial root table size, and the
+ maximum bit length of a code. "enough 286 9 15" for literal/length codes
+ returns returns 852, and "enough 30 6 15" for distance codes returns 592.
+ The initial root table size (9 or 6) is found in the fifth argument of the
+ inflate_table() calls in inflate.c and infback.c. If the root table size is
+ changed, then these maximum sizes would be need to be recalculated and
+ updated. */
+#define ENOUGH_LENS 852
+#define ENOUGH_DISTS 592
+#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)
+
+/* Type of code to build for inflate_table() */
+typedef enum {
+ CODES,
+ LENS,
+ DISTS
+} codetype;
+
+int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,
+ unsigned codes, code FAR * FAR *table,
+ unsigned FAR *bits, unsigned short FAR *work));
diff --git a/zlib/trees.c b/zlib/trees.c
new file mode 100644
index 0000000..9c66770
--- /dev/null
+++ b/zlib/trees.c
@@ -0,0 +1,1204 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2012 Jean-loup Gailly
+ * detect_data_type() function provided freely by Cosmin Truta, 2006
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id$ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+# include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+ const ct_data *static_tree; /* static tree or NULL */
+ const intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, const ct_data *ltree,
+ const ct_data *dtree));
+local int detect_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+# define send_code(s, c, tree) \
+ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (ush)value << s->bi_valid;
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= (ush)value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (ush)val << s->bi_valid;\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (ush)(value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* For some embedded targets, global variables are not initialized: */
+#ifdef NO_INIT_GLOBAL_POINTERS
+ static_l_desc.static_tree = static_ltree;
+ static_l_desc.extra_bits = extra_lbits;
+ static_d_desc.static_tree = static_dtree;
+ static_d_desc.extra_bits = extra_dbits;
+ static_bl_desc.extra_bits = extra_blbits;
+#endif
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ _length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ _length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ _dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ _dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+
+# ifdef GEN_TREES_H
+ gen_trees_header();
+# endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+# ifndef DEBUG
+# include <stdio.h>
+# endif
+
+# define SEPARATOR(i, last, width) \
+ ((i) == (last)? "\n};\n\n" : \
+ ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+ FILE *header = fopen("trees.h", "w");
+ int i;
+
+ Assert (header != NULL, "Can't open trees.h");
+ fprintf(header,
+ "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+ for (i = 0; i < L_CODES+2; i++) {
+ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+ }
+
+ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+ }
+
+ fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n");
+ for (i = 0; i < DIST_CODE_LEN; i++) {
+ fprintf(header, "%2u%s", _dist_code[i],
+ SEPARATOR(i, DIST_CODE_LEN-1, 20));
+ }
+
+ fprintf(header,
+ "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+ fprintf(header, "%2u%s", _length_code[i],
+ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+ }
+
+ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+ for (i = 0; i < LENGTH_CODES; i++) {
+ fprintf(header, "%1u%s", base_length[i],
+ SEPARATOR(i, LENGTH_CODES-1, 20));
+ }
+
+ fprintf(header, "local const int base_dist[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "%5u%s", base_dist[i],
+ SEPARATOR(i, D_CODES-1, 10));
+ }
+
+ fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void ZLIB_INTERNAL _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG
+ s->compressed_len = 0L;
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->sym_next = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ const intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if ((unsigned) tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+ s->depth[n] : s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int last; /* one if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */
+#ifdef DEBUG
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+#endif
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
+ */
+void ZLIB_INTERNAL _tr_flush_bits(s)
+ deflate_state *s;
+{
+ bi_flush(s);
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ */
+void ZLIB_INTERNAL _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+ bi_flush(s);
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int last; /* one if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is binary or text */
+ if (s->strm->data_type == Z_UNKNOWN)
+ s->strm->data_type = detect_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute the block lengths in bytes. */
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->sym_next / 3));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, last);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+last, 3);
+ compress_block(s, (const ct_data *)static_ltree,
+ (const ct_data *)static_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->static_len;
+#endif
+ } else {
+ send_bits(s, (DYN_TREES<<1)+last, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (const ct_data *)s->dyn_ltree,
+ (const ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->opt_len;
+#endif
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and uLong implemented on 32 bits.
+ */
+ init_block(s);
+
+ if (last) {
+ bi_windup(s);
+#ifdef DEBUG
+ s->compressed_len += 7; /* align on byte boundary */
+#endif
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*last));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int ZLIB_INTERNAL _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->sym_buf[s->sym_next++] = dist;
+ s->sym_buf[s->sym_next++] = dist >> 8;
+ s->sym_buf[s->sym_next++] = lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+ return (s->sym_next == s->sym_end);
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ const ct_data *ltree; /* literal tree */
+ const ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned sx = 0; /* running index in sym_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->sym_next != 0) do {
+ dist = s->sym_buf[sx++] & 0xff;
+ dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8;
+ lc = s->sym_buf[sx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and sym_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
+
+ } while (sx < s->sym_next);
+
+ send_code(s, END_BLOCK, ltree);
+}
+
+/* ===========================================================================
+ * Check if the data type is TEXT or BINARY, using the following algorithm:
+ * - TEXT if the two conditions below are satisfied:
+ * a) There are no non-portable control characters belonging to the
+ * "black list" (0..6, 14..25, 28..31).
+ * b) There is at least one printable character belonging to the
+ * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
+ * - BINARY otherwise.
+ * - The following partially-portable control characters form a
+ * "gray list" that is ignored in this detection algorithm:
+ * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
+ * IN assertion: the fields Freq of dyn_ltree are set.
+ */
+local int detect_data_type(s)
+ deflate_state *s;
+{
+ /* black_mask is the bit mask of black-listed bytes
+ * set bits 0..6, 14..25, and 28..31
+ * 0xf3ffc07f = binary 11110011111111111100000001111111
+ */
+ unsigned long black_mask = 0xf3ffc07fUL;
+ int n;
+
+ /* Check for non-textual ("black-listed") bytes. */
+ for (n = 0; n <= 31; n++, black_mask >>= 1)
+ if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))
+ return Z_BINARY;
+
+ /* Check for textual ("white-listed") bytes. */
+ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
+ || s->dyn_ltree[13].Freq != 0)
+ return Z_TEXT;
+ for (n = 32; n < LITERALS; n++)
+ if (s->dyn_ltree[n].Freq != 0)
+ return Z_TEXT;
+
+ /* There are no "black-listed" or "white-listed" bytes:
+ * this stream either is empty or has tolerated ("gray-listed") bytes only.
+ */
+ return Z_BINARY;
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
diff --git a/zlib/trees.h b/zlib/trees.h
new file mode 100644
index 0000000..d35639d
--- /dev/null
+++ b/zlib/trees.h
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
+{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
+{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
+{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
+{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
+{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
+{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
+{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
+{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
+{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
+{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
+{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
+{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
+{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
+{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
+{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
+{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
+{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
+{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
+{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
+{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
+{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
+{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
+{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
+{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
+{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
+{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
+{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
+{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
+{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
+{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
+{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
+{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
+{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
+{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
+{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
+{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
+{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
+{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
+{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
+{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
+{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
+{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
+{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
+{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
+{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
+{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
+{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
+{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
+{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
+{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
+{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
+{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
+{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
+{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
+{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
+{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
+{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
+ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
+};
+
diff --git a/zlib/zconf.h b/zlib/zconf.h
new file mode 100644
index 0000000..9987a77
--- /dev/null
+++ b/zlib/zconf.h
@@ -0,0 +1,511 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2013 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ * Even better than compiling with -DZ_PREFIX would be to use configure to set
+ * this permanently in zconf.h using "./configure --zprefix".
+ */
+#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */
+# define Z_PREFIX_SET
+
+/* all linked symbols */
+# define _dist_code z__dist_code
+# define _length_code z__length_code
+# define _tr_align z__tr_align
+# define _tr_flush_bits z__tr_flush_bits
+# define _tr_flush_block z__tr_flush_block
+# define _tr_init z__tr_init
+# define _tr_stored_block z__tr_stored_block
+# define _tr_tally z__tr_tally
+# define adler32 z_adler32
+# define adler32_combine z_adler32_combine
+# define adler32_combine64 z_adler32_combine64
+# ifndef Z_SOLO
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# endif
+# define crc32 z_crc32
+# define crc32_combine z_crc32_combine
+# define crc32_combine64 z_crc32_combine64
+# define deflate z_deflate
+# define deflateBound z_deflateBound
+# define deflateCopy z_deflateCopy
+# define deflateEnd z_deflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateInit_ z_deflateInit_
+# define deflateParams z_deflateParams
+# define deflatePending z_deflatePending
+# define deflatePrime z_deflatePrime
+# define deflateReset z_deflateReset
+# define deflateResetKeep z_deflateResetKeep
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateSetHeader z_deflateSetHeader
+# define deflateTune z_deflateTune
+# define deflate_copyright z_deflate_copyright
+# define get_crc_table z_get_crc_table
+# ifndef Z_SOLO
+# define gz_error z_gz_error
+# define gz_intmax z_gz_intmax
+# define gz_strwinerror z_gz_strwinerror
+# define gzbuffer z_gzbuffer
+# define gzclearerr z_gzclearerr
+# define gzclose z_gzclose
+# define gzclose_r z_gzclose_r
+# define gzclose_w z_gzclose_w
+# define gzdirect z_gzdirect
+# define gzdopen z_gzdopen
+# define gzeof z_gzeof
+# define gzerror z_gzerror
+# define gzflush z_gzflush
+# define gzgetc z_gzgetc
+# define gzgetc_ z_gzgetc_
+# define gzgets z_gzgets
+# define gzoffset z_gzoffset
+# define gzoffset64 z_gzoffset64
+# define gzopen z_gzopen
+# define gzopen64 z_gzopen64
+# ifdef _WIN32
+# define gzopen_w z_gzopen_w
+# endif
+# define gzprintf z_gzprintf
+# define gzvprintf z_gzvprintf
+# define gzputc z_gzputc
+# define gzputs z_gzputs
+# define gzread z_gzread
+# define gzrewind z_gzrewind
+# define gzseek z_gzseek
+# define gzseek64 z_gzseek64
+# define gzsetparams z_gzsetparams
+# define gztell z_gztell
+# define gztell64 z_gztell64
+# define gzungetc z_gzungetc
+# define gzwrite z_gzwrite
+# endif
+# define inflate z_inflate
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define inflateBackInit_ z_inflateBackInit_
+# define inflateCopy z_inflateCopy
+# define inflateEnd z_inflateEnd
+# define inflateGetHeader z_inflateGetHeader
+# define inflateInit2_ z_inflateInit2_
+# define inflateInit_ z_inflateInit_
+# define inflateMark z_inflateMark
+# define inflatePrime z_inflatePrime
+# define inflateReset z_inflateReset
+# define inflateReset2 z_inflateReset2
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateGetDictionary z_inflateGetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateUndermine z_inflateUndermine
+# define inflateResetKeep z_inflateResetKeep
+# define inflate_copyright z_inflate_copyright
+# define inflate_fast z_inflate_fast
+# define inflate_table z_inflate_table
+# ifndef Z_SOLO
+# define uncompress z_uncompress
+# endif
+# define zError z_zError
+# ifndef Z_SOLO
+# define zcalloc z_zcalloc
+# define zcfree z_zcfree
+# endif
+# define zlibCompileFlags z_zlibCompileFlags
+# define zlibVersion z_zlibVersion
+
+/* all zlib typedefs in zlib.h and zconf.h */
+# define Byte z_Byte
+# define Bytef z_Bytef
+# define alloc_func z_alloc_func
+# define charf z_charf
+# define free_func z_free_func
+# ifndef Z_SOLO
+# define gzFile z_gzFile
+# endif
+# define gz_header z_gz_header
+# define gz_headerp z_gz_headerp
+# define in_func z_in_func
+# define intf z_intf
+# define out_func z_out_func
+# define uInt z_uInt
+# define uIntf z_uIntf
+# define uLong z_uLong
+# define uLongf z_uLongf
+# define voidp z_voidp
+# define voidpc z_voidpc
+# define voidpf z_voidpf
+
+/* all zlib structs in zlib.h and zconf.h */
+# define gz_header_s z_gz_header_s
+# define internal_state z_internal_state
+
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+#if defined(ZLIB_CONST) && !defined(z_const)
+# define z_const const
+#else
+# define z_const
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+#ifndef Z_ARG /* function prototypes for stdarg */
+# if defined(STDC) || defined(Z_HAVE_STDARG_H)
+# define Z_ARG(args) args
+# else
+# define Z_ARG(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
+# include <limits.h>
+# if (UINT_MAX == 0xffffffffUL)
+# define Z_U4 unsigned
+# elif (ULONG_MAX == 0xffffffffUL)
+# define Z_U4 unsigned long
+# elif (USHRT_MAX == 0xffffffffUL)
+# define Z_U4 unsigned short
+# endif
+#endif
+
+#ifdef Z_U4
+ typedef Z_U4 z_crc_t;
+#else
+ typedef unsigned long z_crc_t;
+#endif
+
+#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */
+# define Z_HAVE_UNISTD_H
+#endif
+
+#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */
+# define Z_HAVE_STDARG_H
+#endif
+
+#ifdef STDC
+# ifndef Z_SOLO
+# include <sys/types.h> /* for off_t */
+# endif
+#endif
+
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+# ifndef Z_SOLO
+# include <stdarg.h> /* for va_list */
+# endif
+#endif
+
+#ifdef _WIN32
+# ifndef Z_SOLO
+# include <stddef.h> /* for wchar_t */
+# endif
+#endif
+
+/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
+ * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
+ * though the former does not conform to the LFS document), but considering
+ * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
+ * equivalently requesting no 64-bit operations
+ */
+#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
+# undef _LARGEFILE64_SOURCE
+#endif
+
+#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
+# define Z_HAVE_UNISTD_H
+#endif
+#ifndef Z_SOLO
+# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
+# include <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# ifndef z_off_t
+# define z_off_t off_t
+# endif
+# endif
+#endif
+
+#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
+# define Z_LFS64
+#endif
+
+#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
+# define Z_LARGE64
+#endif
+
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
+# define Z_WANT64
+#endif
+
+#if !defined(SEEK_SET) && !defined(Z_SOLO)
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if !defined(_WIN32) && defined(Z_LARGE64)
+# define z_off64_t off64_t
+#else
+# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
+# define z_off64_t __int64
+# else
+# define z_off64_t z_off_t
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+ #pragma map(deflateInit_,"DEIN")
+ #pragma map(deflateInit2_,"DEIN2")
+ #pragma map(deflateEnd,"DEEND")
+ #pragma map(deflateBound,"DEBND")
+ #pragma map(inflateInit_,"ININ")
+ #pragma map(inflateInit2_,"ININ2")
+ #pragma map(inflateEnd,"INEND")
+ #pragma map(inflateSync,"INSY")
+ #pragma map(inflateSetDictionary,"INSEDI")
+ #pragma map(compressBound,"CMBND")
+ #pragma map(inflate_table,"INTABL")
+ #pragma map(inflate_fast,"INFA")
+ #pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/zlib/zlib.h b/zlib/zlib.h
new file mode 100644
index 0000000..c4536ca
--- /dev/null
+++ b/zlib/zlib.h
@@ -0,0 +1,1769 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.8, April 28th, 2013
+
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.8"
+#define ZLIB_VERNUM 0x1280
+#define ZLIB_VER_MAJOR 1
+#define ZLIB_VER_MINOR 2
+#define ZLIB_VER_REVISION 8
+#define ZLIB_VER_SUBREVISION 0
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed data.
+ This version of the library supports only one compression method (deflation)
+ but other algorithms will be added later and will have the same stream
+ interface.
+
+ Compression can be done in a single step if the buffers are large enough,
+ or can be done by repeated calls of the compression function. In the latter
+ case, the application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The compressed data format used by default by the in-memory functions is
+ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+ around a deflate stream, which is itself documented in RFC 1951.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio using the functions that start
+ with "gz". The gzip format is different from the zlib format. gzip is a
+ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+ This library can optionally read and write gzip streams in memory as well.
+
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never crash
+ even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ z_const Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total number of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total number of bytes output so far */
+
+ z_const char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: binary or text */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ gzip header information passed to and from zlib routines. See RFC 1952
+ for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+ int text; /* true if compressed data believed to be text */
+ uLong time; /* modification time */
+ int xflags; /* extra flags (not used when writing a gzip file) */
+ int os; /* operating system */
+ Bytef *extra; /* pointer to extra field or Z_NULL if none */
+ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */
+ uInt extra_max; /* space at extra (only when reading header) */
+ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */
+ uInt name_max; /* space at name (only when reading header) */
+ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */
+ uInt comm_max; /* space at comment (only when reading header) */
+ int hcrc; /* true if there was or will be a header crc */
+ int done; /* true when done reading gzip header (not used
+ when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+ The application must update next_in and avail_in when avail_in has dropped
+ to zero. It must update next_out and avail_out when avail_out has dropped
+ to zero. The application must initialize zalloc, zfree and opaque before
+ calling the init function. All other fields are set by the compression
+ library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this if
+ the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers
+ returned by zalloc for objects of exactly 65536 bytes *must* have their
+ offset normalized to zero. The default allocation function provided by this
+ library ensures this (see zutil.c). To reduce memory requirements and avoid
+ any allocation of 64K objects, at the expense of compression ratio, compile
+ the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or progress
+ reports. After compression, total_in holds the total size of the
+ uncompressed data and may be saved for use in the decompressor (particularly
+ if the decompressor wants to decompress everything in a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+#define Z_BLOCK 5
+#define Z_TREES 6
+/* Allowed flush values; see deflate() and inflate() below for details */
+#define Z_INSERT_ONLY 7
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_RLE 3
+#define Z_FIXED 4
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_TEXT 1
+#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is not
+ compatible with the zlib.h header file used by the application. This check
+ is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller. If
+ zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
+ allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at all
+ (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION
+ requests a default compromise between speed and compression (currently
+ equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if level is not a valid compression level, or
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION). msg is set to null
+ if there is no error message. deflateInit does not perform any compression:
+ this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications). Some
+ output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming more
+ output, and updating avail_in or avail_out accordingly; avail_out should
+ never be zero before the call. The application can consume the compressed
+ output when it wants, for example when the output buffer is full (avail_out
+ == 0), or after each call of deflate(). If deflate returns Z_OK and with
+ zero avail_out, it must be called again after making room in the output
+ buffer because there might be more output pending.
+
+ Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+ decide how much data to accumulate before producing output, in order to
+ maximize compression.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In
+ particular avail_in is zero after the call if enough output space has been
+ provided before the call.) Flushing may degrade compression for some
+ compression algorithms and so it should be used only when necessary. This
+ completes the current deflate block and follows it with an empty stored block
+ that is three bits plus filler bits to the next byte, followed by four bytes
+ (00 00 ff ff).
+
+ If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the
+ output buffer, but the output is not aligned to a byte boundary. All of the
+ input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
+ This completes the current deflate block and follows it with an empty fixed
+ codes block that is 10 bits long. This assures that enough bytes are output
+ in order for the decompressor to finish the block before the empty fixed code
+ block.
+
+ If flush is set to Z_BLOCK, a deflate block is completed and emitted, as
+ for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to
+ seven bits of the current block are held to be written as the next byte after
+ the next deflate block is completed. In this case, the decompressor may not
+ be provided enough bits at this point in order to complete decompression of
+ the data provided so far to the compressor. It may need to wait for the next
+ block to be emitted. This is for advanced applications that need to control
+ the emission of deflate blocks.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+ avail_out is greater than six to avoid repeated flush markers due to
+ avail_out == 0 on return.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there was
+ enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the stream
+ are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least the
+ value returned by deflateBound (see below). Then deflate is guaranteed to
+ return Z_STREAM_END. If not enough output space is provided, deflate will
+ not return Z_STREAM_END, and it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update strm->data_type if it can make a good guess about
+ the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect the
+ compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+ fatal, and deflate() can be called again with more input and more output
+ space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any pending
+ output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case, msg
+ may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the
+ exact value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+ invalid, such as a null pointer to the structure. msg is set to null if
+ there is no error message. inflateInit does not perform any decompression
+ apart from possibly reading the zlib header if present: actual decompression
+ will be done by inflate(). (So next_in and avail_in may be modified, but
+ next_out and avail_out are unused and unchanged.) The current implementation
+ of inflateInit() does not process any header information -- that is deferred
+ until inflate() is called.
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing will
+ resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there is
+ no more input data or no more space in the output buffer (see below about
+ the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming more
+ output, and updating the next_* and avail_* values accordingly. The
+ application can consume the uncompressed output when it wants, for example
+ when the output buffer is full (avail_out == 0), or after each call of
+ inflate(). If inflate returns Z_OK and with zero avail_out, it must be
+ called again after making room in the output buffer because there might be
+ more output pending.
+
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,
+ Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate()
+ stop if and when it gets to the next deflate block boundary. When decoding
+ the zlib or gzip format, this will cause inflate() to return immediately
+ after the header and before the first block. When doing a raw inflate,
+ inflate() will go ahead and process the first block, and will return when it
+ gets to the end of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64 if
+ inflate() is currently decoding the last block in the deflate stream, plus
+ 128 if inflate() returned immediately after decoding an end-of-block code or
+ decoding the complete header up to just before the first byte of the deflate
+ stream. The end-of-block will not be indicated until all of the uncompressed
+ data from that block has been written to strm->next_out. The number of
+ unused bits may in general be greater than seven, except when bit 7 of
+ data_type is set, in which case the number of unused bits will be less than
+ eight. data_type is set as noted here every time inflate() returns for all
+ flush options, and so can be used to determine the amount of currently
+ consumed input in bits.
+
+ The Z_TREES option behaves as Z_BLOCK does, but it also returns when the
+ end of each deflate block header is reached, before any actual data in that
+ block is decoded. This allows the caller to determine the length of the
+ deflate block header for later use in random access within a deflate block.
+ 256 is added to the value of strm->data_type when inflate() returns
+ immediately after reaching the end of the deflate block header.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step (a
+ single call of inflate), the parameter flush should be set to Z_FINISH. In
+ this case all pending input is processed and all pending output is flushed;
+ avail_out must be large enough to hold all of the uncompressed data for the
+ operation to complete. (The size of the uncompressed data may have been
+ saved by the compressor for this purpose.) The use of Z_FINISH is not
+ required to perform an inflation in one step. However it may be used to
+ inform inflate that a faster approach can be used for the single inflate()
+ call. Z_FINISH also informs inflate to not maintain a sliding window if the
+ stream completes, which reduces inflate's memory footprint. If the stream
+ does not complete, either because not all of the stream is provided or not
+ enough output space is provided, then a sliding window will be allocated and
+ inflate() can be called again to continue the operation as if Z_NO_FLUSH had
+ been used.
+
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the effects of the flush parameter in this implementation are
+ on the return value of inflate() as noted below, when inflate() returns early
+ when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of
+ memory for a sliding window when Z_FINISH is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm->adler to the Adler-32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the Adler-32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() can decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically, if requested when
+ initializing with inflateInit2(). Any information contained in the gzip
+ header is not retained, so applications that need that information should
+ instead use raw inflate, see inflateInit2() below, or inflateBack() and
+ perform their own processing of the gzip header and trailer. When processing
+ gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output
+ producted so far. The CRC-32 is checked against the gzip trailer.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may
+ then call inflateSync() to look for a good compression block if a partial
+ recovery of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any pending
+ output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by the
+ caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+ determines the window size. deflate() will then generate raw deflate data
+ with no zlib header or trailer, and will not compute an adler32 check value.
+
+ windowBits can also be greater than 15 for optional gzip encoding. Add
+ 16 to windowBits to write a simple gzip header and trailer around the
+ compressed data instead of a zlib wrapper. The gzip header will have no
+ file name, no extra data, no comment, no modification time (set to zero), no
+ header crc, and the operating system will be set to 255 (unknown). If a
+ gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but is
+ slow and reduces compression ratio; memLevel=9 uses maximum memory for
+ optimal speed. The default value is 8. See zconf.h for total memory usage
+ as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match), or Z_RLE to limit match distances to one (run-length
+ encoding). Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is tuned to
+ compress them better. The effect of Z_FILTERED is to force more Huffman
+ coding and less string matching; it is somewhat intermediate between
+ Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as
+ fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The
+ strategy parameter only affects the compression ratio but not the
+ correctness of the compressed output even if it is not set appropriately.
+ Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
+ decoder for special applications.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
+ method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is
+ incompatible with the version assumed by the caller (ZLIB_VERSION). msg is
+ set to null if there is no error message. deflateInit2 does not perform any
+ compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. When using the zlib format, this
+ function must be called immediately after deflateInit, deflateInit2 or
+ deflateReset, and before any call of deflate. When doing raw deflate, this
+ function must be called either before any call of deflate, or immediately
+ after the completion of a deflate block, i.e. after all input has been
+ consumed and all output has been delivered when using any of the flush
+ options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The
+ compressor and decompressor must use exactly the same dictionary (see
+ inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size
+ provided in deflateInit or deflateInit2. Thus the strings most likely to be
+ useful should be put at the end of the dictionary, not at the front. In
+ addition, the current implementation of deflate will use at most the window
+ size minus 262 bytes of the provided dictionary.
+
+ Upon return of this function, strm->adler is set to the adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.) If a raw deflate was requested, then the
+ adler32 value is not computed and strm->adler is not set.
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if not at a block boundary for raw deflate). deflateSetDictionary does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and can
+ consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being Z_NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state. The
+ stream will keep the same compression level and any other attributes that
+ may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different strategy.
+ If the compression level is changed, the input available so far is
+ compressed with the old level (and may be flushed); the new level will take
+ effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to be
+ compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if
+ strm->avail_out was zero.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain));
+/*
+ Fine tune deflate's internal compression parameters. This should only be
+ used by someone who understands the algorithm used by zlib's deflate for
+ searching for the best matching string, and even then only by the most
+ fanatic optimizer trying to squeeze out the last compressed bit for their
+ specific input data. Read the deflate.c source code for the meaning of the
+ max_lazy, good_length, nice_length, and max_chain parameters.
+
+ deflateTune() can be called after deflateInit() or deflateInit2(), and
+ returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+ uLong sourceLen));
+/*
+ deflateBound() returns an upper bound on the compressed size after
+ deflation of sourceLen bytes. It must be called after deflateInit() or
+ deflateInit2(), and after deflateSetHeader(), if used. This would be used
+ to allocate an output buffer for deflation in a single pass, and so would be
+ called before deflate(). If that first deflate() call is provided the
+ sourceLen input bytes, an output buffer allocated to the size returned by
+ deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed
+ to return Z_STREAM_END. Note that it is possible for the compressed size to
+ be larger than the value returned by deflateBound() if flush options other
+ than Z_FINISH or Z_NO_FLUSH are used.
+*/
+
+ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
+ unsigned *pending,
+ int *bits));
+/*
+ deflatePending() returns the number of bytes and bits of output that have
+ been generated, but not yet provided in the available output. The bytes not
+ provided would be due to the available output space having being consumed.
+ The number of bits of output not provided are between 0 and 7, where they
+ await more bits to join them in order to fill out a full byte. If pending
+ or bits are Z_NULL, then those values are not set.
+
+ deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+ */
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ deflatePrime() inserts bits in the deflate output stream. The intent
+ is that this function is used to start off the deflate output with the bits
+ leftover from a previous deflate stream when appending to it. As such, this
+ function can only be used for raw deflate, and must be used before the first
+ deflate() call after a deflateInit2() or deflateReset(). bits must be less
+ than or equal to 16, and that many of the least significant bits of value
+ will be inserted in the output.
+
+ deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough
+ room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the
+ source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ deflateSetHeader() provides gzip header information for when a gzip
+ stream is requested by deflateInit2(). deflateSetHeader() may be called
+ after deflateInit2() or deflateReset() and before the first call of
+ deflate(). The text, time, os, extra field, name, and comment information
+ in the provided gz_header structure are written to the gzip header (xflag is
+ ignored -- the extra flags are set according to the compression level). The
+ caller must assure that, if not Z_NULL, name and comment are terminated with
+ a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+ available there. If hcrc is true, a gzip header crc is included. Note that
+ the current versions of the command-line version of gzip (up through version
+ 1.3.x) do not support header crc's, and will report that it is a "multi-part
+ gzip file" and give up.
+
+ If deflateSetHeader is not used, the default gzip header has text false,
+ the time set to zero, and os set to 255, with no extra, name, or comment
+ fields. The gzip header is returned to the default state by deflateReset().
+
+ deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be zero to request that inflate use the window size in
+ the zlib header of the compressed stream.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a
+ crc32 instead of an adler32.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+ invalid, such as a null pointer to the structure. msg is set to null if
+ there is no error message. inflateInit2 does not perform any decompression
+ apart from possibly reading the zlib header if present: actual decompression
+ will be done by inflate(). (So next_in and avail_in may be modified, but
+ next_out and avail_out are unused and unchanged.) The current implementation
+ of inflateInit2() does not process any header information -- that is
+ deferred until inflate() is called.
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate,
+ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by that call of inflate.
+ The compressor and decompressor must use exactly the same dictionary (see
+ deflateSetDictionary). For raw inflate, this function can be called at any
+ time to set the dictionary. If the provided dictionary is smaller than the
+ window and there is already data in the window, then the provided dictionary
+ will amend what's there. The application must insure that the dictionary
+ that was used for compression is provided.
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
+ Bytef *dictionary,
+ uInt *dictLength));
+/*
+ Returns the sliding dictionary being maintained by inflate. dictLength is
+ set to the number of bytes in the dictionary, and that many bytes are copied
+ to dictionary. dictionary must have enough space, where 32768 bytes is
+ always enough. If inflateGetDictionary() is called with dictionary equal to
+ Z_NULL, then only the dictionary length is returned, and nothing is copied.
+ Similary, if dictLength is Z_NULL, then it is not set.
+
+ inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
+ stream state is inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a possible full flush point (see above
+ for the description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync searches for a 00 00 FF FF pattern in the compressed data.
+ All full flush points have this pattern, but not all occurrences of this
+ pattern are full flush points.
+
+ inflateSync returns Z_OK if a possible full flush point has been found,
+ Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
+ has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
+ In the success case, the application may save the current current value of
+ total_in which indicates where valid compressed data was found. In the
+ error case, the application may repeatedly call inflateSync, providing more
+ input each time, until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when randomly accessing a large stream. The
+ first pass through the stream can periodically record the inflate state,
+ allowing restarting inflate at those points when randomly accessing the
+ stream.
+
+ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being Z_NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state. The
+ stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
+ int windowBits));
+/*
+ This function is the same as inflateReset, but it also permits changing
+ the wrap and window size requests. The windowBits parameter is interpreted
+ the same as it is for inflateInit2.
+
+ inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being Z_NULL), or if
+ the windowBits parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ This function inserts bits in the inflate input stream. The intent is
+ that this function is used to start inflating at a bit position in the
+ middle of a byte. The provided bits will be used before any bytes are used
+ from next_in. This function should only be used with raw inflate, and
+ should be used before the first inflate() call after inflateInit2() or
+ inflateReset(). bits must be less than or equal to 16, and that many of the
+ least significant bits of value will be inserted in the input.
+
+ If bits is negative, then the input stream bit buffer is emptied. Then
+ inflatePrime() can be called again to put bits in the buffer. This is used
+ to clear out bits leftover after feeding inflate a block description prior
+ to feeding inflate codes.
+
+ inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
+/*
+ This function returns two values, one in the lower 16 bits of the return
+ value, and the other in the remaining upper bits, obtained by shifting the
+ return value down 16 bits. If the upper value is -1 and the lower value is
+ zero, then inflate() is currently decoding information outside of a block.
+ If the upper value is -1 and the lower value is non-zero, then inflate is in
+ the middle of a stored block, with the lower value equaling the number of
+ bytes from the input remaining to copy. If the upper value is not -1, then
+ it is the number of bits back from the current bit position in the input of
+ the code (literal or length/distance pair) currently being processed. In
+ that case the lower value is the number of bytes already emitted for that
+ code.
+
+ A code is being processed if inflate is waiting for more input to complete
+ decoding of the code, or if it has completed decoding but is waiting for
+ more output space to write the literal or match data.
+
+ inflateMark() is used to mark locations in the input data for random
+ access, which may be at bit positions, and to note those cases where the
+ output of a code may span boundaries of random access blocks. The current
+ location in the input stream can be determined from avail_in and data_type
+ as noted in the description for the Z_BLOCK flush parameter for inflate.
+
+ inflateMark returns the value noted above or -1 << 16 if the provided
+ source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ inflateGetHeader() requests that gzip header information be stored in the
+ provided gz_header structure. inflateGetHeader() may be called after
+ inflateInit2() or inflateReset(), and before the first call of inflate().
+ As inflate() processes the gzip stream, head->done is zero until the header
+ is completed, at which time head->done is set to one. If a zlib stream is
+ being decoded, then head->done is set to -1 to indicate that there will be
+ no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be
+ used to force inflate() to return immediately after header processing is
+ complete and before any actual data is decompressed.
+
+ The text, time, xflags, and os fields are filled in with the gzip header
+ contents. hcrc is set to true if there is a header CRC. (The header CRC
+ was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+ contains the maximum number of bytes to write to extra. Once done is true,
+ extra_len contains the actual extra field length, and extra contains the
+ extra field, or that field truncated if extra_max is less than extra_len.
+ If name is not Z_NULL, then up to name_max characters are written there,
+ terminated with a zero unless the length is greater than name_max. If
+ comment is not Z_NULL, then up to comm_max characters are written there,
+ terminated with a zero unless the length is greater than comm_max. When any
+ of extra, name, or comment are not Z_NULL and the respective field is not
+ present in the header, then that field is set to Z_NULL to signal its
+ absence. This allows the use of deflateSetHeader() with the returned
+ structure to duplicate the header. However if those fields are set to
+ allocated memory, then the application will need to save those pointers
+ elsewhere so that they can be eventually freed.
+
+ If inflateGetHeader is not used, then the header information is simply
+ discarded. The header is always checked for validity, including the header
+ CRC if present. inflateReset() will reset the process to discard the header
+ information. The application would need to call inflateGetHeader() again to
+ retrieve the header from the next gzip stream.
+
+ inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window));
+
+ Initialize the internal stream state for decompression using inflateBack()
+ calls. The fields zalloc, zfree and opaque in strm must be initialized
+ before the call. If zalloc and zfree are Z_NULL, then the default library-
+ derived memory allocation routines are used. windowBits is the base two
+ logarithm of the window size, in the range 8..15. window is a caller
+ supplied buffer of that size. Except for special applications where it is
+ assured that deflate was used with small window sizes, windowBits must be 15
+ and a 32K byte window must be supplied to be able to decompress general
+ deflate streams.
+
+ See inflateBack() for the usage of these routines.
+
+ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+ the parameters are invalid, Z_MEM_ERROR if the internal state could not be
+ allocated, or Z_VERSION_ERROR if the version of the library does not match
+ the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *,
+ z_const unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc));
+/*
+ inflateBack() does a raw inflate with a single call using a call-back
+ interface for input and output. This is potentially more efficient than
+ inflate() for file i/o applications, in that it avoids copying between the
+ output and the sliding window by simply making the window itself the output
+ buffer. inflate() can be faster on modern CPUs when used with large
+ buffers. inflateBack() trusts the application to not change the output
+ buffer passed by the output function, at least until inflateBack() returns.
+
+ inflateBackInit() must be called first to allocate the internal state
+ and to initialize the state with the user-provided window buffer.
+ inflateBack() may then be used multiple times to inflate a complete, raw
+ deflate stream with each call. inflateBackEnd() is then called to free the
+ allocated state.
+
+ A raw deflate stream is one with no zlib or gzip header or trailer.
+ This routine would normally be used in a utility that reads zip or gzip
+ files and writes out uncompressed files. The utility would decode the
+ header and process the trailer on its own, hence this routine expects only
+ the raw deflate stream to decompress. This is different from the normal
+ behavior of inflate(), which expects either a zlib or gzip header and
+ trailer around the deflate stream.
+
+ inflateBack() uses two subroutines supplied by the caller that are then
+ called by inflateBack() for input and output. inflateBack() calls those
+ routines until it reads a complete deflate stream and writes out all of the
+ uncompressed data, or until it encounters an error. The function's
+ parameters and return types are defined above in the in_func and out_func
+ typedefs. inflateBack() will call in(in_desc, &buf) which should return the
+ number of bytes of provided input, and a pointer to that input in buf. If
+ there is no input available, in() must return zero--buf is ignored in that
+ case--and inflateBack() will return a buffer error. inflateBack() will call
+ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out()
+ should return zero on success, or non-zero on failure. If out() returns
+ non-zero, inflateBack() will return with an error. Neither in() nor out()
+ are permitted to change the contents of the window provided to
+ inflateBackInit(), which is also the buffer that out() uses to write from.
+ The length written by out() will be at most the window size. Any non-zero
+ amount of input may be provided by in().
+
+ For convenience, inflateBack() can be provided input on the first call by
+ setting strm->next_in and strm->avail_in. If that input is exhausted, then
+ in() will be called. Therefore strm->next_in must be initialized before
+ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
+ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
+ must also be initialized, and then if strm->avail_in is not zero, input will
+ initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+ The in_desc and out_desc parameters of inflateBack() is passed as the
+ first parameter of in() and out() respectively when they are called. These
+ descriptors can be optionally used to pass any information that the caller-
+ supplied in() and out() functions need to do their job.
+
+ On return, inflateBack() will set strm->next_in and strm->avail_in to
+ pass back any unused input that was provided by the last in() call. The
+ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+ if in() or out() returned an error, Z_DATA_ERROR if there was a format error
+ in the deflate stream (in which case strm->msg is set to indicate the nature
+ of the error), or Z_STREAM_ERROR if the stream was not properly initialized.
+ In the case of Z_BUF_ERROR, an input or output error can be distinguished
+ using strm->next_in which will be Z_NULL only if in() returned an error. If
+ strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning
+ non-zero. (in() will always be called before out(), so strm->next_in is
+ assured to be defined if out() returns non-zero.) Note that inflateBack()
+ cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+ All memory allocated by inflateBackInit() is freed.
+
+ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+ state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+ 1.0: size of uInt
+ 3.2: size of uLong
+ 5.4: size of voidpf (pointer)
+ 7.6: size of z_off_t
+
+ Compiler, assembler, and debug options:
+ 8: DEBUG
+ 9: ASMV or ASMINF -- use ASM code
+ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+ 11: 0 (reserved)
+
+ One-time table building (smaller code, but not thread-safe if true):
+ 12: BUILDFIXED -- build static block decoding tables when needed
+ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+ 14,15: 0 (reserved)
+
+ Library content (indicates missing functionality):
+ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+ deflate code when not needed)
+ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+ and decode gzip streams (to avoid linking crc code)
+ 18-19: 0 (reserved)
+
+ Operation variations (changes in library functionality):
+ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+ 21: FASTEST -- deflate algorithm with only one, lowest compression level
+ 22,23: 0 (reserved)
+
+ The sprintf variant used by gzprintf (zero is best):
+ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+ Remainder:
+ 27-31: 0 (reserved)
+ */
+
+#ifndef Z_SOLO
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the basic
+ stream-oriented functions. To simplify the interface, some default options
+ are assumed (compression level and memory usage, standard memory allocation
+ functions). The source code of these utility functions can be modified if
+ you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total size
+ of the destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+ compressBound() returns an upper bound on the compressed size after
+ compress() or compress2() on sourceLen bytes. It would be used before a
+ compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total size
+ of the destination buffer, which must be large enough to hold the entire
+ uncompressed data. (The size of the uncompressed data must have been saved
+ previously by the compressor and transmitted to the decompressor by some
+ mechanism outside the scope of this compression library.) Upon exit, destLen
+ is the actual size of the uncompressed buffer.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In
+ the case where there is not enough room, uncompress() will fill the output
+ buffer with the uncompressed data up to that point.
+*/
+
+ /* gzip file access functions */
+
+/*
+ This library supports reading and writing files in gzip (.gz) format with
+ an interface similar to that of stdio, using the functions that start with
+ "gz". The gzip format is different from the zlib format. gzip is a gzip
+ wrapper, documented in RFC 1952, wrapped around a deflate stream.
+*/
+
+typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */
+
+/*
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+
+ Opens a gzip (.gz) file for reading or writing. The mode parameter is as
+ in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
+ a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
+ compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
+ for fixed code compression as in "wb9F". (See the description of
+ deflateInit2 for more information about the strategy parameter.) 'T' will
+ request transparent writing or appending with no compression and not using
+ the gzip format.
+
+ "a" can be used instead of "w" to request that the gzip stream that will
+ be written be appended to the file. "+" will result in an error, since
+ reading and writing to the same gzip file is not supported. The addition of
+ "x" when writing will create the file exclusively, which fails if the file
+ already exists. On systems that support it, the addition of "e" when
+ reading or writing will set the flag to close the file on an execve() call.
+
+ These functions, as well as gzip, will read and decode a sequence of gzip
+ streams in a file. The append function of gzopen() can be used to create
+ such a file. (Also see gzflush() for another way to do this.) When
+ appending, gzopen does not test whether the file begins with a gzip stream,
+ nor does it look for the end of the gzip streams to begin appending. gzopen
+ will simply append a gzip stream to the existing file.
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression. When
+ reading, this will be detected automatically by looking for the magic two-
+ byte gzip header.
+
+ gzopen returns NULL if the file could not be opened, if there was
+ insufficient memory to allocate the gzFile state, or if an invalid mode was
+ specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
+ errno can be checked to determine if the reason gzopen failed was that the
+ file could not be opened.
+*/
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen associates a gzFile with the file descriptor fd. File descriptors
+ are obtained from calls like open, dup, creat, pipe or fileno (if the file
+ has been previously opened with fopen). The mode parameter is as in gzopen.
+
+ The next call of gzclose on the returned gzFile will also close the file
+ descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
+ fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
+ mode);. The duplicated descriptor should be saved to avoid a leak, since
+ gzdopen does not close fd if it fails. If you are using fileno() to get the
+ file descriptor from a FILE *, then you will have to use dup() to avoid
+ double-close()ing the file descriptor. Both gzclose() and fclose() will
+ close the associated file descriptor, so they need to have different file
+ descriptors.
+
+ gzdopen returns NULL if there was insufficient memory to allocate the
+ gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
+ provided, or '+' was provided), or if fd is -1. The file descriptor is not
+ used until the next gz* read, write, seek, or close operation, so gzdopen
+ will not detect if fd is invalid (unless fd is -1).
+*/
+
+ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
+/*
+ Set the internal buffer size used by this library's functions. The
+ default buffer size is 8192 bytes. This function must be called after
+ gzopen() or gzdopen(), and before any other calls that read or write the
+ file. The buffer memory allocation is always deferred to the first read or
+ write. Two buffers are allocated, either both of the specified size when
+ writing, or one of the specified size and the other twice that size when
+ reading. A larger buffer size of, for example, 64K or 128K bytes will
+ noticeably increase the speed of decompression (reading).
+
+ The new buffer size also affects the maximum length for gzprintf().
+
+ gzbuffer() returns 0 on success, or -1 on failure, such as being called
+ too late.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file. If
+ the input file is not in gzip format, gzread copies the given number of
+ bytes into the buffer directly from the file.
+
+ After reaching the end of a gzip stream in the input, gzread will continue
+ to read, looking for another gzip stream. Any number of gzip streams may be
+ concatenated in the input file, and will all be decompressed by gzread().
+ If something other than a gzip stream is encountered after a gzip stream,
+ that remaining trailing garbage is ignored (and no error is returned).
+
+ gzread can be used to read a gzip file that is being concurrently written.
+ Upon reaching the end of the input, gzread will return with the available
+ data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then
+ gzclearerr can be used to clear the end of file indicator in order to permit
+ gzread to be tried again. Z_OK indicates that a gzip stream was completed
+ on the last gzread. Z_BUF_ERROR indicates that the input file ended in the
+ middle of a gzip stream. Note that gzread does not return -1 in the event
+ of an incomplete gzip stream. This error is deferred until gzclose(), which
+ will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip
+ stream. Alternatively, gzerror can be used before gzclose to detect this
+ case.
+
+ gzread returns the number of uncompressed bytes actually read, less than
+ len for end of file, or -1 for error.
+*/
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ voidpc buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes written or 0 in case of
+ error.
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the arguments to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written, or 0 in case of error. The number of
+ uncompressed bytes written is limited to 8191, or one less than the buffer
+ size given to gzbuffer(). The caller should assure that this limit is not
+ exceeded. If it is exceeded, then gzprintf() will return an error (0) with
+ nothing written. In this case, there may also be a buffer overflow with
+ unpredictable consequences, which is possible only if zlib was compiled with
+ the insecure functions sprintf() or vsprintf() because the secure snprintf()
+ or vsnprintf() functions were not available. This can be determined using
+ zlibCompileFlags().
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or a
+ newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. If any characters are read or if len == 1, the
+ string is terminated with a null character. If no characters are read due
+ to an end-of-file or len < 1, then the buffer is left untouched.
+
+ gzgets returns buf which is a null-terminated string, or it returns NULL
+ for end-of-file or in case of error. If there was an error, the contents at
+ buf are indeterminate.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file. gzputc
+ returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte or -1
+ in case of end of file or error. This is implemented as a macro for speed.
+ As such, it does not do all of the checking the other functions do. I.e.
+ it does not check to see if file is NULL, nor whether the structure file
+ points to has been clobbered or not.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+ Push one character back onto the stream to be read as the first character
+ on the next read. At least one character of push-back is allowed.
+ gzungetc() returns the character pushed, or -1 on failure. gzungetc() will
+ fail if c is -1, and may fail if a character has been pushed but not read
+ yet. If gzungetc is used immediately after gzopen or gzdopen, at least the
+ output buffer size of pushed characters is allowed. (See gzbuffer above.)
+ The pushed character will be discarded if the stream is repositioned with
+ gzseek() or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter flush
+ is as in the deflate() function. The return value is the zlib error number
+ (see function gzerror below). gzflush is only permitted when writing.
+
+ If the flush parameter is Z_FINISH, the remaining data is written and the
+ gzip stream is completed in the output. If gzwrite() is called again, a new
+ gzip stream will be started in the output. gzread() is able to read such
+ concatented gzip streams.
+
+ gzflush should be called only when strictly necessary because it will
+ degrade compression if called too often.
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+
+ Sets the starting position for the next gzread or gzwrite on the given
+ compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+
+ Returns the starting position for the next gzread or gzwrite on the given
+ compressed file. This position represents a number of bytes in the
+ uncompressed data stream, and is zero when starting, even if appending or
+ reading a gzip stream from the middle of a file using gzdopen().
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
+
+ Returns the current offset in the file being read or written. This offset
+ includes the count of bytes that precede the gzip stream, for example when
+ appending or when using gzdopen() for reading. When reading, the offset
+ does not include as yet unused buffered input. This information can be used
+ for a progress indicator. On error, gzoffset() returns -1.
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns true (1) if the end-of-file indicator has been set while reading,
+ false (0) otherwise. Note that the end-of-file indicator is set only if the
+ read tried to go past the end of the input, but came up short. Therefore,
+ just like feof(), gzeof() may return false even if there is no more data to
+ read, in the event that the last read request was for the exact number of
+ bytes remaining in the input file. This will happen if the input file size
+ is an exact multiple of the buffer size.
+
+ If gzeof() returns true, then the read functions will return no more data,
+ unless the end-of-file indicator is reset by gzclearerr() and the input file
+ has grown since the previous end of file was detected.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+ Returns true (1) if file is being copied directly while reading, or false
+ (0) if file is a gzip stream being decompressed.
+
+ If the input file is empty, gzdirect() will return true, since the input
+ does not contain a gzip stream.
+
+ If gzdirect() is used immediately after gzopen() or gzdopen() it will
+ cause buffers to be allocated to allow reading the file to determine if it
+ is a gzip file. Therefore if gzbuffer() is used, it should be called before
+ gzdirect().
+
+ When writing, gzdirect() returns true (1) if transparent writing was
+ requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note:
+ gzdirect() is not needed when writing. Transparent writing must be
+ explicitly requested, so the application already knows the answer. When
+ linking statically, using gzdirect() will include all of the zlib code for
+ gzip file reading and decompression, which may not be desired.)
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file and
+ deallocates the (de)compression state. Note that once file is closed, you
+ cannot call gzerror with file, since its structures have been deallocated.
+ gzclose must not be called more than once on the same file, just as free
+ must not be called more than once on the same allocation.
+
+ gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
+ file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the
+ last read ended in the middle of a gzip stream, or Z_OK on success.
+*/
+
+ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
+/*
+ Same as gzclose(), but gzclose_r() is only for use when reading, and
+ gzclose_w() is only for use when writing or appending. The advantage to
+ using these instead of gzclose() is that they avoid linking in zlib
+ compression or decompression code that is not used when only reading or only
+ writing respectively. If gzclose() is used, then both compression and
+ decompression code will be included the application when linking to a static
+ zlib library.
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the given
+ compressed file. errnum is set to zlib error number. If an error occurred
+ in the file system and not in the compression library, errnum is set to
+ Z_ERRNO and the application may consult errno to get the exact error code.
+
+ The application must not modify the returned string. Future calls to
+ this function may invalidate the previously returned string. If file is
+ closed, then the string previously returned by gzerror will no longer be
+ available.
+
+ gzerror() should be used to distinguish errors from end-of-file for those
+ functions above that do not distinguish those cases in their return values.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+ Clears the error and end-of-file flags for file. This is analogous to the
+ clearerr() function in stdio. This is useful for continuing to read a gzip
+ file that is being written concurrently.
+*/
+
+#endif /* !Z_SOLO */
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the compression
+ library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is Z_NULL, this function returns the
+ required initial value for the checksum.
+
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster.
+
+ Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+/*
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+ z_off_t len2));
+
+ Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
+ and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+ each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
+ seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note
+ that the z_off_t type (like off_t) is a signed integer. If len2 is
+ negative, the result has no meaning or utility.
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running CRC-32 with the bytes buf[0..len-1] and return the
+ updated CRC-32. If buf is Z_NULL, this function returns the required
+ initial value for the crc. Pre- and post-conditioning (one's complement) is
+ performed within this function so it shouldn't be done by the application.
+
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+/*
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+ Combine two CRC-32 check values into one. For two sequences of bytes,
+ seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+ calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
+ check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+ len2.
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
+ (int)sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+ inflateBackInit_((strm), (windowBits), (window), \
+ ZLIB_VERSION, (int)sizeof(z_stream))
+
+#ifndef Z_SOLO
+
+/* gzgetc() macro and its supporting function and exposed data structure. Note
+ * that the real internal state is much larger than the exposed structure.
+ * This abbreviated structure exposes just enough for the gzgetc() macro. The
+ * user should not mess with these exposed elements, since their names or
+ * behavior could change in the future, perhaps even capriciously. They can
+ * only be used by the gzgetc() macro. You have been warned.
+ */
+struct gzFile_s {
+ unsigned have;
+ unsigned char *next;
+ z_off64_t pos;
+};
+ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
+#ifdef Z_PREFIX_SET
+# undef z_gzgetc
+# define z_gzgetc(g) \
+ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
+#else
+# define gzgetc(g) \
+ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
+#endif
+
+/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
+ * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
+ * both are true, the application gets the *64 functions, and the regular
+ * functions are changed to 64 bits) -- in case these are set on systems
+ * without large file support, _LFS64_LARGEFILE must also be true
+ */
+#ifdef Z_LARGE64
+ ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+ ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
+ ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
+ ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
+ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
+#endif
+
+#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
+# ifdef Z_PREFIX_SET
+# define z_gzopen z_gzopen64
+# define z_gzseek z_gzseek64
+# define z_gztell z_gztell64
+# define z_gzoffset z_gzoffset64
+# define z_adler32_combine z_adler32_combine64
+# define z_crc32_combine z_crc32_combine64
+# else
+# define gzopen gzopen64
+# define gzseek gzseek64
+# define gztell gztell64
+# define gzoffset gzoffset64
+# define adler32_combine adler32_combine64
+# define crc32_combine crc32_combine64
+# endif
+# ifndef Z_LARGE64
+ ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+ ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
+ ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
+ ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
+ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+# endif
+#else
+ ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
+ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
+ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
+ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
+ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+#endif
+
+#else /* Z_SOLO */
+
+ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+
+#endif /* !Z_SOLO */
+
+/* hack for buggy compilers */
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;};
+#endif
+
+/* undocumented functions */
+ZEXTERN const char * ZEXPORT zError OF((int));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp));
+ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void));
+ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int));
+ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp));
+ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp));
+#if defined(_WIN32) && !defined(Z_SOLO)
+ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
+ const char *mode));
+#endif
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+# ifndef Z_SOLO
+ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file,
+ const char *format,
+ va_list va));
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/zlib/zutil.c b/zlib/zutil.c
new file mode 100644
index 0000000..bbba7b2
--- /dev/null
+++ b/zlib/zutil.c
@@ -0,0 +1,324 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+#ifndef Z_SOLO
+# include "gzguts.h"
+#endif
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+z_const char * const z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+ uLong flags;
+
+ flags = 0;
+ switch ((int)(sizeof(uInt))) {
+ case 2: break; /* CONSTANT CONDITION */
+ case 4: flags += 1; break; /* CONSTANT CONDITION */
+ case 8: flags += 2; break; /* CONSTANT CONDITION */
+ default: flags += 3;
+ }
+ switch ((int)(sizeof(uLong))) {
+ case 2: break; /* CONSTANT CONDITION */
+ case 4: flags += 1 << 2; break; /* CONSTANT CONDITION */
+ case 8: flags += 2 << 2; break; /* CONSTANT CONDITION */
+ default: flags += 3 << 2;
+ }
+ switch ((int)(sizeof(voidpf))) {
+ case 2: break; /* CONSTANT CONDITION */
+ case 4: flags += 1 << 4; break; /* CONSTANT CONDITION */
+ case 8: flags += 2 << 4; break; /* CONSTANT CONDITION */
+ default: flags += 3 << 4;
+ }
+ switch ((int)(sizeof(z_off_t))) {
+ case 2: break; /* CONSTANT CONDITION */
+ case 4: flags += 1 << 6; break; /* CONSTANT CONDITION */
+ case 8: flags += 2 << 6; break; /* CONSTANT CONDITION */
+ default: flags += 3 << 6;
+ }
+#ifdef DEBUG
+ flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+ flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+ flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+ flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+ flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+ flags += 1L << 16;
+#endif
+#ifdef NO_GZIP
+ flags += 1L << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+ flags += 1L << 20;
+#endif
+#ifdef FASTEST
+ flags += 1L << 21;
+#endif
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+# ifdef NO_vsnprintf
+ flags += 1L << 25;
+# ifdef HAS_vsprintf_void
+ flags += 1L << 26;
+# endif
+# else
+# ifdef HAS_vsnprintf_void
+ flags += 1L << 26;
+# endif
+# endif
+#else
+ flags += 1L << 24;
+# ifdef NO_snprintf
+ flags += 1L << 25;
+# ifdef HAS_sprintf_void
+ flags += 1L << 26;
+# endif
+# else
+# ifdef HAS_snprintf_void
+ flags += 1L << 26;
+# endif
+# endif
+#endif
+ return flags;
+}
+
+#ifdef DEBUG
+
+# ifndef verbose
+# define verbose 0
+# endif
+int ZLIB_INTERNAL z_verbose = verbose;
+
+void ZLIB_INTERNAL z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+ int err;
+{
+ return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+ /* The Microsoft C Run-Time Library for Windows CE doesn't have
+ * errno. We define it as a global variable to simplify porting.
+ * Its value is always 0 and should not be used.
+ */
+ int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void ZLIB_INTERNAL zmemcpy(dest, source, len)
+ Bytef* dest;
+ const Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int ZLIB_INTERNAL zmemcmp(s1, s2, len)
+ const Bytef* s1;
+ const Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void ZLIB_INTERNAL zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+#ifndef Z_SOLO
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+ (voidpf)calloc(items, size);
+}
+
+void ZLIB_INTERNAL zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
+
+#endif /* !Z_SOLO */
diff --git a/zlib/zutil.h b/zlib/zutil.h
new file mode 100644
index 0000000..254effd
--- /dev/null
+++ b/zlib/zutil.h
@@ -0,0 +1,251 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2013 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#define ZLIB_INTERNAL
+#include "../rsync.h"
+#include "zlib.h"
+
+#if 0
+#if defined(STDC) && !defined(Z_SOLO)
+# if !(defined(_WIN32_WCE) && defined(_MSC_VER))
+# include <stddef.h>
+# endif
+# include <string.h>
+# include <stdlib.h>
+#endif
+#endif
+
+#ifdef Z_SOLO
+ typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+# define OS_CODE 0x00
+# ifndef Z_SOLO
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+# endif
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define F_OPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+# if defined(M_I86) && !defined(Z_SOLO)
+# include <malloc.h>
+# endif
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+# define OS_CODE 0x07
+# ifndef Z_SOLO
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
+# endif
+# endif
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#ifdef WIN32
+# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */
+# define OS_CODE 0x0b
+# endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX
+# if defined(_WIN32_WCE)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# ifndef _PTRDIFF_T_DEFINED
+ typedef int ptrdiff_t;
+# define _PTRDIFF_T_DEFINED
+# endif
+# else
+# define fdopen(fd,type) _fdopen(fd,type)
+# endif
+#endif
+
+#if defined(__BORLANDC__) && !defined(MSDOS)
+ #pragma warn -8004
+ #pragma warn -8008
+ #pragma warn -8066
+#endif
+
+/* provide prototypes for these when building zlib without LFS */
+#if !defined(_WIN32) && \
+ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
+ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+#endif
+
+ /* common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#if defined(pyr) || defined(Z_SOLO)
+# define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ * The __SC__ check is for Symantec.
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+ int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+ void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int ZLIB_INTERNAL z_verbose;
+ extern void ZLIB_INTERNAL z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+#ifndef Z_SOLO
+ voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
+ unsigned size));
+ void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));
+#endif
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+/* Reverse the bytes in a 32-bit value */
+#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+#endif /* ZUTIL_H */