mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-05-18 11:55:32 -04:00
Initial revision
This commit is contained in:
50
.cvsignore
Normal file
50
.cvsignore
Normal file
@@ -0,0 +1,50 @@
|
||||
Makefile
|
||||
a
|
||||
b
|
||||
config.cache
|
||||
config.h
|
||||
config.log
|
||||
config.status
|
||||
dist.tar.gz
|
||||
rsync
|
||||
rsync-0.1
|
||||
rsync-0.1
|
||||
rsync-0.1
|
||||
rsync-0.1.tar.gz
|
||||
rsync-0.2
|
||||
rsync-0.2
|
||||
rsync-0.2.tar.gz
|
||||
rsync-0.3
|
||||
rsync-0.3
|
||||
rsync-0.3.tar.gz
|
||||
rsync-0.4
|
||||
rsync-0.4
|
||||
rsync-0.4.tar.gz
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5.tar.gz
|
||||
rsync-0.6
|
||||
rsync-0.7
|
||||
rsync-0.7
|
||||
rsync-0.8
|
||||
rsync-0.8
|
||||
rsync-0.8
|
||||
rsync-0.8
|
||||
rsync-0.8.tar.gz
|
||||
rsync-0.9
|
||||
rsync-0.9.tar.gz
|
||||
rsync-ERSION
|
||||
rsync.aux
|
||||
rsync.dvi
|
||||
rsync.log
|
||||
tech_report.aux
|
||||
tech_report.dvi
|
||||
tech_report.log
|
||||
tech_report.ps
|
||||
test
|
||||
19
.ignore
Normal file
19
.ignore
Normal file
@@ -0,0 +1,19 @@
|
||||
.#*
|
||||
*.log
|
||||
Makefile
|
||||
config.h
|
||||
*.o
|
||||
CVS
|
||||
.ignore
|
||||
.cvsignore
|
||||
*~
|
||||
rsync
|
||||
config.status
|
||||
config.cache
|
||||
TAGS
|
||||
config.log
|
||||
test
|
||||
*.gz
|
||||
rsync-*
|
||||
*.dvi
|
||||
*.aux
|
||||
339
COPYING
Normal file
339
COPYING
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) 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
|
||||
this service 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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. 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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: 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
|
||||
convey 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) 19yy <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 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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision 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, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This 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 Library General
|
||||
Public License instead of this License.
|
||||
49
Makefile.in
Normal file
49
Makefile.in
Normal file
@@ -0,0 +1,49 @@
|
||||
# Makefile for rsync. This is processed by configure to produce the final
|
||||
# Makefile
|
||||
|
||||
INSTALL_BIN=@prefix@/bin
|
||||
INSTALL_MAN=@prefix@/man
|
||||
|
||||
CCOPTFLAGS = -O
|
||||
|
||||
LIBS=@LIBS@
|
||||
CC=@CC@ $(CCOPTFLAGS)
|
||||
|
||||
INSTALLCMD=@INSTALL@
|
||||
|
||||
|
||||
SRC=@srcdir@
|
||||
SHELL=/bin/sh
|
||||
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
LIBOBJ=lib/getopt.o lib/fnmatch.o
|
||||
OBJS=rsync.o exclude.o util.o md4.o main.o checksum.o match.o flist.o $(LIBOBJ)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $*.c -o $*.o
|
||||
|
||||
all: rsync
|
||||
|
||||
install: all
|
||||
${INSTALLCMD} -m 755 rsync ${INSTALL_BIN}
|
||||
${INSTALLCMD} -m 644 rsync.1 ${INSTALL_MAN}/man1
|
||||
|
||||
rsync: $(OBJS)
|
||||
$(CC) $(CFLAGS) -o rsync $(OBJS) $(LIBS)
|
||||
|
||||
proto:
|
||||
cat *.c | awk -f mkproto.awk > proto.h
|
||||
|
||||
clean:
|
||||
rm -f *~ $(OBJS) rsync config.cache config.log config.status
|
||||
|
||||
dist:
|
||||
tar --exclude-from .ignore -czf dist.tar.gz .
|
||||
-mkdir rsync-$(VERSION)
|
||||
(cd rsync-$(VERSION) ; tar xzf ../dist.tar.gz)
|
||||
tar -czf rsync-$(VERSION).tar.gz rsync-$(VERSION)
|
||||
rm -f dist.tar.gz
|
||||
echo rsync-$(VERSION) >> .cvsignore
|
||||
82
README
Normal file
82
README
Normal file
@@ -0,0 +1,82 @@
|
||||
WHAT IS RSYNC?
|
||||
--------------
|
||||
|
||||
rsync is a replacement for rcp that has many more features.
|
||||
|
||||
rsyns uses the "rsync 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 rcp, but rsync has many additional options.
|
||||
|
||||
Here is a brief description of available options:
|
||||
|
||||
-v, --verbose increase verbosity
|
||||
-c, --checksum always checksum
|
||||
-a, --archive archive mode (same as -rlptDog)
|
||||
-r, --recursive recurse into directories
|
||||
-b, --backup make backups (default ~ extension)
|
||||
-u, --update update only (don't overwrite newer files)
|
||||
-l, --links preserve soft links
|
||||
-p, --perms preserve permissions
|
||||
-o, --owner preserve owner (root only)
|
||||
-g, --group preserve group
|
||||
-D, --devices preserve devices (root only)
|
||||
-t, --times preserve times
|
||||
-n, --dry-run show what would have been transferred
|
||||
-x, --one-file-system don't cross filesystem boundaries
|
||||
-B, --block-size SIZE checksum blocking size
|
||||
-e, --rsh COMMAND specify rsh replacement
|
||||
--rsync-path PATH specify path to rsync on the remote machine
|
||||
-C, --cvs-exclude auto ignore files in the same way CVS does
|
||||
--delete delete files that don't exist on the sending side
|
||||
-I, --ignore-times don't exclude files that match length and time
|
||||
--exclude FILE exclude file FILE
|
||||
--exclude-from FILE exclude files listed in FILE
|
||||
--suffix SUFFIX override backup suffix
|
||||
--version print version number
|
||||
|
||||
|
||||
|
||||
SETUP
|
||||
-----
|
||||
|
||||
Rsync uses rsh or ssh for communication. It does not need to be setuid
|
||||
and requires no special privilages for installation. It does not
|
||||
require a inetd entry or a daemon. You must, however, have a working
|
||||
rsh or ssh system. Using ssh is recommended for its security and
|
||||
compression features.
|
||||
|
||||
To install rsync, first run the "configure" script. This will create a
|
||||
Makefile and config.h appropriate for your system. Then type
|
||||
"make".
|
||||
|
||||
Once built put a copy of rsync in your search path on the local and
|
||||
remote systems (or use "make install"). That's it!
|
||||
|
||||
|
||||
COPYRIGHT
|
||||
---------
|
||||
|
||||
Rsync was written by Andrew Tridgell and Paul Mackerras, and is
|
||||
available under the GPL.
|
||||
|
||||
Andrew.Tridgell@anu.edu.au
|
||||
paulus@cs.anu.edu.au
|
||||
|
||||
|
||||
AVAILABILITY
|
||||
------------
|
||||
|
||||
The main ftp site for rsync is ftp://samba.anu.edu.au/pub/rsync
|
||||
54
byteorder.h
Normal file
54
byteorder.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
simple byteorder handling
|
||||
Copyright (C) Andrew Tridgell 1992-1995
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#undef CAREFUL_ALIGNMENT
|
||||
|
||||
/* we know that the x86 can handle misalignment and has the "right"
|
||||
byteorder */
|
||||
#ifdef __i386__
|
||||
#define CAREFUL_ALIGNMENT 0
|
||||
#endif
|
||||
|
||||
#ifndef CAREFUL_ALIGNMENT
|
||||
#define CAREFUL_ALIGNMENT 1
|
||||
#endif
|
||||
|
||||
#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
|
||||
#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
|
||||
#define SCVAL(buf,pos,val) (CVAL(buf,pos) = (val))
|
||||
|
||||
|
||||
#if CAREFUL_ALIGNMENT
|
||||
#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
|
||||
#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
|
||||
#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
|
||||
#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
|
||||
#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
|
||||
#else
|
||||
/* this handles things for architectures like the 386 that can handle
|
||||
alignment errors */
|
||||
/*
|
||||
WARNING: This section is dependent on the length of int32
|
||||
being correct. set CAREFUL_ALIGNMENT if it is not.
|
||||
*/
|
||||
#define IVAL(buf,pos) (*(uint32 *)((char *)(buf) + (pos)))
|
||||
#define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32)(val))
|
||||
#endif
|
||||
|
||||
|
||||
78
checksum.c
Normal file
78
checksum.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
|
||||
/*
|
||||
a simple 32 bit checksum that can be upadted from either end
|
||||
(inspired by Mark Adler's Adler-32 checksum)
|
||||
*/
|
||||
uint32 get_checksum1(char *buf,int len)
|
||||
{
|
||||
int i;
|
||||
uint32 s1, s2;
|
||||
|
||||
s1 = s2 = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
s1 += buf[i];
|
||||
s2 += s1;
|
||||
}
|
||||
return (s1 & 0xffff) + (s2 << 16);
|
||||
}
|
||||
|
||||
|
||||
void get_checksum2(char *buf,int len,char *sum)
|
||||
{
|
||||
char buf2[64];
|
||||
int i;
|
||||
MDstruct MD;
|
||||
|
||||
MDbegin(&MD);
|
||||
for(i = 0; i + 64 <= len; i += 64) {
|
||||
bcopy(buf+i,buf2,64);
|
||||
MDupdate(&MD, buf2, 512);
|
||||
}
|
||||
bcopy(buf+i,buf2,len-i);
|
||||
MDupdate(&MD, buf2, (len-i)*8);
|
||||
SIVAL(sum,0,MD.buffer[0]);
|
||||
SIVAL(sum,4,MD.buffer[1]);
|
||||
SIVAL(sum,8,MD.buffer[2]);
|
||||
SIVAL(sum,12,MD.buffer[3]);
|
||||
}
|
||||
|
||||
void file_checksum(char *fname,char *sum,off_t size)
|
||||
{
|
||||
char *buf;
|
||||
int fd;
|
||||
bzero(sum,SUM_LENGTH);
|
||||
|
||||
fd = open(fname,O_RDONLY);
|
||||
if (fd == -1) return;
|
||||
|
||||
buf = map_file(fd,size);
|
||||
if (!buf) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
get_checksum2(buf,size,sum);
|
||||
close(fd);
|
||||
unmap_file(buf,size);
|
||||
}
|
||||
48
configure.in
Normal file
48
configure.in
Normal file
@@ -0,0 +1,48 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(byteorder.h)
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_SUBST(SHELL)
|
||||
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_STDC
|
||||
AC_HEADER_TIME
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_CHECK_HEADERS(sys/fcntl.h fcntl.h sys/time.h unistd.h utime.h grp.h)
|
||||
AC_CHECK_HEADERS(compat.h sys/param.h ctype.h sys/wait.h)
|
||||
|
||||
AC_CHECK_SIZEOF(int)
|
||||
AC_CHECK_SIZEOF(long)
|
||||
AC_CHECK_SIZEOF(short)
|
||||
|
||||
AC_C_INLINE
|
||||
|
||||
AC_TYPE_SIGNAL
|
||||
AC_TYPE_UID_T
|
||||
AC_TYPE_MODE_T
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_TYPE_PID_T
|
||||
AC_STRUCT_ST_RDEV
|
||||
|
||||
echo -n "checking for errno in errno.h... "
|
||||
AC_TRY_COMPILE([#include <errno.h>],[int i = errno],
|
||||
echo yes; AC_DEFINE(HAVE_ERRNO_DECL),
|
||||
echo no)
|
||||
|
||||
AC_FUNC_MEMCMP
|
||||
AC_FUNC_MMAP
|
||||
AC_FUNC_UTIME_NULL
|
||||
AC_CHECK_FUNCS(waitpid strtok pipe getcwd mkdir strdup strerror chown chmod mknod)
|
||||
AC_CHECK_FUNCS(fchmod fstat strchr bcopy bzero readlink utime utimes getopt_long)
|
||||
|
||||
echo -n "checking for working fnmatch... "
|
||||
AC_TRY_RUN([#include <fnmatch.h>
|
||||
main() { exit(fnmatch("*.o", "x.o", 0) == 0? 0: 1); }],
|
||||
echo yes;AC_DEFINE(HAVE_FNMATCH),
|
||||
echo no)
|
||||
|
||||
AC_OUTPUT(Makefile)
|
||||
202
exclude.c
Normal file
202
exclude.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
a lot of this stuff was derived from GNU tar
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
static char **exclude_list = NULL;
|
||||
|
||||
static int is_regex(char *str)
|
||||
{
|
||||
return strchr(str, '*') || strchr(str, '[') || strchr(str, '?');
|
||||
}
|
||||
|
||||
|
||||
static int check_one_exclude(char *name,char *pattern)
|
||||
{
|
||||
char *str;
|
||||
char *p;
|
||||
|
||||
if (!strchr(pattern,'/') && (p=strrchr(name,'/')))
|
||||
name = p+1;
|
||||
|
||||
if (!name[0]) return 0;
|
||||
|
||||
if (is_regex(pattern)) {
|
||||
if (fnmatch(pattern, name, 0) == 0)
|
||||
return 1;
|
||||
} 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 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int check_exclude(char *name,char **local_exclude_list)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (exclude_list) {
|
||||
for (n=0; exclude_list[n]; n++)
|
||||
if (check_one_exclude(name,exclude_list[n]))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (local_exclude_list) {
|
||||
for (n=0; local_exclude_list[n]; n++)
|
||||
if (check_one_exclude(name,local_exclude_list[n]))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void add_exclude_list(char *pattern,char ***list)
|
||||
{
|
||||
int len=0;
|
||||
if (list && *list)
|
||||
for (; (*list)[len]; len++) ;
|
||||
|
||||
if (strcmp(pattern,"!") == 0) {
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"clearing exclude list\n");
|
||||
while ((len)--)
|
||||
free((*list)[len]);
|
||||
free((*list));
|
||||
*list = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*list) {
|
||||
*list = (char **)malloc(sizeof(char *)*2);
|
||||
} else {
|
||||
*list = (char **)realloc(*list,sizeof(char *)*(len+2));
|
||||
}
|
||||
|
||||
if (!*list || !((*list)[len] = strdup(pattern)))
|
||||
out_of_memory("add_exclude");
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"add_exclude(%s)\n",pattern);
|
||||
|
||||
(*list)[len+1] = NULL;
|
||||
}
|
||||
|
||||
void add_exclude(char *pattern)
|
||||
{
|
||||
add_exclude_list(pattern,&exclude_list);
|
||||
}
|
||||
|
||||
char **make_exclude_list(char *fname,char **list1,int fatal)
|
||||
{
|
||||
char **list=list1;
|
||||
FILE *f = fopen(fname,"r");
|
||||
char line[MAXPATHLEN];
|
||||
if (!f) {
|
||||
if (fatal) {
|
||||
fprintf(stderr,"%s : %s\n",fname,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
while (fgets(line,MAXPATHLEN,f)) {
|
||||
int l = strlen(line);
|
||||
if (l && line[l-1] == '\n') l--;
|
||||
line[l] = 0;
|
||||
if (line[0]) add_exclude_list(line,&list);
|
||||
}
|
||||
fclose(f);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
void add_exclude_file(char *fname,int fatal)
|
||||
{
|
||||
exclude_list = make_exclude_list(fname,exclude_list,fatal);
|
||||
}
|
||||
|
||||
|
||||
void send_exclude_list(int f)
|
||||
{
|
||||
int i;
|
||||
if (exclude_list)
|
||||
for (i=0;exclude_list[i];i++) {
|
||||
int l = strlen(exclude_list[i]);
|
||||
if (l == 0) continue;
|
||||
write_int(f,l);
|
||||
write_buf(f,exclude_list[i],l);
|
||||
}
|
||||
write_int(f,0);
|
||||
}
|
||||
|
||||
|
||||
void recv_exclude_list(int f)
|
||||
{
|
||||
char line[MAXPATHLEN];
|
||||
int l;
|
||||
while ((l=read_int(f))) {
|
||||
read_buf(f,line,l);
|
||||
line[l] = 0;
|
||||
add_exclude(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *cvs_ignore_list[] = {
|
||||
"RCS","SCCS","CVS","CVS.adm","RCSLOG","cvslog.*",
|
||||
"tags","TAGS",".make.state",".nse_depinfo",
|
||||
"*~", "#*", ".#*", ",*", "*.old", "*.bak", "*.BAK", "*.orig",
|
||||
"*.rej", ".del-*", "*.a", "*.o", "*.obj", "*.so", "*.Z", "*.elc", "*.ln",
|
||||
"core",NULL};
|
||||
|
||||
|
||||
|
||||
void add_cvs_excludes(void)
|
||||
{
|
||||
char fname[MAXPATHLEN];
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
for (i=0; cvs_ignore_list[i]; i++)
|
||||
add_exclude(cvs_ignore_list[i]);
|
||||
|
||||
if ((p=getenv("HOME"))) {
|
||||
sprintf(fname,"%s/.cvsignore",p);
|
||||
add_exclude_file(fname,0);
|
||||
}
|
||||
|
||||
if ((p=getenv("CVSIGNORE"))) {
|
||||
char *tok;
|
||||
for (tok=strtok(p," "); tok; tok=strtok(NULL," "))
|
||||
add_exclude(tok);
|
||||
}
|
||||
}
|
||||
524
flist.c
Normal file
524
flist.c
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* generate and receive file lists */
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
extern int verbose;
|
||||
extern int am_server;
|
||||
extern int always_checksum;
|
||||
extern off_t total_size;
|
||||
|
||||
extern int cvs_exclude;
|
||||
|
||||
extern int one_file_system;
|
||||
extern int make_backups;
|
||||
extern int preserve_links;
|
||||
extern int preserve_perms;
|
||||
extern int preserve_devices;
|
||||
extern int preserve_uid;
|
||||
extern int preserve_gid;
|
||||
extern int preserve_times;
|
||||
|
||||
static char **local_exclude_list = NULL;
|
||||
|
||||
static void clean_fname(char *name);
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
static int match_file_name(char *fname,struct stat *st)
|
||||
{
|
||||
if (check_exclude(fname,local_exclude_list)) {
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"excluding file %s\n",fname);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* used by the one_file_system code */
|
||||
static dev_t filesystem_dev;
|
||||
|
||||
static void set_filesystem(char *fname)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(fname,&st) != 0) return;
|
||||
filesystem_dev = st.st_dev;
|
||||
}
|
||||
|
||||
|
||||
static void send_directory(int f,struct file_list *flist,char *dir);
|
||||
|
||||
static char *flist_dir = NULL;
|
||||
|
||||
static void send_file_entry(struct file_struct *file,int f)
|
||||
{
|
||||
if (f == -1) return;
|
||||
|
||||
write_int(f,strlen(file->name));
|
||||
write_buf(f,file->name,strlen(file->name));
|
||||
write_int(f,(int)file->modtime);
|
||||
write_int(f,(int)file->length);
|
||||
write_int(f,(int)file->mode);
|
||||
if (preserve_uid)
|
||||
write_int(f,(int)file->uid);
|
||||
if (preserve_gid)
|
||||
write_int(f,(int)file->gid);
|
||||
if (preserve_devices) {
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"dev=0x%x\n",(int)file->dev);
|
||||
write_int(f,(int)file->dev);
|
||||
}
|
||||
|
||||
#if SUPPORT_LINKS
|
||||
if (preserve_links && S_ISLNK(file->mode)) {
|
||||
write_int(f,strlen(file->link));
|
||||
write_buf(f,file->link,strlen(file->link));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (always_checksum) {
|
||||
write_buf(f,file->sum,SUM_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct file_struct *make_file(int recurse,char *fname)
|
||||
{
|
||||
static struct file_struct file;
|
||||
struct stat st;
|
||||
char sum[SUM_LENGTH];
|
||||
|
||||
bzero(sum,SUM_LENGTH);
|
||||
|
||||
if (lstat(fname,&st) != 0) {
|
||||
fprintf(stderr,"%s: %s\n",
|
||||
fname,strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode) && !recurse) {
|
||||
fprintf(stderr,"skipping directory %s\n",fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (one_file_system && st.st_dev != filesystem_dev)
|
||||
return NULL;
|
||||
|
||||
if (!match_file_name(fname,&st))
|
||||
return NULL;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"make_file(%s)\n",fname);
|
||||
|
||||
file.name = strdup(fname);
|
||||
file.modtime = st.st_mtime;
|
||||
file.length = st.st_size;
|
||||
file.mode = st.st_mode;
|
||||
file.uid = st.st_uid;
|
||||
file.gid = st.st_gid;
|
||||
#ifdef HAVE_ST_RDEV
|
||||
file.dev = st.st_rdev;
|
||||
#endif
|
||||
|
||||
#if SUPPORT_LINKS
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
int l;
|
||||
char lnk[MAXPATHLEN];
|
||||
if ((l=readlink(fname,lnk,MAXPATHLEN-1)) == -1) {
|
||||
fprintf(stderr,"readlink %s : %s\n",fname,strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
lnk[l] = 0;
|
||||
file.link = strdup(lnk);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (always_checksum && S_ISREG(st.st_mode)) {
|
||||
file_checksum(fname,file.sum,st.st_size);
|
||||
}
|
||||
|
||||
if (flist_dir)
|
||||
file.dir = strdup(flist_dir);
|
||||
else
|
||||
file.dir = NULL;
|
||||
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
total_size += st.st_size;
|
||||
|
||||
return &file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void send_file_name(int f,struct file_list *flist,
|
||||
int recurse,char *fname)
|
||||
{
|
||||
struct file_struct *file;
|
||||
|
||||
file = make_file(recurse,fname);
|
||||
|
||||
if (!file) return;
|
||||
|
||||
if (flist->count >= flist->malloced) {
|
||||
flist->malloced += 100;
|
||||
flist->files = (struct file_struct *)realloc(flist->files,
|
||||
sizeof(flist->files[0])*
|
||||
flist->malloced);
|
||||
if (!flist->files)
|
||||
out_of_memory("send_file_name");
|
||||
}
|
||||
|
||||
if (strcmp(file->name,".") && strcmp(file->name,"/")) {
|
||||
flist->files[flist->count++] = *file;
|
||||
send_file_entry(file,f);
|
||||
}
|
||||
|
||||
if (S_ISDIR(file->mode) && recurse) {
|
||||
char **last_exclude_list = local_exclude_list;
|
||||
send_directory(f,flist,file->name);
|
||||
local_exclude_list = last_exclude_list;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void send_directory(int f,struct file_list *flist,char *dir)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *di;
|
||||
char fname[MAXPATHLEN];
|
||||
int l;
|
||||
char *p;
|
||||
|
||||
d = opendir(dir);
|
||||
if (!d) {
|
||||
fprintf(stderr,"%s: %s\n",
|
||||
dir,strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(fname,dir);
|
||||
l = strlen(fname);
|
||||
if (fname[l-1] != '/')
|
||||
strcat(fname,"/");
|
||||
p = fname + strlen(fname);
|
||||
|
||||
if (cvs_exclude) {
|
||||
strcpy(p,".cvsignore");
|
||||
local_exclude_list = make_exclude_list(fname,NULL,0);
|
||||
}
|
||||
|
||||
for (di=readdir(d); di; di=readdir(d)) {
|
||||
if (strcmp(di->d_name,".")==0 ||
|
||||
strcmp(di->d_name,"..")==0)
|
||||
continue;
|
||||
strcpy(p,di->d_name);
|
||||
send_file_name(f,flist,1,fname);
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct file_list *send_file_list(int f,int recurse,int argc,char *argv[])
|
||||
{
|
||||
int i,l;
|
||||
struct stat st;
|
||||
char *p,*dir;
|
||||
char dbuf[MAXPATHLEN];
|
||||
struct file_list *flist;
|
||||
|
||||
if (verbose && recurse) {
|
||||
fprintf(am_server?stderr:stdout,"building file list ... ");
|
||||
fflush(am_server?stderr:stdout);
|
||||
}
|
||||
|
||||
flist = (struct file_list *)malloc(sizeof(flist[0]));
|
||||
if (!flist) out_of_memory("send_file_list");
|
||||
|
||||
flist->count=0;
|
||||
flist->malloced = 100;
|
||||
flist->files = (struct file_struct *)malloc(sizeof(flist->files[0])*
|
||||
flist->malloced);
|
||||
if (!flist->files) out_of_memory("send_file_list");
|
||||
|
||||
for (i=0;i<argc;i++) {
|
||||
char fname2[MAXPATHLEN];
|
||||
char *fname = fname2;
|
||||
|
||||
strcpy(fname,argv[i]);
|
||||
|
||||
l = strlen(fname);
|
||||
if (l != 1 && fname[l-1] == '/') {
|
||||
strcat(fname,".");
|
||||
}
|
||||
|
||||
if (lstat(fname,&st) != 0) {
|
||||
fprintf(stderr,"%s : %s\n",fname,strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode) && !recurse) {
|
||||
fprintf(stderr,"skipping directory %s\n",fname);
|
||||
continue;
|
||||
}
|
||||
|
||||
dir = NULL;
|
||||
p = strrchr(fname,'/');
|
||||
if (p) {
|
||||
*p = 0;
|
||||
dir = fname;
|
||||
fname = p+1;
|
||||
}
|
||||
if (!*fname)
|
||||
fname = ".";
|
||||
|
||||
if (dir && *dir) {
|
||||
if (getcwd(dbuf,MAXPATHLEN-1) == NULL) {
|
||||
fprintf(stderr,"getwd : %s\n",strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (chdir(dir) != 0) {
|
||||
fprintf(stderr,"chdir %s : %s\n",dir,strerror(errno));
|
||||
continue;
|
||||
}
|
||||
flist_dir = dir;
|
||||
if (one_file_system)
|
||||
set_filesystem(fname);
|
||||
send_file_name(f,flist,recurse,fname);
|
||||
flist_dir = NULL;
|
||||
if (chdir(dbuf) != 0) {
|
||||
fprintf(stderr,"chdir %s : %s\n",dbuf,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (one_file_system)
|
||||
set_filesystem(fname);
|
||||
send_file_name(f,flist,recurse,fname);
|
||||
}
|
||||
|
||||
if (f != -1) {
|
||||
write_int(f,0);
|
||||
write_flush(f);
|
||||
}
|
||||
|
||||
clean_flist(flist);
|
||||
|
||||
if (verbose && recurse)
|
||||
fprintf(am_server?stderr:stdout,"done\n");
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
|
||||
struct file_list *recv_file_list(int f)
|
||||
{
|
||||
int l;
|
||||
struct file_list *flist;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"recv_file_list starting\n");
|
||||
|
||||
flist = (struct file_list *)malloc(sizeof(flist[0]));
|
||||
if (!flist)
|
||||
goto oom;
|
||||
|
||||
flist->count=0;
|
||||
flist->malloced=100;
|
||||
flist->files = (struct file_struct *)malloc(sizeof(flist->files[0])*
|
||||
flist->malloced);
|
||||
if (!flist->files)
|
||||
goto oom;
|
||||
|
||||
|
||||
for (l=read_int(f); l; l=read_int(f)) {
|
||||
int i = flist->count;
|
||||
|
||||
if (i >= flist->malloced) {
|
||||
flist->malloced += 100;
|
||||
flist->files =(struct file_struct *)realloc(flist->files,
|
||||
sizeof(flist->files[0])*
|
||||
flist->malloced);
|
||||
if (!flist->files)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
flist->files[i].name = (char *)malloc(l+1);
|
||||
if (!flist->files[i].name)
|
||||
goto oom;
|
||||
|
||||
read_buf(f,flist->files[i].name,l);
|
||||
flist->files[i].name[l] = 0;
|
||||
flist->files[i].modtime = (time_t)read_int(f);
|
||||
flist->files[i].length = (off_t)read_int(f);
|
||||
flist->files[i].mode = (mode_t)read_int(f);
|
||||
if (preserve_uid)
|
||||
flist->files[i].uid = (uid_t)read_int(f);
|
||||
if (preserve_gid)
|
||||
flist->files[i].gid = (gid_t)read_int(f);
|
||||
if (preserve_devices)
|
||||
flist->files[i].dev = (dev_t)read_int(f);
|
||||
|
||||
#if SUPPORT_LINKS
|
||||
if (preserve_links && S_ISLNK(flist->files[i].mode)) {
|
||||
int l = read_int(f);
|
||||
flist->files[i].link = (char *)malloc(l+1);
|
||||
read_buf(f,flist->files[i].link,l);
|
||||
flist->files[i].link[l] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (always_checksum)
|
||||
read_buf(f,flist->files[i].sum,SUM_LENGTH);
|
||||
|
||||
if (S_ISREG(flist->files[i].mode))
|
||||
total_size += flist->files[i].length;
|
||||
|
||||
flist->count++;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"recv_file_name(%s)\n",flist->files[i].name);
|
||||
}
|
||||
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"received %d names\n",flist->count);
|
||||
|
||||
clean_flist(flist);
|
||||
|
||||
return flist;
|
||||
|
||||
oom:
|
||||
out_of_memory("recv_file_list");
|
||||
return NULL; /* not reached */
|
||||
}
|
||||
|
||||
|
||||
static int flist_compare(struct file_struct *f1,struct file_struct *f2)
|
||||
{
|
||||
if (!f1->name && !f2->name) return 0;
|
||||
if (!f1->name) return -1;
|
||||
if (!f2->name) return 1;
|
||||
return strcmp(f1->name,f2->name);
|
||||
}
|
||||
|
||||
|
||||
int flist_find(struct file_list *flist,struct file_struct *f)
|
||||
{
|
||||
int low=0,high=flist->count;
|
||||
|
||||
while (low != high) {
|
||||
int mid = (low+high)/2;
|
||||
int ret = flist_compare(&flist->files[mid],f);
|
||||
if (ret == 0) return mid;
|
||||
if (ret > 0)
|
||||
high=mid;
|
||||
else
|
||||
low=mid+1;
|
||||
}
|
||||
if (flist_compare(&flist->files[low],f) == 0)
|
||||
return low;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void clean_fname(char *name)
|
||||
{
|
||||
char *p;
|
||||
int l;
|
||||
int modified = 1;
|
||||
|
||||
if (!name) return;
|
||||
|
||||
while (modified) {
|
||||
modified = 0;
|
||||
|
||||
if ((p=strstr(name,"/./"))) {
|
||||
modified = 1;
|
||||
while (*p) {
|
||||
p[0] = p[2];
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((p=strstr(name,"//"))) {
|
||||
modified = 1;
|
||||
while (*p) {
|
||||
p[0] = p[1];
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(p=name,"./",2) == 0) {
|
||||
modified = 1;
|
||||
while (*p) {
|
||||
p[0] = p[2];
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
l = strlen(p=name);
|
||||
if (l > 1 && p[l-1] == '/') {
|
||||
modified = 1;
|
||||
p[l-1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This routine ensures we don't have any duplicate names in our file list.
|
||||
* duplicate names can cause corruption because of the pipelining
|
||||
*/
|
||||
void clean_flist(struct file_list *flist)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!flist || flist->count == 0)
|
||||
return;
|
||||
|
||||
for (i=0;i<flist->count;i++) {
|
||||
clean_fname(flist->files[i].name);
|
||||
}
|
||||
|
||||
qsort(flist->files,flist->count,
|
||||
sizeof(flist->files[0]),
|
||||
(int (*)())flist_compare);
|
||||
|
||||
for (i=1;i<flist->count;i++) {
|
||||
if (flist->files[i].name &&
|
||||
strcmp(flist->files[i].name,flist->files[i-1].name) == 0) {
|
||||
if (verbose > 1 && !am_server)
|
||||
fprintf(stderr,"removing duplicate name %s from file list\n",
|
||||
flist->files[i].name);
|
||||
free(flist->files[i-1].name);
|
||||
flist->files[i-1].name = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
238
install-sh
Executable file
238
install-sh
Executable file
@@ -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 -o -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
|
||||
47
lib/.cvsignore
Normal file
47
lib/.cvsignore
Normal file
@@ -0,0 +1,47 @@
|
||||
Makefile
|
||||
a
|
||||
b
|
||||
config.cache
|
||||
config.h
|
||||
config.log
|
||||
config.status
|
||||
dist.tar.gz
|
||||
rsync
|
||||
rsync-0.1
|
||||
rsync-0.1
|
||||
rsync-0.1
|
||||
rsync-0.1.tar.gz
|
||||
rsync-0.2
|
||||
rsync-0.2
|
||||
rsync-0.2.tar.gz
|
||||
rsync-0.3
|
||||
rsync-0.3
|
||||
rsync-0.3.tar.gz
|
||||
rsync-0.4
|
||||
rsync-0.4
|
||||
rsync-0.4.tar.gz
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5
|
||||
rsync-0.5.tar.gz
|
||||
rsync-ERSION
|
||||
rsync.aux
|
||||
rsync.dvi
|
||||
rsync.log
|
||||
tech_report.aux
|
||||
tech_report.dvi
|
||||
tech_report.log
|
||||
tech_report.ps
|
||||
test
|
||||
rsync-0.6
|
||||
rsync-0.7
|
||||
rsync-0.7
|
||||
rsync-0.8
|
||||
rsync-0.8
|
||||
rsync-0.8
|
||||
rsync-0.8
|
||||
204
lib/fnmatch.c
Normal file
204
lib/fnmatch.c
Normal file
@@ -0,0 +1,204 @@
|
||||
#include "../rsync.h"
|
||||
#ifndef HAVE_FNMATCH
|
||||
|
||||
/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
|
||||
|
||||
NOTE: The canonical source of this file is maintained with the GNU C Library.
|
||||
Bugs can be reported to bug-glibc@prep.ai.mit.edu.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#if defined (STDC_HEADERS) || !defined (isascii)
|
||||
#define ISASCII(c) 1
|
||||
#else
|
||||
#define ISASCII(c) isascii(c)
|
||||
#endif
|
||||
|
||||
#define ISUPPER(c) (ISASCII (c) && isupper (c))
|
||||
|
||||
|
||||
/* Comment out all this code if we are using the GNU C Library, and are not
|
||||
actually compiling the library itself. This code is part of the GNU C
|
||||
Library, but also included in many other GNU distributions. Compiling
|
||||
and linking in this code is a waste when using the GNU C library
|
||||
(especially if it is a shared library). Rather than having every GNU
|
||||
program understand `configure --with-gnu-libc' and omit the object files,
|
||||
it is simpler to just do this in the source for each such file. */
|
||||
|
||||
#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
/* Match STRING against the filename pattern PATTERN, returning zero if
|
||||
it matches, nonzero if not. */
|
||||
int
|
||||
fnmatch (pattern, string, flags)
|
||||
const char *pattern;
|
||||
const char *string;
|
||||
int flags;
|
||||
{
|
||||
register const char *p = pattern, *n = string;
|
||||
register char c;
|
||||
|
||||
/* Note that this evalutes C many times. */
|
||||
#define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
|
||||
|
||||
while ((c = *p++) != '\0')
|
||||
{
|
||||
c = FOLD (c);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
if (*n == '\0')
|
||||
return FNM_NOMATCH;
|
||||
else if ((flags & FNM_FILE_NAME) && *n == '/')
|
||||
return FNM_NOMATCH;
|
||||
else if ((flags & FNM_PERIOD) && *n == '.' &&
|
||||
(n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if (!(flags & FNM_NOESCAPE))
|
||||
{
|
||||
c = *p++;
|
||||
c = FOLD (c);
|
||||
}
|
||||
if (FOLD (*n) != c)
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if ((flags & FNM_PERIOD) && *n == '.' &&
|
||||
(n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
|
||||
if (((flags & FNM_FILE_NAME) && *n == '/') ||
|
||||
(c == '?' && *n == '\0'))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (c == '\0')
|
||||
return 0;
|
||||
|
||||
{
|
||||
char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
|
||||
c1 = FOLD (c1);
|
||||
for (--p; *n != '\0'; ++n)
|
||||
if ((c == '[' || FOLD (*n) == c1) &&
|
||||
fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
|
||||
return 0;
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
|
||||
case '[':
|
||||
{
|
||||
/* Nonzero if the sense of the character class is inverted. */
|
||||
register int not;
|
||||
|
||||
if (*n == '\0')
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if ((flags & FNM_PERIOD) && *n == '.' &&
|
||||
(n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
not = (*p == '!' || *p == '^');
|
||||
if (not)
|
||||
++p;
|
||||
|
||||
c = *p++;
|
||||
for (;;)
|
||||
{
|
||||
register char cstart = c, cend = c;
|
||||
|
||||
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||
cstart = cend = *p++;
|
||||
|
||||
cstart = cend = FOLD (cstart);
|
||||
|
||||
if (c == '\0')
|
||||
/* [ (unterminated) loses. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
c = *p++;
|
||||
c = FOLD (c);
|
||||
|
||||
if ((flags & FNM_FILE_NAME) && c == '/')
|
||||
/* [/] can never match. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (c == '-' && *p != ']')
|
||||
{
|
||||
cend = *p++;
|
||||
if (!(flags & FNM_NOESCAPE) && cend == '\\')
|
||||
cend = *p++;
|
||||
if (cend == '\0')
|
||||
return FNM_NOMATCH;
|
||||
cend = FOLD (cend);
|
||||
|
||||
c = *p++;
|
||||
}
|
||||
|
||||
if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
|
||||
goto matched;
|
||||
|
||||
if (c == ']')
|
||||
break;
|
||||
}
|
||||
if (!not)
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
matched:;
|
||||
/* Skip the rest of the [...] that already matched. */
|
||||
while (c != ']')
|
||||
{
|
||||
if (c == '\0')
|
||||
/* [... (unterminated) loses. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
c = *p++;
|
||||
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||
/* XXX 1003.2d11 is unclear if this is right. */
|
||||
++p;
|
||||
}
|
||||
if (not)
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c != FOLD (*n))
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
if (*n == '\0')
|
||||
return 0;
|
||||
|
||||
if ((flags & FNM_LEADING_DIR) && *n == '/')
|
||||
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
|
||||
return 0;
|
||||
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
|
||||
#else /* HAVE_FNMATCH */
|
||||
void fnmatch_dummy(void) {}
|
||||
#endif
|
||||
69
lib/fnmatch.h
Normal file
69
lib/fnmatch.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
|
||||
|
||||
NOTE: The canonical source of this file is maintained with the GNU C Library.
|
||||
Bugs can be reported to bug-glibc@prep.ai.mit.edu.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#ifndef _FNMATCH_H
|
||||
|
||||
#define _FNMATCH_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
|
||||
#undef __P
|
||||
#define __P(protos) protos
|
||||
#else /* Not C++ or ANSI C. */
|
||||
#undef __P
|
||||
#define __P(protos) ()
|
||||
/* We can get away without defining `const' here only because in this file
|
||||
it is used only inside the prototype for `fnmatch', which is elided in
|
||||
non-ANSI C where `const' is problematical. */
|
||||
#endif /* C++ or ANSI C. */
|
||||
|
||||
|
||||
/* We #undef these before defining them because some losing systems
|
||||
(HP-UX A.08.07 for example) define these in <unistd.h>. */
|
||||
#undef FNM_PATHNAME
|
||||
#undef FNM_NOESCAPE
|
||||
#undef FNM_PERIOD
|
||||
|
||||
/* Bits set in the FLAGS argument to `fnmatch'. */
|
||||
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
|
||||
#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
|
||||
#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
|
||||
|
||||
#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE)
|
||||
#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
|
||||
#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
|
||||
#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
|
||||
#endif
|
||||
|
||||
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
|
||||
#define FNM_NOMATCH 1
|
||||
|
||||
/* Match STRING against the filename pattern PATTERN,
|
||||
returning zero if it matches, FNM_NOMATCH if not. */
|
||||
extern int fnmatch __P ((const char *__pattern, const char *__string,
|
||||
int __flags));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* fnmatch.h */
|
||||
751
lib/getopt.c
Normal file
751
lib/getopt.c
Normal file
@@ -0,0 +1,751 @@
|
||||
#include "../rsync.h"
|
||||
#ifndef HAVE_GETOPT_LONG
|
||||
|
||||
/* Getopt for GNU.
|
||||
NOTE: getopt is now part of the C library, so if you don't know what
|
||||
"Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
|
||||
before changing it!
|
||||
|
||||
Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
|
||||
Ditto for AIX 3.2 and <stdlib.h>. */
|
||||
#ifndef _NO_PROTO
|
||||
#define _NO_PROTO
|
||||
#endif
|
||||
|
||||
/* Comment out all this code if we are using the GNU C Library, and are not
|
||||
actually compiling the library itself. This code is part of the GNU C
|
||||
Library, but also included in many other GNU distributions. Compiling
|
||||
and linking in this code is a waste when using the GNU C library
|
||||
(especially if it is a shared library). Rather than having every GNU
|
||||
program understand `configure --with-gnu-libc' and omit the object files,
|
||||
it is simpler to just do this in the source for each such file. */
|
||||
|
||||
#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
|
||||
|
||||
/* This is for other GNU distributions with internationalized messages.
|
||||
The GNU C Library itself does not yet support such messages. */
|
||||
#if HAVE_LIBINTL_H
|
||||
# include <libintl.h>
|
||||
#else
|
||||
# define gettext(msgid) (msgid)
|
||||
#endif
|
||||
|
||||
/* This version of `getopt' appears to the caller like standard Unix `getopt'
|
||||
but it behaves differently for the user, since it allows the user
|
||||
to intersperse the options with the other arguments.
|
||||
|
||||
As `getopt' works, it permutes the elements of ARGV so that,
|
||||
when it is done, all the options precede everything else. Thus
|
||||
all application programs are extended to handle flexible argument order.
|
||||
|
||||
Setting the environment variable POSIXLY_CORRECT disables permutation.
|
||||
Then the behavior is completely standard.
|
||||
|
||||
GNU application programs can use a third alternative mode in which
|
||||
they can distinguish the relative order of options and other arguments. */
|
||||
|
||||
/* For communication from `getopt' to the caller.
|
||||
When `getopt' finds an option that takes an argument,
|
||||
the argument value is returned here.
|
||||
Also, when `ordering' is RETURN_IN_ORDER,
|
||||
each non-option ARGV-element is returned here. */
|
||||
|
||||
char *optarg = NULL;
|
||||
|
||||
/* Index in ARGV of the next element to be scanned.
|
||||
This is used for communication to and from the caller
|
||||
and for communication between successive calls to `getopt'.
|
||||
|
||||
On entry to `getopt', zero means this is the first call; initialize.
|
||||
|
||||
When `getopt' returns EOF, this is the index of the first of the
|
||||
non-option elements that the caller should itself scan.
|
||||
|
||||
Otherwise, `optind' communicates from one call to the next
|
||||
how much of ARGV has been scanned so far. */
|
||||
|
||||
/* XXX 1003.2 says this must be 1 before any call. */
|
||||
int optind = 0;
|
||||
|
||||
/* The next char to be scanned in the option-element
|
||||
in which the last option character we returned was found.
|
||||
This allows us to pick up the scan where we left off.
|
||||
|
||||
If this is zero, or a null string, it means resume the scan
|
||||
by advancing to the next ARGV-element. */
|
||||
|
||||
static char *nextchar;
|
||||
|
||||
/* Callers store zero here to inhibit the error message
|
||||
for unrecognized options. */
|
||||
|
||||
int opterr = 1;
|
||||
|
||||
/* Set to an option character which was unrecognized.
|
||||
This must be initialized on some systems to avoid linking in the
|
||||
system's own getopt implementation. */
|
||||
|
||||
int optopt = '?';
|
||||
|
||||
/* Describe how to deal with options that follow non-option ARGV-elements.
|
||||
|
||||
If the caller did not specify anything,
|
||||
the default is REQUIRE_ORDER if the environment variable
|
||||
POSIXLY_CORRECT is defined, PERMUTE otherwise.
|
||||
|
||||
REQUIRE_ORDER means don't recognize them as options;
|
||||
stop option processing when the first non-option is seen.
|
||||
This is what Unix does.
|
||||
This mode of operation is selected by either setting the environment
|
||||
variable POSIXLY_CORRECT, or using `+' as the first character
|
||||
of the list of option characters.
|
||||
|
||||
PERMUTE is the default. We permute the contents of ARGV as we scan,
|
||||
so that eventually all the non-options are at the end. This allows options
|
||||
to be given in any order, even with programs that were not written to
|
||||
expect this.
|
||||
|
||||
RETURN_IN_ORDER is an option available to programs that were written
|
||||
to expect options and other ARGV-elements in any order and that care about
|
||||
the ordering of the two. We describe each non-option ARGV-element
|
||||
as if it were the argument of an option with character code 1.
|
||||
Using `-' as the first character of the list of option characters
|
||||
selects this mode of operation.
|
||||
|
||||
The special argument `--' forces an end of option-scanning regardless
|
||||
of the value of `ordering'. In the case of RETURN_IN_ORDER, only
|
||||
`--' can cause `getopt' to return EOF with `optind' != ARGC. */
|
||||
|
||||
static enum
|
||||
{
|
||||
REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
|
||||
} ordering;
|
||||
|
||||
/* Value of POSIXLY_CORRECT environment variable. */
|
||||
static char *posixly_correct;
|
||||
|
||||
#ifdef __GNU_LIBRARY__
|
||||
/* We want to avoid inclusion of string.h with non-GNU libraries
|
||||
because there are many ways it can cause trouble.
|
||||
On some systems, it contains special magic macros that don't work
|
||||
in GCC. */
|
||||
#include <string.h>
|
||||
#define my_index strchr
|
||||
#else
|
||||
|
||||
/* Avoid depending on library functions or files
|
||||
whose names are inconsistent. */
|
||||
|
||||
char *getenv ();
|
||||
|
||||
static char *
|
||||
my_index (str, chr)
|
||||
const char *str;
|
||||
int chr;
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
if (*str == chr)
|
||||
return (char *) str;
|
||||
str++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If using GCC, we can safely declare strlen this way.
|
||||
If not using GCC, it is ok not to declare it. */
|
||||
#ifdef __GNUC__
|
||||
/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
|
||||
That was relevant to code that was here before. */
|
||||
#if !defined (__STDC__) || !__STDC__
|
||||
/* gcc with -traditional declares the built-in strlen to return int,
|
||||
and has done so at least since version 2.4.5. -- rms. */
|
||||
extern int strlen (const char *);
|
||||
#endif /* not __STDC__ */
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#endif /* not __GNU_LIBRARY__ */
|
||||
|
||||
/* Handle permutation of arguments. */
|
||||
|
||||
/* Describe the part of ARGV that contains non-options that have
|
||||
been skipped. `first_nonopt' is the index in ARGV of the first of them;
|
||||
`last_nonopt' is the index after the last of them. */
|
||||
|
||||
static int first_nonopt;
|
||||
static int last_nonopt;
|
||||
|
||||
/* Exchange two adjacent subsequences of ARGV.
|
||||
One subsequence is elements [first_nonopt,last_nonopt)
|
||||
which contains all the non-options that have been skipped so far.
|
||||
The other is elements [last_nonopt,optind), which contains all
|
||||
the options processed since those non-options were skipped.
|
||||
|
||||
`first_nonopt' and `last_nonopt' are relocated so that they describe
|
||||
the new indices of the non-options in ARGV after they are moved. */
|
||||
|
||||
static void
|
||||
exchange (argv)
|
||||
char **argv;
|
||||
{
|
||||
int bottom = first_nonopt;
|
||||
int middle = last_nonopt;
|
||||
int top = optind;
|
||||
char *tem;
|
||||
|
||||
/* Exchange the shorter segment with the far end of the longer segment.
|
||||
That puts the shorter segment into the right place.
|
||||
It leaves the longer segment in the right place overall,
|
||||
but it consists of two parts that need to be swapped next. */
|
||||
|
||||
while (top > middle && middle > bottom)
|
||||
{
|
||||
if (top - middle > middle - bottom)
|
||||
{
|
||||
/* Bottom segment is the short one. */
|
||||
int len = middle - bottom;
|
||||
register int i;
|
||||
|
||||
/* Swap it with the top part of the top segment. */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tem = argv[bottom + i];
|
||||
argv[bottom + i] = argv[top - (middle - bottom) + i];
|
||||
argv[top - (middle - bottom) + i] = tem;
|
||||
}
|
||||
/* Exclude the moved bottom segment from further swapping. */
|
||||
top -= len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Top segment is the short one. */
|
||||
int len = top - middle;
|
||||
register int i;
|
||||
|
||||
/* Swap it with the bottom part of the bottom segment. */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
tem = argv[bottom + i];
|
||||
argv[bottom + i] = argv[middle + i];
|
||||
argv[middle + i] = tem;
|
||||
}
|
||||
/* Exclude the moved top segment from further swapping. */
|
||||
bottom += len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update records for the slots the non-options now occupy. */
|
||||
|
||||
first_nonopt += (optind - last_nonopt);
|
||||
last_nonopt = optind;
|
||||
}
|
||||
|
||||
/* Initialize the internal data when the first call is made. */
|
||||
|
||||
static const char *
|
||||
_getopt_initialize (optstring)
|
||||
const char *optstring;
|
||||
{
|
||||
/* Start processing options with ARGV-element 1 (since ARGV-element 0
|
||||
is the program name); the sequence of previously skipped
|
||||
non-option ARGV-elements is empty. */
|
||||
|
||||
first_nonopt = last_nonopt = optind = 1;
|
||||
|
||||
nextchar = NULL;
|
||||
|
||||
posixly_correct = getenv ("POSIXLY_CORRECT");
|
||||
|
||||
/* Determine how to handle the ordering of options and nonoptions. */
|
||||
|
||||
if (optstring[0] == '-')
|
||||
{
|
||||
ordering = RETURN_IN_ORDER;
|
||||
++optstring;
|
||||
}
|
||||
else if (optstring[0] == '+')
|
||||
{
|
||||
ordering = REQUIRE_ORDER;
|
||||
++optstring;
|
||||
}
|
||||
else if (posixly_correct != NULL)
|
||||
ordering = REQUIRE_ORDER;
|
||||
else
|
||||
ordering = PERMUTE;
|
||||
|
||||
return optstring;
|
||||
}
|
||||
|
||||
/* Scan elements of ARGV (whose length is ARGC) for option characters
|
||||
given in OPTSTRING.
|
||||
|
||||
If an element of ARGV starts with '-', and is not exactly "-" or "--",
|
||||
then it is an option element. The characters of this element
|
||||
(aside from the initial '-') are option characters. If `getopt'
|
||||
is called repeatedly, it returns successively each of the option characters
|
||||
from each of the option elements.
|
||||
|
||||
If `getopt' finds another option character, it returns that character,
|
||||
updating `optind' and `nextchar' so that the next call to `getopt' can
|
||||
resume the scan with the following option character or ARGV-element.
|
||||
|
||||
If there are no more option characters, `getopt' returns `EOF'.
|
||||
Then `optind' is the index in ARGV of the first ARGV-element
|
||||
that is not an option. (The ARGV-elements have been permuted
|
||||
so that those that are not options now come last.)
|
||||
|
||||
OPTSTRING is a string containing the legitimate option characters.
|
||||
If an option character is seen that is not listed in OPTSTRING,
|
||||
return '?' after printing an error message. If you set `opterr' to
|
||||
zero, the error message is suppressed but we still return '?'.
|
||||
|
||||
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
|
||||
so the following text in the same ARGV-element, or the text of the following
|
||||
ARGV-element, is returned in `optarg'. Two colons mean an option that
|
||||
wants an optional arg; if there is text in the current ARGV-element,
|
||||
it is returned in `optarg', otherwise `optarg' is set to zero.
|
||||
|
||||
If OPTSTRING starts with `-' or `+', it requests different methods of
|
||||
handling the non-option ARGV-elements.
|
||||
See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
|
||||
|
||||
Long-named options begin with `--' instead of `-'.
|
||||
Their names may be abbreviated as long as the abbreviation is unique
|
||||
or is an exact match for some defined option. If they have an
|
||||
argument, it follows the option name in the same ARGV-element, separated
|
||||
from the option name by a `=', or else the in next ARGV-element.
|
||||
When `getopt' finds a long-named option, it returns 0 if that option's
|
||||
`flag' field is nonzero, the value of the option's `val' field
|
||||
if the `flag' field is zero.
|
||||
|
||||
The elements of ARGV aren't really const, because we permute them.
|
||||
But we pretend they're const in the prototype to be compatible
|
||||
with other systems.
|
||||
|
||||
LONGOPTS is a vector of `struct option' terminated by an
|
||||
element containing a name which is zero.
|
||||
|
||||
LONGIND returns the index in LONGOPT of the long-named option found.
|
||||
It is only valid when a long-named option has been found by the most
|
||||
recent call.
|
||||
|
||||
If LONG_ONLY is nonzero, '-' as well as '--' can introduce
|
||||
long-named options. */
|
||||
|
||||
int
|
||||
_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
|
||||
int argc;
|
||||
char *const *argv;
|
||||
const char *optstring;
|
||||
const struct option *longopts;
|
||||
int *longind;
|
||||
int long_only;
|
||||
{
|
||||
optarg = NULL;
|
||||
|
||||
if (optind == 0)
|
||||
optstring = _getopt_initialize (optstring);
|
||||
|
||||
if (nextchar == NULL || *nextchar == '\0')
|
||||
{
|
||||
/* Advance to the next ARGV-element. */
|
||||
|
||||
if (ordering == PERMUTE)
|
||||
{
|
||||
/* If we have just processed some options following some non-options,
|
||||
exchange them so that the options come first. */
|
||||
|
||||
if (first_nonopt != last_nonopt && last_nonopt != optind)
|
||||
exchange ((char **) argv);
|
||||
else if (last_nonopt != optind)
|
||||
first_nonopt = optind;
|
||||
|
||||
/* Skip any additional non-options
|
||||
and extend the range of non-options previously skipped. */
|
||||
|
||||
while (optind < argc
|
||||
&& (argv[optind][0] != '-' || argv[optind][1] == '\0'))
|
||||
optind++;
|
||||
last_nonopt = optind;
|
||||
}
|
||||
|
||||
/* The special ARGV-element `--' means premature end of options.
|
||||
Skip it like a null option,
|
||||
then exchange with previous non-options as if it were an option,
|
||||
then skip everything else like a non-option. */
|
||||
|
||||
if (optind != argc && !strcmp (argv[optind], "--"))
|
||||
{
|
||||
optind++;
|
||||
|
||||
if (first_nonopt != last_nonopt && last_nonopt != optind)
|
||||
exchange ((char **) argv);
|
||||
else if (first_nonopt == last_nonopt)
|
||||
first_nonopt = optind;
|
||||
last_nonopt = argc;
|
||||
|
||||
optind = argc;
|
||||
}
|
||||
|
||||
/* If we have done all the ARGV-elements, stop the scan
|
||||
and back over any non-options that we skipped and permuted. */
|
||||
|
||||
if (optind == argc)
|
||||
{
|
||||
/* Set the next-arg-index to point at the non-options
|
||||
that we previously skipped, so the caller will digest them. */
|
||||
if (first_nonopt != last_nonopt)
|
||||
optind = first_nonopt;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/* If we have come to a non-option and did not permute it,
|
||||
either stop the scan or describe it to the caller and pass it by. */
|
||||
|
||||
if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
|
||||
{
|
||||
if (ordering == REQUIRE_ORDER)
|
||||
return EOF;
|
||||
optarg = argv[optind++];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* We have found another option-ARGV-element.
|
||||
Skip the initial punctuation. */
|
||||
|
||||
nextchar = (argv[optind] + 1
|
||||
+ (longopts != NULL && argv[optind][1] == '-'));
|
||||
}
|
||||
|
||||
/* Decode the current option-ARGV-element. */
|
||||
|
||||
/* Check whether the ARGV-element is a long option.
|
||||
|
||||
If long_only and the ARGV-element has the form "-f", where f is
|
||||
a valid short option, don't consider it an abbreviated form of
|
||||
a long option that starts with f. Otherwise there would be no
|
||||
way to give the -f short option.
|
||||
|
||||
On the other hand, if there's a long option "fubar" and
|
||||
the ARGV-element is "-fu", do consider that an abbreviation of
|
||||
the long option, just like "--fu", and not "-f" with arg "u".
|
||||
|
||||
This distinction seems to be the most useful approach. */
|
||||
|
||||
if (longopts != NULL
|
||||
&& (argv[optind][1] == '-'
|
||||
|| (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
|
||||
{
|
||||
char *nameend;
|
||||
const struct option *p;
|
||||
const struct option *pfound = NULL;
|
||||
int exact = 0;
|
||||
int ambig = 0;
|
||||
int indfound;
|
||||
int option_index;
|
||||
|
||||
for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
|
||||
/* Do nothing. */ ;
|
||||
|
||||
/* Test all long options for either exact match
|
||||
or abbreviated matches. */
|
||||
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||
if (!strncmp (p->name, nextchar, nameend - nextchar))
|
||||
{
|
||||
if (nameend - nextchar == strlen (p->name))
|
||||
{
|
||||
/* Exact match found. */
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
exact = 1;
|
||||
break;
|
||||
}
|
||||
else if (pfound == NULL)
|
||||
{
|
||||
/* First nonexact match found. */
|
||||
pfound = p;
|
||||
indfound = option_index;
|
||||
}
|
||||
else
|
||||
/* Second or later nonexact match found. */
|
||||
ambig = 1;
|
||||
}
|
||||
|
||||
if (ambig && !exact)
|
||||
{
|
||||
if (opterr)
|
||||
fprintf (stderr, gettext ("%s: option `%s' is ambiguous\n"),
|
||||
argv[0], argv[optind]);
|
||||
nextchar += strlen (nextchar);
|
||||
optind++;
|
||||
return '?';
|
||||
}
|
||||
|
||||
if (pfound != NULL)
|
||||
{
|
||||
option_index = indfound;
|
||||
optind++;
|
||||
if (*nameend)
|
||||
{
|
||||
/* Don't test has_arg with >, because some C compilers don't
|
||||
allow it to be used on enums. */
|
||||
if (pfound->has_arg)
|
||||
optarg = nameend + 1;
|
||||
else
|
||||
{
|
||||
if (opterr)
|
||||
if (argv[optind - 1][1] == '-')
|
||||
/* --option */
|
||||
fprintf (stderr,
|
||||
gettext ("%s: option `--%s' doesn't allow an argument\n"),
|
||||
argv[0], pfound->name);
|
||||
else
|
||||
/* +option or -option */
|
||||
fprintf (stderr,
|
||||
gettext ("%s: option `%c%s' doesn't allow an argument\n"),
|
||||
argv[0], argv[optind - 1][0], pfound->name);
|
||||
|
||||
nextchar += strlen (nextchar);
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
else if (pfound->has_arg == 1)
|
||||
{
|
||||
if (optind < argc)
|
||||
optarg = argv[optind++];
|
||||
else
|
||||
{
|
||||
if (opterr)
|
||||
fprintf (stderr,
|
||||
gettext ("%s: option `%s' requires an argument\n"),
|
||||
argv[0], argv[optind - 1]);
|
||||
nextchar += strlen (nextchar);
|
||||
return optstring[0] == ':' ? ':' : '?';
|
||||
}
|
||||
}
|
||||
nextchar += strlen (nextchar);
|
||||
if (longind != NULL)
|
||||
*longind = option_index;
|
||||
if (pfound->flag)
|
||||
{
|
||||
*(pfound->flag) = pfound->val;
|
||||
return 0;
|
||||
}
|
||||
return pfound->val;
|
||||
}
|
||||
|
||||
/* Can't find it as a long option. If this is not getopt_long_only,
|
||||
or the option starts with '--' or is not a valid short
|
||||
option, then it's an error.
|
||||
Otherwise interpret it as a short option. */
|
||||
if (!long_only || argv[optind][1] == '-'
|
||||
|| my_index (optstring, *nextchar) == NULL)
|
||||
{
|
||||
if (opterr)
|
||||
{
|
||||
if (argv[optind][1] == '-')
|
||||
/* --option */
|
||||
fprintf (stderr, gettext ("%s: unrecognized option `--%s'\n"),
|
||||
argv[0], nextchar);
|
||||
else
|
||||
/* +option or -option */
|
||||
fprintf (stderr, gettext ("%s: unrecognized option `%c%s'\n"),
|
||||
argv[0], argv[optind][0], nextchar);
|
||||
}
|
||||
nextchar = (char *) "";
|
||||
optind++;
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
/* Look at and handle the next short option-character. */
|
||||
|
||||
{
|
||||
char c = *nextchar++;
|
||||
char *temp = my_index (optstring, c);
|
||||
|
||||
/* Increment `optind' when we start to process its last character. */
|
||||
if (*nextchar == '\0')
|
||||
++optind;
|
||||
|
||||
if (temp == NULL || c == ':')
|
||||
{
|
||||
if (opterr)
|
||||
{
|
||||
if (posixly_correct)
|
||||
/* 1003.2 specifies the format of this message. */
|
||||
fprintf (stderr, gettext ("%s: illegal option -- %c\n"),
|
||||
argv[0], c);
|
||||
else
|
||||
fprintf (stderr, gettext ("%s: invalid option -- %c\n"),
|
||||
argv[0], c);
|
||||
}
|
||||
optopt = c;
|
||||
return '?';
|
||||
}
|
||||
if (temp[1] == ':')
|
||||
{
|
||||
if (temp[2] == ':')
|
||||
{
|
||||
/* This is an option that accepts an argument optionally. */
|
||||
if (*nextchar != '\0')
|
||||
{
|
||||
optarg = nextchar;
|
||||
optind++;
|
||||
}
|
||||
else
|
||||
optarg = NULL;
|
||||
nextchar = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is an option that requires an argument. */
|
||||
if (*nextchar != '\0')
|
||||
{
|
||||
optarg = nextchar;
|
||||
/* If we end this ARGV-element by taking the rest as an arg,
|
||||
we must advance to the next element now. */
|
||||
optind++;
|
||||
}
|
||||
else if (optind == argc)
|
||||
{
|
||||
if (opterr)
|
||||
{
|
||||
/* 1003.2 specifies the format of this message. */
|
||||
fprintf (stderr,
|
||||
gettext ("%s: option requires an argument -- %c\n"),
|
||||
argv[0], c);
|
||||
}
|
||||
optopt = c;
|
||||
if (optstring[0] == ':')
|
||||
c = ':';
|
||||
else
|
||||
c = '?';
|
||||
}
|
||||
else
|
||||
/* We already incremented `optind' once;
|
||||
increment it again when taking next ARGV-elt as argument. */
|
||||
optarg = argv[optind++];
|
||||
nextchar = NULL;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
getopt (argc, argv, optstring)
|
||||
int argc;
|
||||
char *const *argv;
|
||||
const char *optstring;
|
||||
{
|
||||
return _getopt_internal (argc, argv, optstring,
|
||||
(const struct option *) 0,
|
||||
(int *) 0,
|
||||
0);
|
||||
}
|
||||
|
||||
int
|
||||
getopt_long (argc, argv, options, long_options, opt_index)
|
||||
int argc;
|
||||
char *const *argv;
|
||||
const char *options;
|
||||
const struct option *long_options;
|
||||
int *opt_index;
|
||||
{
|
||||
return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
|
||||
}
|
||||
|
||||
#endif /* _LIBC or not __GNU_LIBRARY__. */
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
/* Compile with -DTEST to make an executable for use in testing
|
||||
the above definition of `getopt'. */
|
||||
|
||||
int
|
||||
main (argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
int c;
|
||||
int digit_optind = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int this_option_optind = optind ? optind : 1;
|
||||
|
||||
c = getopt (argc, argv, "abc:d:0123456789");
|
||||
if (c == EOF)
|
||||
break;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
if (digit_optind != 0 && digit_optind != this_option_optind)
|
||||
printf ("digits occur in two different argv-elements.\n");
|
||||
digit_optind = this_option_optind;
|
||||
printf ("option %c\n", c);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
printf ("option a\n");
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
printf ("option b\n");
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
printf ("option c with value `%s'\n", optarg);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
break;
|
||||
|
||||
default:
|
||||
printf ("?? getopt returned character code 0%o ??\n", c);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
printf ("non-option ARGV-elements: ");
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
#endif /* TEST */
|
||||
#else /* HAVE_GETOPT_LONG */
|
||||
void getopt_dummy(void) {}
|
||||
#endif
|
||||
129
lib/getopt.h
Normal file
129
lib/getopt.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* Declarations for getopt.
|
||||
Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#ifndef _GETOPT_H
|
||||
#define _GETOPT_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* For communication from `getopt' to the caller.
|
||||
When `getopt' finds an option that takes an argument,
|
||||
the argument value is returned here.
|
||||
Also, when `ordering' is RETURN_IN_ORDER,
|
||||
each non-option ARGV-element is returned here. */
|
||||
|
||||
extern char *optarg;
|
||||
|
||||
/* Index in ARGV of the next element to be scanned.
|
||||
This is used for communication to and from the caller
|
||||
and for communication between successive calls to `getopt'.
|
||||
|
||||
On entry to `getopt', zero means this is the first call; initialize.
|
||||
|
||||
When `getopt' returns EOF, this is the index of the first of the
|
||||
non-option elements that the caller should itself scan.
|
||||
|
||||
Otherwise, `optind' communicates from one call to the next
|
||||
how much of ARGV has been scanned so far. */
|
||||
|
||||
extern int optind;
|
||||
|
||||
/* Callers store zero here to inhibit the error message `getopt' prints
|
||||
for unrecognized options. */
|
||||
|
||||
extern int opterr;
|
||||
|
||||
/* Set to an option character which was unrecognized. */
|
||||
|
||||
extern int optopt;
|
||||
|
||||
/* Describe the long-named options requested by the application.
|
||||
The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
|
||||
of `struct option' terminated by an element containing a name which is
|
||||
zero.
|
||||
|
||||
The field `has_arg' is:
|
||||
no_argument (or 0) if the option does not take an argument,
|
||||
required_argument (or 1) if the option requires an argument,
|
||||
optional_argument (or 2) if the option takes an optional argument.
|
||||
|
||||
If the field `flag' is not NULL, it points to a variable that is set
|
||||
to the value given in the field `val' when the option is found, but
|
||||
left unchanged if the option is not found.
|
||||
|
||||
To have a long-named option do something other than set an `int' to
|
||||
a compiled-in constant, such as set a value from `optarg', set the
|
||||
option's `flag' field to zero and its `val' field to a nonzero
|
||||
value (the equivalent single-letter option character, if there is
|
||||
one). For long options that have a zero `flag' field, `getopt'
|
||||
returns the contents of the `val' field. */
|
||||
|
||||
struct option
|
||||
{
|
||||
#if defined (__STDC__) && __STDC__
|
||||
const char *name;
|
||||
#else
|
||||
char *name;
|
||||
#endif
|
||||
/* has_arg can't be an enum because some compilers complain about
|
||||
type mismatches in all the code that assumes it is an int. */
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
/* Names for the values of the `has_arg' field of `struct option'. */
|
||||
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
#if defined (__STDC__) && __STDC__
|
||||
#ifdef __GNU_LIBRARY__
|
||||
/* Many other libraries have conflicting prototypes for getopt, with
|
||||
differences in the consts, in stdlib.h. To avoid compilation
|
||||
errors, only prototype getopt for the GNU C library. */
|
||||
extern int getopt (int argc, char *const *argv, const char *shortopts);
|
||||
#else /* not __GNU_LIBRARY__ */
|
||||
extern int getopt ();
|
||||
#endif /* __GNU_LIBRARY__ */
|
||||
extern int getopt_long (int argc, char *const *argv, const char *shortopts,
|
||||
const struct option *longopts, int *longind);
|
||||
extern int getopt_long_only (int argc, char *const *argv,
|
||||
const char *shortopts,
|
||||
const struct option *longopts, int *longind);
|
||||
|
||||
/* Internal only. Users should not call this directly. */
|
||||
extern int _getopt_internal (int argc, char *const *argv,
|
||||
const char *shortopts,
|
||||
const struct option *longopts, int *longind,
|
||||
int long_only);
|
||||
#else /* not __STDC__ */
|
||||
extern int getopt ();
|
||||
extern int getopt_long ();
|
||||
extern int getopt_long_only ();
|
||||
|
||||
extern int _getopt_internal ();
|
||||
#endif /* __STDC__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _GETOPT_H */
|
||||
718
main.c
Normal file
718
main.c
Normal file
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
int verbose = 0;
|
||||
int always_checksum = 0;
|
||||
time_t starttime;
|
||||
off_t total_size = 0;
|
||||
int block_size=BLOCK_SIZE;
|
||||
|
||||
char *backup_suffix = BACKUP_SUFFIX;
|
||||
|
||||
static char *rsync_path = RSYNC_NAME;
|
||||
|
||||
int make_backups = 0;
|
||||
int preserve_links = 0;
|
||||
int preserve_perms = 0;
|
||||
int preserve_devices = 0;
|
||||
int preserve_uid = 0;
|
||||
int preserve_gid = 0;
|
||||
int preserve_times = 0;
|
||||
int update_only = 0;
|
||||
int cvs_exclude = 0;
|
||||
int dry_run=0;
|
||||
int local_server=0;
|
||||
int ignore_times=0;
|
||||
int delete_mode=0;
|
||||
int one_file_system=0;
|
||||
|
||||
int am_server = 0;
|
||||
static int sender = 0;
|
||||
int recurse = 0;
|
||||
|
||||
static void usage(FILE *f);
|
||||
|
||||
static void report(int f)
|
||||
{
|
||||
int in,out,tsize;
|
||||
time_t t = time(NULL);
|
||||
|
||||
if (!verbose) return;
|
||||
|
||||
if (am_server && sender) {
|
||||
write_int(f,read_total());
|
||||
write_int(f,write_total());
|
||||
write_int(f,total_size);
|
||||
write_flush(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender) {
|
||||
in = read_total();
|
||||
out = write_total();
|
||||
tsize = (int)total_size;
|
||||
} else {
|
||||
in = read_int(f);
|
||||
out = read_int(f);
|
||||
tsize = read_int(f);
|
||||
}
|
||||
|
||||
printf("wrote %d bytes read %d bytes %g bytes/sec\n",
|
||||
out,in,(in+out)/(0.5 + (t-starttime)));
|
||||
printf("total size is %d speedup is %g\n",
|
||||
tsize,(1.0*tsize)/(in+out));
|
||||
}
|
||||
|
||||
|
||||
static void server_options(char **args,int *argc)
|
||||
{
|
||||
int ac = *argc;
|
||||
static char argstr[50];
|
||||
static char bsize[30];
|
||||
int i, x;
|
||||
|
||||
args[ac++] = "--server";
|
||||
|
||||
if (!sender)
|
||||
args[ac++] = "--sender";
|
||||
|
||||
x = 1;
|
||||
argstr[0] = '-';
|
||||
for (i=0;i<verbose;i++)
|
||||
argstr[x++] = 'v';
|
||||
if (make_backups)
|
||||
argstr[x++] = 'b';
|
||||
if (update_only)
|
||||
argstr[x++] = 'u';
|
||||
if (dry_run)
|
||||
argstr[x++] = 'n';
|
||||
if (preserve_links)
|
||||
argstr[x++] = 'l';
|
||||
if (preserve_uid)
|
||||
argstr[x++] = 'o';
|
||||
if (preserve_gid)
|
||||
argstr[x++] = 'g';
|
||||
if (preserve_devices)
|
||||
argstr[x++] = 'D';
|
||||
if (preserve_times)
|
||||
argstr[x++] = 't';
|
||||
if (preserve_perms)
|
||||
argstr[x++] = 'p';
|
||||
if (recurse)
|
||||
argstr[x++] = 'r';
|
||||
if (always_checksum)
|
||||
argstr[x++] = 'c';
|
||||
if (cvs_exclude)
|
||||
argstr[x++] = 'C';
|
||||
if (ignore_times)
|
||||
argstr[x++] = 'I';
|
||||
if (one_file_system)
|
||||
argstr[x++] = 'x';
|
||||
argstr[x] = 0;
|
||||
|
||||
if (x != 1) args[ac++] = argstr;
|
||||
|
||||
if (block_size != BLOCK_SIZE) {
|
||||
sprintf(bsize,"-B%d",block_size);
|
||||
args[ac++] = bsize;
|
||||
}
|
||||
|
||||
if (delete_mode)
|
||||
args[ac++] = "--delete";
|
||||
|
||||
*argc = ac;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
|
||||
{
|
||||
char *args[100];
|
||||
int i,argc=0;
|
||||
char *tok,*p;
|
||||
|
||||
if (!local_server) {
|
||||
if (!cmd)
|
||||
cmd = getenv(RSYNC_RSH_ENV);
|
||||
if (!cmd)
|
||||
cmd = RSYNC_RSH;
|
||||
cmd = strdup(cmd);
|
||||
if (!cmd)
|
||||
goto oom;
|
||||
|
||||
for (tok=strtok(cmd," ");tok;tok=strtok(NULL," ")) {
|
||||
args[argc++] = tok;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
args[argc++] = "-l";
|
||||
args[argc++] = user;
|
||||
}
|
||||
args[argc++] = machine;
|
||||
}
|
||||
|
||||
args[argc++] = rsync_path;
|
||||
|
||||
server_options(args,&argc);
|
||||
|
||||
if (path && *path) {
|
||||
char *dir = strdup(path);
|
||||
p = strrchr(dir,'/');
|
||||
if (p) {
|
||||
*p = 0;
|
||||
args[argc++] = dir;
|
||||
p++;
|
||||
} else {
|
||||
args[argc++] = ".";
|
||||
p = dir;
|
||||
}
|
||||
if (p[0])
|
||||
args[argc++] = path;
|
||||
}
|
||||
|
||||
args[argc] = NULL;
|
||||
|
||||
if (verbose > 3) {
|
||||
fprintf(stderr,"cmd=");
|
||||
for (i=0;i<argc;i++)
|
||||
fprintf(stderr,"%s ",args[i]);
|
||||
fprintf(stderr,"\n");
|
||||
}
|
||||
|
||||
return piped_child(args,f_in,f_out);
|
||||
|
||||
oom:
|
||||
out_of_memory("do_cmd");
|
||||
return 0; /* not reached */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static char *get_local_name(struct file_list *flist,char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(name,&st) == 0) {
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (chdir(name) != 0) {
|
||||
fprintf(stderr,"chdir %s : %s\n",name,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (flist->count > 1) {
|
||||
fprintf(stderr,"ERROR: destination must be a directory when copying more than 1 file\n");
|
||||
exit(1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
if (flist->count == 1)
|
||||
return name;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
if (mkdir(name,0777) != 0) {
|
||||
fprintf(stderr,"mkdir %s : %s\n",name,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (chdir(name) != 0) {
|
||||
fprintf(stderr,"chdir %s : %s\n",name,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void do_server_sender(int argc,char *argv[])
|
||||
{
|
||||
int i;
|
||||
char *dir = argv[0];
|
||||
struct file_list *flist;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"server_sender starting pid=%d\n",(int)getpid());
|
||||
|
||||
if (chdir(dir) != 0) {
|
||||
fprintf(stderr,"chdir %s: %s\n",dir,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (strcmp(dir,".")) {
|
||||
int l = strlen(dir);
|
||||
for (i=0;i<argc;i++)
|
||||
argv[i] += l+1;
|
||||
}
|
||||
|
||||
if (argc == 0 && recurse) {
|
||||
argc=1;
|
||||
argv--;
|
||||
argv[0] = ".";
|
||||
}
|
||||
|
||||
|
||||
flist = send_file_list(STDOUT_FILENO,recurse,argc,argv);
|
||||
send_files(flist,STDOUT_FILENO,STDIN_FILENO);
|
||||
report(STDOUT_FILENO);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void do_server_recv(int argc,char *argv[])
|
||||
{
|
||||
int pid,status;
|
||||
char *dir = NULL;
|
||||
struct file_list *flist;
|
||||
char *local_name=NULL;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
|
||||
|
||||
if (argc > 0) {
|
||||
dir = argv[0];
|
||||
argc--;
|
||||
argv++;
|
||||
if (chdir(dir) != 0) {
|
||||
fprintf(stderr,"chdir %s : %s\n",dir,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_mode)
|
||||
recv_exclude_list(STDIN_FILENO);
|
||||
|
||||
flist = recv_file_list(STDIN_FILENO);
|
||||
if (!flist || flist->count == 0) {
|
||||
fprintf(stderr,"nothing to do\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (argc > 0) {
|
||||
if (strcmp(dir,".")) {
|
||||
argv[0] += strlen(dir);
|
||||
if (argv[0][0] == '/') argv[0]++;
|
||||
}
|
||||
local_name = get_local_name(flist,argv[0]);
|
||||
}
|
||||
|
||||
if ((pid=fork()) == 0) {
|
||||
recv_files(STDIN_FILENO,flist,local_name);
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"receiver read %d\n",read_total());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
generate_files(STDOUT_FILENO,flist,local_name);
|
||||
|
||||
waitpid(pid, &status, 0);
|
||||
exit(status);
|
||||
}
|
||||
|
||||
|
||||
static void usage(FILE *f)
|
||||
{
|
||||
fprintf(f,"rsync version %s Copyright Andrew Tridgell and Paul Mackerras\n\n",
|
||||
VERSION);
|
||||
fprintf(f,"Usage:\t%s [options] src user@host:dest\nOR",RSYNC_NAME);
|
||||
fprintf(f,"\t%s [options] user@host:src dest\n\n",RSYNC_NAME);
|
||||
fprintf(f,"Options:\n");
|
||||
fprintf(f,"-v, --verbose increase verbosity\n");
|
||||
fprintf(f,"-c, --checksum always checksum\n");
|
||||
fprintf(f,"-a, --archive archive mode (same as -rlptDog)\n");
|
||||
fprintf(f,"-r, --recursive recurse into directories\n");
|
||||
fprintf(f,"-b, --backup make backups (default ~ extension)\n");
|
||||
fprintf(f,"-u, --update update only (don't overwrite newer files)\n");
|
||||
fprintf(f,"-l, --links preserve soft links\n");
|
||||
fprintf(f,"-p, --perms preserve permissions\n");
|
||||
fprintf(f,"-o, --owner preserve owner (root only)\n");
|
||||
fprintf(f,"-g, --group preserve group\n");
|
||||
fprintf(f,"-D, --devices preserve devices (root only)\n");
|
||||
fprintf(f,"-t, --times preserve times\n");
|
||||
fprintf(f,"-n, --dry-run show what would have been transferred\n");
|
||||
fprintf(f,"-x, --one-file-system don't cross filesystem boundaries\n");
|
||||
fprintf(f,"-B, --block-size SIZE checksum blocking size\n");
|
||||
fprintf(f,"-e, --rsh COMMAND specify rsh replacement\n");
|
||||
fprintf(f," --rsync-path PATH specify path to rsync on the remote machine\n");
|
||||
fprintf(f,"-C, --cvs-exclude auto ignore files in the same way CVS does\n");
|
||||
fprintf(f," --delete delete files that don't exist on the sending side\n");
|
||||
fprintf(f,"-I, --ignore-times don't exclude files that match length and time\n");
|
||||
fprintf(f," --exclude FILE exclude file FILE\n");
|
||||
fprintf(f," --exclude-from FILE exclude files listed in FILE\n");
|
||||
fprintf(f," --suffix SUFFIX override backup suffix\n");
|
||||
fprintf(f," --version print version number\n");
|
||||
|
||||
fprintf(f,"\n");
|
||||
fprintf(f,"the backup suffix defaults to %s\n",BACKUP_SUFFIX);
|
||||
fprintf(f,"the block size defaults to %d\n",BLOCK_SIZE);
|
||||
}
|
||||
|
||||
enum {OPT_VERSION,OPT_SUFFIX,OPT_SENDER,OPT_SERVER,OPT_EXCLUDE,
|
||||
OPT_EXCLUDE_FROM,OPT_DELETE,OPT_RSYNC_PATH};
|
||||
|
||||
static char *short_options = "oblpguDCtcahvrIxne:B:";
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"version", 0, 0, OPT_VERSION},
|
||||
{"server", 0, 0, OPT_SERVER},
|
||||
{"sender", 0, 0, OPT_SENDER},
|
||||
{"delete", 0, 0, OPT_DELETE},
|
||||
{"exclude", 1, 0, OPT_EXCLUDE},
|
||||
{"exclude-from",1, 0, OPT_EXCLUDE_FROM},
|
||||
{"rsync-path", 1, 0, OPT_RSYNC_PATH},
|
||||
{"one-file-system",0, 0, 'x'},
|
||||
{"ignore-times",0, 0, 'I'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"dry-run", 0, 0, 'n'},
|
||||
{"cvs-exclude", 0, 0, 'C'},
|
||||
{"archive", 0, 0, 'a'},
|
||||
{"checksum", 0, 0, 'c'},
|
||||
{"backup", 0, 0, 'b'},
|
||||
{"update", 0, 0, 'u'},
|
||||
{"verbose", 0, 0, 'v'},
|
||||
{"recursive", 0, 0, 'r'},
|
||||
{"devices", 0, 0, 'D'},
|
||||
{"perms", 0, 0, 'p'},
|
||||
{"links", 0, 0, 'l'},
|
||||
{"owner", 0, 0, 'o'},
|
||||
{"group", 0, 0, 'g'},
|
||||
{"times", 0, 0, 't'},
|
||||
{"rsh", 1, 0, 'e'},
|
||||
{"suffix", 1, 0, OPT_SUFFIX},
|
||||
{"block-size", 1, 0, 'B'},
|
||||
{0,0,0,0}};
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
int pid, status, pid2, status2;
|
||||
int opt;
|
||||
int option_index;
|
||||
char *shell_cmd = NULL;
|
||||
char *shell_machine = NULL;
|
||||
char *shell_path = NULL;
|
||||
char *shell_user = NULL;
|
||||
char *p;
|
||||
int f_in,f_out;
|
||||
struct file_list *flist;
|
||||
char *local_name = NULL;
|
||||
|
||||
starttime = time(NULL);
|
||||
|
||||
while ((opt = getopt_long(argc, argv,
|
||||
short_options, long_options, &option_index))
|
||||
!= -1) {
|
||||
switch (opt)
|
||||
{
|
||||
case OPT_VERSION:
|
||||
printf("rsync version %s protocol version %d\n",
|
||||
VERSION,PROTOCOL_VERSION);
|
||||
exit(0);
|
||||
|
||||
case OPT_SUFFIX:
|
||||
backup_suffix = optarg;
|
||||
break;
|
||||
|
||||
case OPT_RSYNC_PATH:
|
||||
rsync_path = optarg;
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
ignore_times = 1;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
one_file_system=1;
|
||||
break;
|
||||
|
||||
case OPT_DELETE:
|
||||
delete_mode = 1;
|
||||
break;
|
||||
|
||||
case OPT_EXCLUDE:
|
||||
add_exclude(optarg);
|
||||
break;
|
||||
|
||||
case OPT_EXCLUDE_FROM:
|
||||
add_exclude_file(optarg,1);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
exit(0);
|
||||
|
||||
case 'b':
|
||||
make_backups=1;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
dry_run=1;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
cvs_exclude=1;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
update_only=1;
|
||||
break;
|
||||
|
||||
#if SUPPORT_LINKS
|
||||
case 'l':
|
||||
preserve_links=1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'p':
|
||||
preserve_perms=1;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
if (getuid() == 0) {
|
||||
preserve_uid=1;
|
||||
} else {
|
||||
fprintf(stderr,"-o only allowed for root\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
preserve_gid=1;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
if (getuid() == 0) {
|
||||
preserve_devices=1;
|
||||
} else {
|
||||
fprintf(stderr,"-D only allowed for root\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
preserve_times=1;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
always_checksum=1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
recurse=1;
|
||||
#if SUPPORT_LINKS
|
||||
preserve_links=1;
|
||||
#endif
|
||||
preserve_perms=1;
|
||||
preserve_times=1;
|
||||
preserve_gid=1;
|
||||
if (getuid() == 0) {
|
||||
preserve_devices=1;
|
||||
preserve_uid=1;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPT_SERVER:
|
||||
am_server = 1;
|
||||
break;
|
||||
|
||||
case OPT_SENDER:
|
||||
if (!am_server) {
|
||||
usage(stderr);
|
||||
exit(1);
|
||||
}
|
||||
sender = 1;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
recurse = 1;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
shell_cmd = optarg;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
block_size = atoi(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr,"bad option -%c\n",opt);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (optind--) {
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (dry_run)
|
||||
verbose = MAX(verbose,1);
|
||||
|
||||
if (am_server) {
|
||||
int version = read_int(STDIN_FILENO);
|
||||
if (version != PROTOCOL_VERSION) {
|
||||
fprintf(stderr,"protocol version mismatch %d %d\n",
|
||||
version,PROTOCOL_VERSION);
|
||||
exit(1);
|
||||
}
|
||||
write_int(STDOUT_FILENO,PROTOCOL_VERSION);
|
||||
write_flush(STDOUT_FILENO);
|
||||
|
||||
if (sender) {
|
||||
recv_exclude_list(STDIN_FILENO);
|
||||
if (cvs_exclude)
|
||||
add_cvs_excludes();
|
||||
do_server_sender(argc,argv);
|
||||
} else {
|
||||
do_server_recv(argc,argv);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
usage(stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
p = strchr(argv[0],':');
|
||||
|
||||
if (p) {
|
||||
sender = 0;
|
||||
*p = 0;
|
||||
shell_machine = argv[0];
|
||||
shell_path = p+1;
|
||||
argc--;
|
||||
argv++;
|
||||
} else {
|
||||
sender = 1;
|
||||
|
||||
p = strchr(argv[argc-1],':');
|
||||
if (!p) {
|
||||
local_server = 1;
|
||||
}
|
||||
|
||||
if (local_server) {
|
||||
shell_machine = NULL;
|
||||
shell_path = argv[argc-1];
|
||||
} else {
|
||||
*p = 0;
|
||||
shell_machine = argv[argc-1];
|
||||
shell_path = p+1;
|
||||
}
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (shell_machine) {
|
||||
p = strchr(shell_machine,'@');
|
||||
if (p) {
|
||||
*p = 0;
|
||||
shell_user = shell_machine;
|
||||
shell_machine = p+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose > 3) {
|
||||
fprintf(stderr,"cmd=%s machine=%s user=%s path=%s\n",
|
||||
shell_cmd?shell_cmd:"",
|
||||
shell_machine?shell_machine:"",
|
||||
shell_user?shell_user:"",
|
||||
shell_path?shell_path:"");
|
||||
}
|
||||
|
||||
signal(SIGCHLD,SIG_IGN);
|
||||
signal(SIGINT,sig_int);
|
||||
|
||||
if (!sender && argc != 1) {
|
||||
usage(stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
|
||||
|
||||
write_int(f_out,PROTOCOL_VERSION);
|
||||
write_flush(f_out);
|
||||
{
|
||||
int version = read_int(f_in);
|
||||
if (version != PROTOCOL_VERSION) {
|
||||
fprintf(stderr,"protocol version mismatch\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"parent=%d child=%d sender=%d recurse=%d\n",
|
||||
(int)getpid(),pid,sender,recurse);
|
||||
|
||||
if (sender) {
|
||||
if (cvs_exclude)
|
||||
add_cvs_excludes();
|
||||
if (delete_mode)
|
||||
send_exclude_list(f_out);
|
||||
flist = send_file_list(f_out,recurse,argc,argv);
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"file list sent\n");
|
||||
send_files(flist,f_out,f_in);
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"waiting on %d\n",pid);
|
||||
waitpid(pid, &status, 0);
|
||||
report(-1);
|
||||
exit(status);
|
||||
}
|
||||
|
||||
send_exclude_list(f_out);
|
||||
|
||||
flist = recv_file_list(f_in);
|
||||
if (!flist || flist->count == 0) {
|
||||
fprintf(stderr,"nothing to do\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
local_name = get_local_name(flist,argv[0]);
|
||||
|
||||
if ((pid2=fork()) == 0) {
|
||||
recv_files(f_in,flist,local_name);
|
||||
if (verbose > 1)
|
||||
fprintf(stderr,"receiver read %d\n",read_total());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
generate_files(f_out,flist,local_name);
|
||||
|
||||
waitpid(pid2, &status2, 0);
|
||||
|
||||
report(f_in);
|
||||
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
return status | status2;
|
||||
}
|
||||
246
match.c
Normal file
246
match.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
extern int verbose;
|
||||
extern int am_server;
|
||||
|
||||
typedef unsigned short tag;
|
||||
|
||||
#define TABLESIZE (1<<16)
|
||||
#define NULL_TAG ((tag)-1)
|
||||
|
||||
static int false_alarms;
|
||||
static int tag_hits;
|
||||
static int matches;
|
||||
static int data_transfer;
|
||||
|
||||
static int total_false_alarms=0;
|
||||
static int total_tag_hits=0;
|
||||
static int total_matches=0;
|
||||
static int total_data_transfer=0;
|
||||
|
||||
|
||||
struct target {
|
||||
tag t;
|
||||
int i;
|
||||
};
|
||||
|
||||
static struct target *targets=NULL;
|
||||
|
||||
static tag *tag_table = NULL;
|
||||
|
||||
#define gettag2(s1,s2) (((s1) + (s2)) & 0xFFFF)
|
||||
#define gettag(sum) gettag2((sum)&0xFFFF,(sum)>>16)
|
||||
|
||||
static int compare_targets(struct target *t1,struct target *t2)
|
||||
{
|
||||
return(t1->t - t2->t);
|
||||
}
|
||||
|
||||
|
||||
static void build_hash_table(struct sum_struct *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!tag_table)
|
||||
tag_table = (tag *)malloc(sizeof(tag)*TABLESIZE);
|
||||
|
||||
targets = (struct target *)malloc(sizeof(targets[0])*s->count);
|
||||
if (!tag_table || !targets)
|
||||
out_of_memory("build_hash_table");
|
||||
|
||||
for (i=0;i<s->count;i++) {
|
||||
targets[i].i = i;
|
||||
targets[i].t = gettag(s->sums[i].sum1);
|
||||
}
|
||||
|
||||
qsort(targets,s->count,sizeof(targets[0]),(int (*)())compare_targets);
|
||||
|
||||
for (i=0;i<TABLESIZE;i++)
|
||||
tag_table[i] = NULL_TAG;
|
||||
|
||||
for (i=s->count-1;i>=0;i--) {
|
||||
tag_table[targets[i].t] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static off_t last_match;
|
||||
|
||||
|
||||
static void matched(int f,struct sum_struct *s,char *buf,off_t len,int offset,int i)
|
||||
{
|
||||
int n = offset - last_match;
|
||||
|
||||
if (verbose > 2)
|
||||
if (i != -1)
|
||||
fprintf(stderr,"match at %d last_match=%d j=%d len=%d n=%d\n",
|
||||
(int)offset,(int)last_match,i,(int)s->sums[i].len,n);
|
||||
|
||||
if (n > 0) {
|
||||
write_int(f,n);
|
||||
write_buf(f,buf+last_match,n);
|
||||
data_transfer += n;
|
||||
}
|
||||
write_int(f,-(i+1));
|
||||
if (i != -1)
|
||||
last_match = offset + s->sums[i].len;
|
||||
if (n > 0)
|
||||
write_flush(f);
|
||||
}
|
||||
|
||||
|
||||
static void hash_search(int f,struct sum_struct *s,char *buf,off_t len)
|
||||
{
|
||||
int offset,j,k;
|
||||
int end;
|
||||
char sum2[SUM_LENGTH];
|
||||
uint32 s1, s2, sum;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"hash search b=%d len=%d\n",s->n,(int)len);
|
||||
|
||||
k = MIN(len, s->n);
|
||||
sum = get_checksum1(buf, k);
|
||||
s1 = sum & 0xFFFF;
|
||||
s2 = sum >> 16;
|
||||
if (verbose > 3)
|
||||
fprintf(stderr, "sum=%.8x k=%d\n", sum, k);
|
||||
|
||||
offset = 0;
|
||||
|
||||
end = len + 1 - s->sums[s->count-1].len;
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"hash search s->n=%d len=%d count=%d\n",
|
||||
s->n,(int)len,s->count);
|
||||
|
||||
do {
|
||||
tag t = gettag2(s1,s2);
|
||||
j = tag_table[t];
|
||||
if (verbose > 4)
|
||||
fprintf(stderr,"offset=%d sum=%08x\n",
|
||||
offset,sum);
|
||||
|
||||
if (j != NULL_TAG) {
|
||||
int done_csum2 = 0;
|
||||
|
||||
sum = (s1 & 0xffff) | (s2 << 16);
|
||||
tag_hits++;
|
||||
do {
|
||||
int i = targets[j].i;
|
||||
|
||||
if (sum == s->sums[i].sum1) {
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"potential match at %d target=%d %d sum=%08x\n",
|
||||
offset,j,i,sum);
|
||||
|
||||
if (!done_csum2) {
|
||||
get_checksum2(buf+offset,MIN(s->n,len-offset),sum2);
|
||||
done_csum2 = 1;
|
||||
}
|
||||
if (memcmp(sum2,s->sums[i].sum2,SUM_LENGTH) == 0) {
|
||||
matched(f,s,buf,len,offset,i);
|
||||
offset += s->sums[i].len - 1;
|
||||
k = MIN((len-offset), s->n);
|
||||
sum = get_checksum1(buf+offset, k);
|
||||
s1 = sum & 0xFFFF;
|
||||
s2 = sum >> 16;
|
||||
++matches;
|
||||
break;
|
||||
} else {
|
||||
false_alarms++;
|
||||
}
|
||||
}
|
||||
j++;
|
||||
} while (j<s->count && targets[j].t == t);
|
||||
}
|
||||
|
||||
/* Trim off the first byte from the checksum */
|
||||
s1 -= buf[offset];
|
||||
s2 -= k * buf[offset];
|
||||
|
||||
/* Add on the next byte (if there is one) to the checksum */
|
||||
if (k < (len-offset)) {
|
||||
s1 += buf[offset+k];
|
||||
s2 += s1;
|
||||
} else {
|
||||
--k;
|
||||
}
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"s2:s1 = %.4x%.4x sum=%.8x k=%d offset=%d took %x added %x\n",
|
||||
s2&0xffff, s1&0xffff, get_checksum1(buf+offset+1,k),
|
||||
k, (int)offset, buf[offset], buf[offset+k]);
|
||||
} while (++offset < end);
|
||||
|
||||
matched(f,s,buf,len,len,-1);
|
||||
}
|
||||
|
||||
|
||||
void match_sums(int f,struct sum_struct *s,char *buf,off_t len)
|
||||
{
|
||||
last_match = 0;
|
||||
false_alarms = 0;
|
||||
tag_hits = 0;
|
||||
matches=0;
|
||||
data_transfer=0;
|
||||
|
||||
if (len > 0 && s->count>0) {
|
||||
build_hash_table(s);
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"built hash table\n");
|
||||
|
||||
hash_search(f,s,buf,len);
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"done hash search\n");
|
||||
} else {
|
||||
matched(f,s,buf,len,len,-1);
|
||||
}
|
||||
|
||||
if (targets) {
|
||||
free(targets);
|
||||
targets=NULL;
|
||||
}
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr, "false_alarms=%d tag_hits=%d matches=%d\n",
|
||||
false_alarms, tag_hits, matches);
|
||||
|
||||
total_tag_hits += tag_hits;
|
||||
total_false_alarms += false_alarms;
|
||||
total_matches += matches;
|
||||
total_data_transfer += data_transfer;
|
||||
}
|
||||
|
||||
void match_report(void)
|
||||
{
|
||||
if (verbose <= 1)
|
||||
return;
|
||||
|
||||
fprintf(am_server?stderr:stdout,
|
||||
"total: matches=%d tag_hits=%d false_alarms=%d data=%d\n",
|
||||
total_matches,total_tag_hits,
|
||||
total_false_alarms,total_data_transfer);
|
||||
}
|
||||
263
md4.c
Normal file
263
md4.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
This code is from rfc1186.
|
||||
|
||||
It has been modified to use the SIVAL() macro to make it
|
||||
byte order and length independent, so we don't need the LOWBYTEFIRST define
|
||||
*/
|
||||
|
||||
/*
|
||||
** ********************************************************************
|
||||
** md4.c -- Implementation of MD4 Message Digest Algorithm **
|
||||
** Updated: 2/16/90 by Ronald L. Rivest **
|
||||
** (C) 1990 RSA Data Security, Inc. **
|
||||
** ********************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
** To use MD4:
|
||||
** -- Include md4.h in your program
|
||||
** -- Declare an MDstruct MD to hold the state of the digest
|
||||
** computation.
|
||||
** -- Initialize MD using MDbegin(&MD)
|
||||
** -- For each full block (64 bytes) X you wish to process, call
|
||||
** MDupdate(&MD,X,512)
|
||||
** (512 is the number of bits in a full block.)
|
||||
** -- For the last block (less than 64 bytes) you wish to process,
|
||||
** MDupdate(&MD,X,n)
|
||||
** where n is the number of bits in the partial block. A partial
|
||||
** block terminates the computation, so every MD computation
|
||||
** should terminate by processing a partial block, even if it
|
||||
** has n = 0.
|
||||
** -- The message digest is available in MD.buffer[0] ...
|
||||
** MD.buffer[3]. (Least-significant byte of each word
|
||||
** should be output first.)
|
||||
** -- You can print out the digest using MDprint(&MD)
|
||||
*/
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
/* Compile-time includes
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
/* Compile-time declarations of MD4 "magic constants".
|
||||
*/
|
||||
#define I0 0x67452301 /* Initial values for MD buffer */
|
||||
#define I1 0xefcdab89
|
||||
#define I2 0x98badcfe
|
||||
#define I3 0x10325476
|
||||
#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */
|
||||
#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */
|
||||
/* C2 and C3 are from Knuth, The Art of Programming, Volume 2
|
||||
** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
|
||||
** Table 2, page 660.
|
||||
*/
|
||||
|
||||
#define fs1 3 /* round 1 shift amounts */
|
||||
#define fs2 7
|
||||
#define fs3 11
|
||||
#define fs4 19
|
||||
#define gs1 3 /* round 2 shift amounts */
|
||||
#define gs2 5
|
||||
#define gs3 9
|
||||
#define gs4 13
|
||||
#define hs1 3 /* round 3 shift amounts */
|
||||
#define hs2 9
|
||||
#define hs3 11
|
||||
#define hs4 15
|
||||
|
||||
/* Compile-time macro declarations for MD4.
|
||||
** Note: The "rot" operator uses the variable "tmp".
|
||||
** It assumes tmp is declared as unsigned int, so that the >>
|
||||
** operator will shift in zeros rather than extending the sign bit.
|
||||
*/
|
||||
#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 rot(X,S) (tmp=X,(tmp<<S) | (tmp>>(32-S)))
|
||||
#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s)
|
||||
#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s)
|
||||
#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s)
|
||||
|
||||
/* MDbegin(MDp)
|
||||
** Initialize message digest buffer MDp.
|
||||
** This is a user-callable routine.
|
||||
*/
|
||||
void
|
||||
MDbegin(MDp)
|
||||
MDptr MDp;
|
||||
{ int i;
|
||||
MDp->buffer[0] = I0;
|
||||
MDp->buffer[1] = I1;
|
||||
MDp->buffer[2] = I2;
|
||||
MDp->buffer[3] = I3;
|
||||
for (i=0;i<8;i++) MDp->count[i] = 0;
|
||||
MDp->done = 0;
|
||||
}
|
||||
|
||||
/* MDreverse(X)
|
||||
** Reverse the byte-ordering of every int in X.
|
||||
** Assumes X is an array of 16 ints.
|
||||
** The macro revx reverses the byte-ordering of the next word of X.
|
||||
*/
|
||||
void MDreverse(X)
|
||||
unsigned int32 *X;
|
||||
{ register unsigned int32 t;
|
||||
register unsigned int i;
|
||||
|
||||
for(i = 0; i < 16; i++) {
|
||||
t = X[i];
|
||||
SIVAL(X,i*4,t);
|
||||
}
|
||||
}
|
||||
|
||||
/* MDblock(MDp,X)
|
||||
** Update message digest buffer MDp->buffer using 16-word data block X.
|
||||
** Assumes all 16 words of X are full of data.
|
||||
** Does not update MDp->count.
|
||||
** This routine is not user-callable.
|
||||
*/
|
||||
static void
|
||||
MDblock(MDp,X)
|
||||
MDptr MDp;
|
||||
unsigned int32 *X;
|
||||
{
|
||||
register unsigned int32 tmp, A, B, C, D;
|
||||
MDreverse(X);
|
||||
A = MDp->buffer[0];
|
||||
B = MDp->buffer[1];
|
||||
C = MDp->buffer[2];
|
||||
D = MDp->buffer[3];
|
||||
/* Update the message digest buffer */
|
||||
ff(A , B , C , D , 0 , fs1); /* Round 1 */
|
||||
ff(D , A , B , C , 1 , fs2);
|
||||
ff(C , D , A , B , 2 , fs3);
|
||||
ff(B , C , D , A , 3 , fs4);
|
||||
ff(A , B , C , D , 4 , fs1);
|
||||
ff(D , A , B , C , 5 , fs2);
|
||||
ff(C , D , A , B , 6 , fs3);
|
||||
ff(B , C , D , A , 7 , fs4);
|
||||
ff(A , B , C , D , 8 , fs1);
|
||||
ff(D , A , B , C , 9 , fs2);
|
||||
ff(C , D , A , B , 10 , fs3);
|
||||
ff(B , C , D , A , 11 , fs4);
|
||||
ff(A , B , C , D , 12 , fs1);
|
||||
ff(D , A , B , C , 13 , fs2);
|
||||
ff(C , D , A , B , 14 , fs3);
|
||||
ff(B , C , D , A , 15 , fs4);
|
||||
gg(A , B , C , D , 0 , gs1); /* Round 2 */
|
||||
gg(D , A , B , C , 4 , gs2);
|
||||
gg(C , D , A , B , 8 , gs3);
|
||||
gg(B , C , D , A , 12 , gs4);
|
||||
gg(A , B , C , D , 1 , gs1);
|
||||
gg(D , A , B , C , 5 , gs2);
|
||||
gg(C , D , A , B , 9 , gs3);
|
||||
gg(B , C , D , A , 13 , gs4);
|
||||
gg(A , B , C , D , 2 , gs1);
|
||||
gg(D , A , B , C , 6 , gs2);
|
||||
gg(C , D , A , B , 10 , gs3);
|
||||
gg(B , C , D , A , 14 , gs4);
|
||||
gg(A , B , C , D , 3 , gs1);
|
||||
gg(D , A , B , C , 7 , gs2);
|
||||
gg(C , D , A , B , 11 , gs3);
|
||||
gg(B , C , D , A , 15 , gs4);
|
||||
hh(A , B , C , D , 0 , hs1); /* Round 3 */
|
||||
hh(D , A , B , C , 8 , hs2);
|
||||
hh(C , D , A , B , 4 , hs3);
|
||||
hh(B , C , D , A , 12 , hs4);
|
||||
hh(A , B , C , D , 2 , hs1);
|
||||
hh(D , A , B , C , 10 , hs2);
|
||||
hh(C , D , A , B , 6 , hs3);
|
||||
hh(B , C , D , A , 14 , hs4);
|
||||
hh(A , B , C , D , 1 , hs1);
|
||||
hh(D , A , B , C , 9 , hs2);
|
||||
hh(C , D , A , B , 5 , hs3);
|
||||
hh(B , C , D , A , 13 , hs4);
|
||||
hh(A , B , C , D , 3 , hs1);
|
||||
hh(D , A , B , C , 11 , hs2);
|
||||
hh(C , D , A , B , 7 , hs3);
|
||||
hh(B , C , D , A , 15 , hs4);
|
||||
MDp->buffer[0] += A;
|
||||
MDp->buffer[1] += B;
|
||||
MDp->buffer[2] += C;
|
||||
MDp->buffer[3] += D;
|
||||
}
|
||||
|
||||
/* MDupdate(MDp,X,count)
|
||||
** Input: MDp -- an MDptr
|
||||
** X -- a pointer to an array of unsigned characters.
|
||||
** count -- the number of bits of X to use.
|
||||
** (if not a multiple of 8, uses high bits of last byte.)
|
||||
** Update MDp using the number of bits of X given by count.
|
||||
** This is the basic input routine for an MD4 user.
|
||||
** The routine completes the MD computation when count < 512, so
|
||||
** every MD computation should end with one call to MDupdate with a
|
||||
** count less than 512. A call with count 0 will be ignored if the
|
||||
** MD has already been terminated (done != 0), so an extra call with
|
||||
** count 0 can be given as a "courtesy close" to force termination
|
||||
** if desired.
|
||||
*/
|
||||
void
|
||||
MDupdate(MDp,X,count)
|
||||
MDptr MDp;
|
||||
unsigned char *X;
|
||||
unsigned int count;
|
||||
{ unsigned int32 i, tmp, bit, byte, mask;
|
||||
unsigned char XX[64];
|
||||
unsigned char *p;
|
||||
/* return with no error if this is a courtesy close with count
|
||||
** zero and MDp->done is true.
|
||||
*/
|
||||
if (count == 0 && MDp->done) return;
|
||||
/* check to see if MD is already done and report error */
|
||||
if (MDp->done)
|
||||
{ printf("\nError: MDupdate MD already done."); return; }
|
||||
/* Add count to MDp->count */
|
||||
tmp = count;
|
||||
p = MDp->count;
|
||||
while (tmp)
|
||||
{ tmp += *p;
|
||||
*p++ = tmp;
|
||||
tmp = tmp >> 8;
|
||||
}
|
||||
/* Process data */
|
||||
if (count == 512)
|
||||
{ /* Full block of data to handle */
|
||||
MDblock(MDp,(unsigned int *)X);
|
||||
}
|
||||
else if (count > 512) /* Check for count too large */
|
||||
{ printf("\nError: MDupdate called with illegal count value %d."
|
||||
,count);
|
||||
return;
|
||||
}
|
||||
else /* partial block -- must be last block so finish up */
|
||||
{ /* Find out how many bytes and residual bits there are */
|
||||
byte = count >> 3;
|
||||
bit = count & 7;
|
||||
/* Copy X into XX since we need to modify it */
|
||||
for (i=0;i<=byte;i++) XX[i] = X[i];
|
||||
for (i=byte+1;i<64;i++) XX[i] = 0;
|
||||
/* Add padding '1' bit and low-order zeros in last byte */
|
||||
mask = 1 << (7 - bit);
|
||||
XX[byte] = (XX[byte] | mask) & ~( mask - 1);
|
||||
/* If room for bit count, finish up with this block */
|
||||
if (byte <= 55)
|
||||
{ for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
|
||||
MDblock(MDp,(unsigned int32 *)XX);
|
||||
}
|
||||
else /* need to do two blocks to finish up */
|
||||
{ MDblock(MDp,(unsigned int32 *)XX);
|
||||
for (i=0;i<56;i++) XX[i] = 0;
|
||||
for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
|
||||
MDblock(MDp,(unsigned int32 *)XX);
|
||||
}
|
||||
/* Set flag saying we're done with MD computation */
|
||||
MDp->done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** End of md4.c
|
||||
*/
|
||||
49
md4.h
Normal file
49
md4.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This code is from rfc1186.
|
||||
*/
|
||||
|
||||
/*
|
||||
** ********************************************************************
|
||||
** md4.h -- Header file for implementation of **
|
||||
** MD4 Message Digest Algorithm **
|
||||
** Updated: 2/13/90 by Ronald L. Rivest **
|
||||
** (C) 1990 RSA Data Security, Inc. **
|
||||
** ********************************************************************
|
||||
*/
|
||||
|
||||
/* MDstruct is the data structure for a message digest computation.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int32 buffer[4]; /* Holds 4-word result of MD computation */
|
||||
unsigned char count[8]; /* Number of bits processed so far */
|
||||
unsigned int done; /* Nonzero means MD computation finished */
|
||||
} MDstruct, *MDptr;
|
||||
|
||||
/* MDbegin(MD)
|
||||
|
||||
|
||||
|
||||
** Input: MD -- an MDptr
|
||||
** Initialize the MDstruct prepatory to doing a message digest
|
||||
** computation.
|
||||
*/
|
||||
extern void MDbegin();
|
||||
|
||||
/* MDupdate(MD,X,count)
|
||||
** Input: MD -- an MDptr
|
||||
** X -- a pointer to an array of unsigned characters.
|
||||
** count -- the number of bits of X to use (an unsigned int).
|
||||
** Updates MD using the first "count" bits of X.
|
||||
** The array pointed to by X is not modified.
|
||||
** If count is not a multiple of 8, MDupdate uses high bits of
|
||||
** last byte.
|
||||
** This is the basic input routine for a user.
|
||||
** The routine terminates the MD computation when count < 512, so
|
||||
** every MD computation should end with one call to MDupdate with a
|
||||
** count less than 512. Zero is OK for a count.
|
||||
*/
|
||||
extern void MDupdate();
|
||||
|
||||
/*
|
||||
** End of md4.h
|
||||
*/
|
||||
41
mkproto.awk
Normal file
41
mkproto.awk
Normal file
@@ -0,0 +1,41 @@
|
||||
# generate prototypes for Samba C code
|
||||
# tridge, June 1996
|
||||
|
||||
BEGIN {
|
||||
inheader=0;
|
||||
print "/* This file is automatically generated with \"make proto\". DO NOT EDIT */"
|
||||
print ""
|
||||
}
|
||||
|
||||
{
|
||||
if (inheader) {
|
||||
if (match($0,"[)][ \t]*$")) {
|
||||
inheader = 0;
|
||||
printf "%s;\n",$0;
|
||||
} else {
|
||||
printf "%s\n",$0;
|
||||
}
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
/^static|^extern/ || !/^[a-zA-Z]/ || /[;]/ {
|
||||
next;
|
||||
}
|
||||
|
||||
!/^off_t|^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^struct|^BOOL|^void|^time/ {
|
||||
next;
|
||||
}
|
||||
|
||||
|
||||
/[(].*[)][ \t]*$/ {
|
||||
printf "%s;\n",$0;
|
||||
next;
|
||||
}
|
||||
|
||||
/[(]/ {
|
||||
inheader=1;
|
||||
printf "%s\n",$0;
|
||||
next;
|
||||
}
|
||||
|
||||
733
rsync.c
Normal file
733
rsync.c
Normal file
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
extern int verbose;
|
||||
extern int am_server;
|
||||
extern int always_checksum;
|
||||
extern time_t starttime;
|
||||
|
||||
extern char *backup_suffix;
|
||||
|
||||
extern int block_size;
|
||||
extern int update_only;
|
||||
extern int make_backups;
|
||||
extern int preserve_links;
|
||||
extern int preserve_perms;
|
||||
extern int preserve_devices;
|
||||
extern int preserve_uid;
|
||||
extern int preserve_gid;
|
||||
extern int preserve_times;
|
||||
extern int dry_run;
|
||||
extern int ignore_times;
|
||||
extern int recurse;
|
||||
extern int delete_mode;
|
||||
extern int cvs_exclude;
|
||||
|
||||
/*
|
||||
free a sums struct
|
||||
*/
|
||||
static void free_sums(struct sum_struct *s)
|
||||
{
|
||||
if (s->sums) free(s->sums);
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
send a sums struct down a fd
|
||||
*/
|
||||
static void send_sums(struct sum_struct *s,int f_out)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* tell the other guy how many we are going to be doing and how many
|
||||
bytes there are in the last chunk */
|
||||
write_int(f_out,s?s->count:0);
|
||||
write_int(f_out,s?s->n:block_size);
|
||||
write_int(f_out,s?s->remainder:0);
|
||||
if (s)
|
||||
for (i=0;i<s->count;i++) {
|
||||
write_int(f_out,s->sums[i].sum1);
|
||||
write_buf(f_out,s->sums[i].sum2,SUM_LENGTH);
|
||||
}
|
||||
write_flush(f_out);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a stream of signatures/checksums that describe a buffer
|
||||
|
||||
generate approximately one checksum every n bytes
|
||||
*/
|
||||
static struct sum_struct *generate_sums(char *buf,off_t len,int n)
|
||||
{
|
||||
int i;
|
||||
struct sum_struct *s;
|
||||
int count;
|
||||
int block_len = n;
|
||||
int remainder = (len%block_len);
|
||||
off_t offset = 0;
|
||||
|
||||
count = (len+(block_len-1))/block_len;
|
||||
|
||||
s = (struct sum_struct *)malloc(sizeof(*s));
|
||||
if (!s) out_of_memory("generate_sums");
|
||||
|
||||
s->count = count;
|
||||
s->remainder = remainder;
|
||||
s->n = n;
|
||||
s->flength = len;
|
||||
|
||||
if (count==0) {
|
||||
s->sums = NULL;
|
||||
return s;
|
||||
}
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"count=%d rem=%d n=%d flength=%d\n",
|
||||
s->count,s->remainder,s->n,(int)s->flength);
|
||||
|
||||
s->sums = (struct sum_buf *)malloc(sizeof(s->sums[0])*s->count);
|
||||
if (!s->sums) out_of_memory("generate_sums");
|
||||
|
||||
for (i=0;i<count;i++) {
|
||||
int n1 = MIN(len,n);
|
||||
|
||||
s->sums[i].sum1 = get_checksum1(buf,n1);
|
||||
get_checksum2(buf,n1,s->sums[i].sum2);
|
||||
|
||||
s->sums[i].offset = offset;
|
||||
s->sums[i].len = n1;
|
||||
s->sums[i].i = i;
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"chunk[%d] offset=%d len=%d sum1=%08x\n",
|
||||
i,(int)s->sums[i].offset,s->sums[i].len,s->sums[i].sum1);
|
||||
|
||||
len -= n1;
|
||||
buf += n1;
|
||||
offset += n1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
receive the checksums for a buffer
|
||||
*/
|
||||
static struct sum_struct *receive_sums(int f)
|
||||
{
|
||||
struct sum_struct *s;
|
||||
int i;
|
||||
off_t offset = 0;
|
||||
int block_len;
|
||||
|
||||
s = (struct sum_struct *)malloc(sizeof(*s));
|
||||
if (!s) out_of_memory("receive_sums");
|
||||
|
||||
s->count = read_int(f);
|
||||
s->n = read_int(f);
|
||||
s->remainder = read_int(f);
|
||||
s->sums = NULL;
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"count=%d n=%d rem=%d\n",
|
||||
s->count,s->n,s->remainder);
|
||||
|
||||
block_len = s->n;
|
||||
|
||||
if (s->count == 0)
|
||||
return(s);
|
||||
|
||||
s->sums = (struct sum_buf *)malloc(sizeof(s->sums[0])*s->count);
|
||||
if (!s->sums) out_of_memory("receive_sums");
|
||||
|
||||
for (i=0;i<s->count;i++) {
|
||||
s->sums[i].sum1 = read_int(f);
|
||||
read_buf(f,s->sums[i].sum2,SUM_LENGTH);
|
||||
|
||||
s->sums[i].offset = offset;
|
||||
s->sums[i].i = i;
|
||||
|
||||
if (i == s->count-1 && s->remainder != 0) {
|
||||
s->sums[i].len = s->remainder;
|
||||
} else {
|
||||
s->sums[i].len = s->n;
|
||||
}
|
||||
offset += s->sums[i].len;
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"chunk[%d] len=%d offset=%d sum1=%08x\n",
|
||||
i,s->sums[i].len,(int)s->sums[i].offset,s->sums[i].sum1);
|
||||
}
|
||||
|
||||
s->flength = offset;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static void set_perms(char *fname,struct file_struct *file,struct stat *st,
|
||||
int report)
|
||||
{
|
||||
int updated = 0;
|
||||
struct stat st2;
|
||||
|
||||
if (dry_run) return;
|
||||
|
||||
if (!st) {
|
||||
if (stat(fname,&st2) != 0) {
|
||||
fprintf(stderr,"stat %s : %s\n",fname,strerror(errno));
|
||||
return;
|
||||
}
|
||||
st = &st2;
|
||||
}
|
||||
|
||||
if (preserve_times && st->st_mtime != file->modtime) {
|
||||
updated = 1;
|
||||
if (set_modtime(fname,file->modtime) != 0) {
|
||||
fprintf(stderr,"failed to set times on %s : %s\n",
|
||||
fname,strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHMOD
|
||||
if (preserve_perms && st->st_mode != file->mode) {
|
||||
updated = 1;
|
||||
if (chmod(fname,file->mode) != 0) {
|
||||
fprintf(stderr,"failed to set permissions on %s : %s\n",
|
||||
fname,strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((preserve_uid && st->st_uid != file->uid) ||
|
||||
(preserve_gid && st->st_gid != file->gid)) {
|
||||
updated = 1;
|
||||
if (chown(fname,
|
||||
preserve_uid?file->uid:-1,
|
||||
preserve_gid?file->gid:-1) != 0) {
|
||||
if (verbose>1 || preserve_uid)
|
||||
fprintf(stderr,"chown %s : %s\n",fname,strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose > 1 && report) {
|
||||
if (updated)
|
||||
fprintf(am_server?stderr:stdout,"%s\n",fname);
|
||||
else
|
||||
fprintf(am_server?stderr:stdout,"%s is uptodate\n",fname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
char *buf;
|
||||
struct sum_struct *s;
|
||||
char sum[SUM_LENGTH];
|
||||
int statret;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"recv_generator(%s)\n",fname);
|
||||
|
||||
statret = lstat(fname,&st);
|
||||
|
||||
#if SUPPORT_LINKS
|
||||
if (preserve_links && S_ISLNK(flist->files[i].mode)) {
|
||||
char lnk[MAXPATHLEN];
|
||||
int l;
|
||||
if (statret == 0) {
|
||||
l = readlink(fname,lnk,MAXPATHLEN-1);
|
||||
if (l > 0) {
|
||||
lnk[l] = 0;
|
||||
if (strcmp(lnk,flist->files[i].link) == 0) {
|
||||
if (verbose > 1)
|
||||
fprintf(am_server?stderr:stdout,"%s is uptodate\n",fname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dry_run) unlink(fname);
|
||||
if (!dry_run && symlink(flist->files[i].link,fname) != 0) {
|
||||
fprintf(stderr,"link %s -> %s : %s\n",
|
||||
fname,flist->files[i].link,strerror(errno));
|
||||
} else {
|
||||
if (verbose)
|
||||
fprintf(am_server?stderr:stdout,"%s -> %s\n",fname,flist->files[i].link);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MKNOD
|
||||
if (preserve_devices &&
|
||||
(S_ISCHR(flist->files[i].mode) || S_ISBLK(flist->files[i].mode))) {
|
||||
if (statret != 0 ||
|
||||
st.st_mode != flist->files[i].mode ||
|
||||
st.st_rdev != flist->files[i].dev) {
|
||||
if (!dry_run) unlink(fname);
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"mknod(%s,0%o,0x%x)\n",
|
||||
fname,(int)flist->files[i].mode,(int)flist->files[i].dev);
|
||||
if (!dry_run &&
|
||||
mknod(fname,flist->files[i].mode,flist->files[i].dev) != 0) {
|
||||
fprintf(stderr,"mknod %s : %s\n",fname,strerror(errno));
|
||||
} else {
|
||||
set_perms(fname,&flist->files[i],NULL,0);
|
||||
if (verbose)
|
||||
fprintf(am_server?stderr:stdout,"%s\n",fname);
|
||||
}
|
||||
} else {
|
||||
set_perms(fname,&flist->files[i],&st,1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!S_ISREG(flist->files[i].mode)) {
|
||||
fprintf(stderr,"skipping non-regular file %s\n",fname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (statret == -1) {
|
||||
if (errno == ENOENT) {
|
||||
write_int(f_out,i);
|
||||
if (!dry_run) send_sums(NULL,f_out);
|
||||
} else {
|
||||
if (verbose > 1)
|
||||
fprintf(stderr,"recv_generator failed to open %s\n",fname);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
fprintf(stderr,"%s : not a regular file\n",fname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_only && st.st_mtime >= flist->files[i].modtime) {
|
||||
if (verbose > 1)
|
||||
fprintf(stderr,"%s is newer\n",fname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (always_checksum && S_ISREG(st.st_mode)) {
|
||||
file_checksum(fname,sum,st.st_size);
|
||||
}
|
||||
|
||||
if (st.st_size == flist->files[i].length &&
|
||||
((!ignore_times && st.st_mtime == flist->files[i].modtime) ||
|
||||
(always_checksum && S_ISREG(st.st_mode) &&
|
||||
memcmp(sum,flist->files[i].sum,SUM_LENGTH) == 0))) {
|
||||
set_perms(fname,&flist->files[i],&st,1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dry_run) {
|
||||
write_int(f_out,i);
|
||||
return;
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
fd = open(fname,O_RDONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
fprintf(stderr,"failed to open %s : %s\n",fname,strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (st.st_size > 0) {
|
||||
buf = map_file(fd,st.st_size);
|
||||
if (!buf) {
|
||||
fprintf(stderr,"mmap : %s\n",strerror(errno));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"mapped %s of size %d\n",fname,(int)st.st_size);
|
||||
|
||||
s = generate_sums(buf,st.st_size,block_size);
|
||||
|
||||
write_int(f_out,i);
|
||||
send_sums(s,f_out);
|
||||
write_flush(f_out);
|
||||
|
||||
close(fd);
|
||||
unmap_file(buf,st.st_size);
|
||||
|
||||
free_sums(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void receive_data(int f_in,char *buf,int fd,char *fname)
|
||||
{
|
||||
int i,n,remainder,len,count;
|
||||
off_t offset = 0;
|
||||
off_t offset2;
|
||||
|
||||
count = read_int(f_in);
|
||||
n = read_int(f_in);
|
||||
remainder = read_int(f_in);
|
||||
|
||||
for (i=read_int(f_in); i != 0; i=read_int(f_in)) {
|
||||
if (i > 0) {
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"data recv %d at %d\n",i,(int)offset);
|
||||
|
||||
if (read_write(f_in,fd,i) != i) {
|
||||
fprintf(stderr,"write failed on %s : %s\n",fname,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
offset += i;
|
||||
} else {
|
||||
i = -(i+1);
|
||||
offset2 = i*n;
|
||||
len = n;
|
||||
if (i == count-1 && remainder != 0)
|
||||
len = remainder;
|
||||
|
||||
if (verbose > 3)
|
||||
fprintf(stderr,"chunk[%d] of size %d at %d offset=%d\n",
|
||||
i,len,(int)offset2,(int)offset);
|
||||
|
||||
if (write(fd,buf+offset2,len) != len) {
|
||||
fprintf(stderr,"write failed on %s : %s\n",fname,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void delete_one(struct file_struct *f)
|
||||
{
|
||||
if (!S_ISDIR(f->mode)) {
|
||||
if (!dry_run && unlink(f->name) != 0) {
|
||||
fprintf(stderr,"unlink %s : %s\n",f->name,strerror(errno));
|
||||
} else if (verbose) {
|
||||
fprintf(stderr,"deleting %s\n",f->name);
|
||||
}
|
||||
} else {
|
||||
if (!dry_run && rmdir(f->name) != 0) {
|
||||
if (errno != ENOTEMPTY)
|
||||
fprintf(stderr,"rmdir %s : %s\n",f->name,strerror(errno));
|
||||
} else if (verbose) {
|
||||
fprintf(stderr,"deleting directory %s\n",f->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void delete_files(struct file_list *flist)
|
||||
{
|
||||
struct file_list *local_file_list;
|
||||
char *dot=".";
|
||||
int i;
|
||||
|
||||
if (!(local_file_list = send_file_list(-1,recurse,1,&dot)))
|
||||
return;
|
||||
|
||||
for (i=local_file_list->count;i>=0;i--) {
|
||||
if (!local_file_list->files[i].name) continue;
|
||||
if (-1 == flist_find(flist,&local_file_list->files[i])) {
|
||||
delete_one(&local_file_list->files[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *cleanup_fname = NULL;
|
||||
|
||||
int sig_int(void)
|
||||
{
|
||||
if (cleanup_fname)
|
||||
unlink(cleanup_fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int recv_files(int f_in,struct file_list *flist,char *local_name)
|
||||
{
|
||||
int fd1,fd2;
|
||||
struct stat st;
|
||||
char *fname;
|
||||
char fnametmp[MAXPATHLEN];
|
||||
char *buf;
|
||||
int i;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"recv_files(%d) starting\n",flist->count);
|
||||
|
||||
if (recurse && delete_mode && !local_name && flist->count>0) {
|
||||
delete_files(flist);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
i = read_int(f_in);
|
||||
if (i == -1) break;
|
||||
|
||||
fname = flist->files[i].name;
|
||||
|
||||
if (local_name)
|
||||
fname = local_name;
|
||||
|
||||
if (dry_run) {
|
||||
if (!am_server && verbose)
|
||||
printf("%s\n",fname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"recv_files(%s)\n",fname);
|
||||
|
||||
/* open the file */
|
||||
if ((fd1 = open(fname,O_RDONLY)) == -1 &&
|
||||
(fd1 = open(fname,O_RDONLY|O_CREAT,flist->files[i].mode)) == -1) {
|
||||
fprintf(stderr,"recv_files failed to open %s\n",fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fstat(fd1,&st) != 0) {
|
||||
fprintf(stderr,"fstat %s : %s\n",fname,strerror(errno));
|
||||
close(fd1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
fprintf(stderr,"%s : not a regular file\n",fname);
|
||||
close(fd1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (st.st_size > 0) {
|
||||
buf = map_file(fd1,st.st_size);
|
||||
if (!buf) {
|
||||
fprintf(stderr,"map_file failed\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"mapped %s of size %d\n",fname,(int)st.st_size);
|
||||
|
||||
/* open tmp file */
|
||||
sprintf(fnametmp,"%s.XXXXXX",fname);
|
||||
if (NULL == mktemp(fnametmp)) {
|
||||
fprintf(stderr,"mktemp %s failed\n",fnametmp);
|
||||
return -1;
|
||||
}
|
||||
fd2 = open(fnametmp,O_WRONLY|O_CREAT,st.st_mode);
|
||||
if (fd2 == -1) {
|
||||
fprintf(stderr,"open %s : %s\n",fnametmp,strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cleanup_fname = fnametmp;
|
||||
|
||||
if (!am_server && verbose)
|
||||
printf("%s\n",fname);
|
||||
|
||||
/* recv file data */
|
||||
receive_data(f_in,buf,fd2,fname);
|
||||
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"renaming %s to %s\n",fnametmp,fname);
|
||||
|
||||
if (make_backups) {
|
||||
char fnamebak[MAXPATHLEN];
|
||||
sprintf(fnamebak,"%s%s",fname,backup_suffix);
|
||||
if (rename(fname,fnamebak) != 0) {
|
||||
fprintf(stderr,"rename %s %s : %s\n",fname,fnamebak,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* move tmp file over real file */
|
||||
if (rename(fnametmp,fname) != 0) {
|
||||
fprintf(stderr,"rename %s -> %s : %s\n",
|
||||
fnametmp,fname,strerror(errno));
|
||||
}
|
||||
|
||||
cleanup_fname = NULL;
|
||||
|
||||
unmap_file(buf,st.st_size);
|
||||
|
||||
set_perms(fname,&flist->files[i],NULL,0);
|
||||
}
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"recv_files finished\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
off_t send_files(struct file_list *flist,int f_out,int f_in)
|
||||
{
|
||||
int fd;
|
||||
struct sum_struct *s;
|
||||
char *buf;
|
||||
struct stat st;
|
||||
char fname[MAXPATHLEN];
|
||||
off_t total=0;
|
||||
int i;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"send_files starting\n");
|
||||
|
||||
while (1)
|
||||
{
|
||||
i = read_int(f_in);
|
||||
if (i == -1) break;
|
||||
|
||||
fname[0] = 0;
|
||||
if (flist->files[i].dir) {
|
||||
strcpy(fname,flist->files[i].dir);
|
||||
strcat(fname,"/");
|
||||
}
|
||||
strcat(fname,flist->files[i].name);
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"send_files(%d,%s)\n",i,fname);
|
||||
|
||||
if (dry_run) {
|
||||
if (!am_server && verbose)
|
||||
printf("%s\n",fname);
|
||||
write_int(f_out,i);
|
||||
continue;
|
||||
}
|
||||
|
||||
s = receive_sums(f_in);
|
||||
if (!s) {
|
||||
fprintf(stderr,"receive_sums failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open(fname,O_RDONLY);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr,"send_files failed to open %s: %s\n",
|
||||
fname,strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* map the local file */
|
||||
if (fstat(fd,&st) != 0) {
|
||||
fprintf(stderr,"fstat failed : %s\n",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (st.st_size > 0) {
|
||||
buf = map_file(fd,st.st_size);
|
||||
if (!buf) {
|
||||
fprintf(stderr,"map_file failed : %s\n",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"send_files mapped %s of size %d\n",
|
||||
fname,(int)st.st_size);
|
||||
|
||||
write_int(f_out,i);
|
||||
|
||||
write_int(f_out,s->count);
|
||||
write_int(f_out,s->n);
|
||||
write_int(f_out,s->remainder);
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"calling match_sums %s\n",fname);
|
||||
|
||||
if (!am_server && verbose)
|
||||
printf("%s\n",fname);
|
||||
|
||||
match_sums(f_out,s,buf,st.st_size);
|
||||
write_flush(f_out);
|
||||
|
||||
unmap_file(buf,st.st_size);
|
||||
close(fd);
|
||||
|
||||
free_sums(s);
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"sender finished %s\n",fname);
|
||||
|
||||
total += st.st_size;
|
||||
}
|
||||
|
||||
match_report();
|
||||
|
||||
write_int(f_out,-1);
|
||||
write_flush(f_out);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void generate_files(int f,struct file_list *flist,char *local_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"generator starting pid=%d count=%d\n",
|
||||
(int)getpid(),flist->count);
|
||||
|
||||
for (i = 0; i < flist->count; i++) {
|
||||
if (!flist->files[i].name) continue;
|
||||
if (S_ISDIR(flist->files[i].mode)) {
|
||||
if (dry_run) continue;
|
||||
if (mkdir(flist->files[i].name,flist->files[i].mode) != 0 &&
|
||||
errno != EEXIST) {
|
||||
fprintf(stderr,"mkdir %s : %s\n",
|
||||
flist->files[i].name,strerror(errno));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
recv_generator(local_name?local_name:flist->files[i].name,
|
||||
flist,i,f);
|
||||
}
|
||||
write_int(f,-1);
|
||||
write_flush(f);
|
||||
if (verbose > 2)
|
||||
fprintf(stderr,"generator wrote %d\n",write_total());
|
||||
}
|
||||
228
rsync.h
Normal file
228
rsync.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#define BLOCK_SIZE 700
|
||||
#define RSYNC_RSH_ENV "RSYNC_RSH"
|
||||
#define RSYNC_RSH "rsh"
|
||||
#define RSYNC_NAME "rsync"
|
||||
#define BACKUP_SUFFIX "~"
|
||||
|
||||
/* update this if you make incompatible changes */
|
||||
#define PROTOCOL_VERSION 9
|
||||
|
||||
/* block size to write files in */
|
||||
#define WRITE_BLOCK_SIZE (32*1024)
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_COMPAT_H
|
||||
#include <compat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifdef TIME_WITH_SYS_TIME
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#else
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#else
|
||||
#ifdef HAVE_SYS_FCNTL_H
|
||||
#include <sys/fcntl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#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>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#ifdef HAVE_UTIME_H
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FNMATCH
|
||||
#include <fnmatch.h>
|
||||
#else
|
||||
#include "lib/fnmatch.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
#include <getopt.h>
|
||||
#else
|
||||
#include "lib/getopt.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(mode) (((mode) & S_IFLNK) == S_IFLNK)
|
||||
#endif
|
||||
|
||||
#ifndef uchar
|
||||
#define uchar unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef int32
|
||||
#if (SIZEOF_INT == 4)
|
||||
#define int32 int
|
||||
#elif (SIZEOF_LONG == 4)
|
||||
#define int32 long
|
||||
#elif (SIZEOF_SHORT == 4)
|
||||
#define int32 short
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef uint32
|
||||
#define uint32 unsigned int32
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
/* the length of the md4 checksum */
|
||||
#define SUM_LENGTH 16
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#define MAXPATHLEN 1024
|
||||
#endif
|
||||
|
||||
struct file_struct {
|
||||
time_t modtime;
|
||||
off_t length;
|
||||
mode_t mode;
|
||||
dev_t dev;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
char *name;
|
||||
char *dir;
|
||||
char *link;
|
||||
char sum[SUM_LENGTH];
|
||||
};
|
||||
|
||||
struct file_list {
|
||||
int count;
|
||||
int malloced;
|
||||
struct file_struct *files;
|
||||
};
|
||||
|
||||
struct sum_buf {
|
||||
off_t offset; /* offset in file of this chunk */
|
||||
int len; /* length of chunk of file */
|
||||
int i; /* index of this chunk */
|
||||
uint32 sum1; /* simple checksum */
|
||||
char sum2[SUM_LENGTH]; /* md4 checksum */
|
||||
};
|
||||
|
||||
struct sum_struct {
|
||||
off_t flength; /* total file length */
|
||||
int count; /* how many chunks */
|
||||
int remainder; /* flength % block_length */
|
||||
int n; /* block_length */
|
||||
struct sum_buf *sums; /* points to info for each chunk */
|
||||
};
|
||||
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "version.h"
|
||||
#include "proto.h"
|
||||
#include "md4.h"
|
||||
|
||||
#if !HAVE_STRERROR
|
||||
extern char *sys_errlist[];
|
||||
#define strerror(i) sys_errlist[i]
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRCHR
|
||||
# define strchr index
|
||||
# define strrchr rindex
|
||||
#endif
|
||||
|
||||
#if HAVE_DIRENT_H
|
||||
# include <dirent.h>
|
||||
#else
|
||||
# define dirent direct
|
||||
# if HAVE_SYS_NDIR_H
|
||||
# include <sys/ndir.h>
|
||||
# endif
|
||||
# if HAVE_SYS_DIR_H
|
||||
# include <sys/dir.h>
|
||||
# endif
|
||||
# if HAVE_NDIR_H
|
||||
# include <ndir.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_ERRNO_DECL
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_BCOPY
|
||||
#define bcopy(src,dest,n) memcpy(dest,src,n)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_BZERO
|
||||
#define bzero(buf,n) memset(buf,0,n)
|
||||
#endif
|
||||
|
||||
#define SUPPORT_LINKS (HAVE_READLINK && defined(S_ISLNK))
|
||||
|
||||
#if !SUPPORT_LINKS
|
||||
#define lstat stat
|
||||
#endif
|
||||
310
tech_report.tex
Normal file
310
tech_report.tex
Normal file
@@ -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 dial up 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 ftp://samba.anu.edu.au/pub/rsync.
|
||||
|
||||
\end{document}
|
||||
229
util.c
Normal file
229
util.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Utilities used in rsync
|
||||
|
||||
tridge, June 1996
|
||||
*/
|
||||
#include "rsync.h"
|
||||
|
||||
static int total_written = 0;
|
||||
static int total_read = 0;
|
||||
|
||||
extern int verbose;
|
||||
|
||||
int write_total(void)
|
||||
{
|
||||
return total_written;
|
||||
}
|
||||
|
||||
int read_total(void)
|
||||
{
|
||||
return total_read;
|
||||
}
|
||||
|
||||
void write_int(int f,int x)
|
||||
{
|
||||
char b[4];
|
||||
SIVAL(b,0,x);
|
||||
if (write(f,b,4) != 4) {
|
||||
fprintf(stderr,"write_int failed : %s\n",strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
total_written += 4;
|
||||
}
|
||||
|
||||
void write_buf(int f,char *buf,int len)
|
||||
{
|
||||
if (write(f,buf,len) != len) {
|
||||
fprintf(stderr,"write_buf failed : %s\n",strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
total_written += len;
|
||||
}
|
||||
|
||||
void write_flush(int f)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
int readfd(int fd,char *buffer,int N)
|
||||
{
|
||||
int ret;
|
||||
int total=0;
|
||||
|
||||
while (total < N)
|
||||
{
|
||||
ret = read(fd,buffer + total,N - total);
|
||||
|
||||
if (ret <= 0)
|
||||
return total;
|
||||
total += ret;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
int read_int(int f)
|
||||
{
|
||||
char b[4];
|
||||
if (readfd(f,b,4) != 4) {
|
||||
if (verbose > 1)
|
||||
fprintf(stderr,"Error reading %d bytes : %s\n",4,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
total_read += 4;
|
||||
return IVAL(b,0);
|
||||
}
|
||||
|
||||
void read_buf(int f,char *buf,int len)
|
||||
{
|
||||
if (readfd(f,buf,len) != len) {
|
||||
if (verbose > 1)
|
||||
fprintf(stderr,"Error reading %d bytes : %s\n",len,strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
total_read += len;
|
||||
}
|
||||
|
||||
|
||||
char *map_file(int fd,off_t len)
|
||||
{
|
||||
char *ret = (char *)mmap(NULL,len,PROT_READ,MAP_SHARED,fd,0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unmap_file(char *buf,off_t len)
|
||||
{
|
||||
if (len > 0)
|
||||
munmap(buf,len);
|
||||
}
|
||||
|
||||
|
||||
int read_write(int fd_in,int fd_out,int size)
|
||||
{
|
||||
static char *buf=NULL;
|
||||
static int bufsize = WRITE_BLOCK_SIZE;
|
||||
int total=0;
|
||||
|
||||
if (!buf) {
|
||||
buf = (char *)malloc(bufsize);
|
||||
if (!buf) out_of_memory("read_write");
|
||||
}
|
||||
|
||||
while (total < size) {
|
||||
int n = MIN(size-total,bufsize);
|
||||
read_buf(fd_in,buf,n);
|
||||
if (write(fd_out,buf,n) != n)
|
||||
return total;
|
||||
total += n;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/* this is taken from CVS */
|
||||
int piped_child(char **command,int *f_in,int *f_out)
|
||||
{
|
||||
int pid;
|
||||
int to_child_pipe[2];
|
||||
int from_child_pipe[2];
|
||||
|
||||
if (pipe(to_child_pipe) < 0 ||
|
||||
pipe(from_child_pipe) < 0) {
|
||||
fprintf(stderr,"pipe: %s\n",strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
fprintf(stderr,"fork: %s\n",strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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) {
|
||||
fprintf(stderr,"Failed to dup/close : %s\n",strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
execvp(command[0], command);
|
||||
fprintf(stderr,"Failed to exec %s : %s\n",
|
||||
command[0],strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (close(from_child_pipe[1]) < 0 ||
|
||||
close(to_child_pipe[0]) < 0) {
|
||||
fprintf(stderr,"Failed to close : %s\n",strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
*f_in = from_child_pipe[0];
|
||||
*f_out = to_child_pipe[1];
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
void out_of_memory(char *str)
|
||||
{
|
||||
fprintf(stderr,"out of memory in %s\n",str);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
char *strdup(char *s)
|
||||
{
|
||||
int l = strlen(s) + 1;
|
||||
char *ret = (char *)malloc(l);
|
||||
if (ret)
|
||||
strcpy(ret,s);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int set_modtime(char *fname,time_t modtime)
|
||||
{
|
||||
#ifdef HAVE_UTIME_H
|
||||
struct utimbuf tbuf;
|
||||
tbuf.actime = time(NULL);
|
||||
tbuf.modtime = modtime;
|
||||
return utime(fname,&tbuf);
|
||||
#elif defined(HAVE_UTIME)
|
||||
time_t t[2];
|
||||
t[0] = time(NULL);
|
||||
t[1] = modtime;
|
||||
return utime(fname,t);
|
||||
#else
|
||||
struct timeval t[2];
|
||||
t[0].tv_sec = time(NULL);
|
||||
t[0].tv_usec = 0;
|
||||
t[1].tv_sec = modtime;
|
||||
t[1].tv_usec = 0;
|
||||
return utimes(fname,t);
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user